diff --git a/locales/en-US.yml b/locales/en-US.yml index 313514148f..527bee9af0 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2839,3 +2839,4 @@ _selfXssPrevention: insertNewNotes: "Insert new notes at current position" insertNewNotesDescription: "Insert new notes at the current position while scrolling timeline." clickToShowInstanceTickerWindow: "Click InstanceTicker to show instance info" +defaultLike: "Default like reaction" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index a9f4ffba75..4eb3622515 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -320,6 +320,8 @@ const renoteCollapsed = ref( ), ); +const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null); + const inReplyToCollapsed = ref(defaultStore.state.collapseNotesRepliedTo); const disableReactionsViewer = ref(defaultStore.reactiveState.disableReactionsViewer); const collapsedUnexpectedLangs = ref(defaultStore.reactiveState.collapsedUnexpectedLangs); @@ -500,15 +502,15 @@ function reply(): void { } function like(): void { - pleaseLogin(undefined, pleaseLoginContext.value); + pleaseLogin({ openOnRemote: pleaseLoginContext.value }); showMovedDialog(); sound.playMisskeySfx('reaction'); if (props.mock) { return; } - misskeyApi('notes/reactions/create', { + misskeyApi('notes/like', { noteId: appearNote.value.id, - reaction: '❤️', + override: defaultLike.value, }); const el = likeButton.value as HTMLElement | null | undefined; if (el) { @@ -531,9 +533,9 @@ function react(): void { return; } - misskeyApi('notes/reactions/create', { + misskeyApi('notes/like', { noteId: appearNote.value.id, - reaction: '❤️', + override: defaultLike.value, }); const el = reactButton.value; if (el) { diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 8ad128e54e..cfa0579bd2 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -348,6 +348,7 @@ const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultS const conversation = ref<Misskey.entities.Note[]>([]); const replies = ref<Misskey.entities.Note[]>([]); const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i?.id); +const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null); type ShowingNoteHistoryState = { createdAt: string | null; @@ -496,15 +497,12 @@ function reply(): void { } function like(): void { - pleaseLogin(undefined, pleaseLoginContext.value); + pleaseLogin({ openOnRemote: pleaseLoginContext.value }); showMovedDialog(); sound.playMisskeySfx('reaction'); - if (props.mock) { - return; - } - misskeyApi('notes/reactions/create', { + misskeyApi('notes/like', { noteId: appearNote.value.id, - reaction: '❤️', + override: defaultLike.value, }); const el = likeButton.value as HTMLElement | null | undefined; if (el) { @@ -523,9 +521,9 @@ function react(): void { if (appearNote.value.reactionAcceptance === 'likeOnly' || disableReactionsViewer.value) { sound.playMisskeySfx('reaction'); - misskeyApi('notes/reactions/create', { + misskeyApi('notes/like', { noteId: appearNote.value.id, - reaction: '❤️', + override: defaultLike.value, }); const el = reactButton.value; if (el) { diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue index fd3581d114..52331a8fa0 100644 --- a/packages/frontend/src/pages/settings/emoji-picker.vue +++ b/packages/frontend/src/pages/settings/emoji-picker.vue @@ -85,6 +85,17 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <FromSlot> + <template #label>{{ i18n.ts.defaultLike }}</template> + <MkCustomEmoji v-if="like && like.startsWith(':')" style="max-height: 3em; font-size: 1.1em;" :useOriginalSize="false" :name="like" :normal="true" :noStyle="true"/> + <MkEmoji v-else-if="like && !like.startsWith(':')" :emoji="like" style="max-height: 3em; font-size: 1.1em;" :normal="true" :noStyle="true"/> + <span v-else-if="!like">{{ i18n.ts.notSet }}</span> + <div class="_buttons" style="padding-top: 8px;"> + <MkButton rounded :small="true" inline @click="chooseNewLike"><i class="ti ti-plus"></i></MkButton> + <MkButton rounded :small="true" inline @click="resetLike"><i class="ti ti-refresh"></i></MkButton> + </div> + </FromSlot> + <FormSection> <template #label>{{ i18n.ts.emojiPickerDisplay }}</template> @@ -131,6 +142,7 @@ import Sortable from 'vuedraggable'; import MkRadios from '@/components/MkRadios.vue'; import MkButton from '@/components/MkButton.vue'; import FormSection from '@/components/form/section.vue'; +import FromSlot from '@/components/form/slot.vue'; import MkSelect from '@/components/MkSelect.vue'; import * as os from '@/os.js'; import { defaultStore } from '@/store.js'; @@ -155,6 +167,8 @@ const removeReaction = (reaction: string, ev: MouseEvent) => remove(pinnedEmojis const chooseReaction = (ev: MouseEvent) => pickEmoji(pinnedEmojisForReaction, ev); const setDefaultReaction = () => setDefault(pinnedEmojisForReaction); +const like = computed(defaultStore.makeGetterSetter('like')); + const removeEmoji = (reaction: string, ev: MouseEvent) => remove(pinnedEmojis, reaction, ev); const chooseEmoji = (ev: MouseEvent) => pickEmoji(pinnedEmojis, ev); const setDefaultEmoji = () => setDefault(pinnedEmojis); @@ -223,6 +237,18 @@ async function pickEmoji(itemsRef: Ref<string[]>, ev: MouseEvent) { }); } +function chooseNewLike(ev: MouseEvent) { + os.pickEmoji(getHTMLElement(ev), { + showPinned: false, + }).then(async emoji => { + defaultStore.set('like', emoji as string); + }); +} + +async function resetLike() { + defaultStore.set('like', null); +} + function getHTMLElement(ev: MouseEvent): HTMLElement { const target = ev.currentTarget ?? ev.target; return target as HTMLElement; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 57a0b63a7c..392de10117 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -136,6 +136,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: 'nonSensitiveOnly' as 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null, }, + like: { + where: 'account', + default: null as string | null, + }, mutedAds: { where: 'account', default: [] as string[],