feat: introduce fluent emoji
This commit is contained in:
		
							parent
							
								
									be0d396106
								
							
						
					
					
						commit
						d106fb39ab
					
				
					 15 changed files with 70 additions and 29 deletions
				
			
		| 
						 | 
					@ -14,6 +14,7 @@ packages/*/node_modules
 | 
				
			||||||
redis/
 | 
					redis/
 | 
				
			||||||
files/
 | 
					files/
 | 
				
			||||||
misskey-assets/
 | 
					misskey-assets/
 | 
				
			||||||
 | 
					fluent-emojis/
 | 
				
			||||||
.pnp.*
 | 
					.pnp.*
 | 
				
			||||||
.yarn/*
 | 
					.yarn/*
 | 
				
			||||||
!.yarn/patches
 | 
					!.yarn/patches
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,3 +1,6 @@
 | 
				
			||||||
[submodule "misskey-assets"]
 | 
					[submodule "misskey-assets"]
 | 
				
			||||||
	path = misskey-assets
 | 
						path = misskey-assets
 | 
				
			||||||
	url = https://github.com/misskey-dev/assets.git
 | 
						url = https://github.com/misskey-dev/assets.git
 | 
				
			||||||
 | 
					[submodule "fluent-emojis"]
 | 
				
			||||||
 | 
						path = fluent-emojis
 | 
				
			||||||
 | 
						url = https://github.com/misskey-dev/emojis.git
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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: 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: show Unicode emoji tooltip with its name in MkReactionsViewer.reaction @saschanaz
 | 
				
			||||||
- Client: add user list widget @syuilo
 | 
					- Client: add user list widget @syuilo
 | 
				
			||||||
 | 
					- Client: introduce fluent emoji @syuilo
 | 
				
			||||||
- Client: improve overall performance of client @syuilo
 | 
					- Client: improve overall performance of client @syuilo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Bugfixes
 | 
					### Bugfixes
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								fluent-emojis
									
										
									
									
									
										Submodule
									
								
							
							
						
						
									
										1
									
								
								fluent-emojis
									
										
									
									
									
										Submodule
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					Subproject commit cae981eb4c5189ea9ea3230e83b876a5068df7d1
 | 
				
			||||||
| 
						 | 
					@ -456,7 +456,8 @@ language: "言語"
 | 
				
			||||||
uiLanguage: "UIの表示言語"
 | 
					uiLanguage: "UIの表示言語"
 | 
				
			||||||
groupInvited: "グループに招待されました"
 | 
					groupInvited: "グループに招待されました"
 | 
				
			||||||
aboutX: "{x}について"
 | 
					aboutX: "{x}について"
 | 
				
			||||||
useOsNativeEmojis: "OSネイティブの絵文字を使用"
 | 
					emojiStyle: "絵文字のスタイル"
 | 
				
			||||||
 | 
					native: "ネイティブ"
 | 
				
			||||||
disableDrawer: "メニューをドロワーで表示しない"
 | 
					disableDrawer: "メニューをドロワーで表示しない"
 | 
				
			||||||
youHaveNoGroups: "グループがありません"
 | 
					youHaveNoGroups: "グループがありません"
 | 
				
			||||||
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
 | 
					joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5
 | 
					Subproject commit cf3ce27b2eb8417233072e3d6d2fb7c5356c2364
 | 
				
			||||||
| 
						 | 
					@ -217,6 +217,21 @@ export class ClientServerService {
 | 
				
			||||||
			return reply.sendFile('/apple-touch-icon.png', staticAssets);
 | 
								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) => {
 | 
							fastify.get<{ Params: { path: string } }>('/twemoji/:path(.*)', async (request, reply) => {
 | 
				
			||||||
			const path = request.params.path;
 | 
								const path = request.params.path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@
 | 
				
			||||||
	<ol v-else-if="emojis.length > 0" ref="suggests" class="emojis">
 | 
						<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">
 | 
							<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-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>
 | 
								<span v-else class="emoji">{{ emoji.emoji }}</span>
 | 
				
			||||||
			<!-- eslint-disable-next-line vue/no-v-html -->
 | 
								<!-- eslint-disable-next-line vue/no-v-html -->
 | 
				
			||||||
			<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
 | 
								<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import { markRaw, ref, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
 | 
					import { markRaw, ref, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
 | 
				
			||||||
import contains from '@/scripts/contains';
 | 
					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 { getStaticImageUrl } from '@/scripts/get-static-image-url';
 | 
				
			||||||
import { acct } from '@/filters/user';
 | 
					import { acct } from '@/filters/user';
 | 
				
			||||||
import * as os from '@/os';
 | 
					import * as os from '@/os';
 | 
				
			||||||
| 
						 | 
					@ -59,9 +59,11 @@ const lib = emojilist.filter(x => x.category !== 'flags');
 | 
				
			||||||
const emjdb: EmojiDef[] = lib.map(x => ({
 | 
					const emjdb: EmojiDef[] = lib.map(x => ({
 | 
				
			||||||
	emoji: x.char,
 | 
						emoji: x.char,
 | 
				
			||||||
	name: x.name,
 | 
						name: x.name,
 | 
				
			||||||
	url: char2filePath(x.char),
 | 
						url: char2path(x.char),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for (const x of lib) {
 | 
					for (const x of lib) {
 | 
				
			||||||
	if (x.keywords) {
 | 
						if (x.keywords) {
 | 
				
			||||||
		for (const k of x.keywords) {
 | 
							for (const k of x.keywords) {
 | 
				
			||||||
| 
						 | 
					@ -69,7 +71,7 @@ for (const x of lib) {
 | 
				
			||||||
				emoji: x.char,
 | 
									emoji: x.char,
 | 
				
			||||||
				name: k,
 | 
									name: k,
 | 
				
			||||||
				aliasOf: x.name,
 | 
									aliasOf: x.name,
 | 
				
			||||||
				url: char2filePath(x.char),
 | 
									url: char2path(x.char),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,6 +56,7 @@ function getReactionName(reaction: string): string {
 | 
				
			||||||
			display: block;
 | 
								display: block;
 | 
				
			||||||
			width: 60px;
 | 
								width: 60px;
 | 
				
			||||||
			font-size: 60px; // unicodeな絵文字についてはwidthが効かないため
 | 
								font-size: 60px; // unicodeな絵文字についてはwidthが効かないため
 | 
				
			||||||
 | 
								object-fit: contain;
 | 
				
			||||||
			margin: 0 auto;
 | 
								margin: 0 auto;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@
 | 
				
			||||||
import { computed } from 'vue';
 | 
					import { computed } from 'vue';
 | 
				
			||||||
import { CustomEmoji } from 'misskey-js/built/entities';
 | 
					import { CustomEmoji } from 'misskey-js/built/entities';
 | 
				
			||||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
 | 
					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 { defaultStore } from '@/store';
 | 
				
			||||||
import { instance } from '@/instance';
 | 
					import { instance } from '@/instance';
 | 
				
			||||||
import { getEmojiName } from '@/scripts/emojilist';
 | 
					import { getEmojiName } from '@/scripts/emojilist';
 | 
				
			||||||
| 
						 | 
					@ -22,14 +22,16 @@ const props = defineProps<{
 | 
				
			||||||
	isReaction?: boolean;
 | 
						isReaction?: boolean;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isCustom = computed(() => props.emoji.startsWith(':'));
 | 
					const isCustom = computed(() => props.emoji.startsWith(':'));
 | 
				
			||||||
const char = computed(() => isCustom.value ? undefined : props.emoji);
 | 
					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 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 customEmoji = computed(() => isCustom.value ? ce.value.find(x => x.name === props.emoji.substr(1, props.emoji.length - 2)) : undefined);
 | 
				
			||||||
const url = computed(() => {
 | 
					const url = computed(() => {
 | 
				
			||||||
	if (char.value) {
 | 
						if (char.value) {
 | 
				
			||||||
		return char2filePath(char.value);
 | 
							return char2path(char.value);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		const rawUrl = (customEmoji.value as CustomEmoji).url;
 | 
							const rawUrl = (customEmoji.value as CustomEmoji).url;
 | 
				
			||||||
		return defaultStore.state.disableShowingAnimatedImages
 | 
							return defaultStore.state.disableShowingAnimatedImages
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,10 +48,16 @@
 | 
				
			||||||
		<FormSwitch v-model="disableShowingAnimatedImages" class="_formBlock">{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch>
 | 
							<FormSwitch v-model="disableShowingAnimatedImages" class="_formBlock">{{ i18n.ts.disableShowingAnimatedImages }}</FormSwitch>
 | 
				
			||||||
		<FormSwitch v-model="squareAvatars" class="_formBlock">{{ i18n.ts.squareAvatars }}</FormSwitch>
 | 
							<FormSwitch v-model="squareAvatars" class="_formBlock">{{ i18n.ts.squareAvatars }}</FormSwitch>
 | 
				
			||||||
		<FormSwitch v-model="useSystemFont" class="_formBlock">{{ i18n.ts.useSystemFont }}</FormSwitch>
 | 
							<FormSwitch v-model="useSystemFont" class="_formBlock">{{ i18n.ts.useSystemFont }}</FormSwitch>
 | 
				
			||||||
		<FormSwitch v-model="useOsNativeEmojis" class="_formBlock">
 | 
							<div class="_formBlock">
 | 
				
			||||||
			{{ i18n.ts.useOsNativeEmojis }}
 | 
								<FormRadios v-model="emojiStyle">
 | 
				
			||||||
			<div><Mfm :key="useOsNativeEmojis" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div>
 | 
									<template #label>{{ i18n.ts.emojiStyle }}</template>
 | 
				
			||||||
		</FormSwitch>
 | 
									<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>
 | 
							<FormSwitch v-model="disableDrawer" class="_formBlock">{{ i18n.ts.disableDrawer }}</FormSwitch>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<FormRadios v-model="fontSize" class="_formBlock">
 | 
							<FormRadios v-model="fontSize" class="_formBlock">
 | 
				
			||||||
| 
						 | 
					@ -129,7 +135,7 @@ const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEff
 | 
				
			||||||
const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
 | 
					const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
 | 
				
			||||||
const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'));
 | 
					const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'));
 | 
				
			||||||
const disableAnimatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v));
 | 
					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 disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
 | 
				
			||||||
const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages'));
 | 
					const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages'));
 | 
				
			||||||
const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
 | 
					const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,7 +63,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
 | 
				
			||||||
	'imageNewTab',
 | 
						'imageNewTab',
 | 
				
			||||||
	'disableShowingAnimatedImages',
 | 
						'disableShowingAnimatedImages',
 | 
				
			||||||
	'disablePagesScript',
 | 
						'disablePagesScript',
 | 
				
			||||||
	'useOsNativeEmojis',
 | 
						'emojiStyle',
 | 
				
			||||||
	'disableDrawer',
 | 
						'disableDrawer',
 | 
				
			||||||
	'useBlurEffectForModal',
 | 
						'useBlurEffectForModal',
 | 
				
			||||||
	'useBlurEffect',
 | 
						'useBlurEffect',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								packages/client/src/scripts/emoji-base.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								packages/client/src/scripts/emoji-base.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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`;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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`;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -174,9 +174,9 @@ export const defaultStore = markRaw(new Storage('base', {
 | 
				
			||||||
		where: 'device',
 | 
							where: 'device',
 | 
				
			||||||
		default: false,
 | 
							default: false,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	useOsNativeEmojis: {
 | 
						emojiStyle: {
 | 
				
			||||||
		where: 'device',
 | 
							where: 'device',
 | 
				
			||||||
		default: false,
 | 
							default: 'twemoji', // twemoji / fluentEmoji / native
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	disableDrawer: {
 | 
						disableDrawer: {
 | 
				
			||||||
		where: 'device',
 | 
							where: 'device',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue