diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-03-25 16:14:53 -0400 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-03-25 16:14:53 -0400 |
| commit | d8908ef2d8fa84d8e0fc1d30ab90a600a3d88054 (patch) | |
| tree | 0c8d3e0385ce7021c7187ef8b608f1abd87496e5 /packages/frontend/src/components/MkNoteDetailed.vue | |
| parent | merge: enhance: Update de-DE.yml (!949) (diff) | |
| parent | enhance(frontend): 設定の移行を手動でトリガーできるように (diff) | |
| download | sharkey-d8908ef2d8fa84d8e0fc1d30ab90a600a3d88054.tar.gz sharkey-d8908ef2d8fa84d8e0fc1d30ab90a600a3d88054.tar.bz2 sharkey-d8908ef2d8fa84d8e0fc1d30ab90a600a3d88054.zip | |
merge upstream
Diffstat (limited to 'packages/frontend/src/components/MkNoteDetailed.vue')
| -rw-r--r-- | packages/frontend/src/components/MkNoteDetailed.vue | 94 |
1 files changed, 54 insertions, 40 deletions
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 1bbd3ba5d0..531232f86a 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -101,13 +101,14 @@ SPDX-License-Identifier: AGPL-3.0-only :enableEmojiMenuReaction="true" :isAnim="allowAnim" :isBlock="true" + class="_selectable" /> <a v-if="appearNote.renote != null" :class="$style.rn">RN:</a> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> <div v-else-if="translation"> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :isBlock="true" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :isBlock="true" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis" class="_selectable"/> </div> </div> <MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" @click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton> @@ -169,9 +170,9 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--MI_THEME-accent);"></i> <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> <i v-else class="ph-smiley ph-bold ph-lg"></i> - <p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p> + <p v-if="(appearNote.reactionAcceptance === 'likeOnly' || prefer.s.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p> </button> - <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="clip()"> + <button v-if="prefer.s.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="clip()"> <i class="ti ti-paperclip"></i> </button> <button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown.prevent="showMenu()"> @@ -240,12 +241,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue'; +import { computed, inject, onMounted, provide, ref, useTemplateRef } from 'vue'; import * as mfm from '@transfem-org/sfm-js'; import * as Misskey from 'misskey-js'; import { isLink } from '@@/js/is-link.js'; import { host } from '@@/js/config.js'; import { computeMergedCw } from '@@/js/compute-merged-cw.js'; +import type { OpenOnRemoteOptions } from '@/utility/please-login.js'; +import type { Paging } from '@/components/MkPagination.vue'; +import type { Keymap } from '@/utility/hotkey.js'; import MkNoteSub from '@/components/MkNoteSub.vue'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; import MkReactionsViewer from '@/components/MkReactionsViewer.vue'; @@ -256,41 +260,41 @@ import MkPoll from '@/components/MkPoll.vue'; import MkUsersTooltip from '@/components/MkUsersTooltip.vue'; import MkUrlPreview from '@/components/MkUrlPreview.vue'; import MkInstanceTicker from '@/components/MkInstanceTicker.vue'; -import { pleaseLogin, type OpenOnRemoteOptions } from '@/scripts/please-login.js'; -import { checkWordMute } from '@/scripts/check-word-mute.js'; +import { pleaseLogin } from '@/utility/please-login.js'; +import { checkWordMute } from '@/utility/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import { notePage } from '@/filters/note.js'; import number from '@/filters/number.js'; import * as os from '@/os.js'; -import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; -import * as sound from '@/scripts/sound.js'; -import { defaultStore, noteViewInterruptors } from '@/store.js'; -import { reactionPicker } from '@/scripts/reaction-picker.js'; -import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; -import { $i } from '@/account.js'; +import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js'; +import * as sound from '@/utility/sound.js'; +import { reactionPicker } from '@/utility/reaction-picker.js'; +import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js'; +import { $i } from '@/i.js'; import { i18n } from '@/i18n.js'; -import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js'; -import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js'; -import { useNoteCapture } from '@/scripts/use-note-capture.js'; -import { deepClone } from '@/scripts/clone.js'; -import { useTooltip } from '@/scripts/use-tooltip.js'; -import { claimAchievement } from '@/scripts/achievements.js'; -import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js'; +import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js'; +import { getNoteVersionsMenu } from '@/utility/get-note-versions-menu.js'; +import { checkAnimationFromMfm } from '@/utility/check-animated-mfm.js'; +import { useNoteCapture } from '@/use/use-note-capture.js'; +import { deepClone } from '@/utility/clone.js'; +import { useTooltip } from '@/use/use-tooltip.js'; +import { claimAchievement } from '@/utility/achievements.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; -import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; +import { showMovedDialog } from '@/utility/show-moved-dialog.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; -import MkPagination, { type Paging } from '@/components/MkPagination.vue'; +import MkPagination from '@/components/MkPagination.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkButton from '@/components/MkButton.vue'; import { boostMenuItems, type Visibility, computeRenoteTooltip } from '@/scripts/boost-quote.js'; import { isEnabledUrlPreview } from '@/instance.js'; -import { getAppearNote } from '@/scripts/get-appear-note.js'; -import { type Keymap } from '@/scripts/hotkey.js'; +import { getAppearNote } from '@/utility/get-appear-note.js'; +import { prefer } from '@/preferences.js'; +import { getPluginHandlers } from '@/plugin.js'; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; + initialTab?: string; expandAllCws?: boolean; - initialTab: string; }>(), { initialTab: 'replies', }); @@ -300,6 +304,7 @@ const inChannel = inject('inChannel', null); const note = ref(deepClone(props.note)); // plugin +const noteViewInterruptors = getPluginHandlers('note_view_interruptor'); if (noteViewInterruptors.length > 0) { onMounted(async () => { let result: Misskey.entities.Note | null = deepClone(note.value); @@ -320,17 +325,17 @@ if (noteViewInterruptors.length > 0) { const isRenote = Misskey.note.isPureRenote(note.value); -const rootEl = shallowRef<HTMLElement>(); -const menuButton = shallowRef<HTMLElement>(); -const menuVersionsButton = shallowRef<HTMLElement>(); -const renoteButton = shallowRef<HTMLElement>(); -const renoteTime = shallowRef<HTMLElement>(); -const reactButton = shallowRef<HTMLElement>(); -const quoteButton = shallowRef<HTMLElement>(); -const clipButton = shallowRef<HTMLElement>(); -const likeButton = shallowRef<HTMLElement>(); +const rootEl = useTemplateRef('rootEl'); +const menuButton = useTemplateRef('menuButton'); +const renoteButton = useTemplateRef('renoteButton'); +const renoteTime = useTemplateRef('renoteTime'); +const reactButton = useTemplateRef('reactButton'); +const clipButton = useTemplateRef('clipButton'); +const menuVersionsButton = useTemplateRef('menuVersionsButton'); +const quoteButton = useTemplateRef('quoteButton'); +const likeButton = useTemplateRef('likeButton'); const appearNote = computed(() => getAppearNote(note.value)); -const galleryEl = shallowRef<InstanceType<typeof MkMediaList>>(); +const galleryEl = useTemplateRef('galleryEl'); const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(defaultStore.state.uncollapseCW); const isDeleted = ref(false); @@ -341,8 +346,8 @@ const translating = ref(false); const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null; const urls = parsed ? extractUrlFromMfm(parsed).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null; const animated = computed(() => parsed ? checkAnimationFromMfm(parsed) : null); -const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false); -const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); +const allowAnim = ref(prefer.s.advancedMfm && prefer.s.animatedMfm); +const showTicker = (prefer.s.instanceTicker === 'always') || (prefer.s.instanceTicker === 'remote' && appearNote.value.user.instance); const conversation = ref<Misskey.entities.Note[]>([]); const replies = ref<Misskey.entities.Note[]>([]); const quotes = ref<Misskey.entities.Note[]>([]); @@ -380,7 +385,7 @@ const keymap = { 'q': () => { if (canRenote.value && !renoted.value && !renoting) renote(defaultStore.state.visibilityOnBoost); }, 'm': () => showMenu(), 'c': () => { - if (!defaultStore.state.showClipButtonInNoteFooter) return; + if (!prefer.s.showClipButtonInNoteFooter) return; clip(); }, 'o': () => galleryEl.value?.openGallery(), @@ -644,7 +649,7 @@ function react(): void { override: defaultLike.value, }); const el = reactButton.value; - if (el) { + if (el && prefer.s.animation) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); @@ -654,7 +659,16 @@ function react(): void { } } else { blur(); - reactionPicker.show(reactButton.value ?? null, note.value, reaction => { + reactionPicker.show(reactButton.value ?? null, note.value, async (reaction) => { + if (prefer.s.confirmOnReact) { + const confirm = await os.confirm({ + type: 'question', + text: i18n.tsx.reactAreYouSure({ emoji: reaction.replace('@.', '') }), + }); + + if (confirm.canceled) return; + } + sound.playMisskeySfx('reaction'); misskeyApi('notes/reactions/create', { @@ -728,7 +742,7 @@ function onContextmenu(ev: MouseEvent): void { if (ev.target && isLink(ev.target as HTMLElement)) return; if (window.getSelection()?.toString() !== '') return; - if (defaultStore.state.useReactionPickerForContextMenu) { + if (prefer.s.useReactionPickerForContextMenu) { ev.preventDefault(); react(); } else { |