diff --git a/CHANGELOG.md b/CHANGELOG.md index 269f8b1000..43cf0ef6e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ You should also include the user name that made the change. - Firefox109以下はサポートされなくなりました #### For app developers +- API: metaのレスポンスに`emojis`プロパティが含まれなくなりました + - カスタム絵文字一覧情報を取得するには、`emojis`エンドポイントにリクエストします - API: カスタム絵文字エンティティに`url`プロパティが含まれなくなりました - 絵文字画像を表示するには、`<instance host>/emoji/<emoji name>.webp`にリクエストすると画像が返ります。 - e.g. `https://p1.a9z.dev/emoji/misskey.webp` diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 8a2dc70eda..2a4e09519f 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -22,23 +22,25 @@ export class EmojiEntityService { @bindThis public async pack( src: Emoji['id'] | Emoji, + opts: { omitHost?: boolean; omitId?: boolean; } = {}, ): Promise<Packed<'Emoji'>> { const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); return { - id: emoji.id, + id: opts.omitId ? undefined : emoji.id, aliases: emoji.aliases, name: emoji.name, category: emoji.category, - host: emoji.host, + host: opts.omitHost ? undefined : emoji.host, }; } @bindThis public packMany( emojis: any[], + opts: { omitHost?: boolean; omitId?: boolean; } = {}, ) { - return Promise.all(emojis.map(x => this.pack(x))); + return Promise.all(emojis.map(x => this.pack(x, opts))); } } diff --git a/packages/backend/src/models/schema/emoji.ts b/packages/backend/src/models/schema/emoji.ts index 9a52609b68..d897a0fc05 100644 --- a/packages/backend/src/models/schema/emoji.ts +++ b/packages/backend/src/models/schema/emoji.ts @@ -3,7 +3,7 @@ export const packedEmojiSchema = { properties: { id: { type: 'string', - optional: false, nullable: false, + optional: true, nullable: false, format: 'id', example: 'xxxxxxxxxx', }, @@ -26,12 +26,8 @@ export const packedEmojiSchema = { }, host: { type: 'string', - optional: false, nullable: true, + optional: true, nullable: true, description: 'The local host is represented with `null`.', }, - url: { - type: 'string', - optional: true, nullable: false, - }, }, } as const; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 60beca4f47..ab9349966d 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -220,6 +220,7 @@ import * as ep___messaging_messages_create from './endpoints/messaging/messages/ import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; import * as ep___meta from './endpoints/meta.js'; +import * as ep___emojis from './endpoints/emojis.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; import * as ep___mute_delete from './endpoints/mute/delete.js'; @@ -550,6 +551,7 @@ const $messaging_messages_create: Provider = { provide: 'ep:messaging/messages/c const $messaging_messages_delete: Provider = { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }; const $messaging_messages_read: Provider = { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }; const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; +const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; const $mute_delete: Provider = { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }; @@ -884,6 +886,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $messaging_messages_delete, $messaging_messages_read, $meta, + $emojis, $miauth_genToken, $mute_create, $mute_delete, @@ -1212,6 +1215,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $messaging_messages_delete, $messaging_messages_read, $meta, + $emojis, $miauth_genToken, $mute_create, $mute_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index d4f8be5b85..f9749ad660 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -219,6 +219,7 @@ import * as ep___messaging_messages_create from './endpoints/messaging/messages/ import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; import * as ep___meta from './endpoints/meta.js'; +import * as ep___emojis from './endpoints/emojis.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; import * as ep___mute_delete from './endpoints/mute/delete.js'; @@ -547,6 +548,7 @@ const eps = [ ['messaging/messages/delete', ep___messaging_messages_delete], ['messaging/messages/read', ep___messaging_messages_read], ['meta', ep___meta], + ['emojis', ep___emojis], ['miauth/gen-token', ep___miauth_genToken], ['mute/create', ep___mute_create], ['mute/delete', ep___mute_delete], diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts new file mode 100644 index 0000000000..0a16268229 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/emojis.ts @@ -0,0 +1,91 @@ +import { IsNull, MoreThan } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { EmojisRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; +import type { Config } from '@/config.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['meta'], + + requireCredential: false, + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + emojis: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + category: { + type: 'string', + optional: false, nullable: true, + }, + }, + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.emojisRepository) + private emojisRepository: EmojisRepository, + + private emojiEntityService: EmojiEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const emojis = await this.emojisRepository.find({ + where: { + host: IsNull(), + }, + order: { + category: 'ASC', + name: 'ASC', + }, + cache: { + id: 'meta_emojis', + milliseconds: 3600000, // 1 hour + }, + }); + + return { + emojis: await this.emojiEntityService.packMany(emojis, { + omitId: true, + omitHost: true, + }), + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 05da011979..c44d63d64b 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -4,7 +4,6 @@ import type { AdsRepository, EmojisRepository, UsersRepository } from '@/models/ import { MAX_NOTE_TEXT_LENGTH, DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; @@ -152,43 +151,6 @@ export const meta = { type: 'number', optional: false, nullable: false, }, - emojis: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - aliases: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - }, - }, - category: { - type: 'string', - optional: false, nullable: true, - }, - host: { - type: 'string', - optional: false, nullable: true, - description: 'The local host is represented with `null`.', - }, - url: { - type: 'string', - optional: false, nullable: false, - format: 'url', - }, - }, - }, - }, ads: { type: 'array', optional: false, nullable: false, @@ -326,30 +288,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.adsRepository) private adsRepository: AdsRepository, - @Inject(DI.emojisRepository) - private emojisRepository: EmojisRepository, - private userEntityService: UserEntityService, - private emojiEntityService: EmojiEntityService, private metaService: MetaService, ) { super(meta, paramDef, async (ps, me) => { const instance = await this.metaService.fetch(true); - const emojis = await this.emojisRepository.find({ - where: { - host: IsNull(), - }, - order: { - category: 'ASC', - name: 'ASC', - }, - cache: { - id: 'meta_emojis', - milliseconds: 3600000, // 1 hour - }, - }); - const ads = await this.adsRepository.find({ where: { expiresAt: MoreThan(new Date()), @@ -390,7 +334,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため - emojis: await this.emojiEntityService.packMany(emojis), defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, ads: ads.map(ad => ({ diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 8ed60bc5dc..0d0c5edbae 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -47,6 +47,9 @@ import { emojilist } from '@/scripts/emojilist'; import { instance } from '@/instance'; import { i18n } from '@/i18n'; import { miLocalStorage } from '@/local-storage'; +import { getCustomEmojis } from '@/custom-emojis'; + +const customEmojis = await getCustomEmojis(); type EmojiDef = { emoji: string; @@ -86,7 +89,6 @@ for (const x of lib) { emjdb.sort((a, b) => a.name.length - b.name.length); //#region Construct Emoji DB -const customEmojis = instance.emojis; const emojiDefinitions: EmojiDef[] = []; for (const x of customEmojis) { @@ -117,7 +119,6 @@ export default { emojiDb, emojiDefinitions, emojilist, - customEmojis, }; </script> diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index f3b3ac0b50..8df01f6c25 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -6,7 +6,7 @@ <div v-if="searchResultCustom.length > 0" class="body"> <button v-for="emoji in searchResultCustom" - :key="emoji.id" + :key="emoji.name" class="_button item" :title="emoji.name" tabindex="0" @@ -85,9 +85,10 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; import * as os from '@/os'; import { isTouchUsing } from '@/scripts/touch'; import { deviceKind } from '@/scripts/device-kind'; -import { emojiCategories, instance } from '@/instance'; +import { instance } from '@/instance'; import { i18n } from '@/i18n'; import { defaultStore } from '@/store'; +import { getCustomEmojiCategories, getCustomEmojis } from '@/custom-emojis'; const props = withDefaults(defineProps<{ showPinned?: boolean; @@ -103,6 +104,7 @@ const emit = defineEmits<{ (ev: 'chosen', v: string): void; }>(); +const customEmojis = await getCustomEmojis(); const search = shallowRef<HTMLInputElement>(); const emojis = shallowRef<HTMLDivElement>(); @@ -118,8 +120,7 @@ const { const size = computed(() => props.asReactionPicker ? reactionPickerSize.value : 1); const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3); const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2); -const customEmojiCategories = emojiCategories; -const customEmojis = instance.emojis; +const customEmojiCategories = await getCustomEmojiCategories(); const q = ref<string>(''); const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]); const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts new file mode 100644 index 0000000000..2a52753f84 --- /dev/null +++ b/packages/frontend/src/custom-emojis.ts @@ -0,0 +1,48 @@ +import { api } from './os'; +import { miLocalStorage } from './local-storage'; + +const storageCache = miLocalStorage.getItem('emojis'); +let cached = storageCache ? JSON.parse(storageCache) : null; +export async function getCustomEmojis() { + const now = Date.now(); + const lastFetchedAt = miLocalStorage.getItem('lastEmojisFetchedAt'); + if (cached && lastFetchedAt && (now - parseInt(lastFetchedAt)) < 1000 * 60 * 60) return cached; + + const res = await api('emojis', {}); + + cached = res.emojis; + miLocalStorage.setItem('emojis', JSON.stringify(cached)); + miLocalStorage.setItem('lastEmojisFetchedAt', now.toString()); +} + +let cachedCategories; +export async function getCustomEmojiCategories() { + if (cachedCategories) return cachedCategories; + + const customEmojis = await getCustomEmojis(); + + const categories = new Set(); + for (const emoji of customEmojis) { + categories.add(emoji.category); + } + const res = Array.from(categories); + cachedCategories = res; + return res; +} + +let cachedTags; +export async function getCustomEmojiTags() { + if (cachedTags) return cachedTags; + + const customEmojis = await getCustomEmojis(); + + const tags = new Set(); + for (const emoji of customEmojis) { + for (const tag of emoji.aliases) { + tags.add(tag); + } + } + const res = Array.from(tags); + cachedTags = res; + return res; +} diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index 82d3e7aea2..08dbd9737c 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -5,11 +5,11 @@ import { miLocalStorage } from './local-storage'; // TODO: 他のタブと永続化されたstateを同期 -const instanceData = miLocalStorage.getItem('instance'); +const cached = miLocalStorage.getItem('instance'); // TODO: instanceをリアクティブにするかは再考の余地あり -export const instance: Misskey.entities.InstanceMetadata = reactive(instanceData ? JSON.parse(instanceData) : { +export const instance: Misskey.entities.InstanceMetadata = reactive(cached ? JSON.parse(cached) : { // TODO: set default values }); @@ -24,23 +24,3 @@ export async function fetchInstance() { miLocalStorage.setItem('instance', JSON.stringify(instance)); } - -export const emojiCategories = computed(() => { - if (instance.emojis == null) return []; - const categories = new Set(); - for (const emoji of instance.emojis) { - categories.add(emoji.category); - } - return Array.from(categories); -}); - -export const emojiTags = computed(() => { - if (instance.emojis == null) return []; - const tags = new Set(); - for (const emoji of instance.emojis) { - for (const tag of emoji.aliases) { - tags.add(tag); - } - } - return Array.from(tags); -}); diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index 50e28d621f..bb8192e980 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -2,6 +2,8 @@ type Keys = 'v' | 'lastVersion' | 'instance' | + 'emojis' | // TODO: indexed db + 'lastEmojisFetchedAt' | 'account' | 'accounts' | 'latestDonationInfoShownAt' | diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index e15a2b1bdf..acf6237c87 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -7,7 +7,7 @@ <!-- たくさんあると邪魔 <div class="tags"> - <span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> + <span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> </div> --> </div> @@ -28,8 +28,8 @@ </div> </template> -<script lang="ts"> -import { defineComponent, computed } from 'vue'; +<script lang="ts" setup> +import { defineComponent, computed, watch } from 'vue'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; @@ -37,62 +37,43 @@ import MkSelect from '@/components/MkSelect.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkTab from '@/components/MkTab.vue'; import * as os from '@/os'; -import { emojiCategories, emojiTags } from '@/instance'; +import { getCustomEmojis, getCustomEmojiCategories, getCustomEmojiTags } from '@/custom-emojis'; -export default defineComponent({ - components: { - MkButton, - MkInput, - MkSelect, - MkFoldableSection, - MkTab, - XEmoji, - }, +const customEmojis = await getCustomEmojis(); +const customEmojiCategories = await getCustomEmojiCategories(); +const customEmojiTags = await getCustomEmojiTags(); +let q = $ref(''); +let searchEmojis = $ref(null); +let selectedTags = $ref(new Set()); - data() { - return { - q: '', - customEmojiCategories: emojiCategories, - customEmojis: this.$instance.emojis, - tags: emojiTags, - selectedTags: new Set(), - searchEmojis: null, - }; - }, +function search() { + if ((q === '' || q == null) && selectedTags.size === 0) { + searchEmojis = null; + return; + } - watch: { - q() { this.search(); }, - selectedTags: { - handler() { - this.search(); - }, - deep: true, - }, - }, + if (selectedTags.size === 0) { + searchEmojis = customEmojis.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q)); + } else { + searchEmojis = customEmojis.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t))); + } +} - methods: { - search() { - if ((this.q === '' || this.q == null) && this.selectedTags.size === 0) { - this.searchEmojis = null; - return; - } +function toggleTag(tag) { + if (selectedTags.has(tag)) { + selectedTags.delete(tag); + } else { + selectedTags.add(tag); + } +} - if (this.selectedTags.size === 0) { - this.searchEmojis = this.customEmojis.filter(emoji => emoji.name.includes(this.q) || emoji.aliases.includes(this.q)); - } else { - this.searchEmojis = this.customEmojis.filter(emoji => (emoji.name.includes(this.q) || emoji.aliases.includes(this.q)) && [...this.selectedTags].every(t => emoji.aliases.includes(t))); - } - }, - - toggleTag(tag) { - if (this.selectedTags.has(tag)) { - this.selectedTags.delete(tag); - } else { - this.selectedTags.add(tag); - } - }, - }, +watch($$(q), () => { + search(); }); + +watch($$(selectedTags), () => { + search(); +}, { deep: true }); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/pages/admin/emoji-edit-dialog.vue b/packages/frontend/src/pages/admin/emoji-edit-dialog.vue index c0a997d7b4..0b6a5e1557 100644 --- a/packages/frontend/src/pages/admin/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/admin/emoji-edit-dialog.vue @@ -36,7 +36,7 @@ import MkInput from '@/components/MkInput.vue'; import * as os from '@/os'; import { unique } from '@/scripts/array'; import { i18n } from '@/i18n'; -import { emojiCategories } from '@/instance'; +import { getCustomEmojiCategories } from '@/custom-emojis'; const props = defineProps<{ emoji: any, @@ -46,7 +46,7 @@ let dialog = $ref(null); let name: string = $ref(props.emoji.name); let category: string = $ref(props.emoji.category); let aliases: string = $ref(props.emoji.aliases.join(' ')); -let categories: string[] = $ref(emojiCategories); +const categories = await getCustomEmojiCategories(); const emit = defineEmits<{ (ev: 'done', v: { deleted?: boolean, updated?: any }): void, diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue index 43a735cbf9..08f31676f3 100644 --- a/packages/frontend/src/pages/admin/overview.stats.vue +++ b/packages/frontend/src/pages/admin/overview.stats.vue @@ -36,7 +36,7 @@ <div class="icon"><i class="ti ti-icons"></i></div> <div class="body"> <div class="value"> - <MkNumber :value="$instance.emojis.length" style="margin-right: 0.5em;"/> + <MkNumber :value="customEmojis.length" style="margin-right: 0.5em;"/> </div> <div class="label">Custom emojis</div> </div> @@ -63,6 +63,9 @@ import number from '@/filters/number'; import MkNumberDiff from '@/components/MkNumberDiff.vue'; import MkNumber from '@/components/MkNumber.vue'; import { i18n } from '@/i18n'; +import { getCustomEmojis } from '@/custom-emojis'; + +const customEmojis = await getCustomEmojis(); let stats: any = $ref(null); let usersComparedToThePrevDay = $ref<number>(); diff --git a/packages/frontend/src/pages/mfm-cheat-sheet.vue b/packages/frontend/src/pages/mfm-cheat-sheet.vue index 697a692743..f49b6959c8 100644 --- a/packages/frontend/src/pages/mfm-cheat-sheet.vue +++ b/packages/frontend/src/pages/mfm-cheat-sheet.vue @@ -317,12 +317,15 @@ import MkTextarea from '@/components/MkTextarea.vue'; import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; +import { getCustomEmojis } from '@/custom-emojis'; + +const customEmojis = await getCustomEmojis(); let preview_mention = $ref('@example'); let preview_hashtag = $ref('#test'); let preview_url = $ref('https://example.com'); let preview_link = $ref(`[${i18n.ts._mfm.dummy}](https://example.com)`); -let preview_emoji = $ref(instance.emojis.length ? `:${instance.emojis[0].name}:` : ':emojiname:'); +let preview_emoji = $ref(customEmojis.length ? `:${customEmojis[0].name}:` : ':emojiname:'); let preview_bold = $ref(`**${i18n.ts._mfm.dummy}**`); let preview_small = $ref(`<small>${i18n.ts._mfm.dummy}</small>`); let preview_center = $ref(`<center>${i18n.ts._mfm.dummy}</center>`); diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 3468d44e00..e1e050ee70 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -183,6 +183,8 @@ const menuDef = computed(() => [{ action: () => { miLocalStorage.removeItem('locale'); miLocalStorage.removeItem('theme'); + miLocalStorage.removeItem('emojis'); + miLocalStorage.removeItem('lastEmojisFetchedAt'); unisonReload(); }, }, {