summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorJohann150 <johann.galle@protonmail.com>2021-11-12 15:15:14 +0100
committerGitHub <noreply@github.com>2021-11-12 23:15:14 +0900
commit0e3213ff6dea77bff64bee2f46cde49ef80dafe4 (patch)
treee362f77fa450c489337e325504d2dd5b4568ce8a /packages
parentrefactoring (diff)
downloadsharkey-0e3213ff6dea77bff64bee2f46cde49ef80dafe4.tar.gz
sharkey-0e3213ff6dea77bff64bee2f46cde49ef80dafe4.tar.bz2
sharkey-0e3213ff6dea77bff64bee2f46cde49ef80dafe4.zip
enhance: show renoters (#7954)
* refactor: deduplicate renote button into component For now the renoters tooltip just uses the reaction viewer component with a fixed emoji symbol instead. * chore: remove unnecessary CSS * fix: forgot to rename variable * enhance: use own tooltip instead of reaction viewer * clean up style * fix additional renoters number * rename file to better represent content
Diffstat (limited to 'packages')
-rw-r--r--packages/client/src/components/note-detailed.vue63
-rw-r--r--packages/client/src/components/note.vue63
-rw-r--r--packages/client/src/components/renote-button.vue149
-rw-r--r--packages/client/src/components/renote.details.vue46
4 files changed, 227 insertions, 94 deletions
diff --git a/packages/client/src/components/note-detailed.vue b/packages/client/src/components/note-detailed.vue
index 7550153521..09c05d7769 100644
--- a/packages/client/src/components/note-detailed.vue
+++ b/packages/client/src/components/note-detailed.vue
@@ -94,12 +94,7 @@
<template v-else><i class="fas fa-reply"></i></template>
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
- <button v-if="canRenote" @click="renote()" class="button _button" ref="renoteButton">
- <i class="fas fa-retweet"></i><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
- </button>
- <button v-else class="button _button">
- <i class="fas fa-ban"></i>
- </button>
+ <XRenoteButton :note="appearNote" :count="appearNote.renoteCount" ref="renoteButton"/>
<button v-if="appearNote.myReaction == null" class="button _button" @click="react()" ref="reactButton">
<i class="fas fa-plus"></i>
</button>
@@ -136,16 +131,17 @@ import XReactionsViewer from './reactions-viewer.vue';
import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue';
import XPoll from './poll.vue';
-import { pleaseLogin } from '@/scripts/please-login';
-import { focusPrev, focusNext } from '@/scripts/focus';
-import { url } from '@/config';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { checkWordMute } from '@/scripts/check-word-mute';
-import { userPage } from '@/filters/user';
-import * as os from '@/os';
-import { noteActions, noteViewInterruptors } from '@/store';
-import { reactionPicker } from '@/scripts/reaction-picker';
-import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
+import XRenoteButton from './renote-button.vue';
+import { pleaseLogin } from '@client/scripts/please-login';
+import { focusPrev, focusNext } from '@client/scripts/focus';
+import { url } from '@client/config';
+import copyToClipboard from '@client/scripts/copy-to-clipboard';
+import { checkWordMute } from '@client/scripts/check-word-mute';
+import { userPage } from '@client/filters/user';
+import * as os from '@client/os';
+import { noteActions, noteViewInterruptors } from '@client/store';
+import { reactionPicker } from '@client/scripts/reaction-picker';
+import { extractUrlFromMfm } from '@/misc/extract-url-from-mfm';
// TODO: note.vueとほぼ同じなので共通化したい
export default defineComponent({
@@ -157,8 +153,9 @@ export default defineComponent({
XMediaList,
XCwButton,
XPoll,
- MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
- MkInstanceTicker: defineAsyncComponent(() => import('@/components/instance-ticker.vue')),
+ XRenoteButton,
+ MkUrlPreview: defineAsyncComponent(() => import('@client/components/url-preview.vue')),
+ MkInstanceTicker: defineAsyncComponent(() => import('@client/components/instance-ticker.vue')),
},
inject: {
@@ -197,7 +194,7 @@ export default defineComponent({
return {
'r': () => this.reply(true),
'e|a|plus': () => this.react(true),
- 'q': () => this.renote(true),
+ 'q': () => this.$refs.renoteButton.renote(true),
'f|b': this.favorite,
'delete|ctrl+d': this.del,
'ctrl+q': this.renoteDirectly,
@@ -238,10 +235,6 @@ export default defineComponent({
return this.$i && (this.$i.id === this.note.userId);
},
- canRenote(): boolean {
- return ['public', 'home'].includes(this.appearNote.visibility) || this.isMyNote;
- },
-
reactionsCount(): number {
return this.appearNote.reactions
? sum(Object.values(this.appearNote.reactions))
@@ -459,30 +452,6 @@ export default defineComponent({
});
},
- renote(viaKeyboard = false) {
- pleaseLogin();
- this.blur();
- os.popupMenu([{
- text: this.$ts.renote,
- icon: 'fas fa-retweet',
- action: () => {
- os.api('notes/create', {
- renoteId: this.appearNote.id
- });
- }
- }, {
- text: this.$ts.quote,
- icon: 'fas fa-quote-right',
- action: () => {
- os.post({
- renote: this.appearNote,
- });
- }
- }], this.$refs.renoteButton, {
- viaKeyboard
- });
- },
-
renoteDirectly() {
os.apiWithDialog('notes/create', {
renoteId: this.appearNote.id
diff --git a/packages/client/src/components/note.vue b/packages/client/src/components/note.vue
index b1ec674b67..19486c4dff 100644
--- a/packages/client/src/components/note.vue
+++ b/packages/client/src/components/note.vue
@@ -78,12 +78,7 @@
<template v-else><i class="fas fa-reply"></i></template>
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
- <button v-if="canRenote" @click="renote()" class="button _button" ref="renoteButton">
- <i class="fas fa-retweet"></i><p class="count" v-if="appearNote.renoteCount > 0">{{ appearNote.renoteCount }}</p>
- </button>
- <button v-else class="button _button">
- <i class="fas fa-ban"></i>
- </button>
+ <XRenoteButton :note="appearNote" :count="appearNote.renoteCount" ref="renoteButton"/>
<button v-if="appearNote.myReaction == null" class="button _button" @click="react()" ref="reactButton">
<i class="fas fa-plus"></i>
</button>
@@ -119,16 +114,17 @@ import XReactionsViewer from './reactions-viewer.vue';
import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue';
import XPoll from './poll.vue';
-import { pleaseLogin } from '@/scripts/please-login';
-import { focusPrev, focusNext } from '@/scripts/focus';
-import { url } from '@/config';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { checkWordMute } from '@/scripts/check-word-mute';
-import { userPage } from '@/filters/user';
-import * as os from '@/os';
-import { noteActions, noteViewInterruptors } from '@/store';
-import { reactionPicker } from '@/scripts/reaction-picker';
-import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
+import XRenoteButton from './renote-button.vue';
+import { pleaseLogin } from '@client/scripts/please-login';
+import { focusPrev, focusNext } from '@client/scripts/focus';
+import { url } from '@client/config';
+import copyToClipboard from '@client/scripts/copy-to-clipboard';
+import { checkWordMute } from '@client/scripts/check-word-mute';
+import { userPage } from '@client/filters/user';
+import * as os from '@client/os';
+import { noteActions, noteViewInterruptors } from '@client/store';
+import { reactionPicker } from '@client/scripts/reaction-picker';
+import { extractUrlFromMfm } from '@/misc/extract-url-from-mfm';
export default defineComponent({
components: {
@@ -139,8 +135,9 @@ export default defineComponent({
XMediaList,
XCwButton,
XPoll,
- MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
- MkInstanceTicker: defineAsyncComponent(() => import('@/components/instance-ticker.vue')),
+ XRenoteButton,
+ MkUrlPreview: defineAsyncComponent(() => import('@client/components/url-preview.vue')),
+ MkInstanceTicker: defineAsyncComponent(() => import('@client/components/instance-ticker.vue')),
},
inject: {
@@ -184,7 +181,7 @@ export default defineComponent({
return {
'r': () => this.reply(true),
'e|a|plus': () => this.react(true),
- 'q': () => this.renote(true),
+ 'q': () => this.$refs.renoteButton.renote(true),
'f|b': this.favorite,
'delete|ctrl+d': this.del,
'ctrl+q': this.renoteDirectly,
@@ -225,10 +222,6 @@ export default defineComponent({
return this.$i && (this.$i.id === this.note.userId);
},
- canRenote(): boolean {
- return ['public', 'home'].includes(this.appearNote.visibility) || this.isMyNote;
- },
-
reactionsCount(): number {
return this.appearNote.reactions
? sum(Object.values(this.appearNote.reactions))
@@ -435,30 +428,6 @@ export default defineComponent({
});
},
- renote(viaKeyboard = false) {
- pleaseLogin();
- this.blur();
- os.popupMenu([{
- text: this.$ts.renote,
- icon: 'fas fa-retweet',
- action: () => {
- os.api('notes/create', {
- renoteId: this.appearNote.id
- });
- }
- }, {
- text: this.$ts.quote,
- icon: 'fas fa-quote-right',
- action: () => {
- os.post({
- renote: this.appearNote,
- });
- }
- }], this.$refs.renoteButton, {
- viaKeyboard
- });
- },
-
renoteDirectly() {
os.apiWithDialog('notes/create', {
renoteId: this.appearNote.id
diff --git a/packages/client/src/components/renote-button.vue b/packages/client/src/components/renote-button.vue
new file mode 100644
index 0000000000..16ae2a2fa4
--- /dev/null
+++ b/packages/client/src/components/renote-button.vue
@@ -0,0 +1,149 @@
+<template>
+<button
+ class="button _button canRenote"
+ @click="renote()"
+ v-if="canRenote"
+ @touchstart.passive="onMouseover"
+ @mouseover="onMouseover"
+ @mouseleave="onMouseleave"
+ @touchend="onMouseleave"
+ ref="renoteButton"
+>
+ <i class="fas fa-retweet"></i>
+ <p class="count" v-if="count > 0">{{ count }}</p>
+</button>
+<button
+ v-else
+ class="button _button"
+>
+ <i class="fas fa-ban"></i>
+</button>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+import XDetails from '@client/components/renote.details.vue';
+import { pleaseLogin } from '@client/scripts/please-login';
+import * as os from '@client/os';
+
+export default defineComponent({
+ props: {
+ count: {
+ type: Number,
+ required: true,
+ },
+ note: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ close: null,
+ detailsTimeoutId: null,
+ isHovering: false
+ };
+ },
+ computed: {
+ canRenote(): boolean {
+ return ['public', 'home'].includes(this.note.visibility) || this.note.userId === this.$i.id;
+ },
+ },
+ watch: {
+ count(newCount, oldCount) {
+ if (oldCount < newCount) this.anime();
+ if (this.close != null) this.openDetails();
+ },
+ },
+ methods: {
+ renote(viaKeyboard = false) {
+ pleaseLogin();
+ os.popupMenu([{
+ text: this.$ts.renote,
+ icon: 'fas fa-retweet',
+ action: () => {
+ os.api('notes/create', {
+ renoteId: this.note.id
+ });
+ }
+ }, {
+ text: this.$ts.quote,
+ icon: 'fas fa-quote-right',
+ action: () => {
+ os.post({
+ renote: this.note,
+ });
+ }
+ }], this.$refs.renoteButton, {
+ viaKeyboard
+ });
+ },
+ onMouseover() {
+ if (this.isHovering) return;
+ this.isHovering = true;
+ this.detailsTimeoutId = setTimeout(this.openDetails, 300);
+ },
+ onMouseleave() {
+ if (!this.isHovering) return;
+ this.isHovering = false;
+ clearTimeout(this.detailsTimeoutId);
+ this.closeDetails();
+ },
+ openDetails() {
+ os.api('notes/renotes', {
+ noteId: this.note.id,
+ limit: 11
+ }).then((renotes: any[]) => {
+ const users = renotes
+ .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
+ .map(x => x.user);
+
+ this.closeDetails();
+ if (!this.isHovering || users.length < 1) return;
+
+ const showing = ref(true);
+ os.popup(XDetails, {
+ showing,
+ users,
+ count: this.count,
+ source: this.$refs.renoteButton
+ }, {}, 'closed');
+
+ this.close = () => {
+ showing.value = false;
+ };
+ });
+ },
+ closeDetails() {
+ if (this.close != null) {
+ this.close();
+ this.close = null;
+ }
+ },
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.button {
+ display: inline-block;
+ height: 32px;
+ margin: 2px;
+ padding: 0 6px;
+ border-radius: 4px;
+
+ &:not(.canRenote) {
+ cursor: default;
+ }
+
+ &.renoted {
+ background: var(--accent);
+ }
+
+ > .count {
+ display: inline;
+ margin-left: 8px;
+ opacity: 0.7;
+ }
+}
+</style>
diff --git a/packages/client/src/components/renote.details.vue b/packages/client/src/components/renote.details.vue
new file mode 100644
index 0000000000..128d97d8de
--- /dev/null
+++ b/packages/client/src/components/renote.details.vue
@@ -0,0 +1,46 @@
+<template>
+<MkTooltip :source="source" ref="tooltip" @closed="$emit('closed')" :max-width="340">
+ <div class="renoteTooltip">
+ <b v-for="u in users" :key="u.id">
+ <MkAvatar :user="u" style="width: 24px; height: 24px;"/><br/>
+ <MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
+ </b>
+ <span v-if="users.length < count" slot="omitted">+{{ count - users.length }}</span>
+ </div>
+</MkTooltip>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import MkTooltip from './ui/tooltip.vue';
+
+export default defineComponent({
+ components: {
+ MkTooltip,
+ },
+ props: {
+ users: {
+ type: Array,
+ required: true,
+ },
+ count: {
+ type: Number,
+ required: true,
+ },
+ source: {
+ required: true,
+ }
+ },
+ emits: ['closed'],
+})
+</script>
+
+<style lang="scss" scoped>
+.renoteTooltip {
+ display: flex;
+ flex: 1;
+ min-width: 0;
+ font-size: 0.9em;
+ gap: 12px;
+}
+</style>