diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2022-12-26 16:04:56 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2022-12-26 16:04:56 +0900 |
| commit | d106fb39abf8fe17c00e562c1237fcacfe158345 (patch) | |
| tree | 5600e668f6048868f94958a722ab3bbe9cb078de | |
| parent | :art: (diff) | |
| download | sharkey-d106fb39abf8fe17c00e562c1237fcacfe158345.tar.gz sharkey-d106fb39abf8fe17c00e562c1237fcacfe158345.tar.bz2 sharkey-d106fb39abf8fe17c00e562c1237fcacfe158345.zip | |
feat: introduce fluent emoji
| -rw-r--r-- | .dockerignore | 1 | ||||
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| m--------- | fluent-emojis | 0 | ||||
| -rw-r--r-- | locales/ja-JP.yml | 3 | ||||
| m--------- | misskey-assets | 0 | ||||
| -rw-r--r-- | packages/backend/src/server/web/ClientServerService.ts | 15 | ||||
| -rw-r--r-- | packages/client/src/components/MkAutocomplete.vue | 10 | ||||
| -rw-r--r-- | packages/client/src/components/MkReactionsViewer.details.vue | 1 | ||||
| -rw-r--r-- | packages/client/src/components/global/MkEmoji.vue | 8 | ||||
| -rw-r--r-- | packages/client/src/pages/settings/general.vue | 16 | ||||
| -rw-r--r-- | packages/client/src/pages/settings/preferences-backups.vue | 2 | ||||
| -rw-r--r-- | packages/client/src/scripts/emoji-base.ts | 20 | ||||
| -rw-r--r-- | packages/client/src/scripts/twemoji-base.ts | 12 | ||||
| -rw-r--r-- | packages/client/src/store.ts | 4 |
15 files changed, 68 insertions, 28 deletions
diff --git a/.dockerignore b/.dockerignore index 84fd34f8e7..854e643d39 100644 --- a/.dockerignore +++ b/.dockerignore @@ -14,6 +14,7 @@ packages/*/node_modules redis/ files/ misskey-assets/ +fluent-emojis/ .pnp.* .yarn/* !.yarn/patches diff --git a/.gitmodules b/.gitmodules index 9246e09b8b..225a69a652 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "misskey-assets"] path = misskey-assets url = https://github.com/misskey-dev/assets.git +[submodule "fluent-emojis"] + path = fluent-emojis + url = https://github.com/misskey-dev/emojis.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f633d43c4..f7b939d621 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ You should also include the user name that made the change. - Client: Implement the toggle to or not to close push notifications when notifications or messages are read @tamaina - Client: show Unicode emoji tooltip with its name in MkReactionsViewer.reaction @saschanaz - Client: add user list widget @syuilo +- Client: introduce fluent emoji @syuilo - Client: improve overall performance of client @syuilo ### Bugfixes diff --git a/fluent-emojis b/fluent-emojis new file mode 160000 +Subproject cae981eb4c5189ea9ea3230e83b876a5068df7d diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 6ebeaf65be..8add6348df 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -456,7 +456,8 @@ language: "言語" uiLanguage: "UIの表示言語" groupInvited: "グループに招待されました" aboutX: "{x}について" -useOsNativeEmojis: "OSネイティブの絵文字を使用" +emojiStyle: "絵文字のスタイル" +native: "ネイティブ" disableDrawer: "メニューをドロワーで表示しない" youHaveNoGroups: "グループがありません" joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。" diff --git a/misskey-assets b/misskey-assets -Subproject 0179793ec891856d6f37a3be16ba4c22f67a81b +Subproject cf3ce27b2eb8417233072e3d6d2fb7c5356c236 diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 1932f88428..97acfcb919 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -217,6 +217,21 @@ export class ClientServerService { return reply.sendFile('/apple-touch-icon.png', staticAssets); }); + fastify.get<{ Params: { path: string } }>('/fluent-emoji/:path(.*)', async (request, reply) => { + const path = request.params.path; + + if (!path.match(/^[0-9a-f-]+\.png$/)) { + reply.code(404); + return; + } + + reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); + + return await reply.sendFile(path, `${_dirname}/../../../../../fluent-emojis/dist/`, { + maxAge: ms('30 days'), + }); + }); + fastify.get<{ Params: { path: string } }>('/twemoji/:path(.*)', async (request, reply) => { const path = request.params.path; diff --git a/packages/client/src/components/MkAutocomplete.vue b/packages/client/src/components/MkAutocomplete.vue index e1fd5693b6..72783921d5 100644 --- a/packages/client/src/components/MkAutocomplete.vue +++ b/packages/client/src/components/MkAutocomplete.vue @@ -18,7 +18,7 @@ <ol v-else-if="emojis.length > 0" ref="suggests" class="emojis"> <li v-for="emoji in emojis" tabindex="-1" @click="complete(type, emoji.emoji)" @keydown="onKeydown"> <span v-if="emoji.isCustomEmoji" class="emoji"><img :src="defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span> - <span v-else-if="!defaultStore.state.useOsNativeEmojis" class="emoji"><img :src="emoji.url" :alt="emoji.emoji"/></span> + <span v-else-if="defaultStore.state.emojiStyle != 'native'" class="emoji"><img :src="emoji.url" :alt="emoji.emoji"/></span> <span v-else class="emoji">{{ emoji.emoji }}</span> <!-- eslint-disable-next-line vue/no-v-html --> <span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span> @@ -36,7 +36,7 @@ <script lang="ts"> import { markRaw, ref, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'; import contains from '@/scripts/contains'; -import { char2filePath } from '@/scripts/twemoji-base'; +import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base'; import { getStaticImageUrl } from '@/scripts/get-static-image-url'; import { acct } from '@/filters/user'; import * as os from '@/os'; @@ -59,9 +59,11 @@ const lib = emojilist.filter(x => x.category !== 'flags'); const emjdb: EmojiDef[] = lib.map(x => ({ emoji: x.char, name: x.name, - url: char2filePath(x.char), + url: char2path(x.char), })); +const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; + for (const x of lib) { if (x.keywords) { for (const k of x.keywords) { @@ -69,7 +71,7 @@ for (const x of lib) { emoji: x.char, name: k, aliasOf: x.name, - url: char2filePath(x.char), + url: char2path(x.char), }); } } diff --git a/packages/client/src/components/MkReactionsViewer.details.vue b/packages/client/src/components/MkReactionsViewer.details.vue index fb8d74ad4b..29902a5075 100644 --- a/packages/client/src/components/MkReactionsViewer.details.vue +++ b/packages/client/src/components/MkReactionsViewer.details.vue @@ -56,6 +56,7 @@ function getReactionName(reaction: string): string { display: block; width: 60px; font-size: 60px; // unicodeな絵文字についてはwidthが効かないため + object-fit: contain; margin: 0 auto; } diff --git a/packages/client/src/components/global/MkEmoji.vue b/packages/client/src/components/global/MkEmoji.vue index 419850d007..ce1299a39f 100644 --- a/packages/client/src/components/global/MkEmoji.vue +++ b/packages/client/src/components/global/MkEmoji.vue @@ -9,7 +9,7 @@ import { computed } from 'vue'; import { CustomEmoji } from 'misskey-js/built/entities'; import { getStaticImageUrl } from '@/scripts/get-static-image-url'; -import { char2filePath } from '@/scripts/twemoji-base'; +import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base'; import { defaultStore } from '@/store'; import { instance } from '@/instance'; import { getEmojiName } from '@/scripts/emojilist'; @@ -22,14 +22,16 @@ const props = defineProps<{ isReaction?: boolean; }>(); +const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; + const isCustom = computed(() => props.emoji.startsWith(':')); const char = computed(() => isCustom.value ? undefined : props.emoji); -const useOsNativeEmojis = computed(() => defaultStore.state.useOsNativeEmojis && !props.isReaction); +const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'native' && !props.isReaction); const ce = computed(() => props.customEmojis ?? instance.emojis ?? []); const customEmoji = computed(() => isCustom.value ? ce.value.find(x => x.name === props.emoji.substr(1, props.emoji.length - 2)) : undefined); const url = computed(() => { if (char.value) { - return char2filePath(char.value); + return char2path(char.value); } else { const rawUrl = (customEmoji.value as CustomEmoji).url; return defaultStore.state.disableShowingAnimatedImages diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue index e4dbde0220..84d99d2fd7 100644 --- a/packages/client/src/pages/settings/general.vue +++ b/packages/client/src/pages/settings/general.vue @@ -48,10 +48,16 @@ <FormSwitch v-model="disableShowingAnimatedImages" class="_formBlock">{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch> <FormSwitch v-model="squareAvatars" class="_formBlock">{{ i18n.ts.squareAvatars }}</FormSwitch> <FormSwitch v-model="useSystemFont" class="_formBlock">{{ i18n.ts.useSystemFont }}</FormSwitch> - <FormSwitch v-model="useOsNativeEmojis" class="_formBlock"> - {{ i18n.ts.useOsNativeEmojis }} - <div><Mfm :key="useOsNativeEmojis" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> - </FormSwitch> + <div class="_formBlock"> + <FormRadios v-model="emojiStyle"> + <template #label>{{ i18n.ts.emojiStyle }}</template> + <option value="native">{{ i18n.ts.native }}</option> + <option value="fluentEmoji">Fluent Emoji</option> + <option value="twemoji">Twemoji</option> + </FormRadios> + <div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> + </div> + <FormSwitch v-model="disableDrawer" class="_formBlock">{{ i18n.ts.disableDrawer }}</FormSwitch> <FormRadios v-model="fontSize" class="_formBlock"> @@ -129,7 +135,7 @@ const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEff const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline')); const disableAnimatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v)); -const useOsNativeEmojis = computed(defaultStore.makeGetterSetter('useOsNativeEmojis')); +const emojiStyle = computed(defaultStore.makeGetterSetter('emojiStyle')); const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer')); const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); diff --git a/packages/client/src/pages/settings/preferences-backups.vue b/packages/client/src/pages/settings/preferences-backups.vue index d2a2dc01ca..f427a170c4 100644 --- a/packages/client/src/pages/settings/preferences-backups.vue +++ b/packages/client/src/pages/settings/preferences-backups.vue @@ -63,7 +63,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'imageNewTab', 'disableShowingAnimatedImages', 'disablePagesScript', - 'useOsNativeEmojis', + 'emojiStyle', 'disableDrawer', 'useBlurEffectForModal', 'useBlurEffect', diff --git a/packages/client/src/scripts/emoji-base.ts b/packages/client/src/scripts/emoji-base.ts new file mode 100644 index 0000000000..3f05642d57 --- /dev/null +++ b/packages/client/src/scripts/emoji-base.ts @@ -0,0 +1,20 @@ +const twemojiSvgBase = '/twemoji'; +const fluentEmojiPngBase = '/fluent-emoji'; + +export function char2twemojiFilePath(char: string): string { + let codes = Array.from(char).map(x => x.codePointAt(0)?.toString(16)); + if (!codes.includes('200d')) codes = codes.filter(x => x !== 'fe0f'); + codes = codes.filter(x => x && x.length); + const fileName = codes.join('-'); + return `${twemojiSvgBase}/${fileName}.svg`; +} + +export function char2fluentEmojiFilePath(char: string): string { + let codes = Array.from(char).map(x => x.codePointAt(0)?.toString(16)); + // Fluent Emojiは国旗非対応 https://github.com/microsoft/fluentui-emoji/issues/25 + if (codes[0]?.startsWith('1f1')) return char2twemojiFilePath(char); + if (!codes.includes('200d')) codes = codes.filter(x => x !== 'fe0f'); + codes = codes.filter(x => x && x.length); + const fileName = codes.map(x => x!.padStart(4, '0')).join('-'); + return `${fluentEmojiPngBase}/${fileName}.png`; +} diff --git a/packages/client/src/scripts/twemoji-base.ts b/packages/client/src/scripts/twemoji-base.ts deleted file mode 100644 index 638aae3284..0000000000 --- a/packages/client/src/scripts/twemoji-base.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const twemojiSvgBase = '/twemoji'; - -export function char2fileName(char: string): string { - let codes = Array.from(char).map(x => x.codePointAt(0)?.toString(16)); - if (!codes.includes('200d')) codes = codes.filter(x => x !== 'fe0f'); - codes = codes.filter(x => x && x.length); - return codes.join('-'); -} - -export function char2filePath(char: string): string { - return `${twemojiSvgBase}/${char2fileName(char)}.svg`; -} diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index d33a351725..1bedab5fad 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -174,9 +174,9 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, - useOsNativeEmojis: { + emojiStyle: { where: 'device', - default: false, + default: 'twemoji', // twemoji / fluentEmoji / native }, disableDrawer: { where: 'device', |