forked from cybrespace/pinafore
		
	
		
			
				
	
	
		
			245 lines
		
	
	
		
			No EOL
		
	
	
		
			8.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			No EOL
		
	
	
		
			8.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <article class={className}
 | |
|          tabindex="0"
 | |
|          delegate-key={delegateKey}
 | |
|          focus-key={delegateKey}
 | |
|          aria-posinset={index}
 | |
|          aria-setsize={length}
 | |
|          aria-label={ariaLabel}
 | |
|          on:recalculateHeight>
 | |
|   {#if showHeader}
 | |
|     <StatusHeader {...params} />
 | |
|   {/if}
 | |
|   <StatusAuthorName {...params} />
 | |
|   <StatusAuthorHandle {...params} />
 | |
|   {#if !isStatusInOwnThread}
 | |
|     <StatusRelativeDate {...params} />
 | |
|   {/if}
 | |
|   <StatusSidebar {...params} />
 | |
|   {#if spoilerText}
 | |
|     <StatusSpoiler {...params} on:recalculateHeight />
 | |
|   {/if}
 | |
|   {#if showContent || contentPreloaded}
 | |
|     <StatusContent {...params} shown={showContent}/>
 | |
|   {/if}
 | |
|   {#if showMedia }
 | |
|     <StatusMediaAttachments {...params} on:recalculateHeight />
 | |
|   {/if}
 | |
|   {#if isStatusInOwnThread}
 | |
|     <StatusDetails {...params} />
 | |
|   {/if}
 | |
|   <StatusToolbar {...params} on:recalculateHeight />
 | |
|   {#if replyShown}
 | |
|     <StatusComposeBox {...params} on:recalculateHeight />
 | |
|   {/if}
 | |
| </article>
 | |
| 
 | |
| <style>
 | |
|   .status-article {
 | |
|     cursor: pointer;
 | |
|     max-width: calc(100vw - 40px);
 | |
|     padding: 10px 20px;
 | |
|     display: grid;
 | |
|     grid-template-areas:
 | |
|         "header  header       header        header"
 | |
|         "sidebar author-name  author-handle relative-date"
 | |
|         "sidebar spoiler      spoiler       spoiler"
 | |
|         "sidebar spoiler-btn  spoiler-btn   spoiler-btn"
 | |
|         "sidebar content      content       content"
 | |
|         "media   media        media         media"
 | |
|         "....... toolbar      toolbar       toolbar"
 | |
|         "compose compose      compose       compose";
 | |
|     grid-template-columns: min-content minmax(0, max-content) 1fr min-content;
 | |
|     grid-template-rows: repeat(8, max-content);
 | |
|   }
 | |
| 
 | |
|   .status-article.status-in-timeline {
 | |
|     width: 560px;
 | |
|     border-bottom: 1px solid var(--main-border);
 | |
|   }
 | |
| 
 | |
|   .status-article.status-direct {
 | |
|     background-color: var(--status-direct-background);
 | |
|   }
 | |
| 
 | |
|   .status-article.status-in-own-thread {
 | |
|     grid-template-areas:
 | |
|       "sidebar     author-name"
 | |
|       "sidebar     author-handle"
 | |
|       "spoiler     spoiler"
 | |
|       "spoiler-btn spoiler-btn"
 | |
|       "content     content"
 | |
|       "media       media"
 | |
|       "details     details"
 | |
|       "toolbar     toolbar"
 | |
|       "compose     compose";
 | |
|     grid-template-columns: min-content 1fr;
 | |
|     grid-template-rows: repeat(7, max-content);
 | |
|   }
 | |
| 
 | |
|   @media (max-width: 767px) {
 | |
|     .status-article {
 | |
|       padding: 10px 10px;
 | |
|       max-width: calc(100vw - 20px);
 | |
|     }
 | |
|     .status-article.status-in-timeline {
 | |
|       width: 580px;
 | |
|     }
 | |
|   }
 | |
| </style>
 | |
| <script>
 | |
|   import StatusSidebar from './StatusSidebar.html'
 | |
|   import StatusHeader from './StatusHeader.html'
 | |
|   import StatusAuthorName from './StatusAuthorName.html'
 | |
|   import StatusAuthorHandle from './StatusAuthorHandle.html'
 | |
|   import StatusRelativeDate from './StatusRelativeDate.html'
 | |
|   import StatusDetails from './StatusDetails.html'
 | |
|   import StatusToolbar from './StatusToolbar.html'
 | |
|   import StatusMediaAttachments from './StatusMediaAttachments.html'
 | |
|   import StatusContent from './StatusContent.html'
 | |
|   import StatusSpoiler from './StatusSpoiler.html'
 | |
|   import StatusComposeBox from './StatusComposeBox.html'
 | |
|   import { store } from '../../_store/store'
 | |
|   import { goto } from 'sapper/runtime.js'
 | |
|   import { registerClickDelegate } from '../../_utils/delegate'
 | |
|   import { classname } from '../../_utils/classname'
 | |
|   import { checkDomAncestors } from '../../_utils/checkDomAncestors'
 | |
|   import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
 | |
| 
 | |
|   const INPUT_TAGS = new Set(['a', 'button', 'input', 'textarea'])
 | |
|   const isUserInputElement = node => INPUT_TAGS.has(node.localName)
 | |
|   const isToolbar = node => node.classList.contains('status-toolbar')
 | |
|   const isStatusArticle = node => node.classList.contains('status-article')
 | |
| 
 | |
|   export default {
 | |
|     oncreate () {
 | |
|       let { delegateKey, isStatusInOwnThread, showContent } = this.get()
 | |
|       if (!isStatusInOwnThread) {
 | |
|         // the whole <article> is clickable in this case
 | |
|         registerClickDelegate(this, delegateKey, (e) => this.onClickOrKeydown(e))
 | |
|       }
 | |
|       if (!showContent) {
 | |
|         scheduleIdleTask(() => {
 | |
|           // Perf optimization: lazily load the StatusContent when the user is idle so that
 | |
|           // it's fast when they click the "show more" button
 | |
|           this.set({contentPreloaded: true})
 | |
|         })
 | |
|       }
 | |
|     },
 | |
|     components: {
 | |
|       StatusSidebar,
 | |
|       StatusHeader,
 | |
|       StatusAuthorName,
 | |
|       StatusAuthorHandle,
 | |
|       StatusRelativeDate,
 | |
|       StatusDetails,
 | |
|       StatusToolbar,
 | |
|       StatusMediaAttachments,
 | |
|       StatusContent,
 | |
|       StatusSpoiler,
 | |
|       StatusComposeBox
 | |
|     },
 | |
|     data: () => ({
 | |
|       notification: void 0,
 | |
|       replyVisibility: void 0,
 | |
|       contentPreloaded: false
 | |
|     }),
 | |
|     store: () => store,
 | |
|     methods: {
 | |
|       onClickOrKeydown (e) {
 | |
|         let { type, keyCode, target } = e
 | |
| 
 | |
|         let isClick = type === 'click'
 | |
|         let isEnter = type === 'keydown' && keyCode === 13
 | |
|         if (!isClick && !isEnter) {
 | |
|           return
 | |
|         }
 | |
|         if (checkDomAncestors(target, isUserInputElement, isStatusArticle)) {
 | |
|           // this element or any ancestors up to the status-article element is
 | |
|           // a user input element
 | |
|           return
 | |
|         }
 | |
|         if (checkDomAncestors(target, isToolbar, isStatusArticle)) {
 | |
|           // this element or any of its ancestors is the toolbar
 | |
|           return
 | |
|         }
 | |
| 
 | |
|         e.preventDefault()
 | |
|         e.stopPropagation()
 | |
|         let { originalStatusId } = this.get()
 | |
|         goto(`/statuses/${originalStatusId}`)
 | |
|       }
 | |
|     },
 | |
|     computed: {
 | |
|       originalStatus: ({ status }) => status.reblog ? status.reblog : status,
 | |
|       originalStatusId: ({ originalStatus }) => originalStatus.id,
 | |
|       statusId: ({ status }) => status.id,
 | |
|       notificationId: ({ notification }) => notification && notification.id,
 | |
|       account: ({ notification, status }) => (
 | |
|         (notification && notification.account) || status.account
 | |
|       ),
 | |
|       accountId: ({ account }) => account.id,
 | |
|       originalAccount: ({ originalStatus }) => originalStatus.account,
 | |
|       originalAccountId: ({ originalAccount }) => originalAccount.id,
 | |
|       visibility: ({ originalStatus }) => originalStatus.visibility,
 | |
|       spoilerText: ({ originalStatus }) => originalStatus.spoiler_text,
 | |
|       uuid: ({ $currentInstance, timelineType, timelineValue, notificationId, statusId }) => (
 | |
|         `${$currentInstance}/${timelineType}/${timelineValue}/${notificationId || ''}/${statusId}`
 | |
|       ),
 | |
|       delegateKey: ({ uuid }) => `status-${uuid}`,
 | |
|       isStatusInOwnThread: ({ timelineType, timelineValue, originalStatusId }) => (
 | |
|         (timelineType === 'status' || timelineType === 'reply') && timelineValue === originalStatusId
 | |
|       ),
 | |
|       isStatusInNotification: ({ originalStatusId, notification }) => (
 | |
|         notification && notification.status &&
 | |
|           notification.type !== 'mention' && notification.status.id === originalStatusId
 | |
|       ),
 | |
|       spoilerShown: ({ $spoilersShown, uuid }) => !!$spoilersShown[uuid],
 | |
|       replyShown: ({ $repliesShown, uuid }) => !!$repliesShown[uuid],
 | |
|       showMedia: ({ originalStatus, isStatusInNotification }) => (
 | |
|         !isStatusInNotification &&
 | |
|         originalStatus.media_attachments &&
 | |
|         originalStatus.media_attachments.length
 | |
|       ),
 | |
|       ariaLabel: ({ originalAccount, originalStatus, visibility }) => (
 | |
|         (visibility === 'direct' ? 'Direct message' : 'Status') +
 | |
|           ` by ${originalAccount.display_name || originalAccount.username}`
 | |
|       ),
 | |
|       showHeader: ({ notification, status, timelineType }) => (
 | |
|         (notification && (notification.type === 'reblog' || notification.type === 'favourite')) ||
 | |
|         status.reblog ||
 | |
|         timelineType === 'pinned'
 | |
|       ),
 | |
|       className: ({ visibility, timelineType, isStatusInOwnThread }) => (classname(
 | |
|         'status-article',
 | |
|         visibility === 'direct' && 'status-direct',
 | |
|         timelineType !== 'search' && 'status-in-timeline',
 | |
|         isStatusInOwnThread && 'status-in-own-thread'
 | |
|       )),
 | |
|       showContent: ({ spoilerText, spoilerShown }) => !spoilerText || spoilerShown,
 | |
|       params: ({ notification, notificationId, status, statusId, timelineType,
 | |
|         account, accountId, uuid, isStatusInNotification, isStatusInOwnThread,
 | |
|         originalAccount, originalAccountId, spoilerShown, visibility, replyShown,
 | |
|         replyVisibility, spoilerText, originalStatus, originalStatusId }) => ({
 | |
|         notification,
 | |
|         notificationId,
 | |
|         status,
 | |
|         statusId,
 | |
|         timelineType,
 | |
|         account,
 | |
|         accountId,
 | |
|         uuid,
 | |
|         isStatusInNotification,
 | |
|         isStatusInOwnThread,
 | |
|         originalAccount,
 | |
|         originalAccountId,
 | |
|         spoilerShown,
 | |
|         visibility,
 | |
|         replyShown,
 | |
|         replyVisibility,
 | |
|         spoilerText,
 | |
|         originalStatus,
 | |
|         originalStatusId
 | |
|       })
 | |
|     }
 | |
|   }
 | |
| </script> |