<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 :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 :replyShown on:recalculateHeight /> {{#if replyShown}} <StatusComposeBox :originalStatusId :uuid 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, unregisterClickDelegate } from '../../_utils/delegate' import { classname } from '../../_utils/classname' const INPUT_TAGS = new Set(['a', 'button', 'input', 'textarea']) 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)) } }, ondestroy() { let delegateKey = this.get('delegateKey') if (!this.get('isStatusInOwnThread')) { unregisterClickDelegate(delegateKey) } }, components: { StatusSidebar, StatusHeader, StatusAuthorName, StatusAuthorHandle, StatusRelativeDate, StatusDetails, StatusToolbar, StatusMediaAttachments, StatusContent, StatusSpoiler, StatusComposeBox }, store: () => store, methods: { onClickOrKeydown(e) { let { type, keyCode } = e let { localName, parentElement } = e.target if ((type === 'click' || (type === 'keydown' && keyCode === 13)) && !INPUT_TAGS.has(localName) && !INPUT_TAGS.has(parentElement.localName) && !INPUT_TAGS.has(parentElement.parentElement.localName)) { 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], replyShown: ($repliesShown, uuid) => !!$repliesShown[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>