summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/app/desktop/views/components/post-detail.sub.vue122
-rw-r--r--src/client/app/desktop/views/components/post-detail.vue434
-rw-r--r--src/client/app/desktop/views/components/post-preview.vue99
-rw-r--r--src/client/app/desktop/views/components/posts.post.sub.vue108
-rw-r--r--src/client/app/desktop/views/components/posts.post.vue585
-rw-r--r--src/client/app/desktop/views/widgets/channel.channel.post.vue65
-rw-r--r--src/client/app/mobile/views/components/post-card.vue85
-rw-r--r--src/client/app/mobile/views/components/post-detail.sub.vue103
-rw-r--r--src/client/app/mobile/views/components/post-detail.vue444
-rw-r--r--src/client/app/mobile/views/components/post-preview.vue100
-rw-r--r--src/client/app/mobile/views/components/post.vue523
11 files changed, 0 insertions, 2668 deletions
diff --git a/src/client/app/desktop/views/components/post-detail.sub.vue b/src/client/app/desktop/views/components/post-detail.sub.vue
deleted file mode 100644
index 16bc2a1d98..0000000000
--- a/src/client/app/desktop/views/components/post-detail.sub.vue
+++ /dev/null
@@ -1,122 +0,0 @@
-<template>
-<div class="sub" :title="title">
- <router-link class="avatar-anchor" :to="note.user | userPage">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="note.userId"/>
- </router-link>
- <div class="main">
- <header>
- <div class="left">
- <router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
- </div>
- <div class="right">
- <router-link class="time" :to="note | notePage">
- <mk-time :time="note.createdAt"/>
- </router-link>
- </div>
- </header>
- <div class="body">
- <mk-note-html v-if="note.text" :text="note.text" :i="os.i" :class="$style.text"/>
- <div class="media" v-if="note.media > 0">
- <mk-media-list :media-list="note.media"/>
- </div>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
-
-export default Vue.extend({
- props: ['note'],
- computed: {
- title(): string {
- return dateStringify(this.note.createdAt);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.sub
- margin 0
- padding 20px 32px
- background #fdfdfd
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > .avatar-anchor
- display block
- float left
- margin 0 16px 0 0
-
- > .avatar
- display block
- width 44px
- height 44px
- margin 0
- border-radius 4px
- vertical-align bottom
-
- > .main
- float left
- width calc(100% - 60px)
-
- > header
- margin-bottom 4px
- white-space nowrap
-
- &:after
- content ""
- display block
- clear both
-
- > .left
- float left
-
- > .name
- display inline
- margin 0
- padding 0
- color #777
- font-size 1em
- font-weight 700
- text-align left
- text-decoration none
-
- &:hover
- text-decoration underline
-
- > .username
- text-align left
- margin 0 0 0 8px
- color #ccc
-
- > .right
- float right
-
- > .time
- font-size 0.9em
- color #c0c0c0
-
-</style>
-
-<style lang="stylus" module>
-.text
- cursor default
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 1em
- color #717171
-</style>
diff --git a/src/client/app/desktop/views/components/post-detail.vue b/src/client/app/desktop/views/components/post-detail.vue
deleted file mode 100644
index 50bbb76988..0000000000
--- a/src/client/app/desktop/views/components/post-detail.vue
+++ /dev/null
@@ -1,434 +0,0 @@
-<template>
-<div class="mk-note-detail" :title="title">
- <button
- class="read-more"
- v-if="p.reply && p.reply.replyId && context == null"
- title="会話をもっと読み込む"
- @click="fetchContext"
- :disabled="contextFetching"
- >
- <template v-if="!contextFetching">%fa:ellipsis-v%</template>
- <template v-if="contextFetching">%fa:spinner .pulse%</template>
- </button>
- <div class="context">
- <x-sub v-for="note in context" :key="note.id" :note="note"/>
- </div>
- <div class="reply-to" v-if="p.reply">
- <x-sub :note="p.reply"/>
- </div>
- <div class="renote" v-if="isRenote">
- <p>
- <router-link class="avatar-anchor" :to="note.user | userPage" v-user-preview="note.userId">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
- </router-link>
- %fa:retweet%
- <router-link class="name" :href="note.user | userPage">{{ note.user | userName }}</router-link>
- がRenote
- </p>
- </div>
- <article>
- <router-link class="avatar-anchor" :to="p.user | userPage">
- <img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="p.user.id"/>
- </router-link>
- <header>
- <router-link class="name" :to="p.user | userPage" v-user-preview="p.user.id">{{ p.user | userName }}</router-link>
- <span class="username">@{{ p.user | acct }}</span>
- <router-link class="time" :to="p | notePage">
- <mk-time :time="p.createdAt"/>
- </router-link>
- </header>
- <div class="body">
- <mk-note-html :class="$style.text" v-if="p.text" :text="p.text" :i="os.i"/>
- <div class="media" v-if="p.media.length > 0">
- <mk-media-list :media-list="p.media"/>
- </div>
- <mk-poll v-if="p.poll" :note="p"/>
- <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
- </div>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
- <div class="map" v-if="p.geo" ref="map"></div>
- <div class="renote" v-if="p.renote">
- <mk-note-preview :note="p.renote"/>
- </div>
- </div>
- <footer>
- <mk-reactions-viewer :note="p"/>
- <button @click="reply" title="返信">
- %fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
- </button>
- <button @click="renote" title="Renote">
- %fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
- </button>
- <button :class="{ reacted: p.myReaction != 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">
- <x-sub v-for="note in replies" :key="note.id" :note="note"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
-import parse from '../../../../../text/parse';
-
-import MkPostFormWindow from './post-form-window.vue';
-import MkRenoteFormWindow from './renote-form-window.vue';
-import MkNoteMenu from '../../../common/views/components/note-menu.vue';
-import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
-import XSub from './note-detail.sub.vue';
-
-export default Vue.extend({
- components: {
- XSub
- },
-
- props: {
- note: {
- type: Object,
- required: true
- },
- compact: {
- default: false
- }
- },
-
- data() {
- return {
- context: [],
- contextFetching: false,
- replies: []
- };
- },
-
- computed: {
- isRenote(): boolean {
- return (this.note.renote &&
- this.note.text == null &&
- this.note.mediaIds.length == 0 &&
- this.note.poll == null);
- },
- p(): any {
- return this.isRenote ? this.note.renote : this.note;
- },
- reactionsCount(): number {
- return this.p.reactionCounts
- ? Object.keys(this.p.reactionCounts)
- .map(key => this.p.reactionCounts[key])
- .reduce((a, b) => a + b)
- : 0;
- },
- title(): string {
- return dateStringify(this.p.createdAt);
- },
- urls(): string[] {
- if (this.p.text) {
- const ast = parse(this.p.text);
- return 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('notes/replies', {
- noteId: this.p.id,
- limit: 8
- }).then(replies => {
- this.replies = replies;
- });
- }
-
- // Draw map
- if (this.p.geo) {
- const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
- if (shouldShowMap) {
- (this as any).os.getGoogleMaps().then(maps => {
- const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
- const map = new maps.Map(this.$refs.map, {
- center: uluru,
- zoom: 15
- });
- new maps.Marker({
- position: uluru,
- map: map
- });
- });
- }
- }
- },
-
- methods: {
- fetchContext() {
- this.contextFetching = true;
-
- // Fetch context
- (this as any).api('notes/context', {
- noteId: this.p.replyId
- }).then(context => {
- this.contextFetching = false;
- this.context = context.reverse();
- });
- },
- reply() {
- (this as any).os.new(MkPostFormWindow, {
- reply: this.p
- });
- },
- renote() {
- (this as any).os.new(MkRenoteFormWindow, {
- note: this.p
- });
- },
- react() {
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- note: this.p
- });
- },
- menu() {
- (this as any).os.new(MkNoteMenu, {
- source: this.$refs.menuButton,
- note: this.p
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-note-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
-
- > .renote
- 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
-
- > .renote
- margin 8px 0
-
- > .mk-note-preview
- padding 16px
- border dashed 1px #c0dac6
- border-radius 8px
-
- > .location
- margin 4px 0
- font-size 12px
- color #ccc
-
- > .map
- width 100%
- height 300px
-
- &:empty
- display none
-
- > .mk-url-preview
- margin-top 8px
-
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background #fff
- border-radius 100%
-
- &:hover
- text-decoration none
- background #e2e7ec
-
- > 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>
-
-<style lang="stylus" module>
-.text
- cursor default
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 1.5em
- color #717171
-</style>
diff --git a/src/client/app/desktop/views/components/post-preview.vue b/src/client/app/desktop/views/components/post-preview.vue
deleted file mode 100644
index ff3ecadc20..0000000000
--- a/src/client/app/desktop/views/components/post-preview.vue
+++ /dev/null
@@ -1,99 +0,0 @@
-<template>
-<div class="mk-note-preview" :title="title">
- <router-link class="avatar-anchor" :to="note.user | userPage">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="note.userId"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
- <router-link class="time" :to="note | notePage">
- <mk-time :time="note.createdAt"/>
- </router-link>
- </header>
- <div class="body">
- <mk-sub-note-content class="text" :note="note"/>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
-
-export default Vue.extend({
- props: ['note'],
- computed: {
- title(): string {
- return dateStringify(this.note.createdAt);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-note-preview
- font-size 0.9em
- background #fff
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > .avatar-anchor
- display block
- float left
- margin 0 16px 0 0
-
- > .avatar
- display block
- width 52px
- height 52px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- > .main
- float left
- width calc(100% - 68px)
-
- > header
- display flex
- white-space nowrap
-
- > .name
- margin 0 .5em 0 0
- padding 0
- color #607073
- font-size 1em
- font-weight bold
- text-decoration none
- white-space normal
-
- &:hover
- text-decoration underline
-
- > .username
- margin 0 .5em 0 0
- color #d1d8da
-
- > .time
- margin-left auto
- color #b2b8bb
-
- > .body
-
- > .text
- cursor default
- margin 0
- padding 0
- font-size 1.1em
- color #717171
-
-</style>
diff --git a/src/client/app/desktop/views/components/posts.post.sub.vue b/src/client/app/desktop/views/components/posts.post.sub.vue
deleted file mode 100644
index e854785783..0000000000
--- a/src/client/app/desktop/views/components/posts.post.sub.vue
+++ /dev/null
@@ -1,108 +0,0 @@
-<template>
-<div class="sub" :title="title">
- <router-link class="avatar-anchor" :to="note.user | userPage">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="note.userId"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
- <router-link class="created-at" :to="note | notePage">
- <mk-time :time="note.createdAt"/>
- </router-link>
- </header>
- <div class="body">
- <mk-sub-note-content class="text" :note="note"/>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
-
-export default Vue.extend({
- props: ['note'],
- computed: {
- title(): string {
- return dateStringify(this.note.createdAt);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.sub
- margin 0
- padding 16px
- font-size 0.9em
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > .avatar-anchor
- display block
- float left
- margin 0 14px 0 0
-
- > .avatar
- display block
- width 52px
- height 52px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- > .main
- float left
- width calc(100% - 66px)
-
- > header
- display flex
- margin-bottom 2px
- white-space nowrap
- line-height 21px
-
- > .name
- display block
- margin 0 .5em 0 0
- padding 0
- overflow hidden
- color #607073
- font-size 1em
- font-weight bold
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .username
- margin 0 .5em 0 0
- color #d1d8da
-
- > .created-at
- margin-left auto
- color #b2b8bb
-
- > .body
-
- > .text
- cursor default
- margin 0
- padding 0
- font-size 1.1em
- color #717171
-
- pre
- max-height 120px
- font-size 80%
-
-</style>
diff --git a/src/client/app/desktop/views/components/posts.post.vue b/src/client/app/desktop/views/components/posts.post.vue
deleted file mode 100644
index 322bf29221..0000000000
--- a/src/client/app/desktop/views/components/posts.post.vue
+++ /dev/null
@@ -1,585 +0,0 @@
-<template>
-<div class="note" tabindex="-1" :title="title" @keydown="onKeydown">
- <div class="reply-to" v-if="p.reply">
- <x-sub :note="p.reply"/>
- </div>
- <div class="renote" v-if="isRenote">
- <p>
- <router-link class="avatar-anchor" :to="note.user | userPage" v-user-preview="note.userId">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
- </router-link>
- %fa:retweet%
- <span>{{ '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
- <a class="name" :href="note.user | userPage" v-user-preview="note.userId">{{ note.user | userName }}</a>
- <span>{{ '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.substr('%i18n:desktop.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
- </p>
- <mk-time :time="note.createdAt"/>
- </div>
- <article>
- <router-link class="avatar-anchor" :to="p.user | userPage">
- <img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="p.user.id"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="p.user | userPage" v-user-preview="p.user.id">{{ p.user | userName }}</router-link>
- <span class="is-bot" v-if="p.user.host === null && p.user.isBot">bot</span>
- <span class="username">@{{ p.user | acct }}</span>
- <div class="info">
- <span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
- <span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
- <router-link class="created-at" :to="url">
- <mk-time :time="p.createdAt"/>
- </router-link>
- </div>
- </header>
- <div class="body">
- <p class="channel" v-if="p.channel">
- <a :href="`${_CH_URL_}/${p.channel.id}`" target="_blank">{{ p.channel.title }}</a>:
- </p>
- <div class="text">
- <a class="reply" v-if="p.reply">%fa:reply%</a>
- <mk-note-html v-if="p.textHtml" :text="p.text" :i="os.i" :class="$style.text"/>
- <a class="rp" v-if="p.renote">RP:</a>
- </div>
- <div class="media" v-if="p.media.length > 0">
- <mk-media-list :media-list="p.media"/>
- </div>
- <mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
- </div>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
- <div class="map" v-if="p.geo" ref="map"></div>
- <div class="renote" v-if="p.renote">
- <mk-note-preview :note="p.renote"/>
- </div>
- <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- </div>
- <footer>
- <mk-reactions-viewer :note="p" ref="reactionsViewer"/>
- <button @click="reply" title="%i18n:desktop.tags.mk-timeline-note.reply%">
- %fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
- </button>
- <button @click="renote" title="%i18n:desktop.tags.mk-timeline-note.renote%">
- %fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
- </button>
- <button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:desktop.tags.mk-timeline-note.add-reaction%">
- %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>
- <button title="%i18n:desktop.tags.mk-timeline-note.detail">
- <template v-if="!isDetailOpened">%fa:caret-down%</template>
- <template v-if="isDetailOpened">%fa:caret-up%</template>
- </button>
- </footer>
- </div>
- </article>
- <div class="detail" v-if="isDetailOpened">
- <mk-note-status-graph width="462" height="130" :note="p"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import dateStringify from '../../../common/scripts/date-stringify';
-import parse from '../../../../../text/parse';
-
-import MkPostFormWindow from './post-form-window.vue';
-import MkRenoteFormWindow from './renote-form-window.vue';
-import MkNoteMenu from '../../../common/views/components/note-menu.vue';
-import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
-import XSub from './notes.note.sub.vue';
-
-function focus(el, fn) {
- const target = fn(el);
- if (target) {
- if (target.hasAttribute('tabindex')) {
- target.focus();
- } else {
- focus(target, fn);
- }
- }
-}
-
-export default Vue.extend({
- components: {
- XSub
- },
-
- props: ['note'],
-
- data() {
- return {
- isDetailOpened: false,
- connection: null,
- connectionId: null
- };
- },
-
- computed: {
- isRenote(): boolean {
- return (this.note.renote &&
- this.note.text == null &&
- this.note.mediaIds.length == 0 &&
- this.note.poll == null);
- },
- p(): any {
- return this.isRenote ? this.note.renote : this.note;
- },
- reactionsCount(): number {
- return this.p.reactionCounts
- ? Object.keys(this.p.reactionCounts)
- .map(key => this.p.reactionCounts[key])
- .reduce((a, b) => a + b)
- : 0;
- },
- title(): string {
- return dateStringify(this.p.createdAt);
- },
- urls(): string[] {
- if (this.p.text) {
- const ast = parse(this.p.text);
- return ast
- .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
- .map(t => t.url);
- } else {
- return null;
- }
- }
- },
-
- created() {
- if ((this as any).os.isSignedIn) {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
- }
- },
-
- mounted() {
- this.capture(true);
-
- if ((this as any).os.isSignedIn) {
- this.connection.on('_connected_', this.onStreamConnected);
- }
-
- // Draw map
- if (this.p.geo) {
- const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
- if (shouldShowMap) {
- (this as any).os.getGoogleMaps().then(maps => {
- const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
- const map = new maps.Map(this.$refs.map, {
- center: uluru,
- zoom: 15
- });
- new maps.Marker({
- position: uluru,
- map: map
- });
- });
- }
- }
- },
-
- beforeDestroy() {
- this.decapture(true);
-
- if ((this as any).os.isSignedIn) {
- this.connection.off('_connected_', this.onStreamConnected);
- (this as any).os.stream.dispose(this.connectionId);
- }
- },
-
- methods: {
- capture(withHandler = false) {
- if ((this as any).os.isSignedIn) {
- this.connection.send({
- type: 'capture',
- id: this.p.id
- });
- if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
- }
- },
- decapture(withHandler = false) {
- if ((this as any).os.isSignedIn) {
- this.connection.send({
- type: 'decapture',
- id: this.p.id
- });
- if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
- }
- },
- onStreamConnected() {
- this.capture();
- },
- onStreamNoteUpdated(data) {
- const note = data.note;
- if (note.id == this.note.id) {
- this.$emit('update:note', note);
- } else if (note.id == this.note.renoteId) {
- this.note.renote = note;
- }
- },
- reply() {
- (this as any).os.new(MkPostFormWindow, {
- reply: this.p
- });
- },
- renote() {
- (this as any).os.new(MkRenoteFormWindow, {
- note: this.p
- });
- },
- react() {
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- note: this.p
- });
- },
- menu() {
- (this as any).os.new(MkNoteMenu, {
- source: this.$refs.menuButton,
- note: this.p
- });
- },
- onKeydown(e) {
- let shouldBeCancel = true;
-
- switch (true) {
- case e.which == 38: // [↑]
- case e.which == 74: // [j]
- case e.which == 9 && e.shiftKey: // [Shift] + [Tab]
- focus(this.$el, e => e.previousElementSibling);
- break;
-
- case e.which == 40: // [↓]
- case e.which == 75: // [k]
- case e.which == 9: // [Tab]
- focus(this.$el, e => e.nextElementSibling);
- break;
-
- case e.which == 81: // [q]
- case e.which == 69: // [e]
- this.renote();
- break;
-
- case e.which == 70: // [f]
- case e.which == 76: // [l]
- //this.like();
- break;
-
- case e.which == 82: // [r]
- this.reply();
- break;
-
- default:
- shouldBeCancel = false;
- }
-
- if (shouldBeCancel) e.preventDefault();
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.note
- margin 0
- padding 0
- background #fff
- border-bottom solid 1px #eaeaea
-
- &:first-child
- border-top-left-radius 6px
- border-top-right-radius 6px
-
- > .renote
- border-top-left-radius 6px
- border-top-right-radius 6px
-
- &:last-of-type
- border-bottom none
-
- &:focus
- z-index 1
-
- &:after
- content ""
- pointer-events none
- position absolute
- top 2px
- right 2px
- bottom 2px
- left 2px
- border 2px solid rgba($theme-color, 0.3)
- border-radius 4px
-
- > .renote
- color #9dbb00
- background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
-
- > p
- margin 0
- padding 16px 32px
- line-height 28px
-
- .avatar-anchor
- display inline-block
-
- .avatar
- vertical-align bottom
- width 28px
- height 28px
- margin 0 8px 0 0
- border-radius 6px
-
- [data-fa]
- margin-right 4px
-
- .name
- font-weight bold
-
- > .mk-time
- position absolute
- top 16px
- right 32px
- font-size 0.9em
- line-height 28px
-
- & + article
- padding-top 8px
-
- > .reply-to
- padding 0 16px
- background rgba(0, 0, 0, 0.0125)
-
- > .mk-note-preview
- background transparent
-
- > article
- padding 28px 32px 18px 32px
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > .avatar-anchor
- display block
- float left
- margin 0 16px 10px 0
- //position -webkit-sticky
- //position sticky
- //top 74px
-
- > .avatar
- display block
- width 58px
- height 58px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- > .main
- float left
- width calc(100% - 74px)
-
- > header
- display flex
- align-items center
- margin-bottom 4px
- white-space nowrap
-
- > .name
- display block
- margin 0 .5em 0 0
- padding 0
- overflow hidden
- color #627079
- font-size 1em
- font-weight bold
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .is-bot
- margin 0 .5em 0 0
- padding 1px 6px
- font-size 12px
- color #aaa
- border solid 1px #ddd
- border-radius 3px
-
- > .username
- margin 0 .5em 0 0
- color #ccc
-
- > .info
- margin-left auto
- font-size 0.9em
-
- > .mobile
- margin-right 8px
- color #ccc
-
- > .app
- margin-right 8px
- padding-right 8px
- color #ccc
- border-right solid 1px #eaeaea
-
- > .created-at
- color #c0c0c0
-
- > .body
-
- > .text
- cursor default
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 1.1em
- color #717171
-
- >>> .quote
- margin 8px
- padding 6px 12px
- color #aaa
- border-left solid 3px #eee
-
- > .reply
- margin-right 8px
- color #717171
-
- > .rp
- margin-left 4px
- font-style oblique
- color #a0bf46
-
- > .location
- margin 4px 0
- font-size 12px
- color #ccc
-
- > .map
- width 100%
- height 300px
-
- &:empty
- display none
-
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background #fff
- border-radius 100%
-
- &:hover
- text-decoration none
- background #e2e7ec
-
- .mk-url-preview
- margin-top 8px
-
- > .channel
- margin 0
-
- > .mk-poll
- font-size 80%
-
- > .renote
- margin 8px 0
-
- > .mk-note-preview
- padding 16px
- border dashed 1px #c0dac6
- border-radius 8px
-
- > footer
- > button
- margin 0 28px 0 0
- padding 0 8px
- line-height 32px
- font-size 1em
- color #ddd
- background transparent
- border none
- cursor pointer
-
- &:hover
- color #666
-
- > .count
- display inline
- margin 0 0 0 8px
- color #999
-
- &.reacted
- color $theme-color
-
- &:last-child
- position absolute
- right 0
- margin 0
-
- > .detail
- padding-top 4px
- background rgba(0, 0, 0, 0.0125)
-
-</style>
-
-<style lang="stylus" module>
-.text
-
- code
- padding 4px 8px
- margin 0 0.5em
- font-size 80%
- color #525252
- background #f8f8f8
- border-radius 2px
-
- pre > code
- padding 16px
- margin 0
-
- [data-is-me]:after
- content "you"
- padding 0 4px
- margin-left 4px
- font-size 80%
- color $theme-color-foreground
- background $theme-color
- border-radius 4px
-</style>
diff --git a/src/client/app/desktop/views/widgets/channel.channel.post.vue b/src/client/app/desktop/views/widgets/channel.channel.post.vue
deleted file mode 100644
index 7767919066..0000000000
--- a/src/client/app/desktop/views/widgets/channel.channel.post.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<template>
-<div class="note">
- <header>
- <a class="index" @click="reply">{{ note.index }}:</a>
- <router-link class="name" :to="note.user | userPage" v-user-preview="note.user.id"><b>{{ note.user | userName }}</b></router-link>
- <span>ID:<i>{{ note.user | acct }}</i></span>
- </header>
- <div>
- <a v-if="note.reply">&gt;&gt;{{ note.reply.index }}</a>
- {{ note.text }}
- <div class="media" v-if="note.media">
- <a v-for="file in note.media" :href="file.url" target="_blank">
- <img :src="`${file.url}?thumbnail&size=512`" :alt="file.name" :title="file.name"/>
- </a>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: ['note'],
- methods: {
- reply() {
- this.$emit('reply', this.note);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.note
- margin 0
- padding 0
- color #444
-
- > header
- position -webkit-sticky
- position sticky
- z-index 1
- top 0
- padding 8px 4px 4px 16px
- background rgba(255, 255, 255, 0.9)
-
- > .index
- margin-right 0.25em
-
- > .name
- margin-right 0.5em
- color #008000
-
- > div
- padding 0 16px 16px 16px
-
- > .media
- > a
- display inline-block
-
- > img
- max-width 100%
- vertical-align bottom
-
-</style>
diff --git a/src/client/app/mobile/views/components/post-card.vue b/src/client/app/mobile/views/components/post-card.vue
deleted file mode 100644
index 393fa9b831..0000000000
--- a/src/client/app/mobile/views/components/post-card.vue
+++ /dev/null
@@ -1,85 +0,0 @@
-<template>
-<div class="mk-note-card">
- <a :href="note | notePage">
- <header>
- <img :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/><h3>{{ note.user | userName }}</h3>
- </header>
- <div>
- {{ text }}
- </div>
- <mk-time :time="note.createdAt"/>
- </a>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import summary from '../../../../../renderers/get-note-summary';
-
-export default Vue.extend({
- props: ['note'],
- computed: {
- text(): string {
- return summary(this.note);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-note-card
- display inline-block
- width 150px
- //height 120px
- font-size 12px
- background #fff
- border-radius 4px
-
- > a
- display block
- color #2c3940
-
- &:hover
- text-decoration none
-
- > header
- > img
- position absolute
- top 8px
- left 8px
- width 28px
- height 28px
- border-radius 6px
-
- > h3
- display inline-block
- overflow hidden
- width calc(100% - 45px)
- margin 8px 0 0 42px
- line-height 28px
- white-space nowrap
- text-overflow ellipsis
- font-size 12px
-
- > div
- padding 2px 8px 8px 8px
- height 60px
- overflow hidden
- white-space normal
-
- &:after
- content ""
- display block
- position absolute
- top 40px
- left 0
- width 100%
- height 20px
- background linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #fff 100%)
-
- > .mk-time
- display inline-block
- padding 8px
- color #aaa
-
-</style>
diff --git a/src/client/app/mobile/views/components/post-detail.sub.vue b/src/client/app/mobile/views/components/post-detail.sub.vue
deleted file mode 100644
index 06f442d308..0000000000
--- a/src/client/app/mobile/views/components/post-detail.sub.vue
+++ /dev/null
@@ -1,103 +0,0 @@
-<template>
-<div class="root sub">
- <router-link class="avatar-anchor" :to="note.user | userPage">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
- <router-link class="time" :to="note | notePage">
- <mk-time :time="note.createdAt"/>
- </router-link>
- </header>
- <div class="body">
- <mk-sub-note-content class="text" :note="note"/>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: ['note']
-});
-</script>
-
-<style lang="stylus" scoped>
-.root.sub
- padding 8px
- font-size 0.9em
- background #fdfdfd
-
- @media (min-width 500px)
- padding 12px
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > .avatar-anchor
- display block
- float left
- margin 0 12px 0 0
-
- > .avatar
- display block
- width 48px
- height 48px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- > .main
- float left
- width calc(100% - 60px)
-
- > header
- display flex
- margin-bottom 4px
- white-space nowrap
-
- > .name
- display block
- margin 0 .5em 0 0
- padding 0
- overflow hidden
- color #607073
- font-size 1em
- font-weight 700
- text-align left
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .username
- text-align left
- margin 0 .5em 0 0
- color #d1d8da
-
- > .time
- margin-left auto
- color #b2b8bb
-
- > .body
-
- > .text
- cursor default
- margin 0
- padding 0
- font-size 1.1em
- color #717171
-
-</style>
-
diff --git a/src/client/app/mobile/views/components/post-detail.vue b/src/client/app/mobile/views/components/post-detail.vue
deleted file mode 100644
index de32f0a748..0000000000
--- a/src/client/app/mobile/views/components/post-detail.vue
+++ /dev/null
@@ -1,444 +0,0 @@
-<template>
-<div class="mk-note-detail">
- <button
- class="more"
- v-if="p.reply && p.reply.replyId && context == null"
- @click="fetchContext"
- :disabled="fetchingContext"
- >
- <template v-if="!contextFetching">%fa:ellipsis-v%</template>
- <template v-if="contextFetching">%fa:spinner .pulse%</template>
- </button>
- <div class="context">
- <x-sub v-for="note in context" :key="note.id" :note="note"/>
- </div>
- <div class="reply-to" v-if="p.reply">
- <x-sub :note="p.reply"/>
- </div>
- <div class="renote" v-if="isRenote">
- <p>
- <router-link class="avatar-anchor" :to="note.user | userPage">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
- </router-link>
- %fa:retweet%<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>がRenote
- </p>
- </div>
- <article>
- <header>
- <router-link class="avatar-anchor" :to="p.user | userPage">
- <img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div>
- <router-link class="name" :to="p.user | userPage">{{ p.user | userName }}</router-link>
- <span class="username">@{{ p.user | acct }}</span>
- </div>
- </header>
- <div class="body">
- <mk-note-html v-if="p.text" :ast="p.text" :i="os.i" :class="$style.text"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
- </div>
- <div class="media" v-if="p.media.length > 0">
- <mk-media-list :media-list="p.media"/>
- </div>
- <mk-poll v-if="p.poll" :note="p"/>
- <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
- <div class="map" v-if="p.geo" ref="map"></div>
- <div class="renote" v-if="p.renote">
- <mk-note-preview :note="p.renote"/>
- </div>
- </div>
- <router-link class="time" :to="`/@${pAcct}/${p.id}`">
- <mk-time :time="p.createdAt" mode="detail"/>
- </router-link>
- <footer>
- <mk-reactions-viewer :note="p"/>
- <button @click="reply" title="%i18n:mobile.tags.mk-note-detail.reply%">
- %fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
- </button>
- <button @click="renote" title="Renote">
- %fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
- </button>
- <button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-note-detail.reaction%">
- %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">
- <x-sub v-for="note in replies" :key="note.id" :note="note"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import parse from '../../../../../text/parse';
-
-import MkNoteMenu from '../../../common/views/components/note-menu.vue';
-import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
-import XSub from './note-detail.sub.vue';
-
-export default Vue.extend({
- components: {
- XSub
- },
-
- props: {
- note: {
- type: Object,
- required: true
- },
- compact: {
- default: false
- }
- },
-
- data() {
- return {
- context: [],
- contextFetching: false,
- replies: []
- };
- },
-
- computed: {
- isRenote(): boolean {
- return (this.note.renote &&
- this.note.text == null &&
- this.note.mediaIds.length == 0 &&
- this.note.poll == null);
- },
- p(): any {
- return this.isRenote ? this.note.renote : this.note;
- },
- reactionsCount(): number {
- return this.p.reactionCounts
- ? Object.keys(this.p.reactionCounts)
- .map(key => this.p.reactionCounts[key])
- .reduce((a, b) => a + b)
- : 0;
- },
- urls(): string[] {
- if (this.p.text) {
- const ast = parse(this.p.text);
- return 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('notes/replies', {
- noteId: this.p.id,
- limit: 8
- }).then(replies => {
- this.replies = replies;
- });
- }
-
- // Draw map
- if (this.p.geo) {
- const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
- if (shouldShowMap) {
- (this as any).os.getGoogleMaps().then(maps => {
- const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
- const map = new maps.Map(this.$refs.map, {
- center: uluru,
- zoom: 15
- });
- new maps.Marker({
- position: uluru,
- map: map
- });
- });
- }
- }
- },
-
- methods: {
- fetchContext() {
- this.contextFetching = true;
-
- // Fetch context
- (this as any).api('notes/context', {
- noteId: this.p.replyId
- }).then(context => {
- this.contextFetching = false;
- this.context = context.reverse();
- });
- },
- reply() {
- (this as any).apis.post({
- reply: this.p
- });
- },
- renote() {
- (this as any).apis.post({
- renote: this.p
- });
- },
- react() {
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- note: this.p,
- compact: true
- });
- },
- menu() {
- (this as any).os.new(MkNoteMenu, {
- source: this.$refs.menuButton,
- note: this.p,
- compact: true
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-note-detail
- overflow hidden
- margin 0 auto
- padding 0
- width 100%
- text-align left
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- > .fetching
- padding 64px 0
-
- > .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
- box-shadow none
-
- &:hover
- background #f6f6f6
-
- &:active
- background #f0f0f0
-
- &:disabled
- color #ccc
-
- > .context
- > *
- border-bottom 1px solid #eef0f2
-
- > .renote
- 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 14px 16px 9px 16px
-
- @media (min-width 500px)
- padding 28px 32px 18px 32px
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > header
- display flex
- line-height 1.1
-
- > .avatar-anchor
- display block
- padding 0 .5em 0 0
-
- > .avatar
- display block
- width 54px
- height 54px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- @media (min-width 500px)
- width 60px
- height 60px
-
- > div
-
- > .name
- display inline-block
- margin .4em 0
- color #777
- font-size 16px
- font-weight bold
- text-align left
- text-decoration none
-
- &:hover
- text-decoration underline
-
- > .username
- display block
- text-align left
- margin 0
- color #ccc
-
- > .body
- padding 8px 0
-
- > .renote
- margin 8px 0
-
- > .mk-note-preview
- padding 16px
- border dashed 1px #c0dac6
- border-radius 8px
-
- > .location
- margin 4px 0
- font-size 12px
- color #ccc
-
- > .map
- width 100%
- height 200px
-
- &:empty
- display none
-
- > .mk-url-preview
- margin-top 8px
-
- > .media
- > img
- display block
- max-width 100%
-
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background #fff
- border-radius 100%
-
- > .time
- font-size 16px
- color #c0c0c0
-
- > footer
- font-size 1.2em
-
- > button
- margin 0
- padding 8px
- background transparent
- border none
- box-shadow none
- font-size 1em
- color #ddd
- cursor pointer
-
- &:not(:last-child)
- margin-right 28px
-
- &:hover
- color #666
-
- > .count
- display inline
- margin 0 0 0 8px
- color #999
-
- &.reacted
- color $theme-color
-
- > .replies
- > *
- border-top 1px solid #eef0f2
-
-</style>
-
-<style lang="stylus" module>
-.text
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 16px
- color #717171
-
- @media (min-width 500px)
- font-size 24px
-
-</style>
diff --git a/src/client/app/mobile/views/components/post-preview.vue b/src/client/app/mobile/views/components/post-preview.vue
deleted file mode 100644
index b9a6db315d..0000000000
--- a/src/client/app/mobile/views/components/post-preview.vue
+++ /dev/null
@@ -1,100 +0,0 @@
-<template>
-<div class="mk-note-preview">
- <router-link class="avatar-anchor" :to="note.user | userPage">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
- <router-link class="time" :to="note | notePage">
- <mk-time :time="note.createdAt"/>
- </router-link>
- </header>
- <div class="body">
- <mk-sub-note-content class="text" :note="note"/>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: ['note']
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-note-preview
- margin 0
- padding 0
- font-size 0.9em
- background #fff
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > .avatar-anchor
- display block
- float left
- margin 0 12px 0 0
-
- > .avatar
- display block
- width 48px
- height 48px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- > .main
- float left
- width calc(100% - 60px)
-
- > header
- display flex
- margin-bottom 4px
- white-space nowrap
-
- > .name
- display block
- margin 0 .5em 0 0
- padding 0
- overflow hidden
- color #607073
- font-size 1em
- font-weight 700
- text-align left
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .username
- text-align left
- margin 0 .5em 0 0
- color #d1d8da
-
- > .time
- margin-left auto
- color #b2b8bb
-
- > .body
-
- > .text
- cursor default
- margin 0
- padding 0
- font-size 1.1em
- color #717171
-
-</style>
diff --git a/src/client/app/mobile/views/components/post.vue b/src/client/app/mobile/views/components/post.vue
deleted file mode 100644
index 033de4f429..0000000000
--- a/src/client/app/mobile/views/components/post.vue
+++ /dev/null
@@ -1,523 +0,0 @@
-<template>
-<div class="note" :class="{ renote: isRenote }">
- <div class="reply-to" v-if="p.reply">
- <x-sub :note="p.reply"/>
- </div>
- <div class="renote" v-if="isRenote">
- <p>
- <router-link class="avatar-anchor" :to="note.user | userPage">
- <img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- %fa:retweet%
- <span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
- <router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
- <span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
- </p>
- <mk-time :time="note.createdAt"/>
- </div>
- <article>
- <router-link class="avatar-anchor" :to="p.user | userPage">
- <img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="p.user | userPage">{{ p.user | userName }}</router-link>
- <span class="is-bot" v-if="p.user.host === null && p.user.isBot">bot</span>
- <span class="username">@{{ p.user | acct }}</span>
- <div class="info">
- <span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
- <router-link class="created-at" :to="p | notePage">
- <mk-time :time="p.createdAt"/>
- </router-link>
- </div>
- </header>
- <div class="body">
- <p class="channel" v-if="p.channel != null"><a target="_blank">{{ p.channel.title }}</a>:</p>
- <div class="text">
- <a class="reply" v-if="p.reply">
- %fa:reply%
- </a>
- <mk-note-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
- <a class="rp" v-if="p.renote != null">RP:</a>
- </div>
- <div class="media" v-if="p.media.length > 0">
- <mk-media-list :media-list="p.media"/>
- </div>
- <mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
- </div>
- <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
- <div class="map" v-if="p.geo" ref="map"></div>
- <span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
- <div class="renote" v-if="p.renote">
- <mk-note-preview :note="p.renote"/>
- </div>
- </div>
- <footer>
- <mk-reactions-viewer :note="p" ref="reactionsViewer"/>
- <button @click="reply">
- %fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
- </button>
- <button @click="renote" title="Renote">
- %fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
- </button>
- <button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">
- %fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
- </button>
- <button class="menu" @click="menu" ref="menuButton">
- %fa:ellipsis-h%
- </button>
- </footer>
- </div>
- </article>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import parse from '../../../../../text/parse';
-
-import MkNoteMenu from '../../../common/views/components/note-menu.vue';
-import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
-import XSub from './note.sub.vue';
-
-export default Vue.extend({
- components: {
- XSub
- },
-
- props: ['note'],
-
- data() {
- return {
- connection: null,
- connectionId: null
- };
- },
-
- computed: {
- isRenote(): boolean {
- return (this.note.renote &&
- this.note.text == null &&
- this.note.mediaIds.length == 0 &&
- this.note.poll == null);
- },
- p(): any {
- return this.isRenote ? this.note.renote : this.note;
- },
- reactionsCount(): number {
- return this.p.reactionCounts
- ? Object.keys(this.p.reactionCounts)
- .map(key => this.p.reactionCounts[key])
- .reduce((a, b) => a + b)
- : 0;
- },
- urls(): string[] {
- if (this.p.text) {
- const ast = parse(this.p.text);
- return ast
- .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
- .map(t => t.url);
- } else {
- return null;
- }
- }
- },
-
- created() {
- if ((this as any).os.isSignedIn) {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
- }
- },
-
- mounted() {
- this.capture(true);
-
- if ((this as any).os.isSignedIn) {
- this.connection.on('_connected_', this.onStreamConnected);
- }
-
- // Draw map
- if (this.p.geo) {
- const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.clientSettings.showMaps : true;
- if (shouldShowMap) {
- (this as any).os.getGoogleMaps().then(maps => {
- const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
- const map = new maps.Map(this.$refs.map, {
- center: uluru,
- zoom: 15
- });
- new maps.Marker({
- position: uluru,
- map: map
- });
- });
- }
- }
- },
-
- beforeDestroy() {
- this.decapture(true);
-
- if ((this as any).os.isSignedIn) {
- this.connection.off('_connected_', this.onStreamConnected);
- (this as any).os.stream.dispose(this.connectionId);
- }
- },
-
- methods: {
- capture(withHandler = false) {
- if ((this as any).os.isSignedIn) {
- this.connection.send({
- type: 'capture',
- id: this.p.id
- });
- if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
- }
- },
- decapture(withHandler = false) {
- if ((this as any).os.isSignedIn) {
- this.connection.send({
- type: 'decapture',
- id: this.p.id
- });
- if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
- }
- },
- onStreamConnected() {
- this.capture();
- },
- onStreamNoteUpdated(data) {
- const note = data.note;
- if (note.id == this.note.id) {
- this.$emit('update:note', note);
- } else if (note.id == this.note.renoteId) {
- this.note.renote = note;
- }
- },
- reply() {
- (this as any).apis.post({
- reply: this.p
- });
- },
- renote() {
- (this as any).apis.post({
- renote: this.p
- });
- },
- react() {
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- note: this.p,
- compact: true
- });
- },
- menu() {
- (this as any).os.new(MkNoteMenu, {
- source: this.$refs.menuButton,
- note: this.p,
- compact: true
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.note
- font-size 12px
- border-bottom solid 1px #eaeaea
-
- &:first-child
- border-radius 8px 8px 0 0
-
- > .renote
- border-radius 8px 8px 0 0
-
- &:last-of-type
- border-bottom none
-
- @media (min-width 350px)
- font-size 14px
-
- @media (min-width 500px)
- font-size 16px
-
- > .renote
- color #9dbb00
- background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
-
- > p
- margin 0
- padding 8px 16px
- line-height 28px
-
- @media (min-width 500px)
- padding 16px
-
- .avatar-anchor
- display inline-block
-
- .avatar
- vertical-align bottom
- width 28px
- height 28px
- margin 0 8px 0 0
- border-radius 6px
-
- [data-fa]
- margin-right 4px
-
- .name
- font-weight bold
-
- > .mk-time
- position absolute
- top 8px
- right 16px
- font-size 0.9em
- line-height 28px
-
- @media (min-width 500px)
- top 16px
-
- & + article
- padding-top 8px
-
- > .reply-to
- background rgba(0, 0, 0, 0.0125)
-
- > .mk-note-preview
- background transparent
-
- > article
- padding 14px 16px 9px 16px
-
- &:after
- content ""
- display block
- clear both
-
- > .avatar-anchor
- display block
- float left
- margin 0 10px 8px 0
- position -webkit-sticky
- position sticky
- top 62px
-
- @media (min-width 500px)
- margin-right 16px
-
- > .avatar
- display block
- width 48px
- height 48px
- margin 0
- border-radius 6px
- vertical-align bottom
-
- @media (min-width 500px)
- width 58px
- height 58px
- border-radius 8px
-
- > .main
- float left
- width calc(100% - 58px)
-
- @media (min-width 500px)
- width calc(100% - 74px)
-
- > header
- display flex
- align-items center
- white-space nowrap
-
- @media (min-width 500px)
- margin-bottom 2px
-
- > .name
- display block
- margin 0 0.5em 0 0
- padding 0
- overflow hidden
- color #627079
- font-size 1em
- font-weight bold
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .is-bot
- margin 0 0.5em 0 0
- padding 1px 6px
- font-size 12px
- color #aaa
- border solid 1px #ddd
- border-radius 3px
-
- > .username
- margin 0 0.5em 0 0
- color #ccc
-
- > .info
- margin-left auto
- font-size 0.9em
-
- > .mobile
- margin-right 6px
- color #c0c0c0
-
- > .created-at
- color #c0c0c0
-
- > .body
-
- > .text
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 1.1em
- color #717171
-
- >>> .quote
- margin 8px
- padding 6px 12px
- color #aaa
- border-left solid 3px #eee
-
- > .reply
- margin-right 8px
- color #717171
-
- > .rp
- margin-left 4px
- font-style oblique
- color #a0bf46
-
- [data-is-me]:after
- content "you"
- padding 0 4px
- margin-left 4px
- font-size 80%
- color $theme-color-foreground
- background $theme-color
- border-radius 4px
-
- .mk-url-preview
- margin-top 8px
-
- > .channel
- margin 0
-
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background #fff
- border-radius 100%
-
- > .media
- > img
- display block
- max-width 100%
-
- > .location
- margin 4px 0
- font-size 12px
- color #ccc
-
- > .map
- width 100%
- height 200px
-
- &:empty
- display none
-
- > .app
- font-size 12px
- color #ccc
-
- > .mk-poll
- font-size 80%
-
- > .renote
- margin 8px 0
-
- > .mk-note-preview
- padding 16px
- border dashed 1px #c0dac6
- border-radius 8px
-
- > footer
- > button
- margin 0
- padding 8px
- background transparent
- border none
- box-shadow none
- font-size 1em
- color #ddd
- cursor pointer
-
- &:not(:last-child)
- margin-right 28px
-
- &:hover
- color #666
-
- > .count
- display inline
- margin 0 0 0 8px
- color #999
-
- &.reacted
- color $theme-color
-
- &.menu
- @media (max-width 350px)
- display none
-
-</style>
-
-<style lang="stylus" module>
-.text
- code
- padding 4px 8px
- margin 0 0.5em
- font-size 80%
- color #525252
- background #f8f8f8
- border-radius 2px
-
- pre > code
- padding 16px
- margin 0
-</style>