From 2a50e6e9e72f198eb0d9c51c5677af342f73f755 Mon Sep 17 00:00:00 2001 From: Marie <marie@kaifa.ch> Date: Sat, 3 Feb 2024 21:29:46 +0100 Subject: [PATCH] upd: apply changes --- packages/frontend/src/components/MkNote.vue | 6 +- .../src/components/MkNoteDetailed.vue | 6 +- packages/frontend/src/components/SkNote.vue | 64 ++++++++----------- .../src/components/SkNoteDetailed.vue | 47 +++++++------- .../frontend/src/components/SkNoteHeader.vue | 2 +- 5 files changed, 59 insertions(+), 66 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index d8446daaa9..bdad1fc1fe 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -332,7 +332,7 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - 'q': () => renote(true), + 'q': () => renote(appearNote.value.visibility), 'up|k|shift+tab': focusBefore, 'down|j|tab': focusAfter, 'esc': blur, @@ -526,7 +526,7 @@ function quote() { }).then(() => { misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -548,7 +548,7 @@ function quote() { }).then(() => { misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index f89945eb9c..de028f4c34 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -340,7 +340,7 @@ if ($i) { const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - 'q': () => renote(true), + 'q': () => renote(appearNote.value.visibility), 'esc': blur, 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, @@ -540,7 +540,7 @@ function quote() { }).then(() => { misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -562,7 +562,7 @@ function quote() { }).then(() => { misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue index 776b70f82c..4e224d2e6d 100644 --- a/packages/frontend/src/components/SkNote.vue +++ b/packages/frontend/src/components/SkNote.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="!hardMuted && muted === false" v-show="!isDeleted" - ref="el" + ref="rootEl" v-hotkey="keymap" :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" :tabindex="!isDeleted ? '-1' : undefined" @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only /> <div v-if="translating || translation" :class="$style.translation"> <MkLoading v-if="translating" mini/> - <div v-else> + <div v-else-if="translation"> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> @@ -255,7 +255,7 @@ if (noteViewInterruptors.length > 0) { let result: Misskey.entities.Note | null = deepClone(note.value); for (const interruptor of noteViewInterruptors) { try { - result = await interruptor.handler(result); + result = await interruptor.handler(result!) as Misskey.entities.Note | null; if (result === null) { isDeleted.value = true; return; @@ -264,7 +264,7 @@ if (noteViewInterruptors.length > 0) { console.error(err); } } - note.value = result; + note.value = result as Misskey.entities.Note; }); } @@ -272,11 +272,11 @@ const isRenote = ( note.value.renote != null && note.value.text == null && note.value.cw == null && - note.value.fileIds.length === 0 && + note.value.fileIds && note.value.fileIds.length === 0 && note.value.poll == null ); -const el = shallowRef<HTMLElement>(); +const rootEl = shallowRef<HTMLElement>(); const menuButton = shallowRef<HTMLElement>(); const menuVersionsButton = shallowRef<HTMLElement>(); const renoteButton = shallowRef<HTMLElement>(); @@ -333,11 +333,11 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - 'q': () => renoteButton.value.renote(true), + 'q': () => renote(appearNote.value.visibility), 'up|k|shift+tab': focusBefore, 'down|j|tab': focusAfter, 'esc': blur, - 'm|o': () => menu(true), + 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, }; @@ -354,7 +354,7 @@ if (props.mock) { }, { deep: true }); } else { useNoteCapture({ - rootEl: el, + rootEl: rootEl, note: appearNote, pureNote: note, isDeletedRef: isDeleted, @@ -527,7 +527,7 @@ function quote() { }).then(() => { misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -549,7 +549,7 @@ function quote() { }).then(() => { misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -577,7 +577,7 @@ function reply(viaKeyboard = false): void { reply: appearNote.value, channel: appearNote.value.channel, animation: !viaKeyboard, - }, () => { + }).then(() => { focus(); }); } @@ -616,7 +616,7 @@ function react(viaKeyboard = false): void { noteId: appearNote.value.id, override: defaultLike.value, }); - const el = reactButton.value as HTMLElement | null | undefined; + const el = reactButton.value; if (el) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); @@ -625,7 +625,7 @@ function react(viaKeyboard = false): void { } } else { blur(); - reactionPicker.show(reactButton.value, reaction => { + reactionPicker.show(reactButton.value ?? null, reaction => { sound.playMisskeySfx('reaction'); if (props.mock) { @@ -646,8 +646,8 @@ function react(viaKeyboard = false): void { } } -function undoReact(note): void { - const oldReaction = note.myReaction; +function undoReact(targetNote: Misskey.entities.Note): void { + const oldReaction = targetNote.myReaction; if (!oldReaction) return; if (props.mock) { @@ -656,7 +656,7 @@ function undoReact(note): void { } misskeyApi('notes/reactions/delete', { - noteId: note.id, + noteId: targetNote.id, }); } @@ -684,22 +684,24 @@ function onContextmenu(ev: MouseEvent): void { return; } - const isLink = (el: HTMLElement) => { + const isLink = (el: HTMLElement): boolean => { if (el.tagName === 'A') return true; // 再生速度の選択などのために、Audio要素のコンテキストメニューはブラウザデフォルトとする。 if (el.tagName === 'AUDIO') return true; if (el.parentElement) { return isLink(el.parentElement); } + return false; }; - if (isLink(ev.target)) return; - if (window.getSelection().toString() !== '') return; + + if (ev.target && isLink(ev.target as HTMLElement)) return; + if (window.getSelection()?.toString() !== '') return; if (defaultStore.state.useReactionPickerForContextMenu) { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } @@ -763,7 +765,7 @@ function showRenoteMenu(viaKeyboard = false): void { getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), { type: 'divider' }, getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote), - $i.isModerator || $i.isAdmin ? getUnrenote() : undefined, + ($i?.isModerator || $i?.isAdmin) ? getUnrenote() : undefined, ], renoteTime.value, { viaKeyboard: viaKeyboard, }); @@ -783,23 +785,19 @@ function animatedMFM() { } function focus() { - el.value.focus(); + rootEl.value?.focus(); } function blur() { - el.value.blur(); + rootEl.value?.blur(); } function focusBefore() { - focusPrev(el.value); + focusPrev(rootEl.value ?? null); } function focusAfter() { - focusNext(el.value); -} - -function scrollIntoView() { - el.value.scrollIntoView(); + focusNext(rootEl.value ?? null); } function readPromo() { @@ -816,12 +814,6 @@ function emitUpdReaction(emoji: string, delta: number) { emit('reaction', emoji); } } - -defineExpose({ - focus, - blur, - scrollIntoView, -}); </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue index 8be17fbbb8..54505c9bb7 100644 --- a/packages/frontend/src/components/SkNoteDetailed.vue +++ b/packages/frontend/src/components/SkNoteDetailed.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="!muted" v-show="!isDeleted" - ref="el" + ref="rootEl" v-hotkey="keymap" :class="$style.root" > @@ -96,7 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only <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> + <div v-else-if="translation"> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> @@ -281,7 +281,7 @@ if (noteViewInterruptors.length > 0) { let result: Misskey.entities.Note | null = deepClone(note.value); for (const interruptor of noteViewInterruptors) { try { - result = await interruptor.handler(result); + result = await interruptor.handler(result!) as Misskey.entities.Note | null; if (result === null) { isDeleted.value = true; return; @@ -290,19 +290,18 @@ if (noteViewInterruptors.length > 0) { console.error(err); } } - note.value = result; + note.value = result as Misskey.entities.Note; }); } const isRenote = ( note.value.renote != null && note.value.text == null && - note.value.fileIds.length === 0 && + note.value.fileIds && note.value.fileIds.length === 0 && note.value.poll == null ); -const el = shallowRef<HTMLElement>(); -const noteEl = shallowRef<HTMLElement>(); +const rootEl = shallowRef<HTMLElement>(); const menuButton = shallowRef<HTMLElement>(); const menuVersionsButton = shallowRef<HTMLElement>(); const renoteButton = shallowRef<HTMLElement>(); @@ -349,9 +348,9 @@ if ($i) { const keymap = { 'r': () => reply(true), 'e|a|plus': () => react(true), - 'q': () => renoteButton.value.renote(true), + 'q': () => renote(appearNote.value.visibility), 'esc': blur, - 'm|o': () => menu(true), + 'm|o': () => showMenu(true), 's': () => showContent.value !== showContent.value, }; @@ -396,7 +395,7 @@ async function removeReply(id: Misskey.entities.Note['id']) { } useNoteCapture({ - rootEl: el, + rootEl: rootEl, note: appearNote, pureNote: note, isDeletedRef: isDeleted, @@ -549,7 +548,7 @@ function quote() { }).then(() => { misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -571,7 +570,7 @@ function quote() { }).then(() => { misskeyApi('notes/renotes', { noteId: appearNote.value.id, - userId: $i.id, + userId: $i?.id, limit: 1, quote: true, }).then((res) => { @@ -597,7 +596,7 @@ function reply(viaKeyboard = false): void { reply: appearNote.value, channel: appearNote.value.channel, animation: !viaKeyboard, - }, () => { + }).then(() => { focus(); }); } @@ -621,7 +620,7 @@ function react(viaKeyboard = false): void { } } else { blur(); - reactionPicker.show(reactButton.value, reaction => { + reactionPicker.show(reactButton.value ?? null, reaction => { sound.playMisskeySfx('reaction'); misskeyApi('notes/reactions/create', { @@ -680,20 +679,22 @@ function undoRenote() : void { } function onContextmenu(ev: MouseEvent): void { - const isLink = (el: HTMLElement) => { + const isLink = (el: HTMLElement): boolean => { if (el.tagName === 'A') return true; if (el.parentElement) { return isLink(el.parentElement); } + return false; }; - if (isLink(ev.target)) return; - if (window.getSelection().toString() !== '') return; + + if (ev.target && isLink(ev.target as HTMLElement)) return; + if (window.getSelection()?.toString() !== '') return; if (defaultStore.state.useReactionPickerForContextMenu) { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted }); + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } @@ -735,11 +736,11 @@ function showRenoteMenu(viaKeyboard = false): void { } function focus() { - noteEl.value?.focus(); + rootEl.value?.focus(); } function blur() { - noteEl.value?.blur(); + rootEl.value?.blur(); } const repliesLoaded = ref(false); @@ -776,11 +777,11 @@ const conversationLoaded = ref(false); function loadConversation() { conversationLoaded.value = true; + if (appearNote.value.replyId == null) return; misskeyApi('notes/conversation', { noteId: appearNote.value.replyId, }).then(res => { conversation.value = res.reverse(); - focus(); }); } @@ -807,12 +808,12 @@ function setScrolling() { onMounted(() => { document.addEventListener('wheel', setScrolling); isScrolling = false; - noteEl.value?.scrollIntoView({ block: 'center' }); + rootEl.value?.scrollIntoView({ block: 'center' }); }); onUpdated(() => { if (!isScrolling) { - noteEl.value?.scrollIntoView({ block: 'center' }); + rootEl.value?.scrollIntoView({ block: 'center' }); if (location.hash) { location.replace(location.hash); // Jump to highlighted reply } diff --git a/packages/frontend/src/components/SkNoteHeader.vue b/packages/frontend/src/components/SkNoteHeader.vue index e016cc163e..26ebfc6e8b 100644 --- a/packages/frontend/src/components/SkNoteHeader.vue +++ b/packages/frontend/src/components/SkNoteHeader.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> <div v-if="note.user.isBot" :class="$style.isBot">bot</div> <div v-if="note.user.badgeRoles" :class="$style.badgeRoles"> - <img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/> + <img v-for="(role, i) in note.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl!"/> </div> </div> <div :class="$style.username"><MkAcct :user="note.user"/></div>