From d6bc8c3cfc6c75794644dbc06fc7e653be39938b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AC=B4=EB=9D=BC=EC=BF=A0=EB=AA=A8?= Date: Thu, 20 Jun 2024 01:04:53 +0900 Subject: [PATCH] feat: translate button on note footer --- packages/frontend/src/components/MkNote.vue | 18 +++++++++++++++++- .../frontend/src/components/MkNoteDetailed.vue | 17 ++++++++++++++++- .../frontend/src/pages/settings/general.vue | 2 ++ packages/frontend/src/scripts/get-note-menu.ts | 6 +++--- packages/frontend/src/store.ts | 4 ++++ 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index f17645b46..b88eeb484 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -128,6 +128,9 @@ SPDX-License-Identifier: AGPL-3.0-only + @@ -193,6 +196,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; import { isEnabledUrlPreview } from '@/instance.js'; +import {miLocalStorage} from "@/local-storage.js"; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -250,6 +254,7 @@ const renoteButton = shallowRef(); const renoteTime = shallowRef(); const reactButton = shallowRef(); const clipButton = shallowRef(); +const translateButton = shallowRef(); const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value); const isMyRenote = $i && ($i.id === note.value.userId); const showContent = ref(false); @@ -481,7 +486,7 @@ function showMenu(viaKeyboard = false): void { }).then(focus).finally(cleanup); } -async function clip() { +async function clip(): Promise { if (props.mock) { return; } @@ -489,6 +494,17 @@ async function clip() { os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus); } +async function translate(): Promise { + if (translation.value != null) return; + translating.value = true; + const res = await misskeyApi('notes/translate', { + noteId: appearNote.value.id, + targetLang: miLocalStorage.getItem('lang') ?? navigator.language, + }); + translating.value = false; + translation.value = res; +} + function showRenoteMenu(viaKeyboard = false): void { if (props.mock) { return; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 9091473c7..9437d3b90 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -136,6 +136,9 @@ SPDX-License-Identifier: AGPL-3.0-only + @@ -233,6 +236,7 @@ import MkPagination, { type Paging } from '@/components/MkPagination.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkButton from '@/components/MkButton.vue'; import { isEnabledUrlPreview } from '@/instance.js'; +import {miLocalStorage} from "@/local-storage.js"; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -478,10 +482,21 @@ function showMenu(viaKeyboard = false): void { }).then(focus).finally(cleanup); } -async function clip() { +async function clip(): Promise { os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted }), clipButton.value).then(focus); } +async function translate(): Promise { + if (translation.value != null) return; + translating.value = true; + const res = await misskeyApi('notes/translate', { + noteId: appearNote.value.id, + targetLang: miLocalStorage.getItem('lang') ?? navigator.language, + }); + translating.value = false; + translation.value = res; +} + function showRenoteMenu(viaKeyboard = false): void { if (!isMyRenote) return; pleaseLogin(); diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index db7d5ddcf..10e6eb1a0 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.showNoteActionsOnlyHover }} {{ i18n.ts.showClipButtonInNoteFooter }} + {{ i18n.ts.showTranslateButtonInNoteFooter }} {{ i18n.ts.collapseRenotes }} {{ i18n.ts._wordMute.hideMutedNotes }} {{ i18n.ts.enableAdvancedMfm }} @@ -278,6 +279,7 @@ const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDev const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior')); const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover')); const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter')); +const showTranslateButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showTranslateButtonInNoteFooter')); const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction')); const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index e7c9a848e..359e8b379 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -37,7 +37,7 @@ export async function getNoteClipMenu(props: { const isRenote = ( props.note.renote != null && props.note.text == null && - props.note.fileIds.length === 0 && + props.note.fileIds?.length === 0 && props.note.poll == null ); @@ -165,7 +165,7 @@ export function getNoteMenu(props: { const isRenote = ( props.note.renote != null && props.note.text == null && - props.note.fileIds.length === 0 && + props.note.fileIds?.length === 0 && props.note.poll == null ); @@ -321,7 +321,7 @@ export function getNoteMenu(props: { text: i18n.ts.share, action: share, }] : []), - $i && $i.policies.canUseTranslator && instance.translatorAvailable ? { + $i && $i.policies.canUseTranslator && instance.translatorAvailable && !defaultStore.state.showTranslateButtonInNoteFooter? { icon: 'ti ti-language-hiragana', text: i18n.ts.translate, action: translate, diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index e813722a6..7619b53a9 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -366,6 +366,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + showTranslateButtonInNoteFooter: { + where: 'device', + default: false, + }, reactionsDisplaySize: { where: 'device', default: 'medium' as 'small' | 'medium' | 'large',