summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-10-12 14:28:48 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-10-12 14:28:48 +0900
commitdded76099cd0214828ef493aaa555347a43bf58e (patch)
treeffcdf72e6ed6dcfb1b2b0f779f000807ee8db3f5 /src/client
parentFix bug (diff)
downloadsharkey-dded76099cd0214828ef493aaa555347a43bf58e.tar.gz
sharkey-dded76099cd0214828ef493aaa555347a43bf58e.tar.bz2
sharkey-dded76099cd0214828ef493aaa555347a43bf58e.zip
Refactor and usability improvements
Diffstat (limited to 'src/client')
-rw-r--r--src/client/app/common/scripts/note-mixin.ts141
-rw-r--r--src/client/app/desktop/views/components/notes.note.vue202
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.note.vue149
-rw-r--r--src/client/app/mobile/views/components/note.vue148
4 files changed, 299 insertions, 341 deletions
diff --git a/src/client/app/common/scripts/note-mixin.ts b/src/client/app/common/scripts/note-mixin.ts
new file mode 100644
index 0000000000..ac276dbe68
--- /dev/null
+++ b/src/client/app/common/scripts/note-mixin.ts
@@ -0,0 +1,141 @@
+import parse from '../../../../mfm/parse';
+import { sum } from '../../../../prelude/array';
+import MkNoteMenu from '..//views/components/note-menu.vue';
+import MkReactionPicker from '../views/components/reaction-picker.vue';
+
+function focus(el, fn) {
+ const target = fn(el);
+ if (target) {
+ if (target.hasAttribute('tabindex')) {
+ target.focus();
+ } else {
+ focus(target, fn);
+ }
+ }
+}
+
+type Opts = {
+ mobile?: boolean;
+};
+
+export default (opts: Opts = {}) => ({
+ data() {
+ return {
+ showContent: false
+ };
+ },
+
+ computed: {
+ keymap(): any {
+ return {
+ 'r|left': () => this.reply(true),
+ 'e|a|plus': () => this.react(true),
+ 'q|right': () => this.renote(true),
+ 'ctrl+q|ctrl+right': this.renoteDirectly,
+ 'up|k|shift+tab': this.focusBefore,
+ 'down|j|tab': this.focusAfter,
+ 'esc': this.blur,
+ 'm|o': () => this.menu(true),
+ 's': this.toggleShowContent,
+ '1': () => this.reactDirectly('like'),
+ '2': () => this.reactDirectly('love'),
+ '3': () => this.reactDirectly('laugh'),
+ '4': () => this.reactDirectly('hmm'),
+ '5': () => this.reactDirectly('surprise'),
+ '6': () => this.reactDirectly('congrats'),
+ '7': () => this.reactDirectly('angry'),
+ '8': () => this.reactDirectly('confused'),
+ '9': () => this.reactDirectly('rip'),
+ '0': () => this.reactDirectly('pudding'),
+ };
+ },
+
+ isRenote(): boolean {
+ return (this.note.renote &&
+ this.note.text == null &&
+ this.note.fileIds.length == 0 &&
+ this.note.poll == null);
+ },
+
+ appearNote(): any {
+ return this.isRenote ? this.note.renote : this.note;
+ },
+
+ reactionsCount(): number {
+ return this.appearNote.reactionCounts
+ ? sum(Object.values(this.appearNote.reactionCounts))
+ : 0;
+ },
+
+ title(): string {
+ return new Date(this.appearNote.createdAt).toLocaleString();
+ },
+
+ urls(): string[] {
+ if (this.appearNote.text) {
+ const ast = parse(this.appearNote.text);
+ return ast
+ .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
+ .map(t => t.url);
+ } else {
+ return null;
+ }
+ }
+ },
+
+ methods: {
+ renoteDirectly() {
+ (this as any).api('notes/create', {
+ renoteId: this.appearNote.id
+ });
+ },
+
+ react(viaKeyboard = false) {
+ this.blur();
+ (this as any).os.new(MkReactionPicker, {
+ source: this.$refs.reactButton,
+ note: this.appearNote,
+ showFocus: viaKeyboard,
+ animation: !viaKeyboard,
+ compact: opts.mobile,
+ big: opts.mobile
+ }).$once('closed', this.focus);
+ },
+
+ reactDirectly(reaction) {
+ (this as any).api('notes/reactions/create', {
+ noteId: this.appearNote.id,
+ reaction: reaction
+ });
+ },
+
+ menu(viaKeyboard = false) {
+ (this as any).os.new(MkNoteMenu, {
+ source: this.$refs.menuButton,
+ note: this.appearNote,
+ animation: !viaKeyboard,
+ compact: opts.mobile,
+ }).$once('closed', this.focus);
+ },
+
+ toggleShowContent() {
+ this.showContent = !this.showContent;
+ },
+
+ focus() {
+ this.$el.focus();
+ },
+
+ blur() {
+ this.$el.blur();
+ },
+
+ focusBefore() {
+ focus(this.$el, e => e.previousElementSibling);
+ },
+
+ focusAfter() {
+ focus(this.$el, e => e.nextElementSibling);
+ }
+ }
+});
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index f6e100a2a7..a53759cf40 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -1,7 +1,7 @@
<template>
-<div class="note" v-show="p.deletedAt == null" :tabindex="p.deletedAt == null ? '-1' : null" v-hotkey="keymap" :title="title">
- <div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
- <x-sub :note="p.reply"/>
+<div class="note" v-show="appearNote.deletedAt == null" :tabindex="appearNote.deletedAt == null ? '-1' : null" v-hotkey="keymap" :title="title">
+ <div class="reply-to" v-if="appearNote.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
+ <x-sub :note="appearNote.reply"/>
</div>
<div class="renote" v-if="isRenote">
<mk-avatar class="avatar" :user="note.user"/>
@@ -12,90 +12,70 @@
<mk-time :time="note.createdAt"/>
</div>
<article>
- <mk-avatar class="avatar" :user="p.user"/>
+ <mk-avatar class="avatar" :user="appearNote.user"/>
<div class="main">
- <mk-note-header class="header" :note="p"/>
+ <mk-note-header class="header" :note="appearNote"/>
<div class="body">
- <p v-if="p.cw != null" class="cw">
- <span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
+ <p v-if="appearNote.cw != null" class="cw">
+ <span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
<mk-cw-button v-model="showContent"/>
</p>
- <div class="content" v-show="p.cw == null || showContent">
+ <div class="content" v-show="appearNote.cw == null || showContent">
<div class="text">
- <span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
- <span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
- <a class="reply" v-if="p.reply">%fa:reply%</a>
- <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :class="$style.text"/>
- <a class="rp" v-if="p.renote">RP:</a>
+ <span v-if="appearNote.isHidden" style="opacity: 0.5">%i18n:@private%</span>
+ <a class="reply" v-if="appearNote.reply">%fa:reply%</a>
+ <misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text"/>
+ <a class="rp" v-if="appearNote.renote">RP:</a>
</div>
- <div class="files" v-if="p.files.length > 0">
- <mk-media-list :media-list="p.files"/>
+ <div class="files" v-if="appearNote.files.length > 0">
+ <mk-media-list :media-list="appearNote.files"/>
</div>
- <mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <a class="location" v-if="p.geo" :href="`https://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-poll v-if="appearNote.poll" :note="appearNote" ref="pollViewer"/>
+ <a class="location" v-if="appearNote.geo" :href="`https://maps.google.com/maps?q=${appearNote.geo.coordinates[1]},${appearNote.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
+ <div class="renote" v-if="appearNote.renote"><mk-note-preview :note="appearNote.renote"/></div>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
</div>
</div>
- <footer v-if="p.deletedAt == null">
- <mk-reactions-viewer :note="p" ref="reactionsViewer"/>
+ <footer v-if="appearNote.deletedAt == null">
+ <mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
<button class="replyButton" @click="reply()" title="%i18n:@reply%">
- <template v-if="p.reply">%fa:reply-all%</template>
+ <template v-if="appearNote.reply">%fa:reply-all%</template>
<template v-else>%fa:reply%</template>
- <p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
+ <p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
<button class="renoteButton" @click="renote()" title="%i18n:@renote%">
- %fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
+ %fa:retweet%<p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
</button>
- <button class="reactionButton" :class="{ reacted: p.myReaction != null }" @click="react()" ref="reactButton" title="%i18n:@add-reaction%">
- %fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
+ <button class="reactionButton" :class="{ reacted: appearNote.myReaction != null }" @click="react()" ref="reactButton" title="%i18n:@add-reaction%">
+ %fa:plus%<p class="count" v-if="appearNote.reactions_count > 0">{{ appearNote.reactions_count }}</p>
</button>
<button @click="menu()" ref="menuButton">
%fa:ellipsis-h%
</button>
- <!-- <button title="%i18n:@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 parse from '../../../../../mfm/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';
-import { sum } from '../../../../../prelude/array';
+import noteMixin from '../../../common/scripts/note-mixin';
import noteSubscriber from '../../../common/scripts/note-subscriber';
-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
},
- mixins: [noteSubscriber('note')],
+ mixins: [
+ noteMixin(),
+ noteSubscriber('note')
+ ],
props: {
note: {
@@ -104,136 +84,20 @@ export default Vue.extend({
}
},
- data() {
- return {
- showContent: false,
- isDetailOpened: false
- };
- },
-
- computed: {
- keymap(): any {
- return {
- 'r|left': () => this.reply(true),
- 'e|a|plus': () => this.react(true),
- 'q|right': () => this.renote(true),
- 'ctrl+q|ctrl+right': this.renoteDirectly,
- 'up|k|shift+tab': this.focusBefore,
- 'down|j|tab': this.focusAfter,
- 'esc': this.blur,
- 'm|o': () => this.menu(true),
- 's': this.toggleShowContent,
- '1': () => this.reactDirectly('like'),
- '2': () => this.reactDirectly('love'),
- '3': () => this.reactDirectly('laugh'),
- '4': () => this.reactDirectly('hmm'),
- '5': () => this.reactDirectly('surprise'),
- '6': () => this.reactDirectly('congrats'),
- '7': () => this.reactDirectly('angry'),
- '8': () => this.reactDirectly('confused'),
- '9': () => this.reactDirectly('rip'),
- '0': () => this.reactDirectly('pudding'),
- };
- },
-
- isRenote(): boolean {
- return (this.note.renote &&
- this.note.text == null &&
- this.note.fileIds.length == 0 &&
- this.note.poll == null);
- },
-
- p(): any {
- return this.isRenote ? this.note.renote : this.note;
- },
-
- reactionsCount(): number {
- return this.p.reactionCounts
- ? sum(Object.values(this.p.reactionCounts))
- : 0;
- },
-
- title(): string {
- return new Date(this.p.createdAt).toLocaleString();
- },
-
- 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;
- }
- }
- },
-
methods: {
reply(viaKeyboard = false) {
(this as any).os.new(MkPostFormWindow, {
- reply: this.p,
+ reply: this.appearNote,
animation: !viaKeyboard
}).$once('closed', this.focus);
},
renote(viaKeyboard = false) {
(this as any).os.new(MkRenoteFormWindow, {
- note: this.p,
- animation: !viaKeyboard
- }).$once('closed', this.focus);
- },
-
- renoteDirectly() {
- (this as any).api('notes/create', {
- renoteId: this.p.id
- });
- },
-
- react(viaKeyboard = false) {
- this.blur();
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- note: this.p,
- showFocus: viaKeyboard,
+ note: this.appearNote,
animation: !viaKeyboard
}).$once('closed', this.focus);
},
-
- reactDirectly(reaction) {
- (this as any).api('notes/reactions/create', {
- noteId: this.p.id,
- reaction: reaction
- });
- },
-
- menu(viaKeyboard = false) {
- (this as any).os.new(MkNoteMenu, {
- source: this.$refs.menuButton,
- note: this.p,
- animation: !viaKeyboard
- }).$once('closed', this.focus);
- },
-
- toggleShowContent() {
- this.showContent = !this.showContent;
- },
-
- focus() {
- this.$el.focus();
- },
-
- blur() {
- this.$el.blur();
- },
-
- focusBefore() {
- focus(this.$el, e => e.previousElementSibling);
- },
-
- focusAfter() {
- focus(this.$el, e => e.nextElementSibling);
- }
}
});
</script>
@@ -445,10 +309,6 @@ export default Vue.extend({
&.reacted, &.reacted:hover
color var(--noteActionsReactionHover)
- > .detail
- padding-top 4px
- background rgba(#000, 0.0125)
-
</style>
<style lang="stylus" module>
diff --git a/src/client/app/desktop/views/pages/deck/deck.note.vue b/src/client/app/desktop/views/pages/deck/deck.note.vue
index f495731c79..055bd71573 100644
--- a/src/client/app/desktop/views/pages/deck/deck.note.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.note.vue
@@ -1,7 +1,15 @@
<template>
-<div v-if="!mediaView" v-show="p.deletedAt == null" :tabindex="p.deletedAt == null ? '-1' : null" class="zyjjkidcqjnlegkqebitfviomuqmseqk" :class="{ renote: isRenote }">
- <div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
- <x-sub :note="p.reply"/>
+<div
+ v-if="!mediaView"
+ v-show="appearNote.deletedAt == null"
+ :tabindex="appearNote.deletedAt == null ? '-1' : null"
+ class="zyjjkidcqjnlegkqebitfviomuqmseqk"
+ :class="{ renote: isRenote }"
+ v-hotkey="keymap"
+ :title="title"
+>
+ <div class="reply-to" v-if="appearNote.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
+ <x-sub :note="appearNote.reply"/>
</div>
<div class="renote" v-if="isRenote">
<mk-avatar class="avatar" :user="note.user"/>
@@ -12,43 +20,42 @@
<mk-time :time="note.createdAt"/>
</div>
<article>
- <mk-avatar class="avatar" :user="p.user"/>
+ <mk-avatar class="avatar" :user="appearNote.user"/>
<div class="main">
- <mk-note-header class="header" :note="p" :mini="true"/>
+ <mk-note-header class="header" :note="appearNote" :mini="true"/>
<div class="body">
- <p v-if="p.cw != null" class="cw">
- <span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
+ <p v-if="appearNote.cw != null" class="cw">
+ <span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
<mk-cw-button v-model="showContent"/>
</p>
- <div class="content" v-show="p.cw == null || showContent">
+ <div class="content" v-show="appearNote.cw == null || showContent">
<div class="text">
- <span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
- <span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
- <a class="reply" v-if="p.reply">%fa:reply%</a>
- <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
- <a class="rp" v-if="p.renote != null">RP:</a>
+ <span v-if="appearNote.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
+ <a class="reply" v-if="appearNote.reply">%fa:reply%</a>
+ <misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i"/>
+ <a class="rp" v-if="appearNote.renote != null">RP:</a>
</div>
- <div class="files" v-if="p.files.length > 0">
- <mk-media-list :media-list="p.files"/>
+ <div class="files" v-if="appearNote.files.length > 0">
+ <mk-media-list :media-list="appearNote.files"/>
</div>
- <mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
- <div class="renote" v-if="p.renote">
- <mk-note-preview :note="p.renote" :mini="true"/>
+ <mk-poll v-if="appearNote.poll" :note="appearNote" ref="pollViewer"/>
+ <a class="location" v-if="appearNote.geo" :href="`https://maps.google.com/maps?q=${appearNote.geo.coordinates[1]},${appearNote.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
+ <div class="renote" v-if="appearNote.renote">
+ <mk-note-preview :note="appearNote.renote" :mini="true"/>
</div>
<mk-url-preview v-for="url in urls" :url="url" :key="url" :detail="false" :mini="true"/>
</div>
- <span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
+ <span class="app" v-if="appearNote.app">via <b>{{ appearNote.app.name }}</b></span>
</div>
<footer>
- <mk-reactions-viewer :note="p" ref="reactionsViewer"/>
- <button @click="reply">
- <template v-if="p.reply">%fa:reply-all%</template>
+ <mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
+ <button @click="reply()">
+ <template v-if="appearNote.reply">%fa:reply-all%</template>
<template v-else>%fa:reply%</template>
</button>
- <button @click="renote" title="Renote">%fa:retweet%</button>
- <button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">%fa:plus%</button>
- <button class="menu" @click="menu" ref="menuButton">%fa:ellipsis-h%</button>
+ <button @click="renote()" title="Renote">%fa:retweet%</button>
+ <button :class="{ reacted: appearNote.myReaction != null }" @click="react()" ref="reactButton">%fa:plus%</button>
+ <button class="menu" @click="menu()" ref="menuButton">%fa:ellipsis-h%</button>
</footer>
</div>
</article>
@@ -65,11 +72,10 @@
<script lang="ts">
import Vue from 'vue';
-import parse from '../../../../../../mfm/parse';
-
-import MkNoteMenu from '../../../../common/views/components/note-menu.vue';
-import MkReactionPicker from '../../../../common/views/components/reaction-picker.vue';
+import MkPostFormWindow from '../../components/post-form-window.vue';
+import MkRenoteFormWindow from '../../components/renote-form-window.vue';
import XSub from './deck.note.sub.vue';
+import noteMixin from '../../../../common/scripts/note-mixin';
import noteSubscriber from '../../../../common/scripts/note-subscriber';
export default Vue.extend({
@@ -77,7 +83,10 @@ export default Vue.extend({
XSub
},
- mixins: [noteSubscriber('note')],
+ mixins: [
+ noteMixin(),
+ noteSubscriber('note')
+ ],
props: {
note: {
@@ -91,64 +100,20 @@ export default Vue.extend({
}
},
- data() {
- return {
- showContent: false
- };
- },
-
- computed: {
- isRenote(): boolean {
- return (this.note.renote &&
- this.note.text == null &&
- this.note.fileIds.length == 0 &&
- this.note.poll == null);
- },
-
- p(): any {
- return this.isRenote ? this.note.renote : this.note;
- },
-
- 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;
- }
- }
- },
-
methods: {
- reply() {
- (this as any).apis.post({
- reply: this.p
- });
- },
-
- renote() {
- (this as any).apis.post({
- renote: this.p
- });
+ reply(viaKeyboard = false) {
+ (this as any).os.new(MkPostFormWindow, {
+ reply: this.appearNote,
+ animation: !viaKeyboard
+ }).$once('closed', this.focus);
},
- react() {
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- note: this.p,
- compact: true
- });
+ renote(viaKeyboard = false) {
+ (this as any).os.new(MkRenoteFormWindow, {
+ note: this.appearNote,
+ animation: !viaKeyboard
+ }).$once('closed', this.focus);
},
-
- menu() {
- (this as any).os.new(MkNoteMenu, {
- source: this.$refs.menuButton,
- note: this.p,
- compact: true
- });
- }
}
});
</script>
@@ -168,6 +133,20 @@ export default Vue.extend({
font-size 13px
border-bottom solid 1px var(--faceDivider)
+ &:focus
+ z-index 1
+
+ &:after
+ content ""
+ pointer-events none
+ position absolute
+ top 2px
+ right 2px
+ bottom 2px
+ left 2px
+ border 2px solid var(--primaryAlpha03)
+ border-radius 4px
+
&:last-of-type
border-bottom none
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index a63ae3f633..c68e6bf1aa 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -1,7 +1,13 @@
<template>
-<div class="note" v-show="p.deletedAt == null" :tabindex="p.deletedAt == null ? '-1' : null" :class="{ renote: isRenote, smart: $store.state.device.postStyle == 'smart' }">
- <div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
- <x-sub :note="p.reply"/>
+<div
+ class="note"
+ v-show="appearNote.deletedAt == null"
+ :tabindex="appearNote.deletedAt == null ? '-1' : null"
+ :class="{ renote: isRenote, smart: $store.state.device.postStyle == 'smart' }"
+ v-hotkey="keymap"
+>
+ <div class="reply-to" v-if="appearNote.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)">
+ <x-sub :note="appearNote.reply"/>
</div>
<div class="renote" v-if="isRenote">
<mk-avatar class="avatar" :user="note.user"/>
@@ -12,47 +18,45 @@
<mk-time :time="note.createdAt"/>
</div>
<article>
- <mk-avatar class="avatar" :user="p.user" v-if="$store.state.device.postStyle != 'smart'"/>
+ <mk-avatar class="avatar" :user="appearNote.user" v-if="$store.state.device.postStyle != 'smart'"/>
<div class="main">
- <mk-note-header class="header" :note="p" :mini="true"/>
+ <mk-note-header class="header" :note="appearNote" :mini="true"/>
<div class="body">
- <p v-if="p.cw != null" class="cw">
- <span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
+ <p v-if="appearNote.cw != null" class="cw">
+ <span class="text" v-if="appearNote.cw != ''">{{ appearNote.cw }}</span>
<mk-cw-button v-model="showContent"/>
</p>
- <div class="content" v-show="p.cw == null || showContent">
+ <div class="content" v-show="appearNote.cw == null || showContent">
<div class="text">
- <span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
- <span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
- <a class="reply" v-if="p.reply">%fa:reply%</a>
- <misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :class="$style.text"/>
- <a class="rp" v-if="p.renote != null">RP:</a>
+ <span v-if="appearNote.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
+ <a class="reply" v-if="appearNote.reply">%fa:reply%</a>
+ <misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text"/>
+ <a class="rp" v-if="appearNote.renote != null">RP:</a>
</div>
- <div class="files" v-if="p.files.length > 0">
- <mk-media-list :media-list="p.files"/>
+ <div class="files" v-if="appearNote.files.length > 0">
+ <mk-media-list :media-list="appearNote.files"/>
</div>
- <mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
+ <mk-poll v-if="appearNote.poll" :note="appearNote" ref="pollViewer"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- <a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</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>
+ <a class="location" v-if="appearNote.geo" :href="`https://maps.google.com/maps?q=${appearNote.geo.coordinates[1]},${appearNote.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
+ <div class="renote" v-if="appearNote.renote"><mk-note-preview :note="appearNote.renote"/></div>
</div>
- <span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
+ <span class="app" v-if="appearNote.app">via <b>{{ appearNote.apappearNote.name }}</b></span>
</div>
- <footer v-if="p.deletedAt == null">
- <mk-reactions-viewer :note="p" ref="reactionsViewer"/>
- <button @click="reply">
- <template v-if="p.reply">%fa:reply-all%</template>
+ <footer v-if="appearNote.deletedAt == null">
+ <mk-reactions-viewer :note="appearNote" ref="reactionsViewer"/>
+ <button @click="reply()">
+ <template v-if="appearNote.reply">%fa:reply-all%</template>
<template v-else>%fa:reply%</template>
- <p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
+ <p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
- <button @click="renote" title="Renote">
- %fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
+ <button @click="renote()" title="Renote">
+ %fa:retweet%<p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.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 :class="{ reacted: appearNote.myReaction != null }" @click="react()" ref="reactButton">
+ %fa:plus%<p class="count" v-if="appearNote.reactions_count > 0">{{ appearNote.reactions_count }}</p>
</button>
- <button class="menu" @click="menu" ref="menuButton">
+ <button class="menu" @click="menu()" ref="menuButton">
%fa:ellipsis-h%
</button>
</footer>
@@ -63,12 +67,9 @@
<script lang="ts">
import Vue from 'vue';
-import parse from '../../../../../mfm/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';
-import { sum } from '../../../../../prelude/array';
+import noteMixin from '../../../common/scripts/note-mixin';
import noteSubscriber from '../../../common/scripts/note-subscriber';
export default Vue.extend({
@@ -76,9 +77,19 @@ export default Vue.extend({
XSub
},
- mixins: [noteSubscriber('note')],
+ mixins: [
+ noteMixin({
+ mobile: true
+ }),
+ noteSubscriber('note')
+ ],
- props: ['note'],
+ props: {
+ note: {
+ type: Object,
+ required: true
+ }
+ },
data() {
return {
@@ -86,65 +97,18 @@ export default Vue.extend({
};
},
- computed: {
- isRenote(): boolean {
- return (this.note.renote &&
- this.note.text == null &&
- this.note.fileIds.length == 0 &&
- this.note.poll == null);
- },
-
- p(): any {
- return this.isRenote ? this.note.renote : this.note;
- },
-
- reactionsCount(): number {
- return this.p.reactionCounts
- ? sum(Object.values(this.p.reactionCounts))
- : 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;
- }
- }
- },
-
methods: {
reply() {
(this as any).apis.post({
- reply: this.p
+ reply: this.appearNote
});
},
renote() {
(this as any).apis.post({
- renote: this.p
+ renote: this.appearNote
});
},
-
- react() {
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- note: this.p,
- compact: true,
- big: true
- });
- },
-
- menu() {
- (this as any).os.new(MkNoteMenu, {
- source: this.$refs.menuButton,
- note: this.p,
- compact: true
- });
- }
}
});
</script>
@@ -154,6 +118,20 @@ export default Vue.extend({
font-size 12px
border-bottom solid 1px var(--faceDivider)
+ &:focus
+ z-index 1
+
+ &:after
+ content ""
+ pointer-events none
+ position absolute
+ top 2px
+ right 2px
+ bottom 2px
+ left 2px
+ border 2px solid var(--primaryAlpha03)
+ border-radius 4px
+
&:last-of-type
border-bottom none