summaryrefslogtreecommitdiff
path: root/packages/client/src/components
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-11-12 23:53:10 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-11-12 23:53:10 +0900
commit4b7b51d5ccdcdad5134edc0232c98e9e8ce2caf5 (patch)
treeefac7fb1f1206466f10923e44c3033564a3b361f /packages/client/src/components
parentenhance: show renoters (#7954) (diff)
downloadsharkey-4b7b51d5ccdcdad5134edc0232c98e9e8ce2caf5.tar.gz
sharkey-4b7b51d5ccdcdad5134edc0232c98e9e8ce2caf5.tar.bz2
sharkey-4b7b51d5ccdcdad5134edc0232c98e9e8ce2caf5.zip
refactor(client): use composition api for tooltip logic
Diffstat (limited to 'packages/client/src/components')
-rw-r--r--packages/client/src/components/note-detailed.vue26
-rw-r--r--packages/client/src/components/note.vue26
-rw-r--r--packages/client/src/components/notification.vue38
-rw-r--r--packages/client/src/components/reactions-viewer.reaction.vue142
-rw-r--r--packages/client/src/components/renote-button.vue126
5 files changed, 141 insertions, 217 deletions
diff --git a/packages/client/src/components/note-detailed.vue b/packages/client/src/components/note-detailed.vue
index 09c05d7769..3b5b12a60a 100644
--- a/packages/client/src/components/note-detailed.vue
+++ b/packages/client/src/components/note-detailed.vue
@@ -94,7 +94,7 @@
<template v-else><i class="fas fa-reply"></i></template>
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
- <XRenoteButton :note="appearNote" :count="appearNote.renoteCount" ref="renoteButton"/>
+ <XRenoteButton class="button" :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>
@@ -132,16 +132,16 @@ import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue';
import XPoll from './poll.vue';
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';
+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';
// TODO: note.vueとほぼ同じなので共通化したい
export default defineComponent({
@@ -154,8 +154,8 @@ export default defineComponent({
XCwButton,
XPoll,
XRenoteButton,
- MkUrlPreview: defineAsyncComponent(() => import('@client/components/url-preview.vue')),
- MkInstanceTicker: defineAsyncComponent(() => import('@client/components/instance-ticker.vue')),
+ MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
+ MkInstanceTicker: defineAsyncComponent(() => import('@/components/instance-ticker.vue')),
},
inject: {
diff --git a/packages/client/src/components/note.vue b/packages/client/src/components/note.vue
index 19486c4dff..2ab769db43 100644
--- a/packages/client/src/components/note.vue
+++ b/packages/client/src/components/note.vue
@@ -78,7 +78,7 @@
<template v-else><i class="fas fa-reply"></i></template>
<p class="count" v-if="appearNote.repliesCount > 0">{{ appearNote.repliesCount }}</p>
</button>
- <XRenoteButton :note="appearNote" :count="appearNote.renoteCount" ref="renoteButton"/>
+ <XRenoteButton class="button" :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>
@@ -115,16 +115,16 @@ import XMediaList from './media-list.vue';
import XCwButton from './cw-button.vue';
import XPoll from './poll.vue';
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';
+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';
export default defineComponent({
components: {
@@ -136,8 +136,8 @@ export default defineComponent({
XCwButton,
XPoll,
XRenoteButton,
- MkUrlPreview: defineAsyncComponent(() => import('@client/components/url-preview.vue')),
- MkInstanceTicker: defineAsyncComponent(() => import('@client/components/instance-ticker.vue')),
+ MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
+ MkInstanceTicker: defineAsyncComponent(() => import('@/components/instance-ticker.vue')),
},
inject: {
diff --git a/packages/client/src/components/notification.vue b/packages/client/src/components/notification.vue
index 1f61bee6f8..40670daa9c 100644
--- a/packages/client/src/components/notification.vue
+++ b/packages/client/src/components/notification.vue
@@ -78,6 +78,7 @@ import notePage from '@/filters/note';
import { userPage } from '@/filters/user';
import { i18n } from '@/i18n';
import * as os from '@/os';
+import { useTooltip } from '@/scripts/use-tooltip';
export default defineComponent({
components: {
@@ -153,47 +154,14 @@ export default defineComponent({
os.api('users/groups/invitations/reject', { invitationId: props.notification.invitation.id });
};
- let isReactionHovering = false;
- let reactionTooltipTimeoutId;
-
- const onReactionMouseover = () => {
- if (isReactionHovering) return;
- isReactionHovering = true;
- reactionTooltipTimeoutId = setTimeout(openReactionTooltip, 300);
- };
-
- const onReactionMouseleave = () => {
- if (!isReactionHovering) return;
- isReactionHovering = false;
- clearTimeout(reactionTooltipTimeoutId);
- closeReactionTooltip();
- };
-
- let changeReactionTooltipShowingState: (() => void) | null;
-
- const openReactionTooltip = () => {
- closeReactionTooltip();
- if (!isReactionHovering) return;
-
- const showing = ref(true);
+ const { onMouseover: onReactionMouseover, onMouseleave: onReactionMouseleave } = useTooltip((showing) => {
os.popup(XReactionTooltip, {
showing,
reaction: props.notification.reaction ? props.notification.reaction.replace(/^:(\w+):$/, ':$1@.:') : props.notification.reaction,
emojis: props.notification.note.emojis,
source: reactionRef.value.$el,
}, {}, 'closed');
-
- changeReactionTooltipShowingState = () => {
- showing.value = false;
- };
- };
-
- const closeReactionTooltip = () => {
- if (changeReactionTooltipShowingState != null) {
- changeReactionTooltipShowingState();
- changeReactionTooltipShowingState = null;
- }
- };
+ });
return {
getNoteSummary: (note: misskey.entities.Note) => getNoteSummary(note),
diff --git a/packages/client/src/components/reactions-viewer.reaction.vue b/packages/client/src/components/reactions-viewer.reaction.vue
index 47a3bb9720..a7769868b9 100644
--- a/packages/client/src/components/reactions-viewer.reaction.vue
+++ b/packages/client/src/components/reactions-viewer.reaction.vue
@@ -2,13 +2,13 @@
<button
class="hkzvhatu _button"
:class="{ reacted: note.myReaction == reaction, canToggle }"
- @click="toggleReaction(reaction)"
+ @click="toggleReaction()"
v-if="count > 0"
@touchstart.passive="onMouseover"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
@touchend="onMouseleave"
- ref="reaction"
+ ref="buttonRef"
v-particle="canToggle"
>
<XReactionIcon :reaction="reaction" :custom-emojis="note.emojis"/>
@@ -17,15 +17,18 @@
</template>
<script lang="ts">
-import { defineComponent, ref } from 'vue';
+import { computed, defineComponent, onMounted, ref, watch } from 'vue';
import XDetails from '@/components/reactions-viewer.details.vue';
import XReactionIcon from '@/components/reaction-icon.vue';
import * as os from '@/os';
+import { useTooltip } from '@/scripts/use-tooltip';
+import { $i } from '@/account';
export default defineComponent({
components: {
XReactionIcon
},
+
props: {
reaction: {
type: String,
@@ -44,101 +47,78 @@ export default defineComponent({
required: true,
},
},
- data() {
- return {
- close: null,
- detailsTimeoutId: null,
- isHovering: false
- };
- },
- computed: {
- canToggle(): boolean {
- return !this.reaction.match(/@\w/) && this.$i;
- },
- },
- watch: {
- count(newCount, oldCount) {
- if (oldCount < newCount) this.anime();
- if (this.close != null) this.openDetails();
- },
- },
- mounted() {
- if (!this.isInitial) this.anime();
- },
- methods: {
- toggleReaction() {
- if (!this.canToggle) return;
- const oldReaction = this.note.myReaction;
+ setup(props) {
+ const buttonRef = ref<HTMLElement>();
+
+ const canToggle = computed(() => !props.reaction.match(/@\w/) && $i);
+
+ const toggleReaction = () => {
+ if (!canToggle.value) return;
+
+ const oldReaction = props.note.myReaction;
if (oldReaction) {
os.api('notes/reactions/delete', {
- noteId: this.note.id
+ noteId: props.note.id
}).then(() => {
- if (oldReaction !== this.reaction) {
+ if (oldReaction !== props.reaction) {
os.api('notes/reactions/create', {
- noteId: this.note.id,
- reaction: this.reaction
+ noteId: props.note.id,
+ reaction: props.reaction
});
}
});
} else {
os.api('notes/reactions/create', {
- noteId: this.note.id,
- reaction: this.reaction
+ noteId: props.note.id,
+ reaction: props.reaction
});
}
- },
- 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/reactions', {
- noteId: this.note.id,
- type: this.reaction,
- limit: 11
- }).then((reactions: any[]) => {
- const users = reactions
- .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
- .map(x => x.user);
+ };
- this.closeDetails();
- if (!this.isHovering) return;
+ const anime = () => {
+ if (document.hidden) return;
+
+ // TODO: 新しくリアクションが付いたことが視覚的に分かりやすいアニメーション
+ };
+
+ watch(() => props.count, (newCount, oldCount) => {
+ if (oldCount < newCount) anime();
+ });
- const showing = ref(true);
- os.popup(XDetails, {
- showing,
- reaction: this.reaction,
- emojis: this.note.emojis,
- users,
- count: this.count,
- source: this.$refs.reaction
- }, {}, 'closed');
+ onMounted(() => {
+ if (!props.isInitial) anime();
+ });
- this.close = () => {
- showing.value = false;
- };
+ const { onMouseover, onMouseleave } = useTooltip(async (showing) => {
+ const reactions = await os.api('notes/reactions', {
+ noteId: props.note.id,
+ type: props.reaction,
+ limit: 11
});
- },
- closeDetails() {
- if (this.close != null) {
- this.close();
- this.close = null;
- }
- },
- anime() {
- if (document.hidden) return;
- // TODO
- },
- }
+ const users = reactions
+ .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
+ .map(x => x.user);
+
+ os.popup(XDetails, {
+ showing,
+ reaction: props.reaction,
+ emojis: props.note.emojis,
+ users,
+ count: props.count,
+ source: buttonRef.value
+ }, {}, 'closed');
+ });
+
+ return {
+ buttonRef,
+ canToggle,
+ toggleReaction,
+ onMouseover,
+ onMouseleave,
+ };
+ },
});
</script>
diff --git a/packages/client/src/components/renote-button.vue b/packages/client/src/components/renote-button.vue
index 16ae2a2fa4..5ddc1602ed 100644
--- a/packages/client/src/components/renote-button.vue
+++ b/packages/client/src/components/renote-button.vue
@@ -1,13 +1,13 @@
<template>
<button
- class="button _button canRenote"
+ class="eddddedb _button canRenote"
@click="renote()"
v-if="canRenote"
@touchstart.passive="onMouseover"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
@touchend="onMouseleave"
- ref="renoteButton"
+ ref="buttonRef"
>
<i class="fas fa-retweet"></i>
<p class="count" v-if="count > 0">{{ count }}</p>
@@ -21,10 +21,13 @@
</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';
+import { computed, defineComponent, ref } from 'vue';
+import XDetails from '@/components/renote.details.vue';
+import { pleaseLogin } from '@/scripts/please-login';
+import * as os from '@/os';
+import { $i } from '@/account';
+import { useTooltip } from '@/scripts/use-tooltip';
+import { i18n } from '@/i18n';
export default defineComponent({
props: {
@@ -37,95 +40,68 @@ export default defineComponent({
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) {
+
+ setup(props) {
+ const buttonRef = ref<HTMLElement>();
+
+ const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
+
+ const { onMouseover, onMouseleave } = useTooltip(async (showing) => {
+ const renotes = await os.api('notes/renotes', {
+ noteId: props.note.id,
+ limit: 11
+ });
+
+ const users = renotes
+ .sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime())
+ .map(x => x.user);
+
+ if (users.length < 1) return;
+
+ os.popup(XDetails, {
+ showing,
+ users,
+ count: props.count,
+ source: buttonRef.value
+ }, {}, 'closed');
+ });
+
+ const renote = (viaKeyboard = false) => {
pleaseLogin();
os.popupMenu([{
- text: this.$ts.renote,
+ text: i18n.locale.renote,
icon: 'fas fa-retweet',
action: () => {
os.api('notes/create', {
- renoteId: this.note.id
+ renoteId: props.note.id
});
}
}, {
- text: this.$ts.quote,
+ text: i18n.locale.quote,
icon: 'fas fa-quote-right',
action: () => {
os.post({
- renote: this.note,
+ renote: props.note,
});
}
- }], this.$refs.renoteButton, {
+ }], buttonRef.value, {
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;
- }
- },
- }
+ return {
+ buttonRef,
+ canRenote,
+ renote,
+ onMouseover,
+ onMouseleave,
+ };
+ },
});
</script>
<style lang="scss" scoped>
-.button {
+.eddddedb {
display: inline-block;
height: 32px;
margin: 2px;