<article class="{{className}}" tabindex="0" delegate-key="{{delegateKey}}" focus-key="{{delegateKey}}" aria-posinset="{{index}}" aria-setsize="{{length}}" aria-label="{{ariaLabel}}" on:recalculateHeight ref:node > {{#if showHeader}} <StatusHeader :notification :notificationId :status :statusId :timelineType :account :accountId :uuid :isStatusInNotification /> {{/if}} <StatusAuthorName :isStatusInNotification :isStatusInOwnThread :originalAccountId :originalAccount :uuid /> <StatusAuthorHandle :isStatusInNotification :originalAccount /> {{#if !isStatusInOwnThread}} <StatusRelativeDate :isStatusInNotification :originalStatus :originalStatusId :uuid /> {{/if}} <StatusSidebar :isStatusInOwnThread :originalAccount :originalAccountId :uuid /> {{#if originalStatus.spoiler_text}} <StatusSpoiler :isStatusInOwnThread :isStatusInNotification :originalStatus :uuid :spoilerShown on:recalculateHeight /> {{/if}} {{#if !originalStatus.spoiler_text || spoilerShown}} <StatusContent :isStatusInOwnThread :isStatusInNotification :originalStatus :uuid /> {{/if}} {{#if originalStatus.media_attachments && originalStatus.media_attachments.length}} <StatusMediaAttachments :originalStatus :uuid on:recalculateHeight /> {{/if}} {{#if isStatusInOwnThread}} <StatusDetails :originalStatus :originalStatusId /> {{/if}} <StatusToolbar :originalStatus :originalStatusId :originalAccountId :isStatusInOwnThread :uuid :visibility /> </article> <style> .status-article { cursor: pointer; max-width: calc(100vw - 40px); padding: 10px 20px; display: grid; grid-template-areas: "....... 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"; grid-template-columns: min-content minmax(0, max-content) 1fr min-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"; grid-template-columns: min-content 1fr; } @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 { store } from '../../_store/store' import { goto } from 'sapper/runtime.js' import { registerClickDelegate, unregisterClickDelegate } from '../../_utils/delegate' import { classname } from '../../_utils/classname' import { restoreFocus } from '../../_utils/restoreFocus' export default { oncreate() { let delegateKey = this.get('delegateKey') if (!this.get('isStatusInOwnThread')) { // the whole <article> is clickable in this case registerClickDelegate(delegateKey, (e) => this.onClickOrKeydown(e)) } let focusSelector = this.get('focusSelector') if (this.refs.node && focusSelector && this.store.getForCurrentTimeline('shouldRestoreFocus')) { restoreFocus(this.refs.node, focusSelector) } }, ondestroy() { let delegateKey = this.get('delegateKey') if (!this.get('isStatusInOwnThread')) { unregisterClickDelegate(delegateKey) } }, components: { StatusSidebar, StatusHeader, StatusAuthorName, StatusAuthorHandle, StatusRelativeDate, StatusDetails, StatusToolbar, StatusMediaAttachments, StatusContent, StatusSpoiler }, store: () => store, methods: { onClickOrKeydown(e) { let { type, keyCode } = e let { localName, parentElement } = e.target if ((type === 'click' || (type === 'keydown' && keyCode === 13)) && localName !== 'a' && localName !== 'button' && parentElement.localName !== 'a' && parentElement.localName !== 'button' && parentElement.parentElement.localName !== 'a' && parentElement.parentElement.localName !== 'button') { e.preventDefault() e.stopPropagation() goto(`/statuses/${this.get('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) => { return (notification && notification.account) || status.account }, accountId: (account) => account.id, originalAccount: (originalStatus) => originalStatus.account, originalAccountId: (originalAccount) => originalAccount.id, visibility: (originalStatus) => originalStatus.visibility, uuid: ($currentInstance, timelineType, timelineValue, notificationId, statusId) => { return `${$currentInstance}/${timelineType}/${timelineValue}/${notificationId || ''}/${statusId}` }, delegateKey: (uuid) => `status-${uuid}`, isStatusInOwnThread: (timelineType, timelineValue, originalStatusId) => { return (timelineType === 'status' || timelineType === 'reply') && timelineValue === originalStatusId }, isStatusInNotification: (originalStatusId, notification) => { return notification && notification.status && notification.type !== 'mention' && notification.status.id === originalStatusId }, spoilerShown: ($spoilersShown, uuid) => !!$spoilersShown[uuid], ariaLabel: (originalAccount, originalStatus, visibility) => { return (visibility === 'direct' ? 'Direct message' : 'Status') + ` by ${originalAccount.display_name || originalAccount.username}` }, showHeader: (notification, status, timelineType) => { return (notification && (notification.type === 'reblog' || notification.type === 'favourite')) || status.reblog || timelineType === 'pinned' }, className: (visibility, timelineType, isStatusInOwnThread) => { return classname( 'status-article', visibility === 'direct' && 'status-direct', timelineType !== 'search' && 'status-in-timeline', isStatusInOwnThread && 'status-in-own-thread' ) } } } </script>