forked from cybrespace/pinafore
		
	add spoiler text support
This commit is contained in:
		
							parent
							
								
									18ad6ab1ab
								
							
						
					
					
						commit
						6790cfd187
					
				
					 8 changed files with 90 additions and 33 deletions
				
			
		| 
						 | 
				
			
			@ -5,20 +5,20 @@
 | 
			
		|||
    <button type="button"
 | 
			
		||||
            class="play-video-button"
 | 
			
		||||
            aria-label="Play video"
 | 
			
		||||
            on:click="onClickPlayVideoButton(media, getSmallWidth(media), getSmallHeight(media))">
 | 
			
		||||
            on:click="onClickPlayVideoButton(media, getSmallWidth(media), getSmallHeight(media), media.description)">
 | 
			
		||||
      <div class="svg-wrapper">
 | 
			
		||||
        <svg>
 | 
			
		||||
          <use xlink:href="#fa-play-circle" />
 | 
			
		||||
        </svg>
 | 
			
		||||
      </div>
 | 
			
		||||
      <img aria-hidden="true"
 | 
			
		||||
           alt=""
 | 
			
		||||
      <img alt="{{media.description || ''}}"
 | 
			
		||||
           src="{{media.preview_url}}"
 | 
			
		||||
           width="{{getSmallWidth(media)}}"
 | 
			
		||||
           height="{{getSmallHeight(media)}}" />
 | 
			
		||||
    </button>
 | 
			
		||||
    {{elseif media.type === 'gifv'}}
 | 
			
		||||
    <video
 | 
			
		||||
           aria-label="Animated GIF: {{media.description || ''}}"
 | 
			
		||||
           poster="{{media.preview_url}}"
 | 
			
		||||
           src="{{media.url}}"
 | 
			
		||||
           width="{{getSmallWidth(media)}}"
 | 
			
		||||
| 
						 | 
				
			
			@ -29,8 +29,8 @@
 | 
			
		|||
           playsinline
 | 
			
		||||
    />
 | 
			
		||||
    {{else}}
 | 
			
		||||
      <img src="{{media.preview_url}}"
 | 
			
		||||
           alt="{{media.description || ''}}"
 | 
			
		||||
      <img alt="{{media.description || ''}}"
 | 
			
		||||
           src="{{media.preview_url}}"
 | 
			
		||||
           width="{{getSmallWidth(media)}}"
 | 
			
		||||
           height="{{getSmallHeight(media)}}"/>
 | 
			
		||||
    {{/if}}
 | 
			
		||||
| 
						 | 
				
			
			@ -112,8 +112,8 @@
 | 
			
		|||
      minMediaWidth: (mediaAttachments) => Math.min.apply(Math, mediaAttachments.map(media => media.meta && media.meta.small && typeof media.meta.small.width === 'number' ?  media.meta.small.width : DEFAULT_MEDIA_WIDTH))
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      async onClickPlayVideoButton(media, width, height) {
 | 
			
		||||
        showVideoDialog(media.preview_url, media.url, width, height)
 | 
			
		||||
      async onClickPlayVideoButton(media, width, height, description) {
 | 
			
		||||
        showVideoDialog(media.preview_url, media.url, width, height, description)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
<article class="status-article" tabindex="0" aria-posinset="{{index}}" aria-setsize="{{length}}">
 | 
			
		||||
<article class="status-article" tabindex="0" aria-posinset="{{index}}" aria-setsize="{{length}}" on:recalculateHeight>
 | 
			
		||||
  {{#if status.reblog}}
 | 
			
		||||
    <div class="status-boosted">
 | 
			
		||||
      <svg>
 | 
			
		||||
| 
						 | 
				
			
			@ -16,14 +16,26 @@
 | 
			
		|||
      {{originalAccount.display_name || originalAccount.username}}
 | 
			
		||||
    </a>
 | 
			
		||||
    <span class="status-author-handle">
 | 
			
		||||
      @{{originalAccount.acct}}
 | 
			
		||||
      {{'@' + originalAccount.acct}}
 | 
			
		||||
    </span>
 | 
			
		||||
    <a class="status-author-date" rel="noopener" target="_blank" href="{{originalStatus.uri}}">
 | 
			
		||||
      <time datetime={{createdAtDate}} title="{{relativeDate}}">{{relativeDate}}</time>
 | 
			
		||||
    </a>
 | 
			
		||||
  </div>
 | 
			
		||||
  <Avatar account={{originalAccount}} className="status-sidebar"/>
 | 
			
		||||
  <div class="status-content">{{{status.content}}}</div>
 | 
			
		||||
  {{#if status.spoiler_text}}
 | 
			
		||||
    <div class="status-spoiler">{{status.spoiler_text}}</div>
 | 
			
		||||
  {{/if}}
 | 
			
		||||
  {{#if status.spoiler_text}}
 | 
			
		||||
    <div class="status-spoiler-button">
 | 
			
		||||
      <button type="button" on:click="onClickSpoilerButton()">Show more</button>
 | 
			
		||||
    </div>
 | 
			
		||||
  {{/if}}
 | 
			
		||||
  {{#if !status.spoiler_text || spoilerShown}}
 | 
			
		||||
    <div class="status-content">
 | 
			
		||||
      {{{status.content}}}
 | 
			
		||||
    </div>
 | 
			
		||||
  {{/if}}
 | 
			
		||||
  <Toolbar :status />
 | 
			
		||||
  <Media mediaAttachments="{{originalMediaAttachments}}" />
 | 
			
		||||
</article>
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +49,8 @@
 | 
			
		|||
    grid-template-areas:
 | 
			
		||||
        ".............. status-boosted"
 | 
			
		||||
        "status-sidebar status-author"
 | 
			
		||||
        "status-sidebar status-spoiler"
 | 
			
		||||
        "status-sidebar status-spoiler-button"
 | 
			
		||||
        "status-sidebar status-content"
 | 
			
		||||
        ".............. status-toolbar"
 | 
			
		||||
        "status-media   status-media";
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +64,25 @@
 | 
			
		|||
    margin: 0 10px 0 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .status-spoiler {
 | 
			
		||||
    grid-area: status-spoiler;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    white-space: pre-wrap;
 | 
			
		||||
    font-size: 1.1em;
 | 
			
		||||
    margin: 5px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .status-spoiler-button {
 | 
			
		||||
    grid-area: status-spoiler-button;
 | 
			
		||||
    margin: 5px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .status-spoiler-button button {
 | 
			
		||||
    padding: 5px 10px;
 | 
			
		||||
    font-size: 1.1em;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .status-author {
 | 
			
		||||
    grid-area: status-author;
 | 
			
		||||
    display: flex;
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +127,7 @@
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  .status-content {
 | 
			
		||||
    margin: 10px 10px 20px 5px;
 | 
			
		||||
    margin: 10px 10px 10px 5px;
 | 
			
		||||
    grid-area: status-content;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +193,12 @@
 | 
			
		|||
      originalStatus: (status) => status.reblog ? status.reblog : status,
 | 
			
		||||
      originalAccount: (originalStatus) => originalStatus.account,
 | 
			
		||||
      originalMediaAttachments: (originalStatus) => originalStatus.media_attachments,
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      onClickSpoilerButton() {
 | 
			
		||||
        this.set({spoilerShown: !this.get('spoilerShown')})
 | 
			
		||||
        this.fire('recalculateHeight')
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
<Status status="{{virtualProps}}" index="{{virtualIndex}}" length="{{virtualLength}}"/>
 | 
			
		||||
<Status status="{{virtualProps}}" index="{{virtualIndex}}" length="{{virtualLength}}" on:recalculateHeight />
 | 
			
		||||
<script>
 | 
			
		||||
  import Status from './Status.html'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
         src="{{src}}"
 | 
			
		||||
         width="{{width}}"
 | 
			
		||||
         height="{{height}}"
 | 
			
		||||
         aria-label="Video: {{description || ''}}"
 | 
			
		||||
         controls
 | 
			
		||||
  />
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,9 @@
 | 
			
		|||
<div class="virtual-list-item {{shown ? 'shown' : ''}}"
 | 
			
		||||
     virtual-list-key="{{key}}"
 | 
			
		||||
     ref:node
 | 
			
		||||
     style="transform: translateY({{offset}}px
 | 
			
		||||
     );"
 | 
			
		||||
>
 | 
			
		||||
  <:Component {component} virtualProps="{{props}}" virtualIndex="{{index}}" virtualLength="{{$numItems}}"/>
 | 
			
		||||
     style="transform: translateY({{offset}}px);" >
 | 
			
		||||
  <:Component {component} virtualProps="{{props}}" virtualIndex="{{index}}" virtualLength="{{$numItems}}"
 | 
			
		||||
              on:recalculateHeight="doRecalculateHeight()"/>
 | 
			
		||||
</div>
 | 
			
		||||
<style>
 | 
			
		||||
  .virtual-list-item {
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +22,8 @@
 | 
			
		|||
  import { virtualListStore } from '../_utils/virtualListStore'
 | 
			
		||||
  import { AsyncLayout } from '../_utils/AsyncLayout'
 | 
			
		||||
 | 
			
		||||
  const asyncLayout = new AsyncLayout(node => node.getAttribute('virtual-list-key'))
 | 
			
		||||
  const keyGetter = node => node.getAttribute('virtual-list-key')
 | 
			
		||||
  const asyncLayout = new AsyncLayout(keyGetter)
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    oncreate() {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +40,17 @@
 | 
			
		|||
    store: () => virtualListStore,
 | 
			
		||||
    computed: {
 | 
			
		||||
      'shown': ($itemHeights, key) => $itemHeights[key] > 0
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      doRecalculateHeight() {
 | 
			
		||||
        let tempAsyncLayout = new AsyncLayout(keyGetter)
 | 
			
		||||
        let key = this.get('key')
 | 
			
		||||
        tempAsyncLayout.observe(key, this.refs.node, (rect) => {
 | 
			
		||||
          tempAsyncLayout.disconnect()
 | 
			
		||||
          // update all item heights in one microtask batch for better perf
 | 
			
		||||
          this.store.batchUpdate('itemHeights', key, rect.height)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +26,11 @@ class AsyncLayout {
 | 
			
		|||
    this._intersectionObserver.unobserve(node)
 | 
			
		||||
    delete this._onIntersectionCallbacks[key]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  disconnect() {
 | 
			
		||||
    this._intersectionObserver.disconnect()
 | 
			
		||||
    this._intersectionObserver = null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { AsyncLayout }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
import VideoDialog from '../_components/VideoDialog.html'
 | 
			
		||||
 | 
			
		||||
export function showVideoDialog(poster, src, width, height) {
 | 
			
		||||
export function showVideoDialog(poster, src, width, height, description) {
 | 
			
		||||
  let dialog = document.createElement('dialog')
 | 
			
		||||
  dialog.classList.add('video-dialog')
 | 
			
		||||
  dialog.setAttribute('aria-label', 'Video dialog')
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +12,8 @@ export function showVideoDialog(poster, src, width, height) {
 | 
			
		|||
      src: src,
 | 
			
		||||
      dialog: dialog,
 | 
			
		||||
      width: width,
 | 
			
		||||
      height: height
 | 
			
		||||
      height: height,
 | 
			
		||||
      description: description
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  videoDialog.showModal()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,10 +9,10 @@
 | 
			
		|||
    {{#if instanceUserAccount}}
 | 
			
		||||
      <h2>Logged in as:</h2>
 | 
			
		||||
      <div class="acct-current-user">
 | 
			
		||||
        <img alt="Profile picture for @{{instanceUserAccount.acct}}"
 | 
			
		||||
        <img alt="Profile picture for {{'@' + instanceUserAccount.acct}}"
 | 
			
		||||
             class="acct-avatar" src="{{instanceUserAccount.avatar}}" />
 | 
			
		||||
        <a class="acct-handle" rel="noopener" target="_blank"
 | 
			
		||||
           href="{{instanceUserAccount.url}}">@{{instanceUserAccount.acct}}</a>
 | 
			
		||||
           href="{{instanceUserAccount.url}}">{{'@' + instanceUserAccount.acct}}</a>
 | 
			
		||||
        <span class="acct-display-name">{{instanceUserAccount.display_name}}</span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <h2>Theme:</h2>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue