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,29 +1,41 @@ | |||
| <article class="status-article" tabindex="0" aria-posinset="{{index}}" aria-setsize="{{length}}"> | ||||
|     {{#if status.reblog}} | ||||
|       <div class="status-boosted"> | ||||
|         <svg> | ||||
|           <use xlink:href="#fa-retweet" /> | ||||
|         </svg> | ||||
|         <span> | ||||
|           <a href="/accounts/{{status.account.id}}"> | ||||
|             {{status.account.username}} | ||||
|           </a> boosted | ||||
|         </span> | ||||
|       </div> | ||||
|     {{/if}} | ||||
| <article class="status-article" tabindex="0" aria-posinset="{{index}}" aria-setsize="{{length}}" on:recalculateHeight> | ||||
|   {{#if status.reblog}} | ||||
|     <div class="status-boosted"> | ||||
|       <svg> | ||||
|         <use xlink:href="#fa-retweet" /> | ||||
|       </svg> | ||||
|       <span> | ||||
|         <a href="/accounts/{{status.account.id}}"> | ||||
|           {{status.account.username}} | ||||
|         </a> boosted | ||||
|       </span> | ||||
|     </div> | ||||
|   {{/if}} | ||||
|   <div class="status-author"> | ||||
|     <a class="status-author-name" href="/accounts/{{originalAccount.id}}"> | ||||
|       {{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