summaryrefslogtreecommitdiff
path: root/packages/client/src/scripts
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2022-06-21 07:49:52 +0000
committertamaina <tamaina@hotmail.co.jp>2022-06-21 07:49:52 +0000
commitf33654fb9ae562a43745b612a3007c248d988f2b (patch)
tree25b8599f7d28bf02cd0d3970b735d9db5e84742b /packages/client/src/scripts
parentMerge branch 'develop' into pizzax-indexeddb (diff)
parentrefactor(client): use composition api (diff)
downloadsharkey-f33654fb9ae562a43745b612a3007c248d988f2b.tar.gz
sharkey-f33654fb9ae562a43745b612a3007c248d988f2b.tar.bz2
sharkey-f33654fb9ae562a43745b612a3007c248d988f2b.zip
Merge branch 'develop' into pizzax-indexeddb
Diffstat (limited to 'packages/client/src/scripts')
-rw-r--r--packages/client/src/scripts/format-time-string.ts6
-rw-r--r--packages/client/src/scripts/get-note-menu.ts44
-rw-r--r--packages/client/src/scripts/get-user-menu.ts60
-rw-r--r--packages/client/src/scripts/hpml/evaluator.ts2
-rw-r--r--packages/client/src/scripts/navigate.ts34
-rw-r--r--packages/client/src/scripts/page-metadata.ts41
-rw-r--r--packages/client/src/scripts/physics.ts6
-rw-r--r--packages/client/src/scripts/popout.ts5
-rw-r--r--packages/client/src/scripts/scroll.ts15
-rw-r--r--packages/client/src/scripts/search.ts16
-rw-r--r--packages/client/src/scripts/theme-editor.ts2
-rw-r--r--packages/client/src/scripts/theme.ts49
-rw-r--r--packages/client/src/scripts/twemoji-base.ts11
-rw-r--r--packages/client/src/scripts/use-leave-guard.ts1
14 files changed, 182 insertions, 110 deletions
diff --git a/packages/client/src/scripts/format-time-string.ts b/packages/client/src/scripts/format-time-string.ts
index bfb2c397ae..fb4718c007 100644
--- a/packages/client/src/scripts/format-time-string.ts
+++ b/packages/client/src/scripts/format-time-string.ts
@@ -13,7 +13,7 @@ const defaultLocaleStringFormats: {[index: string]: string} = {
function formatLocaleString(date: Date, format: string): string {
return format.replace(/\{\{(\w+)(:(\w+))?\}\}/g, (match: string, kind: string, unused?, option?: string) => {
if (['weekday', 'era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName'].includes(kind)) {
- return date.toLocaleString(window.navigator.language, {[kind]: option ? option : defaultLocaleStringFormats[kind]});
+ return date.toLocaleString(window.navigator.language, { [kind]: option ? option : defaultLocaleStringFormats[kind] });
} else {
return match;
}
@@ -24,8 +24,8 @@ export function formatDateTimeString(date: Date, format: string): string {
return format
.replace(/yyyy/g, date.getFullYear().toString())
.replace(/yy/g, date.getFullYear().toString().slice(-2))
- .replace(/MMMM/g, date.toLocaleString(window.navigator.language, { month: 'long'}))
- .replace(/MMM/g, date.toLocaleString(window.navigator.language, { month: 'short'}))
+ .replace(/MMMM/g, date.toLocaleString(window.navigator.language, { month: 'long' }))
+ .replace(/MMM/g, date.toLocaleString(window.navigator.language, { month: 'short' }))
.replace(/MM/g, (`0${date.getMonth() + 1}`).slice(-2))
.replace(/M/g, (date.getMonth() + 1).toString())
.replace(/dd/g, (`0${date.getDate()}`).slice(-2))
diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts
index aeb09ef97a..283c90362c 100644
--- a/packages/client/src/scripts/get-note-menu.ts
+++ b/packages/client/src/scripts/get-note-menu.ts
@@ -1,4 +1,4 @@
-import { defineAsyncComponent, Ref } from 'vue';
+import { defineAsyncComponent, Ref, inject } from 'vue';
import * as misskey from 'misskey-js';
import { $i } from '@/account';
import { i18n } from '@/i18n';
@@ -14,6 +14,8 @@ export function getNoteMenu(props: {
menuButton: Ref<HTMLElement>;
translation: Ref<any>;
translating: Ref<boolean>;
+ isDeleted: Ref<boolean>;
+ currentClipPage?: Ref<misskey.entities.Clip>;
}) {
const isRenote = (
props.note.renote != null &&
@@ -22,7 +24,7 @@ export function getNoteMenu(props: {
props.note.poll == null
);
- let appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note;
+ const appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note;
function del(): void {
os.confirm({
@@ -125,12 +127,37 @@ export function getNoteMenu(props: {
}, null, ...clips.map(clip => ({
text: clip.name,
action: () => {
- os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id });
+ os.promiseDialog(
+ os.api('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
+ null,
+ async (err) => {
+ if (err.id === '734806c4-542c-463a-9311-15c512803965') {
+ const confirm = await os.confirm({
+ type: 'warning',
+ text: i18n.t('confirmToUnclipAlreadyClippedNote', { name: clip.name }),
+ });
+ if (!confirm.canceled) {
+ os.apiWithDialog('clips/remove-note', { clipId: clip.id, noteId: appearNote.id });
+ if (props.currentClipPage?.value.id === clip.id) props.isDeleted.value = true;
+ }
+ } else {
+ os.alert({
+ type: 'error',
+ text: err.message + '\n' + err.id,
+ });
+ }
+ }
+ );
}
}))], props.menuButton.value, {
}).then(focus);
}
+ async function unclip(): Promise<void> {
+ os.apiWithDialog('clips/remove-note', { clipId: props.currentClipPage.value.id, noteId: appearNote.id });
+ props.isDeleted.value = true;
+ }
+
async function promote(): Promise<void> {
const { canceled, result: days } = await os.inputNumber({
title: i18n.ts.numberOfDays,
@@ -169,7 +196,16 @@ export function getNoteMenu(props: {
noteId: appearNote.id
});
- menu = [{
+ menu = [
+ ...(
+ props.currentClipPage?.value.userId === $i.id ? [{
+ icon: 'fas fa-circle-minus',
+ text: i18n.ts.unclip,
+ danger: true,
+ action: unclip,
+ }, null] : []
+ ),
+ {
icon: 'fas fa-copy',
text: i18n.ts.copyContent,
action: copyContent
diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts
index 1d2b761117..25bcd90e9f 100644
--- a/packages/client/src/scripts/get-user-menu.ts
+++ b/packages/client/src/scripts/get-user-menu.ts
@@ -1,12 +1,12 @@
+import * as Acct from 'misskey-js/built/acct';
+import { defineAsyncComponent } from 'vue';
import { i18n } from '@/i18n';
import copyToClipboard from '@/scripts/copy-to-clipboard';
import { host } from '@/config';
-import * as Acct from 'misskey-js/built/acct';
import * as os from '@/os';
import { userActions } from '@/store';
-import { router } from '@/router';
import { $i, iAmModerator } from '@/account';
-import { defineAsyncComponent } from 'vue';
+import { mainRouter } from '@/router';
export function getUserMenu(user) {
const meId = $i ? $i.id : null;
@@ -17,20 +17,20 @@ export function getUserMenu(user) {
if (lists.length === 0) {
os.alert({
type: 'error',
- text: i18n.ts.youHaveNoLists
+ text: i18n.ts.youHaveNoLists,
});
return;
}
const { canceled, result: listId } = await os.select({
title: t,
items: lists.map(list => ({
- value: list.id, text: list.name
- }))
+ value: list.id, text: list.name,
+ })),
});
if (canceled) return;
os.apiWithDialog('users/lists/push', {
listId: listId,
- userId: user.id
+ userId: user.id,
});
}
@@ -39,20 +39,20 @@ export function getUserMenu(user) {
if (groups.length === 0) {
os.alert({
type: 'error',
- text: i18n.ts.youHaveNoGroups
+ text: i18n.ts.youHaveNoGroups,
});
return;
}
const { canceled, result: groupId } = await os.select({
title: i18n.ts.group,
items: groups.map(group => ({
- value: group.id, text: group.name
- }))
+ value: group.id, text: group.name,
+ })),
});
if (canceled) return;
os.apiWithDialog('users/groups/invite', {
groupId: groupId,
- userId: user.id
+ userId: user.id,
});
}
@@ -101,7 +101,7 @@ export function getUserMenu(user) {
if (!await getConfirmed(user.isBlocking ? i18n.ts.unblockConfirm : i18n.ts.blockConfirm)) return;
os.apiWithDialog(user.isBlocking ? 'blocking/delete' : 'blocking/create', {
- userId: user.id
+ userId: user.id,
}).then(() => {
user.isBlocking = !user.isBlocking;
});
@@ -111,7 +111,7 @@ export function getUserMenu(user) {
if (!await getConfirmed(i18n.t(user.isSilenced ? 'unsilenceConfirm' : 'silenceConfirm'))) return;
os.apiWithDialog(user.isSilenced ? 'admin/unsilence-user' : 'admin/silence-user', {
- userId: user.id
+ userId: user.id,
}).then(() => {
user.isSilenced = !user.isSilenced;
});
@@ -121,7 +121,7 @@ export function getUserMenu(user) {
if (!await getConfirmed(i18n.t(user.isSuspended ? 'unsuspendConfirm' : 'suspendConfirm'))) return;
os.apiWithDialog(user.isSuspended ? 'admin/unsuspend-user' : 'admin/suspend-user', {
- userId: user.id
+ userId: user.id,
}).then(() => {
user.isSuspended = !user.isSuspended;
});
@@ -145,10 +145,10 @@ export function getUserMenu(user) {
async function invalidateFollow() {
os.apiWithDialog('following/invalidate', {
- userId: user.id
+ userId: user.id,
}).then(() => {
user.isFollowed = !user.isFollowed;
- })
+ });
}
let menu = [{
@@ -156,19 +156,19 @@ export function getUserMenu(user) {
text: i18n.ts.copyUsername,
action: () => {
copyToClipboard(`@${user.username}@${user.host || host}`);
- }
+ },
}, {
icon: 'fas fa-info-circle',
text: i18n.ts.info,
action: () => {
os.pageWindow(`/user-info/${user.id}`);
- }
+ },
}, {
icon: 'fas fa-envelope',
text: i18n.ts.sendMessage,
action: () => {
os.post({ specified: user });
- }
+ },
}, meId !== user.id ? {
type: 'link',
icon: 'fas fa-comments',
@@ -177,47 +177,47 @@ export function getUserMenu(user) {
} : undefined, null, {
icon: 'fas fa-list-ul',
text: i18n.ts.addToList,
- action: pushList
+ action: pushList,
}, meId !== user.id ? {
icon: 'fas fa-users',
text: i18n.ts.inviteToGroup,
- action: inviteGroup
+ action: inviteGroup,
} : undefined] as any;
if ($i && meId !== user.id) {
menu = menu.concat([null, {
icon: user.isMuted ? 'fas fa-eye' : 'fas fa-eye-slash',
text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
- action: toggleMute
+ action: toggleMute,
}, {
icon: 'fas fa-ban',
text: user.isBlocking ? i18n.ts.unblock : i18n.ts.block,
- action: toggleBlock
+ action: toggleBlock,
}]);
if (user.isFollowed) {
menu = menu.concat([{
icon: 'fas fa-unlink',
text: i18n.ts.breakFollow,
- action: invalidateFollow
+ action: invalidateFollow,
}]);
}
menu = menu.concat([null, {
icon: 'fas fa-exclamation-circle',
text: i18n.ts.reportAbuse,
- action: reportAbuse
+ action: reportAbuse,
}]);
if (iAmModerator) {
menu = menu.concat([null, {
icon: 'fas fa-microphone-slash',
text: user.isSilenced ? i18n.ts.unsilence : i18n.ts.silence,
- action: toggleSilence
+ action: toggleSilence,
}, {
icon: 'fas fa-snowflake',
text: user.isSuspended ? i18n.ts.unsuspend : i18n.ts.suspend,
- action: toggleSuspend
+ action: toggleSuspend,
}]);
}
}
@@ -227,8 +227,8 @@ export function getUserMenu(user) {
icon: 'fas fa-pencil-alt',
text: i18n.ts.editProfile,
action: () => {
- router.push('/settings/profile');
- }
+ mainRouter.push('/settings/profile');
+ },
}]);
}
@@ -238,7 +238,7 @@ export function getUserMenu(user) {
text: action.title,
action: () => {
action.handler(user);
- }
+ },
}))]);
}
diff --git a/packages/client/src/scripts/hpml/evaluator.ts b/packages/client/src/scripts/hpml/evaluator.ts
index 0469a31cbb..8106687b61 100644
--- a/packages/client/src/scripts/hpml/evaluator.ts
+++ b/packages/client/src/scripts/hpml/evaluator.ts
@@ -36,7 +36,7 @@ export class Hpml {
if (this.opts.enableAiScript) {
this.aiscript = markRaw(new AiScript({ ...createAiScriptEnv({
storageKey: 'pages:' + this.page.id
- }), ...initAiLib(this)}, {
+ }), ...initAiLib(this) }, {
in: (q) => {
return new Promise(ok => {
os.inputText({
diff --git a/packages/client/src/scripts/navigate.ts b/packages/client/src/scripts/navigate.ts
deleted file mode 100644
index 08b891ec5b..0000000000
--- a/packages/client/src/scripts/navigate.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { inject } from 'vue';
-import { router } from '@/router';
-import { defaultStore } from '@/store';
-
-export type Navigate = (path: string, record?: boolean) => void;
-
-export class MisskeyNavigator {
- public readonly navHook: Navigate | null = null;
- public readonly sideViewHook: Navigate | null = null;
-
- // It should be constructed during vue creating in order for inject function to work
- constructor() {
- this.navHook = inject<Navigate | null>('navHook', null);
- this.sideViewHook = inject<Navigate | null>('sideViewHook', null);
- }
-
- // Use this method instead of router.push()
- public push(path: string, record = true) {
- if (this.navHook) {
- this.navHook(path, record);
- } else {
- if (defaultStore.state.defaultSideView && this.sideViewHook && path !== '/') {
- return this.sideViewHook(path, record);
- }
-
- if (router.currentRoute.value.path === path) {
- window.scroll({ top: 0, behavior: 'smooth' });
- } else {
- if (record) router.push(path);
- else router.replace(path);
- }
- }
- }
-}
diff --git a/packages/client/src/scripts/page-metadata.ts b/packages/client/src/scripts/page-metadata.ts
new file mode 100644
index 0000000000..0db8369f9d
--- /dev/null
+++ b/packages/client/src/scripts/page-metadata.ts
@@ -0,0 +1,41 @@
+import * as misskey from 'misskey-js';
+import { ComputedRef, inject, isRef, onActivated, onMounted, provide, ref, Ref } from 'vue';
+
+export const setPageMetadata = Symbol('setPageMetadata');
+export const pageMetadataProvider = Symbol('pageMetadataProvider');
+
+export type PageMetadata = {
+ title: string;
+ subtitle?: string;
+ icon?: string | null;
+ avatar?: misskey.entities.User | null;
+ userName?: misskey.entities.User | null;
+ bg?: string;
+};
+
+export function definePageMetadata(metadata: PageMetadata | null | Ref<PageMetadata | null> | ComputedRef<PageMetadata | null>): void {
+ const _metadata = isRef(metadata) ? metadata : ref(metadata);
+
+ provide(pageMetadataProvider, _metadata);
+
+ const set = inject(setPageMetadata) as any;
+ if (set) {
+ set(_metadata);
+
+ onMounted(() => {
+ set(_metadata);
+ });
+
+ onActivated(() => {
+ set(_metadata);
+ });
+ }
+}
+
+export function provideMetadataReceiver(callback: (info: ComputedRef<PageMetadata>) => void): void {
+ provide(setPageMetadata, callback);
+}
+
+export function injectPageMetadata(): PageMetadata | undefined {
+ return inject(pageMetadataProvider);
+}
diff --git a/packages/client/src/scripts/physics.ts b/packages/client/src/scripts/physics.ts
index 36e476b6f9..9e657906c2 100644
--- a/packages/client/src/scripts/physics.ts
+++ b/packages/client/src/scripts/physics.ts
@@ -41,9 +41,9 @@ export function physics(container: HTMLElement) {
const groundThickness = 1024;
const ground = Matter.Bodies.rectangle(containerCenterX, containerHeight + (groundThickness / 2), containerWidth, groundThickness, {
- isStatic: true,
- restitution: 0.1,
- friction: 2
+ isStatic: true,
+ restitution: 0.1,
+ friction: 2
});
//const wallRight = Matter.Bodies.rectangle(window.innerWidth+50, window.innerHeight/2, 100, window.innerHeight, wallopts);
diff --git a/packages/client/src/scripts/popout.ts b/packages/client/src/scripts/popout.ts
index b8286a2a76..580031d0a3 100644
--- a/packages/client/src/scripts/popout.ts
+++ b/packages/client/src/scripts/popout.ts
@@ -1,8 +1,9 @@
import * as config from '@/config';
+import { appendQuery } from './url';
export function popout(path: string, w?: HTMLElement) {
- let url = path.startsWith('http://') || path.startsWith('https://') ? path : config.url + "/" + path;
- url += '?zen';
+ let url = path.startsWith('http://') || path.startsWith('https://') ? path : config.url + path;
+ url = appendQuery(url, 'zen');
if (w) {
const position = w.getBoundingClientRect();
const width = parseInt(getComputedStyle(w, '').width, 10);
diff --git a/packages/client/src/scripts/scroll.ts b/packages/client/src/scripts/scroll.ts
index 621fe88105..0643bad2fb 100644
--- a/packages/client/src/scripts/scroll.ts
+++ b/packages/client/src/scripts/scroll.ts
@@ -1,9 +1,13 @@
type ScrollBehavior = 'auto' | 'smooth' | 'instant';
-export function getScrollContainer(el: Element | null): Element | null {
- if (el == null || el.tagName === 'BODY') return null;
+export function getScrollContainer(el: HTMLElement | null): HTMLElement | null {
+ if (el == null || el.tagName === 'HTML') return null;
const overflow = window.getComputedStyle(el).getPropertyValue('overflow');
- if (overflow.endsWith('auto')) { // xとyを個別に指定している場合、hidden auto みたいな値になる
+ if (
+ // xとyを個別に指定している場合、`hidden scroll`みたいな値になる
+ overflow.endsWith('scroll') ||
+ overflow.endsWith('auto')
+ ) {
return el;
} else {
return getScrollContainer(el.parentElement);
@@ -22,6 +26,11 @@ export function isTopVisible(el: Element | null): boolean {
return scrollTop <= topPosition;
}
+export function isBottomVisible(el: HTMLElement, tolerance = 1, container = getScrollContainer(el)) {
+ if (container) return el.scrollHeight <= container.clientHeight + Math.abs(container.scrollTop) + tolerance;
+ return el.scrollHeight <= window.innerHeight + window.scrollY + tolerance;
+}
+
export function onScrollTop(el: Element, cb) {
const container = getScrollContainer(el) || window;
const onScroll = ev => {
diff --git a/packages/client/src/scripts/search.ts b/packages/client/src/scripts/search.ts
index 0aedee9c98..64914d3d65 100644
--- a/packages/client/src/scripts/search.ts
+++ b/packages/client/src/scripts/search.ts
@@ -1,6 +1,6 @@
import * as os from '@/os';
import { i18n } from '@/i18n';
-import { router } from '@/router';
+import { mainRouter } from '@/router';
export async function search() {
const { canceled, result: query } = await os.inputText({
@@ -11,12 +11,12 @@ export async function search() {
const q = query.trim();
if (q.startsWith('@') && !q.includes(' ')) {
- router.push(`/${q}`);
+ mainRouter.push(`/${q}`);
return;
}
if (q.startsWith('#')) {
- router.push(`/tags/${encodeURIComponent(q.substr(1))}`);
+ mainRouter.push(`/tags/${encodeURIComponent(q.substr(1))}`);
return;
}
@@ -36,14 +36,14 @@ export async function search() {
//v.$root.$emit('warp', date);
os.alert({
icon: 'fas fa-history',
- iconOnly: true, autoClose: true
+ iconOnly: true, autoClose: true,
});
return;
}
if (q.startsWith('https://')) {
const promise = os.api('ap/show', {
- uri: q
+ uri: q,
});
os.promiseDialog(promise, null, null, i18n.ts.fetchingAsApObject);
@@ -51,13 +51,13 @@ export async function search() {
const res = await promise;
if (res.type === 'User') {
- router.push(`/@${res.object.username}@${res.object.host}`);
+ mainRouter.push(`/@${res.object.username}@${res.object.host}`);
} else if (res.type === 'Note') {
- router.push(`/notes/${res.object.id}`);
+ mainRouter.push(`/notes/${res.object.id}`);
}
return;
}
- router.push(`/search?q=${encodeURIComponent(q)}`);
+ mainRouter.push(`/search?q=${encodeURIComponent(q)}`);
}
diff --git a/packages/client/src/scripts/theme-editor.ts b/packages/client/src/scripts/theme-editor.ts
index 3d69d2836a..2c917e280d 100644
--- a/packages/client/src/scripts/theme-editor.ts
+++ b/packages/client/src/scripts/theme-editor.ts
@@ -1,4 +1,4 @@
-import { v4 as uuid} from 'uuid';
+import { v4 as uuid } from 'uuid';
import { themeProps, Theme } from './theme';
diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts
index b61b1684a8..dec9fb355c 100644
--- a/packages/client/src/scripts/theme.ts
+++ b/packages/client/src/scripts/theme.ts
@@ -1,3 +1,4 @@
+import { ref } from 'vue';
import { globalEvents } from '@/events';
import tinycolor from 'tinycolor2';
@@ -10,30 +11,38 @@ export type Theme = {
props: Record<string, string>;
};
-export const lightTheme: Theme = await import('@/themes/_light.json5');
-export const darkTheme: Theme = await import('@/themes/_dark.json5');
+import lightTheme from '@/themes/_light.json5';
+import darkTheme from '@/themes/_dark.json5';
export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
-export const builtinThemes = [
- await import('@/themes/l-light.json5'),
- await import('@/themes/l-coffee.json5'),
- await import('@/themes/l-apricot.json5'),
- await import('@/themes/l-rainy.json5'),
- await import('@/themes/l-vivid.json5'),
- await import('@/themes/l-cherry.json5'),
- await import('@/themes/l-sushi.json5'),
+export const getBuiltinThemes = () => Promise.all(
+ [
+ 'l-light',
+ 'l-coffee',
+ 'l-apricot',
+ 'l-rainy',
+ 'l-vivid',
+ 'l-cherry',
+ 'l-sushi',
- await import('@/themes/d-dark.json5'),
- await import('@/themes/d-persimmon.json5'),
- await import('@/themes/d-astro.json5'),
- await import('@/themes/d-future.json5'),
- await import('@/themes/d-botanical.json5'),
- await import('@/themes/d-cherry.json5'),
- await import('@/themes/d-ice.json5'),
- await import('@/themes/d-pumpkin.json5'),
- await import('@/themes/d-black.json5'),
-] as Theme[];
+ 'd-dark',
+ 'd-persimmon',
+ 'd-astro',
+ 'd-future',
+ 'd-botanical',
+ 'd-cherry',
+ 'd-ice',
+ 'd-pumpkin',
+ 'd-black',
+ ].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default))
+);
+
+export const getBuiltinThemesRef = () => {
+ const builtinThemes = ref<Theme[]>([]);
+ getBuiltinThemes().then(themes => builtinThemes.value = themes);
+ return builtinThemes;
+};
let timeout = null;
diff --git a/packages/client/src/scripts/twemoji-base.ts b/packages/client/src/scripts/twemoji-base.ts
index cd50311b15..638aae3284 100644
--- a/packages/client/src/scripts/twemoji-base.ts
+++ b/packages/client/src/scripts/twemoji-base.ts
@@ -1 +1,12 @@
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/scripts/use-leave-guard.ts b/packages/client/src/scripts/use-leave-guard.ts
index 33eea6b522..379a7e577c 100644
--- a/packages/client/src/scripts/use-leave-guard.ts
+++ b/packages/client/src/scripts/use-leave-guard.ts
@@ -1,5 +1,4 @@
import { inject, onUnmounted, Ref } from 'vue';
-import { onBeforeRouteLeave } from 'vue-router';
import { i18n } from '@/i18n';
import * as os from '@/os';