<template> <div class="mk-post-detail" :title="title"> <button class="read-more" v-if="p.reply && p.reply.reply_id && context == null" title="会話をもっと読み込む" @click="loadContext" disabled={ contextFetching }> <template v-if="!contextFetching">%fa:ellipsis-v%</template> <template v-if="contextFetching">%fa:spinner .pulse%</template> </button> <div class="context"> <template each={ post in context }> <mk-post-detail-sub post={ post }/> </template> </div> <div class="reply-to" v-if="p.reply"> <mk-post-detail-sub post={ p.reply }/> </div> <div class="repost" v-if="isRepost"> <p> <a class="avatar-anchor" href={ '/' + post.user.username } data-user-preview={ post.user_id }> <img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=32' } alt="avatar"/> </a> %fa:retweet%<a class="name" href={ '/' + post.user.username }> { post.user.name } </a> がRepost </p> </div> <article> <a class="avatar-anchor" href={ '/' + p.user.username }> <img class="avatar" src={ p.user.avatar_url + '?thumbnail&size=64' } alt="avatar" data-user-preview={ p.user.id }/> </a> <header> <a class="name" href={ '/' + p.user.username } data-user-preview={ p.user.id }>{ p.user.name }</a> <span class="username">@{ p.user.username }</span> <a class="time" href={ '/' + p.user.username + '/' + p.id }> <mk-time time={ p.created_at }/> </a> </header> <div class="body"> <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/> <mk-url-preview v-for="url in urls" :url="url" :key="url"/> <div class="media" v-if="p.media"> <mk-images images={ p.media }/> </div> <mk-poll v-if="p.poll" post={ p }/> </div> <footer> <mk-reactions-viewer post={ p }/> <button @click="reply" title="返信"> %fa:reply%<p class="count" v-if="p.replies_count > 0">{ p.replies_count }</p> </button> <button @click="repost" title="Repost"> %fa:retweet%<p class="count" v-if="p.repost_count > 0">{ p.repost_count }</p> </button> <button :class="{ reacted: p.my_reaction != null }" @click="react" ref="reactButton" title="リアクション"> %fa:plus%<p class="count" v-if="p.reactions_count > 0">{ p.reactions_count }</p> </button> <button @click="menu" ref="menuButton"> %fa:ellipsis-h% </button> </footer> </article> <div class="replies" v-if="!compact"> <template each={ post in replies }> <mk-post-detail-sub post={ post }/> </template> </div> </div> </template> <script lang="ts"> import Vue from 'vue'; import dateStringify from '../../../common/scripts/date-stringify'; export default Vue.extend({ props: { post: { type: Object, required: true }, compact: { default: false } }, data() { return { context: [], contextFetching: false, replies: [], }; }, computed: { isRepost(): boolean { return this.post.repost != null; }, p(): any { return this.isRepost ? this.post.repost : this.post; }, reactionsCount(): number { return this.p.reaction_counts ? Object.keys(this.p.reaction_counts) .map(key => this.p.reaction_counts[key]) .reduce((a, b) => a + b) : 0; }, title(): string { return dateStringify(this.p.created_at); }, urls(): string[] { if (this.p.ast) { return this.p.ast .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) .map(t => t.url); } else { return null; } } }, mounted() { // Get replies if (!this.compact) { (this as any).api('posts/replies', { post_id: this.p.id, limit: 8 }).then(replies => { this.replies = replies; }); } }, methods: { fetchContext() { this.contextFetching = true; // Fetch context (this as any).api('posts/context', { post_id: this.p.reply_id }).then(context => { this.contextFetching = false; this.context = context.reverse(); }); } } }); </script> <style lang="stylus" scoped> .mk-post-detail margin 0 padding 0 overflow hidden text-align left background #fff border solid 1px rgba(0, 0, 0, 0.1) border-radius 8px > .read-more display block margin 0 padding 10px 0 width 100% font-size 1em text-align center color #999 cursor pointer background #fafafa outline none border none border-bottom solid 1px #eef0f2 border-radius 6px 6px 0 0 &:hover background #f6f6f6 &:active background #f0f0f0 &:disabled color #ccc > .context > * border-bottom 1px solid #eef0f2 > .repost color #9dbb00 background linear-gradient(to bottom, #edfde2 0%, #fff 100%) > p margin 0 padding 16px 32px .avatar-anchor display inline-block .avatar vertical-align bottom min-width 28px min-height 28px max-width 28px max-height 28px margin 0 8px 0 0 border-radius 6px [data-fa] margin-right 4px .name font-weight bold & + article padding-top 8px > .reply-to border-bottom 1px solid #eef0f2 > article padding 28px 32px 18px 32px &:after content "" display block clear both &:hover > .main > footer > button color #888 > .avatar-anchor display block width 60px height 60px > .avatar display block width 60px height 60px margin 0 border-radius 8px vertical-align bottom > header position absolute top 28px left 108px width calc(100% - 108px) > .name display inline-block margin 0 line-height 24px color #777 font-size 18px font-weight 700 text-align left text-decoration none &:hover text-decoration underline > .username display block text-align left margin 0 color #ccc > .time position absolute top 0 right 32px font-size 1em color #c0c0c0 > .body padding 8px 0 > .text cursor default display block margin 0 padding 0 overflow-wrap break-word font-size 1.5em color #717171 > .mk-url-preview margin-top 8px > footer font-size 1.2em > button margin 0 28px 0 0 padding 8px background transparent border none font-size 1em color #ddd cursor pointer &:hover color #666 > .count display inline margin 0 0 0 8px color #999 &.reacted color $theme-color > .replies > * border-top 1px solid #eef0f2 </style>