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"
 | 
					    <button type="button"
 | 
				
			||||||
            class="play-video-button"
 | 
					            class="play-video-button"
 | 
				
			||||||
            aria-label="Play video"
 | 
					            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">
 | 
					      <div class="svg-wrapper">
 | 
				
			||||||
        <svg>
 | 
					        <svg>
 | 
				
			||||||
          <use xlink:href="#fa-play-circle" />
 | 
					          <use xlink:href="#fa-play-circle" />
 | 
				
			||||||
        </svg>
 | 
					        </svg>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <img aria-hidden="true"
 | 
					      <img alt="{{media.description || ''}}"
 | 
				
			||||||
           alt=""
 | 
					 | 
				
			||||||
           src="{{media.preview_url}}"
 | 
					           src="{{media.preview_url}}"
 | 
				
			||||||
           width="{{getSmallWidth(media)}}"
 | 
					           width="{{getSmallWidth(media)}}"
 | 
				
			||||||
           height="{{getSmallHeight(media)}}" />
 | 
					           height="{{getSmallHeight(media)}}" />
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
    {{elseif media.type === 'gifv'}}
 | 
					    {{elseif media.type === 'gifv'}}
 | 
				
			||||||
    <video
 | 
					    <video
 | 
				
			||||||
 | 
					           aria-label="Animated GIF: {{media.description || ''}}"
 | 
				
			||||||
           poster="{{media.preview_url}}"
 | 
					           poster="{{media.preview_url}}"
 | 
				
			||||||
           src="{{media.url}}"
 | 
					           src="{{media.url}}"
 | 
				
			||||||
           width="{{getSmallWidth(media)}}"
 | 
					           width="{{getSmallWidth(media)}}"
 | 
				
			||||||
| 
						 | 
					@ -29,8 +29,8 @@
 | 
				
			||||||
           playsinline
 | 
					           playsinline
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
    {{else}}
 | 
					    {{else}}
 | 
				
			||||||
      <img src="{{media.preview_url}}"
 | 
					      <img alt="{{media.description || ''}}"
 | 
				
			||||||
           alt="{{media.description || ''}}"
 | 
					           src="{{media.preview_url}}"
 | 
				
			||||||
           width="{{getSmallWidth(media)}}"
 | 
					           width="{{getSmallWidth(media)}}"
 | 
				
			||||||
           height="{{getSmallHeight(media)}}"/>
 | 
					           height="{{getSmallHeight(media)}}"/>
 | 
				
			||||||
    {{/if}}
 | 
					    {{/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))
 | 
					      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: {
 | 
					    methods: {
 | 
				
			||||||
      async onClickPlayVideoButton(media, width, height) {
 | 
					      async onClickPlayVideoButton(media, width, height, description) {
 | 
				
			||||||
        showVideoDialog(media.preview_url, media.url, width, height)
 | 
					        showVideoDialog(media.preview_url, media.url, width, height, description)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,29 +1,41 @@
 | 
				
			||||||
<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}}
 | 
					  {{#if status.reblog}}
 | 
				
			||||||
      <div class="status-boosted">
 | 
					    <div class="status-boosted">
 | 
				
			||||||
        <svg>
 | 
					      <svg>
 | 
				
			||||||
          <use xlink:href="#fa-retweet" />
 | 
					        <use xlink:href="#fa-retweet" />
 | 
				
			||||||
        </svg>
 | 
					      </svg>
 | 
				
			||||||
        <span>
 | 
					      <span>
 | 
				
			||||||
          <a href="/accounts/{{status.account.id}}">
 | 
					        <a href="/accounts/{{status.account.id}}">
 | 
				
			||||||
            {{status.account.username}}
 | 
					          {{status.account.username}}
 | 
				
			||||||
          </a> boosted
 | 
					        </a> boosted
 | 
				
			||||||
        </span>
 | 
					      </span>
 | 
				
			||||||
      </div>
 | 
					    </div>
 | 
				
			||||||
    {{/if}}
 | 
					  {{/if}}
 | 
				
			||||||
  <div class="status-author">
 | 
					  <div class="status-author">
 | 
				
			||||||
    <a class="status-author-name" href="/accounts/{{originalAccount.id}}">
 | 
					    <a class="status-author-name" href="/accounts/{{originalAccount.id}}">
 | 
				
			||||||
      {{originalAccount.display_name || originalAccount.username}}
 | 
					      {{originalAccount.display_name || originalAccount.username}}
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
    <span class="status-author-handle">
 | 
					    <span class="status-author-handle">
 | 
				
			||||||
      @{{originalAccount.acct}}
 | 
					      {{'@' + originalAccount.acct}}
 | 
				
			||||||
    </span>
 | 
					    </span>
 | 
				
			||||||
    <a class="status-author-date" rel="noopener" target="_blank" href="{{originalStatus.uri}}">
 | 
					    <a class="status-author-date" rel="noopener" target="_blank" href="{{originalStatus.uri}}">
 | 
				
			||||||
      <time datetime={{createdAtDate}} title="{{relativeDate}}">{{relativeDate}}</time>
 | 
					      <time datetime={{createdAtDate}} title="{{relativeDate}}">{{relativeDate}}</time>
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <Avatar account={{originalAccount}} className="status-sidebar"/>
 | 
					  <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 />
 | 
					  <Toolbar :status />
 | 
				
			||||||
  <Media mediaAttachments="{{originalMediaAttachments}}" />
 | 
					  <Media mediaAttachments="{{originalMediaAttachments}}" />
 | 
				
			||||||
</article>
 | 
					</article>
 | 
				
			||||||
| 
						 | 
					@ -37,6 +49,8 @@
 | 
				
			||||||
    grid-template-areas:
 | 
					    grid-template-areas:
 | 
				
			||||||
        ".............. status-boosted"
 | 
					        ".............. status-boosted"
 | 
				
			||||||
        "status-sidebar status-author"
 | 
					        "status-sidebar status-author"
 | 
				
			||||||
 | 
					        "status-sidebar status-spoiler"
 | 
				
			||||||
 | 
					        "status-sidebar status-spoiler-button"
 | 
				
			||||||
        "status-sidebar status-content"
 | 
					        "status-sidebar status-content"
 | 
				
			||||||
        ".............. status-toolbar"
 | 
					        ".............. status-toolbar"
 | 
				
			||||||
        "status-media   status-media";
 | 
					        "status-media   status-media";
 | 
				
			||||||
| 
						 | 
					@ -50,6 +64,25 @@
 | 
				
			||||||
    margin: 0 10px 0 0;
 | 
					    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 {
 | 
					  .status-author {
 | 
				
			||||||
    grid-area: status-author;
 | 
					    grid-area: status-author;
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
| 
						 | 
					@ -94,7 +127,7 @@
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .status-content {
 | 
					  .status-content {
 | 
				
			||||||
    margin: 10px 10px 20px 5px;
 | 
					    margin: 10px 10px 10px 5px;
 | 
				
			||||||
    grid-area: status-content;
 | 
					    grid-area: status-content;
 | 
				
			||||||
    word-wrap: break-word;
 | 
					    word-wrap: break-word;
 | 
				
			||||||
    overflow: hidden;
 | 
					    overflow: hidden;
 | 
				
			||||||
| 
						 | 
					@ -160,6 +193,12 @@
 | 
				
			||||||
      originalStatus: (status) => status.reblog ? status.reblog : status,
 | 
					      originalStatus: (status) => status.reblog ? status.reblog : status,
 | 
				
			||||||
      originalAccount: (originalStatus) => originalStatus.account,
 | 
					      originalAccount: (originalStatus) => originalStatus.account,
 | 
				
			||||||
      originalMediaAttachments: (originalStatus) => originalStatus.media_attachments,
 | 
					      originalMediaAttachments: (originalStatus) => originalStatus.media_attachments,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    methods: {
 | 
				
			||||||
 | 
					      onClickSpoilerButton() {
 | 
				
			||||||
 | 
					        this.set({spoilerShown: !this.get('spoilerShown')})
 | 
				
			||||||
 | 
					        this.fire('recalculateHeight')
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
<Status status="{{virtualProps}}" index="{{virtualIndex}}" length="{{virtualLength}}"/>
 | 
					<Status status="{{virtualProps}}" index="{{virtualIndex}}" length="{{virtualLength}}" on:recalculateHeight />
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
  import Status from './Status.html'
 | 
					  import Status from './Status.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@
 | 
				
			||||||
         src="{{src}}"
 | 
					         src="{{src}}"
 | 
				
			||||||
         width="{{width}}"
 | 
					         width="{{width}}"
 | 
				
			||||||
         height="{{height}}"
 | 
					         height="{{height}}"
 | 
				
			||||||
 | 
					         aria-label="Video: {{description || ''}}"
 | 
				
			||||||
         controls
 | 
					         controls
 | 
				
			||||||
  />
 | 
					  />
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,9 @@
 | 
				
			||||||
<div class="virtual-list-item {{shown ? 'shown' : ''}}"
 | 
					<div class="virtual-list-item {{shown ? 'shown' : ''}}"
 | 
				
			||||||
     virtual-list-key="{{key}}"
 | 
					     virtual-list-key="{{key}}"
 | 
				
			||||||
     ref:node
 | 
					     ref:node
 | 
				
			||||||
     style="transform: translateY({{offset}}px
 | 
					     style="transform: translateY({{offset}}px);" >
 | 
				
			||||||
     );"
 | 
					  <:Component {component} virtualProps="{{props}}" virtualIndex="{{index}}" virtualLength="{{$numItems}}"
 | 
				
			||||||
>
 | 
					              on:recalculateHeight="doRecalculateHeight()"/>
 | 
				
			||||||
  <:Component {component} virtualProps="{{props}}" virtualIndex="{{index}}" virtualLength="{{$numItems}}"/>
 | 
					 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<style>
 | 
					<style>
 | 
				
			||||||
  .virtual-list-item {
 | 
					  .virtual-list-item {
 | 
				
			||||||
| 
						 | 
					@ -23,7 +22,8 @@
 | 
				
			||||||
  import { virtualListStore } from '../_utils/virtualListStore'
 | 
					  import { virtualListStore } from '../_utils/virtualListStore'
 | 
				
			||||||
  import { AsyncLayout } from '../_utils/AsyncLayout'
 | 
					  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 {
 | 
					  export default {
 | 
				
			||||||
    oncreate() {
 | 
					    oncreate() {
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,17 @@
 | 
				
			||||||
    store: () => virtualListStore,
 | 
					    store: () => virtualListStore,
 | 
				
			||||||
    computed: {
 | 
					    computed: {
 | 
				
			||||||
      'shown': ($itemHeights, key) => $itemHeights[key] > 0
 | 
					      '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>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,11 @@ class AsyncLayout {
 | 
				
			||||||
    this._intersectionObserver.unobserve(node)
 | 
					    this._intersectionObserver.unobserve(node)
 | 
				
			||||||
    delete this._onIntersectionCallbacks[key]
 | 
					    delete this._onIntersectionCallbacks[key]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  disconnect() {
 | 
				
			||||||
 | 
					    this._intersectionObserver.disconnect()
 | 
				
			||||||
 | 
					    this._intersectionObserver = null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { AsyncLayout }
 | 
					export { AsyncLayout }
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import VideoDialog from '../_components/VideoDialog.html'
 | 
					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')
 | 
					  let dialog = document.createElement('dialog')
 | 
				
			||||||
  dialog.classList.add('video-dialog')
 | 
					  dialog.classList.add('video-dialog')
 | 
				
			||||||
  dialog.setAttribute('aria-label', 'Video dialog')
 | 
					  dialog.setAttribute('aria-label', 'Video dialog')
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,8 @@ export function showVideoDialog(poster, src, width, height) {
 | 
				
			||||||
      src: src,
 | 
					      src: src,
 | 
				
			||||||
      dialog: dialog,
 | 
					      dialog: dialog,
 | 
				
			||||||
      width: width,
 | 
					      width: width,
 | 
				
			||||||
      height: height
 | 
					      height: height,
 | 
				
			||||||
 | 
					      description: description
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  videoDialog.showModal()
 | 
					  videoDialog.showModal()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,10 +9,10 @@
 | 
				
			||||||
    {{#if instanceUserAccount}}
 | 
					    {{#if instanceUserAccount}}
 | 
				
			||||||
      <h2>Logged in as:</h2>
 | 
					      <h2>Logged in as:</h2>
 | 
				
			||||||
      <div class="acct-current-user">
 | 
					      <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}}" />
 | 
					             class="acct-avatar" src="{{instanceUserAccount.avatar}}" />
 | 
				
			||||||
        <a class="acct-handle" rel="noopener" target="_blank"
 | 
					        <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>
 | 
					        <span class="acct-display-name">{{instanceUserAccount.display_name}}</span>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <h2>Theme:</h2>
 | 
					      <h2>Theme:</h2>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue