summaryrefslogtreecommitdiff
path: root/packages/frontend/src/scripts
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-09-24 18:21:31 +0900
committerGitHub <noreply@github.com>2023-09-24 18:21:31 +0900
commitf32915b515f4cbc3b1a877cfb8e8e35bf6a31efa (patch)
tree0f6f098cbb282e4b6619152b14b9e6f57e6b448f /packages/frontend/src/scripts
parentMerge pull request #11384 from misskey-dev/develop (diff)
parent2023.9.0 (diff)
downloadmisskey-f32915b515f4cbc3b1a877cfb8e8e35bf6a31efa.tar.gz
misskey-f32915b515f4cbc3b1a877cfb8e8e35bf6a31efa.tar.bz2
misskey-f32915b515f4cbc3b1a877cfb8e8e35bf6a31efa.zip
Merge pull request #11874 from misskey-dev/develop
Release: 2023.9.0
Diffstat (limited to 'packages/frontend/src/scripts')
-rw-r--r--packages/frontend/src/scripts/2fa.ts33
-rw-r--r--packages/frontend/src/scripts/achievements.ts15
-rw-r--r--packages/frontend/src/scripts/aiscript/api.ts28
-rw-r--r--packages/frontend/src/scripts/aiscript/ui.ts63
-rw-r--r--packages/frontend/src/scripts/api.ts19
-rw-r--r--packages/frontend/src/scripts/array.ts8
-rw-r--r--packages/frontend/src/scripts/autocomplete.ts7
-rw-r--r--packages/frontend/src/scripts/cache.ts53
-rw-r--r--packages/frontend/src/scripts/chart-legend.ts5
-rw-r--r--packages/frontend/src/scripts/chart-vline.ts5
-rw-r--r--packages/frontend/src/scripts/check-word-mute.ts5
-rw-r--r--packages/frontend/src/scripts/clicker-game.ts7
-rw-r--r--packages/frontend/src/scripts/clone.ts5
-rw-r--r--packages/frontend/src/scripts/collapsed.ts11
-rw-r--r--packages/frontend/src/scripts/collect-page-vars.ts5
-rw-r--r--packages/frontend/src/scripts/color.ts5
-rw-r--r--packages/frontend/src/scripts/confetti.ts7
-rw-r--r--packages/frontend/src/scripts/contains.ts5
-rw-r--r--packages/frontend/src/scripts/copy-to-clipboard.ts5
-rw-r--r--packages/frontend/src/scripts/device-kind.ts7
-rw-r--r--packages/frontend/src/scripts/emoji-base.ts5
-rw-r--r--packages/frontend/src/scripts/emojilist.ts5
-rw-r--r--packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts13
-rw-r--r--packages/frontend/src/scripts/extract-mentions.ts5
-rw-r--r--packages/frontend/src/scripts/extract-url-from-mfm.ts7
-rw-r--r--packages/frontend/src/scripts/focus.ts5
-rw-r--r--packages/frontend/src/scripts/form.ts5
-rw-r--r--packages/frontend/src/scripts/format-time-string.ts5
-rw-r--r--packages/frontend/src/scripts/gen-search-query.ts11
-rw-r--r--packages/frontend/src/scripts/get-account-from-id.ts7
-rw-r--r--packages/frontend/src/scripts/get-drive-file-menu.ts17
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts146
-rw-r--r--packages/frontend/src/scripts/get-note-summary.ts11
-rw-r--r--packages/frontend/src/scripts/get-user-menu.ts115
-rw-r--r--packages/frontend/src/scripts/get-user-name.ts5
-rw-r--r--packages/frontend/src/scripts/hotkey.ts7
-rw-r--r--packages/frontend/src/scripts/i18n.ts5
-rw-r--r--packages/frontend/src/scripts/idb-proxy.ts5
-rw-r--r--packages/frontend/src/scripts/idle-render.ts5
-rw-r--r--packages/frontend/src/scripts/init-chart.ts7
-rw-r--r--packages/frontend/src/scripts/initialize-sw.ts9
-rw-r--r--packages/frontend/src/scripts/intl-const.ts19
-rw-r--r--packages/frontend/src/scripts/is-device-darkmode.ts5
-rw-r--r--packages/frontend/src/scripts/isFfVisibleForMe.ts11
-rw-r--r--packages/frontend/src/scripts/keycode.ts5
-rw-r--r--packages/frontend/src/scripts/langmap.ts5
-rw-r--r--packages/frontend/src/scripts/login-id.ts5
-rw-r--r--packages/frontend/src/scripts/lookup-user.ts15
-rw-r--r--packages/frontend/src/scripts/lookup.ts13
-rw-r--r--packages/frontend/src/scripts/media-proxy.ts13
-rw-r--r--packages/frontend/src/scripts/mfm-tags.ts5
-rw-r--r--packages/frontend/src/scripts/page-metadata.ts11
-rw-r--r--packages/frontend/src/scripts/physics.ts5
-rw-r--r--packages/frontend/src/scripts/please-login.ts11
-rw-r--r--packages/frontend/src/scripts/popout.ts9
-rw-r--r--packages/frontend/src/scripts/popup-position.ts4
-rw-r--r--packages/frontend/src/scripts/reaction-picker.ts7
-rw-r--r--packages/frontend/src/scripts/safe-uri-decode.ts5
-rw-r--r--packages/frontend/src/scripts/scroll.ts9
-rw-r--r--packages/frontend/src/scripts/select-file.ts29
-rw-r--r--packages/frontend/src/scripts/show-moved-dialog.ts11
-rw-r--r--packages/frontend/src/scripts/show-suspended-dialog.ts9
-rw-r--r--packages/frontend/src/scripts/shuffle.ts5
-rw-r--r--packages/frontend/src/scripts/sound.ts25
-rw-r--r--packages/frontend/src/scripts/sticky-sidebar.ts5
-rw-r--r--packages/frontend/src/scripts/test-utils.ts5
-rw-r--r--packages/frontend/src/scripts/theme-editor.ts9
-rw-r--r--packages/frontend/src/scripts/theme.ts7
-rw-r--r--packages/frontend/src/scripts/time.ts5
-rw-r--r--packages/frontend/src/scripts/timezones.ts5
-rw-r--r--packages/frontend/src/scripts/touch.ts7
-rw-r--r--packages/frontend/src/scripts/unison-reload.ts5
-rw-r--r--packages/frontend/src/scripts/upload.ts17
-rw-r--r--packages/frontend/src/scripts/upload/compress-config.ts5
-rw-r--r--packages/frontend/src/scripts/upload/isWebpSupported.ts5
-rw-r--r--packages/frontend/src/scripts/url.ts5
-rw-r--r--packages/frontend/src/scripts/use-chart-tooltip.ts7
-rw-r--r--packages/frontend/src/scripts/use-document-visibility.ts5
-rw-r--r--packages/frontend/src/scripts/use-interval.ts5
-rw-r--r--packages/frontend/src/scripts/use-leave-guard.ts5
-rw-r--r--packages/frontend/src/scripts/use-note-capture.ts13
-rw-r--r--packages/frontend/src/scripts/use-tooltip.ts5
-rw-r--r--packages/frontend/src/scripts/worker-multi-dispatch.ts127
83 files changed, 809 insertions, 372 deletions
diff --git a/packages/frontend/src/scripts/2fa.ts b/packages/frontend/src/scripts/2fa.ts
deleted file mode 100644
index 62a38ff02a..0000000000
--- a/packages/frontend/src/scripts/2fa.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-export function byteify(string: string, encoding: 'ascii' | 'base64' | 'hex') {
- switch (encoding) {
- case 'ascii':
- return Uint8Array.from(string, c => c.charCodeAt(0));
- case 'base64':
- return Uint8Array.from(
- atob(
- string
- .replace(/-/g, '+')
- .replace(/_/g, '/'),
- ),
- c => c.charCodeAt(0),
- );
- case 'hex':
- return new Uint8Array(
- string
- .match(/.{1,2}/g)
- .map(byte => parseInt(byte, 16)),
- );
- }
-}
-
-export function hexify(buffer: ArrayBuffer) {
- return Array.from(new Uint8Array(buffer))
- .reduce(
- (str, byte) => str + byte.toString(16).padStart(2, '0'),
- '',
- );
-}
-
-export function stringify(buffer: ArrayBuffer) {
- return String.fromCharCode(... new Uint8Array(buffer));
-}
diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts
index fbca005769..af7c6b060c 100644
--- a/packages/frontend/src/scripts/achievements.ts
+++ b/packages/frontend/src/scripts/achievements.ts
@@ -1,5 +1,10 @@
-import * as os from '@/os';
-import { $i } from '@/account';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
export const ACHIEVEMENT_TYPES = [
'notes1',
@@ -76,6 +81,7 @@ export const ACHIEVEMENT_TYPES = [
'setNameToSyuilo',
'cookieClicked',
'brainDiver',
+ 'smashTestNotificationButton',
] as const;
export const ACHIEVEMENT_BADGES = {
@@ -449,6 +455,11 @@ export const ACHIEVEMENT_BADGES = {
bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))',
frame: 'bronze',
},
+ 'smashTestNotificationButton': {
+ img: '/fluent-emoji/1f514.png',
+ bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))',
+ frame: 'bronze',
+ },
/* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107>
} as const satisfies Record<typeof ACHIEVEMENT_TYPES[number], {
img: string;
diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts
index c8b90b4fd7..9f60e52cea 100644
--- a/packages/frontend/src/scripts/aiscript/api.ts
+++ b/packages/frontend/src/scripts/aiscript/api.ts
@@ -1,16 +1,22 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { utils, values } from '@syuilo/aiscript';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { miLocalStorage } from '@/local-storage';
-import { customEmojis } from '@/custom-emojis';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { customEmojis } from '@/custom-emojis.js';
+import { lang } from '@/config.js';
export function createAiScriptEnv(opts) {
- let apiRequests = 0;
return {
USER_ID: $i ? values.STR($i.id) : values.NULL,
USER_NAME: $i ? values.STR($i.name) : values.NULL,
USER_USERNAME: $i ? values.STR($i.username) : values.NULL,
CUSTOM_EMOJIS: utils.jsToVal(customEmojis.value),
+ LOCALE: values.STR(lang),
'Mk:dialog': values.FN_NATIVE(async ([title, text, type]) => {
await os.alert({
type: type ? type.value : 'info',
@@ -28,15 +34,19 @@ export function createAiScriptEnv(opts) {
return confirm.canceled ? values.FALSE : values.TRUE;
}),
'Mk:api': values.FN_NATIVE(async ([ep, param, token]) => {
+ utils.assertString(ep);
+ if (ep.value.includes('://')) throw new Error('invalid endpoint');
if (token) {
utils.assertString(token);
// バグがあればundefinedもあり得るため念のため
if (typeof token.value !== 'string') throw new Error('invalid token');
}
- apiRequests++;
- if (apiRequests > 16) return values.NULL;
- const res = await os.api(ep.value, utils.valToJs(param), token ? token.value : (opts.token ?? null));
- return utils.jsToVal(res);
+ const actualToken: string|null = token?.value ?? opts.token ?? null;
+ return os.api(ep.value, utils.valToJs(param), actualToken).then(res => {
+ return utils.jsToVal(res);
+ }, err => {
+ return values.ERROR('request_failed', utils.jsToVal(err));
+ });
}),
'Mk:save': values.FN_NATIVE(([key, value]) => {
utils.assertString(key);
diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts
index c26ae5a4df..d326b956e8 100644
--- a/packages/frontend/src/scripts/aiscript/ui.ts
+++ b/packages/frontend/src/scripts/aiscript/ui.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { utils, values } from '@syuilo/aiscript';
import { v4 as uuid } from 'uuid';
import { ref, Ref } from 'vue';
@@ -119,7 +124,14 @@ export type AsUiPostFormButton = AsUiComponentBase & {
};
};
-export type AsUiComponent = AsUiRoot | AsUiContainer | AsUiText | AsUiMfm | AsUiButton | AsUiButtons | AsUiSwitch | AsUiTextarea | AsUiTextInput | AsUiNumberInput | AsUiSelect | AsUiFolder | AsUiPostFormButton;
+export type AsUiPostForm = AsUiComponentBase & {
+ type: 'postForm';
+ form?: {
+ text: string;
+ };
+};
+
+export type AsUiComponent = AsUiRoot | AsUiContainer | AsUiText | AsUiMfm | AsUiButton | AsUiButtons | AsUiSwitch | AsUiTextarea | AsUiTextInput | AsUiNumberInput | AsUiSelect | AsUiFolder | AsUiPostFormButton | AsUiPostForm;
export function patch(id: string, def: values.Value, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>) {
// TODO
@@ -457,6 +469,27 @@ function getPostFormButtonOptions(def: values.Value | undefined, call: (fn: valu
};
}
+function getPostFormOptions(def: values.Value | undefined, call: (fn: values.VFn, args: values.Value[]) => Promise<values.Value>): Omit<AsUiPostForm, 'id' | 'type'> {
+ utils.assertObject(def);
+
+ const form = def.value.get('form');
+ if (form) utils.assertObject(form);
+
+ const getForm = () => {
+ const text = form!.value.get('text');
+ utils.assertString(text);
+ return {
+ text: text.value,
+ };
+ };
+
+ return {
+ form: form ? getForm() : {
+ text: '',
+ },
+ };
+}
+
export function registerAsUiLib(components: Ref<AsUiComponent>[], done: (root: Ref<AsUiRoot>) => void) {
const instances = {};
@@ -518,51 +551,55 @@ export function registerAsUiLib(components: Ref<AsUiComponent>[], done: (root: R
}),
'Ui:C:container': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('container', def, id, getContainerOptions, opts.call);
+ return createComponentInstance('container', def, id, getContainerOptions, opts.topCall);
}),
'Ui:C:text': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('text', def, id, getTextOptions, opts.call);
+ return createComponentInstance('text', def, id, getTextOptions, opts.topCall);
}),
'Ui:C:mfm': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('mfm', def, id, getMfmOptions, opts.call);
+ return createComponentInstance('mfm', def, id, getMfmOptions, opts.topCall);
}),
'Ui:C:textarea': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('textarea', def, id, getTextareaOptions, opts.call);
+ return createComponentInstance('textarea', def, id, getTextareaOptions, opts.topCall);
}),
'Ui:C:textInput': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('textInput', def, id, getTextInputOptions, opts.call);
+ return createComponentInstance('textInput', def, id, getTextInputOptions, opts.topCall);
}),
'Ui:C:numberInput': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('numberInput', def, id, getNumberInputOptions, opts.call);
+ return createComponentInstance('numberInput', def, id, getNumberInputOptions, opts.topCall);
}),
'Ui:C:button': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('button', def, id, getButtonOptions, opts.call);
+ return createComponentInstance('button', def, id, getButtonOptions, opts.topCall);
}),
'Ui:C:buttons': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('buttons', def, id, getButtonsOptions, opts.call);
+ return createComponentInstance('buttons', def, id, getButtonsOptions, opts.topCall);
}),
'Ui:C:switch': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('switch', def, id, getSwitchOptions, opts.call);
+ return createComponentInstance('switch', def, id, getSwitchOptions, opts.topCall);
}),
'Ui:C:select': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('select', def, id, getSelectOptions, opts.call);
+ return createComponentInstance('select', def, id, getSelectOptions, opts.topCall);
}),
'Ui:C:folder': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('folder', def, id, getFolderOptions, opts.call);
+ return createComponentInstance('folder', def, id, getFolderOptions, opts.topCall);
}),
'Ui:C:postFormButton': values.FN_NATIVE(([def, id], opts) => {
- return createComponentInstance('postFormButton', def, id, getPostFormButtonOptions, opts.call);
+ return createComponentInstance('postFormButton', def, id, getPostFormButtonOptions, opts.topCall);
+ }),
+
+ 'Ui:C:postForm': values.FN_NATIVE(([def, id], opts) => {
+ return createComponentInstance('postForm', def, id, getPostFormOptions, opts.topCall);
}),
};
}
diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/api.ts
index 97081d170f..9259c88013 100644
--- a/packages/frontend/src/scripts/api.ts
+++ b/packages/frontend/src/scripts/api.ts
@@ -1,18 +1,23 @@
-import { Endpoints } from 'misskey-js/built/api.types';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
import { ref } from 'vue';
-import { apiUrl } from '@/config';
-import { $i } from '@/account';
+import { apiUrl } from '@/config.js';
+import { $i } from '@/account.js';
export const pendingApiRequestsCount = ref(0);
// Implements Misskey.api.ApiClient.request
-export function api<E extends keyof Endpoints, P extends Endpoints[E]['req']>(endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Endpoints[E]['res']> {
+export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Misskey.Endpoints[E]['res']> {
pendingApiRequestsCount.value++;
const onFinally = () => {
pendingApiRequestsCount.value--;
};
- const promise = new Promise<Endpoints[E]['res'] | void>((resolve, reject) => {
+ const promise = new Promise<Misskey.Endpoints[E]['res'] | void>((resolve, reject) => {
// Append a credential
if ($i) (data as any).i = $i.token;
if (token !== undefined) (data as any).i = token;
@@ -46,7 +51,7 @@ export function api<E extends keyof Endpoints, P extends Endpoints[E]['req']>(en
}
// Implements Misskey.api.ApiClient.request
-export function apiGet <E extends keyof Endpoints, P extends Endpoints[E]['req']>(endpoint: E, data: P = {} as any): Promise<Endpoints[E]['res']> {
+export function apiGet <E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(endpoint: E, data: P = {} as any): Promise<Misskey.Endpoints[E]['res']> {
pendingApiRequestsCount.value++;
const onFinally = () => {
@@ -55,7 +60,7 @@ export function apiGet <E extends keyof Endpoints, P extends Endpoints[E]['req']
const query = new URLSearchParams(data as any);
- const promise = new Promise<Endpoints[E]['res'] | void>((resolve, reject) => {
+ const promise = new Promise<Misskey.Endpoints[E]['res'] | void>((resolve, reject) => {
// Send request
window.fetch(`${apiUrl}/${endpoint}?${query}`, {
method: 'GET',
diff --git a/packages/frontend/src/scripts/array.ts b/packages/frontend/src/scripts/array.ts
index c9a146e707..082703a450 100644
--- a/packages/frontend/src/scripts/array.ts
+++ b/packages/frontend/src/scripts/array.ts
@@ -1,4 +1,10 @@
-import { EndoRelation, Predicate } from './relation';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+type EndoRelation<T> = (a: T, b: T) => boolean;
+type Predicate<T> = (x: T) => boolean;
/**
* Count the number of elements that satisfy the predicate
diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index 564573ae8a..0b4ebb4410 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -1,7 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { nextTick, Ref, ref, defineAsyncComponent } from 'vue';
import getCaretCoordinates from 'textarea-caret';
import { toASCII } from 'punycode/';
-import { popup } from '@/os';
+import { popup } from '@/os.js';
export class Autocomplete {
private suggestion: {
diff --git a/packages/frontend/src/scripts/cache.ts b/packages/frontend/src/scripts/cache.ts
index a61d858353..12347cf4b1 100644
--- a/packages/frontend/src/scripts/cache.ts
+++ b/packages/frontend/src/scripts/cache.ts
@@ -1,12 +1,19 @@
-import { ref } from "vue";
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { ref } from 'vue';
export class Cache<T> {
private cachedAt: number | null = null;
public value = ref<T | undefined>();
private lifetime: number;
+ private fetcher: () => Promise<T>;
- constructor(lifetime: Cache<never>['lifetime']) {
+ constructor(lifetime: Cache<never>['lifetime'], fetcher: () => Promise<T>) {
this.lifetime = lifetime;
+ this.fetcher = fetcher;
}
public set(value: T): void {
@@ -30,51 +37,17 @@ export class Cache<T> {
/**
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
- * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
*/
- public async fetch(fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
+ public async fetch(): Promise<T> {
const cachedValue = this.get();
if (cachedValue !== undefined) {
- if (validator) {
- if (validator(cachedValue)) {
- // Cache HIT
- return cachedValue;
- }
- } else {
- // Cache HIT
- return cachedValue;
- }
+ // Cache HIT
+ return cachedValue;
}
// Cache MISS
- const value = await fetcher();
+ const value = await this.fetcher();
this.set(value);
return value;
}
-
- /**
- * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
- * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
- */
- public async fetchMaybe(fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
- const cachedValue = this.get();
- if (cachedValue !== undefined) {
- if (validator) {
- if (validator(cachedValue)) {
- // Cache HIT
- return cachedValue;
- }
- } else {
- // Cache HIT
- return cachedValue;
- }
- }
-
- // Cache MISS
- const value = await fetcher();
- if (value !== undefined) {
- this.set(value);
- }
- return value;
- }
}
diff --git a/packages/frontend/src/scripts/chart-legend.ts b/packages/frontend/src/scripts/chart-legend.ts
index 6a5370cc87..e91908e0cb 100644
--- a/packages/frontend/src/scripts/chart-legend.ts
+++ b/packages/frontend/src/scripts/chart-legend.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { Plugin } from 'chart.js';
import MkChartLegend from '@/components/MkChartLegend.vue';
diff --git a/packages/frontend/src/scripts/chart-vline.ts b/packages/frontend/src/scripts/chart-vline.ts
index f321443834..336ec6cfbb 100644
--- a/packages/frontend/src/scripts/chart-vline.ts
+++ b/packages/frontend/src/scripts/chart-vline.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { Plugin } from 'chart.js';
export const chartVLine = (vLineColor: string) => ({
diff --git a/packages/frontend/src/scripts/check-word-mute.ts b/packages/frontend/src/scripts/check-word-mute.ts
index 35d40a6e08..5ac19c8d5b 100644
--- a/packages/frontend/src/scripts/check-word-mute.ts
+++ b/packages/frontend/src/scripts/check-word-mute.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export function checkWordMute(note: Record<string, any>, me: Record<string, any> | null | undefined, mutedWords: Array<string | string[]>): boolean {
// 自分自身
if (me && (note.userId === me.id)) return false;
diff --git a/packages/frontend/src/scripts/clicker-game.ts b/packages/frontend/src/scripts/clicker-game.ts
index d3b0f9d1e2..5ad076e5ef 100644
--- a/packages/frontend/src/scripts/clicker-game.ts
+++ b/packages/frontend/src/scripts/clicker-game.ts
@@ -1,5 +1,10 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { ref, computed } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
type SaveData = {
gameVersion: number;
diff --git a/packages/frontend/src/scripts/clone.ts b/packages/frontend/src/scripts/clone.ts
index cf8fa64ba3..96b53684f3 100644
--- a/packages/frontend/src/scripts/clone.ts
+++ b/packages/frontend/src/scripts/clone.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
// structredCloneが遅いため
// SEE: http://var.blog.jp/archives/86038606.html
// あと、Vue RefをIndexedDBに保存しようとしてstructredCloneを使ったらエラーになった
diff --git a/packages/frontend/src/scripts/collapsed.ts b/packages/frontend/src/scripts/collapsed.ts
index 1bf56f233b..c3c767bcfa 100644
--- a/packages/frontend/src/scripts/collapsed.ts
+++ b/packages/frontend/src/scripts/collapsed.ts
@@ -1,8 +1,13 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import * as mfm from 'mfm-js';
-import * as misskey from 'misskey-js';
-import { extractUrlFromMfm } from './extract-url-from-mfm';
+import * as Misskey from 'misskey-js';
+import { extractUrlFromMfm } from './extract-url-from-mfm.js';
-export function shouldCollapsed(note: misskey.entities.Note): boolean {
+export function shouldCollapsed(note: Misskey.entities.Note): boolean {
const urls = note.text ? extractUrlFromMfm(mfm.parse(note.text)) : null;
const collapsed = note.cw == null && note.text != null && (
(note.text.includes('$[x2')) ||
diff --git a/packages/frontend/src/scripts/collect-page-vars.ts b/packages/frontend/src/scripts/collect-page-vars.ts
index 76b68beaf6..79356e60eb 100644
--- a/packages/frontend/src/scripts/collect-page-vars.ts
+++ b/packages/frontend/src/scripts/collect-page-vars.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
interface StringPageVar {
name: string,
type: 'string',
diff --git a/packages/frontend/src/scripts/color.ts b/packages/frontend/src/scripts/color.ts
index 10a99a5a05..25ef41d9b7 100644
--- a/packages/frontend/src/scripts/color.ts
+++ b/packages/frontend/src/scripts/color.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export const alpha = (hex: string, a: number): string => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!;
const r = parseInt(result[1], 16);
diff --git a/packages/frontend/src/scripts/confetti.ts b/packages/frontend/src/scripts/confetti.ts
index 9e03acbf8d..b394ba3e2a 100644
--- a/packages/frontend/src/scripts/confetti.ts
+++ b/packages/frontend/src/scripts/confetti.ts
@@ -1,5 +1,10 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import _confetti from 'canvas-confetti';
-import * as os from '@/os';
+import * as os from '@/os.js';
export function confetti(options: { duration?: number; } = {}) {
const duration = options.duration ?? 1000 * 4;
diff --git a/packages/frontend/src/scripts/contains.ts b/packages/frontend/src/scripts/contains.ts
index 256e09d293..b50ce4128c 100644
--- a/packages/frontend/src/scripts/contains.ts
+++ b/packages/frontend/src/scripts/contains.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export default (parent, child, checkSame = true) => {
if (checkSame && parent === child) return true;
let node = child.parentNode;
diff --git a/packages/frontend/src/scripts/copy-to-clipboard.ts b/packages/frontend/src/scripts/copy-to-clipboard.ts
index ab13cab970..3884d4a20a 100644
--- a/packages/frontend/src/scripts/copy-to-clipboard.ts
+++ b/packages/frontend/src/scripts/copy-to-clipboard.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
/**
* Clipboardに値をコピー(TODO: 文字列以外も対応)
*/
diff --git a/packages/frontend/src/scripts/device-kind.ts b/packages/frontend/src/scripts/device-kind.ts
index b575db9606..3843052a24 100644
--- a/packages/frontend/src/scripts/device-kind.ts
+++ b/packages/frontend/src/scripts/device-kind.ts
@@ -1,4 +1,9 @@
-import { defaultStore } from '@/store';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { defaultStore } from '@/store.js';
await defaultStore.ready;
diff --git a/packages/frontend/src/scripts/emoji-base.ts b/packages/frontend/src/scripts/emoji-base.ts
index e91f2fa22d..46a13462a1 100644
--- a/packages/frontend/src/scripts/emoji-base.ts
+++ b/packages/frontend/src/scripts/emoji-base.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
const twemojiSvgBase = '/twemoji';
const fluentEmojiPngBase = '/fluent-emoji';
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index 79661b7ce9..4159da84c8 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export const unicodeEmojiCategories = ['face', 'people', 'animals_and_nature', 'food_and_drink', 'activity', 'travel_and_places', 'objects', 'symbols', 'flags'] as const;
export type UnicodeEmojiDef = {
diff --git a/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts b/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts
index af517f2672..57b296ab2a 100644
--- a/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts
+++ b/packages/frontend/src/scripts/extract-avg-color-from-blurhash.ts
@@ -1,9 +1,14 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export function extractAvgColorFromBlurhash(hash: string) {
return typeof hash === 'string'
? '#' + [...hash.slice(2, 6)]
- .map(x => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~'.indexOf(x))
- .reduce((a, c) => a * 83 + c, 0)
- .toString(16)
- .padStart(6, '0')
+ .map(x => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~'.indexOf(x))
+ .reduce((a, c) => a * 83 + c, 0)
+ .toString(16)
+ .padStart(6, '0')
: undefined;
}
diff --git a/packages/frontend/src/scripts/extract-mentions.ts b/packages/frontend/src/scripts/extract-mentions.ts
index cc19b161a8..74ce45d324 100644
--- a/packages/frontend/src/scripts/extract-mentions.ts
+++ b/packages/frontend/src/scripts/extract-mentions.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
// test is located in test/extract-mentions
import * as mfm from 'mfm-js';
diff --git a/packages/frontend/src/scripts/extract-url-from-mfm.ts b/packages/frontend/src/scripts/extract-url-from-mfm.ts
index 34e3eb6c19..c1ed9338f8 100644
--- a/packages/frontend/src/scripts/extract-url-from-mfm.ts
+++ b/packages/frontend/src/scripts/extract-url-from-mfm.ts
@@ -1,5 +1,10 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import * as mfm from 'mfm-js';
-import { unique } from '@/scripts/array';
+import { unique } from '@/scripts/array.js';
// unique without hash
// [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ]
diff --git a/packages/frontend/src/scripts/focus.ts b/packages/frontend/src/scripts/focus.ts
index d6802fa322..6a31ebd431 100644
--- a/packages/frontend/src/scripts/focus.ts
+++ b/packages/frontend/src/scripts/focus.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export function focusPrev(el: Element | null, self = false, scroll = true) {
if (el == null) return;
if (!self) el = el.previousElementSibling;
diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts
index 635803a2bc..222fd9b0b7 100644
--- a/packages/frontend/src/scripts/form.ts
+++ b/packages/frontend/src/scripts/form.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
type EnumItem = string | {label: string; value: string;};
export type FormItem = {
label?: string;
diff --git a/packages/frontend/src/scripts/format-time-string.ts b/packages/frontend/src/scripts/format-time-string.ts
index c20db5e827..918996dd10 100644
--- a/packages/frontend/src/scripts/format-time-string.ts
+++ b/packages/frontend/src/scripts/format-time-string.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
const defaultLocaleStringFormats: {[index: string]: string} = {
'weekday': 'narrow',
'era': 'narrow',
diff --git a/packages/frontend/src/scripts/gen-search-query.ts b/packages/frontend/src/scripts/gen-search-query.ts
index 956e0f35d0..54654980f2 100644
--- a/packages/frontend/src/scripts/gen-search-query.ts
+++ b/packages/frontend/src/scripts/gen-search-query.ts
@@ -1,5 +1,10 @@
-import * as Acct from 'misskey-js/built/acct';
-import { host as localHost } from '@/config';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+import { host as localHost } from '@/config.js';
export async function genSearchQuery(v: any, q: string) {
let host: string;
@@ -13,7 +18,7 @@ export async function genSearchQuery(v: any, q: string) {
host = at;
}
} else {
- const user = await v.os.api('users/show', Acct.parse(at)).catch(x => null);
+ const user = await v.os.api('users/show', Misskey.acct.parse(at)).catch(x => null);
if (user) {
userId = user.id;
} else {
diff --git a/packages/frontend/src/scripts/get-account-from-id.ts b/packages/frontend/src/scripts/get-account-from-id.ts
index 1da897f176..346d283572 100644
--- a/packages/frontend/src/scripts/get-account-from-id.ts
+++ b/packages/frontend/src/scripts/get-account-from-id.ts
@@ -1,4 +1,9 @@
-import { get } from '@/scripts/idb-proxy';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { get } from '@/scripts/idb-proxy.js';
export async function getAccountFromId(id: string) {
const accounts = await get('accounts') as { token: string; id: string; }[];
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index 9b488087e2..0964108249 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -1,10 +1,15 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import * as Misskey from 'misskey-js';
import { defineAsyncComponent } from 'vue';
-import { i18n } from '@/i18n';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import * as os from '@/os';
-import { MenuItem } from '@/types/menu';
-import { defaultStore } from '@/store';
+import { i18n } from '@/i18n.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import * as os from '@/os.js';
+import { MenuItem } from '@/types/menu.js';
+import { defaultStore } from '@/store.js';
function rename(file: Misskey.entities.DriveFile) {
os.inputText({
@@ -88,7 +93,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
icon: 'ti ti-crop',
action: () => os.cropImage(file, {
aspectRatio: NaN,
- uploadFolder: folder ? folder.id : folder
+ uploadFolder: folder ? folder.id : folder,
}),
}] : [], null, {
text: i18n.ts.createNoteFromTheFile,
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 960f26ca67..734a632039 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -1,21 +1,27 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { defineAsyncComponent, Ref } from 'vue';
-import * as misskey from 'misskey-js';
-import { claimAchievement } from './achievements';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import * as os from '@/os';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { url } from '@/config';
-import { defaultStore, noteActions } from '@/store';
-import { miLocalStorage } from '@/local-storage';
-import { getUserMenu } from '@/scripts/get-user-menu';
-import { clipsCache } from '@/cache';
+import * as Misskey from 'misskey-js';
+import { claimAchievement } from './achievements.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import * as os from '@/os.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { url } from '@/config.js';
+import { defaultStore, noteActions } from '@/store.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { getUserMenu } from '@/scripts/get-user-menu.js';
+import { clipsCache } from '@/cache.js';
+import { MenuItem } from '@/types/menu.js';
export async function getNoteClipMenu(props: {
- note: misskey.entities.Note;
+ note: Misskey.entities.Note;
isDeleted: Ref<boolean>;
- currentClip?: misskey.entities.Clip;
+ currentClip?: Misskey.entities.Clip;
}) {
const isRenote = (
props.note.renote != null &&
@@ -24,9 +30,9 @@ export async function getNoteClipMenu(props: {
props.note.poll == null
);
- const appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note;
+ const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
- const clips = await clipsCache.fetch(() => os.api('clips/list'));
+ const clips = await clipsCache.fetch();
return [...clips.map(clip => ({
text: clip.name,
action: () => {
@@ -86,13 +92,38 @@ export async function getNoteClipMenu(props: {
}];
}
+export function getAbuseNoteMenu(note: misskey.entities.Note, text: string): MenuItem {
+ return {
+ icon: 'ti ti-exclamation-circle',
+ text,
+ action: (): void => {
+ const u = note.url ?? note.uri ?? `${url}/notes/${note.id}`;
+ os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
+ user: note.user,
+ initialComment: `Note: ${u}\n-----\n`,
+ }, {}, 'closed');
+ },
+ };
+}
+
+export function getCopyNoteLinkMenu(note: misskey.entities.Note, text: string): MenuItem {
+ return {
+ icon: 'ti ti-link',
+ text,
+ action: (): void => {
+ copyToClipboard(`${url}/notes/${note.id}`);
+ os.success();
+ },
+ };
+}
+
export function getNoteMenu(props: {
- note: misskey.entities.Note;
+ note: Misskey.entities.Note;
menuButton: Ref<HTMLElement>;
translation: Ref<any>;
translating: Ref<boolean>;
isDeleted: Ref<boolean>;
- currentClip?: misskey.entities.Clip;
+ currentClip?: Misskey.entities.Clip;
}) {
const isRenote = (
props.note.renote != null &&
@@ -101,7 +132,9 @@ export function getNoteMenu(props: {
props.note.poll == null
);
- const appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note;
+ const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
+
+ const cleanups = [] as (() => void)[];
function del(): void {
os.confirm({
@@ -205,18 +238,6 @@ export function getNoteMenu(props: {
os.pageWindow(`/notes/${appearNote.id}`);
}
- function showReactions(): void {
- os.popup(defineAsyncComponent(() => import('@/components/MkReactedUsersDialog.vue')), {
- noteId: appearNote.id,
- }, {}, 'closed');
- }
-
- function showRenotes(): void {
- os.popup(defineAsyncComponent(() => import('@/components/MkRenotedUsersDialog.vue')), {
- noteId: appearNote.id,
- }, {}, 'closed');
- }
-
async function translate(): Promise<void> {
if (props.translation.value != null) return;
props.translating.value = true;
@@ -228,7 +249,7 @@ export function getNoteMenu(props: {
props.translation.value = res;
}
- let menu;
+ let menu: MenuItem[];
if ($i) {
const statePromise = os.api('notes/state', {
noteId: appearNote.id,
@@ -247,22 +268,11 @@ export function getNoteMenu(props: {
text: i18n.ts.details,
action: openDetail,
}, {
- icon: 'ti ti-repeat',
- text: i18n.ts.renotesList,
- action: showRenotes,
- }, {
- icon: 'ti ti-icons',
- text: i18n.ts.reactionsList,
- action: showReactions,
- }, {
icon: 'ti ti-copy',
text: i18n.ts.copyContent,
action: copyContent,
- }, {
- icon: 'ti ti-link',
- text: i18n.ts.copyLink,
- action: copyLink,
- }, (appearNote.url || appearNote.uri) ? {
+ }, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink)
+ , (appearNote.url || appearNote.uri) ? {
icon: 'ti ti-external-link',
text: i18n.ts.showOnRemote,
action: () => {
@@ -290,7 +300,7 @@ export function getNoteMenu(props: {
action: () => toggleFavorite(true),
}),
{
- type: 'parent',
+ type: 'parent' as const,
icon: 'ti ti-paperclip',
text: i18n.ts.clip,
children: () => getNoteClipMenu(props),
@@ -313,15 +323,17 @@ export function getNoteMenu(props: {
text: i18n.ts.pin,
action: () => togglePin(true),
} : undefined,
- appearNote.userId !== $i.id ? {
- type: 'parent',
+ {
+ type: 'parent' as const,
icon: 'ti ti-user',
text: i18n.ts.user,
children: async () => {
- const user = await os.api('users/show', { userId: appearNote.userId });
- return getUserMenu(user);
+ const user = appearNote.userId === $i?.id ? $i : await os.api('users/show', { userId: appearNote.userId });
+ const { menu, cleanup } = getUserMenu(user);
+ cleanups.push(cleanup);
+ return menu;
},
- } : undefined,
+ },
/*
...($i.isModerator || $i.isAdmin ? [
null,
@@ -334,17 +346,8 @@ export function getNoteMenu(props: {
),*/
...(appearNote.userId !== $i.id ? [
null,
- {
- icon: 'ti ti-exclamation-circle',
- text: i18n.ts.reportAbuse,
- action: () => {
- const u = appearNote.url ?? appearNote.uri ?? `${url}/notes/${appearNote.id}`;
- os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
- user: appearNote.user,
- initialComment: `Note: ${u}\n-----\n`,
- }, {}, 'closed');
- },
- }]
+ appearNote.userId !== $i.id ? getAbuseNoteMenu(appearNote, i18n.ts.reportAbuse) : undefined,
+ ]
: []
),
...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
@@ -372,11 +375,8 @@ export function getNoteMenu(props: {
icon: 'ti ti-copy',
text: i18n.ts.copyContent,
action: copyContent,
- }, {
- icon: 'ti ti-link',
- text: i18n.ts.copyLink,
- action: copyLink,
- }, (appearNote.url || appearNote.uri) ? {
+ }, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink)
+ , (appearNote.url || appearNote.uri) ? {
icon: 'ti ti-external-link',
text: i18n.ts.showOnRemote,
action: () => {
@@ -406,5 +406,15 @@ export function getNoteMenu(props: {
}]);
}
- return menu;
+ const cleanup = () => {
+ if (_DEV_) console.log('note menu cleanup', cleanups);
+ for (const cl of cleanups) {
+ cl();
+ }
+ };
+
+ return {
+ menu,
+ cleanup,
+ };
}
diff --git a/packages/frontend/src/scripts/get-note-summary.ts b/packages/frontend/src/scripts/get-note-summary.ts
index d57e1c3029..1fd9f04d46 100644
--- a/packages/frontend/src/scripts/get-note-summary.ts
+++ b/packages/frontend/src/scripts/get-note-summary.ts
@@ -1,11 +1,16 @@
-import * as misskey from 'misskey-js';
-import { i18n } from '@/i18n';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+import { i18n } from '@/i18n.js';
/**
* 投稿を表す文字列を取得します。
* @param {*} note (packされた)投稿
*/
-export const getNoteSummary = (note: misskey.entities.Note): string => {
+export const getNoteSummary = (note: Misskey.entities.Note): string => {
if (note.deletedAt) {
return `(${i18n.ts.deletedNote})`;
}
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 1c93d58b44..128cbafb15 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -1,19 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { toUnicode } from 'punycode';
-import { defineAsyncComponent } from 'vue';
-import * as misskey from 'misskey-js';
-import { i18n } from '@/i18n';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { host, url } from '@/config';
-import * as os from '@/os';
-import { defaultStore, userActions } from '@/store';
-import { $i, iAmModerator } from '@/account';
-import { mainRouter } from '@/router';
-import { Router } from '@/nirax';
-import { antennasCache, rolesCache, userListsCache } from '@/cache';
+import { defineAsyncComponent, ref, watch } from 'vue';
+import * as Misskey from 'misskey-js';
+import { i18n } from '@/i18n.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { host, url } from '@/config.js';
+import * as os from '@/os.js';
+import { defaultStore, userActions } from '@/store.js';
+import { $i, iAmModerator } from '@/account.js';
+import { mainRouter } from '@/router.js';
+import { Router } from '@/nirax.js';
+import { antennasCache, rolesCache, userListsCache } from '@/cache.js';
-export function getUserMenu(user: misskey.entities.UserDetailed, router: Router = mainRouter) {
+export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router = mainRouter) {
const meId = $i ? $i.id : null;
+ const cleanups = [] as (() => void)[];
+
async function toggleMute() {
if (user.isMuted) {
os.apiWithDialog('mute/delete', {
@@ -73,6 +80,15 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
});
}
+ async function toggleNotify() {
+ os.apiWithDialog('following/update', {
+ userId: user.id,
+ notify: user.notify === 'normal' ? 'none' : 'normal',
+ }).then(() => {
+ user.notify = user.notify === 'normal' ? 'none' : 'normal';
+ });
+ }
+
function reportAbuse() {
os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), {
user: user,
@@ -126,13 +142,13 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
action: () => {
copyToClipboard(`@${user.username}@${user.host ?? host}`);
},
- }, {
- icon: 'ti ti-info-circle',
- text: i18n.ts.info,
+ }, ...(iAmModerator ? [{
+ icon: 'ti ti-user-exclamation',
+ text: i18n.ts.moderation,
action: () => {
- router.push(`/user-info/${user.id}`);
+ router.push(`/admin/user/${user.id}`);
},
- }, {
+ }] : []), {
icon: 'ti ti-rss',
text: i18n.ts.copyRSS,
action: () => {
@@ -149,7 +165,8 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
icon: 'ti ti-mail',
text: i18n.ts.sendMessage,
action: () => {
- os.post({ specified: user, initialText: `@${user.username} ` });
+ const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${user.host}`;
+ os.post({ specified: user, initialText: `${canonical} ` });
},
}, null, {
icon: 'ti ti-pencil',
@@ -162,25 +179,40 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
icon: 'ti ti-list',
text: i18n.ts.addToList,
children: async () => {
- const lists = await userListsCache.fetch(() => os.api('users/lists/list'));
+ const lists = await userListsCache.fetch();
+ return lists.map(list => {
+ const isListed = ref(list.userIds.includes(user.id));
+ cleanups.push(watch(isListed, () => {
+ if (isListed.value) {
+ os.apiWithDialog('users/lists/push', {
+ listId: list.id,
+ userId: user.id,
+ }).then(() => {
+ list.userIds.push(user.id);
+ });
+ } else {
+ os.apiWithDialog('users/lists/pull', {
+ listId: list.id,
+ userId: user.id,
+ }).then(() => {
+ list.userIds.splice(list.userIds.indexOf(user.id), 1);
+ });
+ }
+ }));
- return lists.map(list => ({
- text: list.name,
- action: async () => {
- await os.apiWithDialog('users/lists/push', {
- listId: list.id,
- userId: user.id,
- });
- userListsCache.delete();
- },
- }));
+ return {
+ type: 'switch',
+ text: list.name,
+ ref: isListed,
+ };
+ });
},
}, {
type: 'parent',
icon: 'ti ti-antenna',
text: i18n.ts.addToAntenna,
children: async () => {
- const antennas = await antennasCache.fetch(() => os.api('antennas/list'));
+ const antennas = await antennasCache.fetch();
const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${toUnicode(user.host)}`;
return antennas.filter((a) => a.src === 'users').map(antenna => ({
text: antenna.name,
@@ -211,7 +243,7 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
icon: 'ti ti-badges',
text: i18n.ts.roles,
children: async () => {
- const roles = await rolesCache.fetch(() => os.api('admin/roles/list'));
+ const roles = await rolesCache.fetch();
return roles.filter(r => r.target === 'manual').map(r => ({
text: r.name,
@@ -247,6 +279,15 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
}]);
}
+ // フォローしたとしても user.isFollowing はリアルタイム更新されないので不便なため
+ //if (user.isFollowing) {
+ menu = menu.concat([{
+ icon: user.notify === 'none' ? 'ti ti-bell' : 'ti ti-bell-off',
+ text: user.notify === 'none' ? i18n.ts.notifyNotes : i18n.ts.unnotifyNotes,
+ action: toggleNotify,
+ }]);
+ //}
+
menu = menu.concat([null, {
icon: user.isMuted ? 'ti ti-eye' : 'ti ti-eye-off',
text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
@@ -306,5 +347,15 @@ export function getUserMenu(user: misskey.entities.UserDetailed, router: Router
}))]);
}
- return menu;
+ const cleanup = () => {
+ if (_DEV_) console.log('user menu cleanup', cleanups);
+ for (const cl of cleanups) {
+ cl();
+ }
+ };
+
+ return {
+ menu,
+ cleanup,
+ };
}
diff --git a/packages/frontend/src/scripts/get-user-name.ts b/packages/frontend/src/scripts/get-user-name.ts
index 4daf203e06..3ae80d7fc3 100644
--- a/packages/frontend/src/scripts/get-user-name.ts
+++ b/packages/frontend/src/scripts/get-user-name.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export default function(user: { name?: string | null, username: string }): string {
return user.name === '' ? user.username : user.name ?? user.username;
}
diff --git a/packages/frontend/src/scripts/hotkey.ts b/packages/frontend/src/scripts/hotkey.ts
index b7238016c6..48c80c066b 100644
--- a/packages/frontend/src/scripts/hotkey.ts
+++ b/packages/frontend/src/scripts/hotkey.ts
@@ -1,4 +1,9 @@
-import keyCode from './keycode';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import keyCode from './keycode.js';
type Callback = (ev: KeyboardEvent) => void;
diff --git a/packages/frontend/src/scripts/i18n.ts b/packages/frontend/src/scripts/i18n.ts
index 54184386da..8e5f17f38a 100644
--- a/packages/frontend/src/scripts/i18n.ts
+++ b/packages/frontend/src/scripts/i18n.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export class I18n<T extends Record<string, any>> {
public ts: T;
diff --git a/packages/frontend/src/scripts/idb-proxy.ts b/packages/frontend/src/scripts/idb-proxy.ts
index 7dbca01dea..a20cfcb1d0 100644
--- a/packages/frontend/src/scripts/idb-proxy.ts
+++ b/packages/frontend/src/scripts/idb-proxy.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
// FirefoxのプライベートモードなどではindexedDBが使用不可能なので、
// indexedDBが使えない環境ではlocalStorageを使う
import {
diff --git a/packages/frontend/src/scripts/idle-render.ts b/packages/frontend/src/scripts/idle-render.ts
index a1470b82e9..ac1be50c73 100644
--- a/packages/frontend/src/scripts/idle-render.ts
+++ b/packages/frontend/src/scripts/idle-render.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
const requestIdleCallback: typeof globalThis.requestIdleCallback = globalThis.requestIdleCallback ?? ((callback) => {
const start = performance.now();
const timeoutId = setTimeout(() => {
diff --git a/packages/frontend/src/scripts/init-chart.ts b/packages/frontend/src/scripts/init-chart.ts
index fc18869009..ebf27667d7 100644
--- a/packages/frontend/src/scripts/init-chart.ts
+++ b/packages/frontend/src/scripts/init-chart.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import {
Chart,
ArcElement,
@@ -19,7 +24,7 @@ import {
import gradient from 'chartjs-plugin-gradient';
import zoomPlugin from 'chartjs-plugin-zoom';
import { MatrixController, MatrixElement } from 'chartjs-chart-matrix';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
import 'chartjs-adapter-date-fns';
export function initChart() {
diff --git a/packages/frontend/src/scripts/initialize-sw.ts b/packages/frontend/src/scripts/initialize-sw.ts
index de52f30523..007fc0f2f7 100644
--- a/packages/frontend/src/scripts/initialize-sw.ts
+++ b/packages/frontend/src/scripts/initialize-sw.ts
@@ -1,9 +1,14 @@
-import { lang } from '@/config';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { lang } from '@/config.js';
export async function initializeSw() {
if (!('serviceWorker' in navigator)) return;
- navigator.serviceWorker.register(`/sw.js`, { scope: '/', type: 'classic' });
+ navigator.serviceWorker.register('/sw.js', { scope: '/', type: 'classic' });
navigator.serviceWorker.ready.then(registration => {
registration.active?.postMessage({
msg: 'initialize',
diff --git a/packages/frontend/src/scripts/intl-const.ts b/packages/frontend/src/scripts/intl-const.ts
index 081ff6248c..8012a677ef 100644
--- a/packages/frontend/src/scripts/intl-const.ts
+++ b/packages/frontend/src/scripts/intl-const.ts
@@ -1,12 +1,17 @@
-import { lang } from '@/config';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { lang } from '@/config.js';
export const versatileLang = (lang ?? 'ja-JP').replace('ja-KS', 'ja-JP');
export const dateTimeFormat = new Intl.DateTimeFormat(versatileLang, {
- year: 'numeric',
- month: 'numeric',
- day: 'numeric',
- hour: 'numeric',
- minute: 'numeric',
- second: 'numeric',
+ year: 'numeric',
+ month: 'numeric',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ second: 'numeric',
});
export const numberFormat = new Intl.NumberFormat(versatileLang);
diff --git a/packages/frontend/src/scripts/is-device-darkmode.ts b/packages/frontend/src/scripts/is-device-darkmode.ts
index 854f38e517..badc295726 100644
--- a/packages/frontend/src/scripts/is-device-darkmode.ts
+++ b/packages/frontend/src/scripts/is-device-darkmode.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export function isDeviceDarkmode() {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
diff --git a/packages/frontend/src/scripts/isFfVisibleForMe.ts b/packages/frontend/src/scripts/isFfVisibleForMe.ts
index 0ddd3f377d..0567f3b34a 100644
--- a/packages/frontend/src/scripts/isFfVisibleForMe.ts
+++ b/packages/frontend/src/scripts/isFfVisibleForMe.ts
@@ -1,7 +1,12 @@
-import * as misskey from 'misskey-js';
-import { $i } from '@/account';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
-export function isFfVisibleForMe(user: misskey.entities.UserDetailed): boolean {
+import * as Misskey from 'misskey-js';
+import { $i } from '@/account.js';
+
+export function isFfVisibleForMe(user: Misskey.entities.UserDetailed): boolean {
if ($i && $i.id === user.id) return true;
if (user.ffVisibility === 'private') return false;
diff --git a/packages/frontend/src/scripts/keycode.ts b/packages/frontend/src/scripts/keycode.ts
index 35813edbd5..57bc4d19ba 100644
--- a/packages/frontend/src/scripts/keycode.ts
+++ b/packages/frontend/src/scripts/keycode.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export default (input: string): string[] => {
if (Object.keys(aliases).some(a => a.toLowerCase() === input.toLowerCase())) {
const codes = aliases[input];
diff --git a/packages/frontend/src/scripts/langmap.ts b/packages/frontend/src/scripts/langmap.ts
index 25f5b366c8..3912d58d82 100644
--- a/packages/frontend/src/scripts/langmap.ts
+++ b/packages/frontend/src/scripts/langmap.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
// TODO: sharedに置いてバックエンドのと統合したい
export const langmap = {
'ach': {
diff --git a/packages/frontend/src/scripts/login-id.ts b/packages/frontend/src/scripts/login-id.ts
index 0f9c6be4a9..fe0e17e66e 100644
--- a/packages/frontend/src/scripts/login-id.ts
+++ b/packages/frontend/src/scripts/login-id.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export function getUrlWithLoginId(url: string, loginId: string) {
const u = new URL(url, origin);
u.searchParams.append('loginId', loginId);
diff --git a/packages/frontend/src/scripts/lookup-user.ts b/packages/frontend/src/scripts/lookup-user.ts
index 3ab9d55300..3dbc03f777 100644
--- a/packages/frontend/src/scripts/lookup-user.ts
+++ b/packages/frontend/src/scripts/lookup-user.ts
@@ -1,6 +1,11 @@
-import * as Acct from 'misskey-js/built/acct';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
export async function lookupUser() {
const { canceled, result } = await os.inputText({
@@ -9,10 +14,10 @@ export async function lookupUser() {
if (canceled) return;
const show = (user) => {
- os.pageWindow(`/user-info/${user.id}`);
+ os.pageWindow(`/admin/user/${user.id}`);
};
- const usernamePromise = os.api('users/show', Acct.parse(result));
+ const usernamePromise = os.api('users/show', Misskey.acct.parse(result));
const idPromise = os.api('users/show', { userId: result });
let _notFound = false;
const notFound = () => {
diff --git a/packages/frontend/src/scripts/lookup.ts b/packages/frontend/src/scripts/lookup.ts
index 3f357a3c92..979f40f038 100644
--- a/packages/frontend/src/scripts/lookup.ts
+++ b/packages/frontend/src/scripts/lookup.ts
@@ -1,7 +1,12 @@
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { mainRouter } from '@/router';
-import { Router } from '@/nirax';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { mainRouter } from '@/router.js';
+import { Router } from '@/nirax.js';
export async function lookup(router?: Router) {
const _router = router ?? mainRouter;
diff --git a/packages/frontend/src/scripts/media-proxy.ts b/packages/frontend/src/scripts/media-proxy.ts
index 3a3dbffb47..559e61211d 100644
--- a/packages/frontend/src/scripts/media-proxy.ts
+++ b/packages/frontend/src/scripts/media-proxy.ts
@@ -1,8 +1,13 @@
-import { query } from '@/scripts/url';
-import { url } from '@/config';
-import { instance } from '@/instance';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
-export function getProxiedImageUrl(imageUrl: string, type?: 'preview' | 'emoji' | 'avatar', mustOrigin: boolean = false, noFallback: boolean = false): string {
+import { query } from '@/scripts/url.js';
+import { url } from '@/config.js';
+import { instance } from '@/instance.js';
+
+export function getProxiedImageUrl(imageUrl: string, type?: 'preview' | 'emoji' | 'avatar', mustOrigin = false, noFallback = false): string {
const localProxy = `${url}/proxy`;
if (imageUrl.startsWith(instance.mediaProxy + '/') || imageUrl.startsWith('/proxy/') || imageUrl.startsWith(localProxy + '/')) {
diff --git a/packages/frontend/src/scripts/mfm-tags.ts b/packages/frontend/src/scripts/mfm-tags.ts
index a84198282d..dc78e42238 100644
--- a/packages/frontend/src/scripts/mfm-tags.ts
+++ b/packages/frontend/src/scripts/mfm-tags.ts
@@ -1 +1,6 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate'];
diff --git a/packages/frontend/src/scripts/page-metadata.ts b/packages/frontend/src/scripts/page-metadata.ts
index 8810e26960..330ba8da83 100644
--- a/packages/frontend/src/scripts/page-metadata.ts
+++ b/packages/frontend/src/scripts/page-metadata.ts
@@ -1,4 +1,9 @@
-import * as misskey from 'misskey-js';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
import { ComputedRef, inject, isRef, onActivated, onMounted, provide, ref, Ref } from 'vue';
export const setPageMetadata = Symbol('setPageMetadata');
@@ -8,8 +13,8 @@ export type PageMetadata = {
title: string;
subtitle?: string;
icon?: string | null;
- avatar?: misskey.entities.User | null;
- userName?: misskey.entities.User | null;
+ avatar?: Misskey.entities.User | null;
+ userName?: Misskey.entities.User | null;
};
export function definePageMetadata(metadata: PageMetadata | null | Ref<PageMetadata | null> | ComputedRef<PageMetadata | null>): void {
diff --git a/packages/frontend/src/scripts/physics.ts b/packages/frontend/src/scripts/physics.ts
index efda80f074..cf9fad70eb 100644
--- a/packages/frontend/src/scripts/physics.ts
+++ b/packages/frontend/src/scripts/physics.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import * as Matter from 'matter-js';
export function physics(container: HTMLElement) {
diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts
index c101a127f3..e6c08dfbc0 100644
--- a/packages/frontend/src/scripts/please-login.ts
+++ b/packages/frontend/src/scripts/please-login.ts
@@ -1,7 +1,12 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { defineAsyncComponent } from 'vue';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { popup } from '@/os';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { popup } from '@/os.js';
export function pleaseLogin(path?: string) {
if ($i) return;
diff --git a/packages/frontend/src/scripts/popout.ts b/packages/frontend/src/scripts/popout.ts
index 580031d0a3..0c2ff16992 100644
--- a/packages/frontend/src/scripts/popout.ts
+++ b/packages/frontend/src/scripts/popout.ts
@@ -1,5 +1,10 @@
-import * as config from '@/config';
-import { appendQuery } from './url';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { appendQuery } from './url.js';
+import * as config from '@/config.js';
export function popout(path: string, w?: HTMLElement) {
let url = path.startsWith('http://') || path.startsWith('https://') ? path : config.url + path;
diff --git a/packages/frontend/src/scripts/popup-position.ts b/packages/frontend/src/scripts/popup-position.ts
index cb45002202..0a799c5665 100644
--- a/packages/frontend/src/scripts/popup-position.ts
+++ b/packages/frontend/src/scripts/popup-position.ts
@@ -1,3 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
export function calcPopupPosition(el: HTMLElement, props: {
anchorElement: HTMLElement | null;
diff --git a/packages/frontend/src/scripts/reaction-picker.ts b/packages/frontend/src/scripts/reaction-picker.ts
index fe32e719da..19e1bfba2c 100644
--- a/packages/frontend/src/scripts/reaction-picker.ts
+++ b/packages/frontend/src/scripts/reaction-picker.ts
@@ -1,5 +1,10 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { defineAsyncComponent, Ref, ref } from 'vue';
-import { popup } from '@/os';
+import { popup } from '@/os.js';
class ReactionPicker {
private src: Ref<HTMLElement | null> = ref(null);
diff --git a/packages/frontend/src/scripts/safe-uri-decode.ts b/packages/frontend/src/scripts/safe-uri-decode.ts
index 301b56d7fd..625d8c34a7 100644
--- a/packages/frontend/src/scripts/safe-uri-decode.ts
+++ b/packages/frontend/src/scripts/safe-uri-decode.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export function safeURIDecode(str: string): string {
try {
return decodeURIComponent(str);
diff --git a/packages/frontend/src/scripts/scroll.ts b/packages/frontend/src/scripts/scroll.ts
index a002f02b5a..7338de62b6 100644
--- a/packages/frontend/src/scripts/scroll.ts
+++ b/packages/frontend/src/scripts/scroll.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
type ScrollBehavior = 'auto' | 'smooth' | 'instant';
export function getScrollContainer(el: HTMLElement | null): HTMLElement | null {
@@ -25,7 +30,7 @@ export function getScrollPosition(el: HTMLElement | null): number {
export function onScrollTop(el: HTMLElement, cb: () => unknown, tolerance = 1, once = false) {
// とりあえず評価してみる
- if (isTopVisible(el)) {
+ if (el.isConnected && isTopVisible(el)) {
cb();
if (once) return null;
}
@@ -49,7 +54,7 @@ export function onScrollBottom(el: HTMLElement, cb: () => unknown, tolerance = 1
const container = getScrollContainer(el);
// とりあえず評価してみる
- if (isBottomVisible(el, tolerance, container)) {
+ if (el.isConnected && isBottomVisible(el, tolerance, container)) {
cb();
if (once) return null;
}
diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts
index 891da6eb78..53e2cd5b16 100644
--- a/packages/frontend/src/scripts/select-file.ts
+++ b/packages/frontend/src/scripts/select-file.ts
@@ -1,12 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { ref } from 'vue';
-import { DriveFile } from 'misskey-js/built/entities';
-import * as os from '@/os';
-import { useStream } from '@/stream';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
-import { uploadFile } from '@/scripts/upload';
+import * as Misskey from 'misskey-js';
+import * as os from '@/os.js';
+import { useStream } from '@/stream.js';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import { uploadFile } from '@/scripts/upload.js';
-export function chooseFileFromPc(multiple: boolean, keepOriginal = false): Promise<DriveFile[]> {
+export function chooseFileFromPc(multiple: boolean, keepOriginal = false): Promise<Misskey.entities.DriveFile[]> {
return new Promise((res, rej) => {
const input = document.createElement('input');
input.type = 'file';
@@ -33,7 +38,7 @@ export function chooseFileFromPc(multiple: boolean, keepOriginal = false): Promi
});
}
-export function chooseFileFromDrive(multiple: boolean): Promise<DriveFile[]> {
+export function chooseFileFromDrive(multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
return new Promise((res, rej) => {
os.selectDriveFile(multiple).then(files => {
res(files);
@@ -41,7 +46,7 @@ export function chooseFileFromDrive(multiple: boolean): Promise<DriveFile[]> {
});
}
-export function chooseFileFromUrl(): Promise<DriveFile> {
+export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> {
return new Promise((res, rej) => {
os.inputText({
title: i18n.ts.uploadFromUrl,
@@ -74,7 +79,7 @@ export function chooseFileFromUrl(): Promise<DriveFile> {
});
}
-function select(src: any, label: string | null, multiple: boolean): Promise<DriveFile[]> {
+function select(src: any, label: string | null, multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
return new Promise((res, rej) => {
const keepOriginal = ref(defaultStore.state.keepOriginalUploading);
@@ -101,10 +106,10 @@ function select(src: any, label: string | null, multiple: boolean): Promise<Driv
});
}
-export function selectFile(src: any, label: string | null = null): Promise<DriveFile> {
+export function selectFile(src: any, label: string | null = null): Promise<Misskey.entities.DriveFile> {
return select(src, label, false).then(files => files[0]);
}
-export function selectFiles(src: any, label: string | null = null): Promise<DriveFile[]> {
+export function selectFiles(src: any, label: string | null = null): Promise<Misskey.entities.DriveFile[]> {
return select(src, label, true);
}
diff --git a/packages/frontend/src/scripts/show-moved-dialog.ts b/packages/frontend/src/scripts/show-moved-dialog.ts
index acb26c36e2..b4defbfe7d 100644
--- a/packages/frontend/src/scripts/show-moved-dialog.ts
+++ b/packages/frontend/src/scripts/show-moved-dialog.ts
@@ -1,6 +1,11 @@
-import * as os from '@/os';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
export function showMovedDialog() {
if (!$i) return;
diff --git a/packages/frontend/src/scripts/show-suspended-dialog.ts b/packages/frontend/src/scripts/show-suspended-dialog.ts
index e11569ecd4..a2fd5db453 100644
--- a/packages/frontend/src/scripts/show-suspended-dialog.ts
+++ b/packages/frontend/src/scripts/show-suspended-dialog.ts
@@ -1,5 +1,10 @@
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
export function showSuspendedDialog() {
return os.alert({
diff --git a/packages/frontend/src/scripts/shuffle.ts b/packages/frontend/src/scripts/shuffle.ts
index 05e6cdfbcf..d9d5bb1037 100644
--- a/packages/frontend/src/scripts/shuffle.ts
+++ b/packages/frontend/src/scripts/shuffle.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
/**
* 配列をシャッフル (破壊的)
*/
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 68136cdcfe..1ef41b47d3 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -1,42 +1,47 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { markRaw } from 'vue';
-import { Storage } from '@/pizzax';
+import { Storage } from '@/pizzax.js';
export const soundConfigStore = markRaw(new Storage('sound', {
mediaVolume: {
where: 'device',
- default: 0.5
+ default: 0.5,
},
sound_masterVolume: {
where: 'device',
- default: 0.3
+ default: 0.3,
},
sound_note: {
where: 'account',
- default: { type: 'syuilo/n-aec', volume: 1 }
+ default: { type: 'syuilo/n-aec', volume: 1 },
},
sound_noteMy: {
where: 'account',
- default: { type: 'syuilo/n-cea-4va', volume: 1 }
+ default: { type: 'syuilo/n-cea-4va', volume: 1 },
},
sound_notification: {
where: 'account',
- default: { type: 'syuilo/n-ea', volume: 1 }
+ default: { type: 'syuilo/n-ea', volume: 1 },
},
sound_chat: {
where: 'account',
- default: { type: 'syuilo/pope1', volume: 1 }
+ default: { type: 'syuilo/pope1', volume: 1 },
},
sound_chatBg: {
where: 'account',
- default: { type: 'syuilo/waon', volume: 1 }
+ default: { type: 'syuilo/waon', volume: 1 },
},
sound_antenna: {
where: 'account',
- default: { type: 'syuilo/triple', volume: 1 }
+ default: { type: 'syuilo/triple', volume: 1 },
},
sound_channel: {
where: 'account',
- default: { type: 'syuilo/square-pico', volume: 1 }
+ default: { type: 'syuilo/square-pico', volume: 1 },
},
}));
diff --git a/packages/frontend/src/scripts/sticky-sidebar.ts b/packages/frontend/src/scripts/sticky-sidebar.ts
index e6e125c6e8..f233c3648e 100644
--- a/packages/frontend/src/scripts/sticky-sidebar.ts
+++ b/packages/frontend/src/scripts/sticky-sidebar.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export class StickySidebar {
private lastScrollTop = 0;
private container: HTMLElement;
diff --git a/packages/frontend/src/scripts/test-utils.ts b/packages/frontend/src/scripts/test-utils.ts
index 3e018f2d7e..1b42811faa 100644
--- a/packages/frontend/src/scripts/test-utils.ts
+++ b/packages/frontend/src/scripts/test-utils.ts
@@ -1,4 +1,7 @@
-/// <reference types="@testing-library/jest-dom"/>
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
export async function tick(): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
diff --git a/packages/frontend/src/scripts/theme-editor.ts b/packages/frontend/src/scripts/theme-editor.ts
index 001d87381c..275f4bcdaa 100644
--- a/packages/frontend/src/scripts/theme-editor.ts
+++ b/packages/frontend/src/scripts/theme-editor.ts
@@ -1,6 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { v4 as uuid } from 'uuid';
-import { themeProps, Theme } from './theme';
+import { themeProps, Theme } from './theme.js';
export type Default = null;
export type Color = string;
@@ -76,6 +81,6 @@ export const convertToViewModel = (theme: Theme): ThemeViewModel => {
.filter(k => k.startsWith('$'))
.map(k => [k, fromThemeString(theme.props[k])] as [ string, ThemeValue ]);
- vm.push(...consts);
+ vm.push(...consts);
return vm;
};
diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index bc61256cac..1c924e774f 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { ref } from 'vue';
import tinycolor from 'tinycolor2';
import { globalEvents } from '@/events';
@@ -14,7 +19,7 @@ export type Theme = {
import lightTheme from '@/themes/_light.json5';
import darkTheme from '@/themes/_dark.json5';
import { deepClone } from './clone';
-import { miLocalStorage } from '@/local-storage';
+import { miLocalStorage } from '@/local-storage.js';
export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
diff --git a/packages/frontend/src/scripts/time.ts b/packages/frontend/src/scripts/time.ts
index b21978b186..4479db1081 100644
--- a/packages/frontend/src/scripts/time.ts
+++ b/packages/frontend/src/scripts/time.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
const dateTimeIntervals = {
'day': 86400000,
'hour': 3600000,
diff --git a/packages/frontend/src/scripts/timezones.ts b/packages/frontend/src/scripts/timezones.ts
index 8ce07323f6..55f9be393f 100644
--- a/packages/frontend/src/scripts/timezones.ts
+++ b/packages/frontend/src/scripts/timezones.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
export const timezones = [{
name: 'UTC',
abbrev: 'UTC',
diff --git a/packages/frontend/src/scripts/touch.ts b/packages/frontend/src/scripts/touch.ts
index 4afd0e5dd4..05f379e4aa 100644
--- a/packages/frontend/src/scripts/touch.ts
+++ b/packages/frontend/src/scripts/touch.ts
@@ -1,4 +1,9 @@
-import { deviceKind } from '@/scripts/device-kind';
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { deviceKind } from '@/scripts/device-kind.js';
const isTouchSupported = 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
diff --git a/packages/frontend/src/scripts/unison-reload.ts b/packages/frontend/src/scripts/unison-reload.ts
index 59af584c1b..65fc090888 100644
--- a/packages/frontend/src/scripts/unison-reload.ts
+++ b/packages/frontend/src/scripts/unison-reload.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
// SafariがBroadcastChannel未実装なのでライブラリを使う
import { BroadcastChannel } from 'broadcast-channel';
diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts
index 2dd11c9fa2..b896376ec8 100644
--- a/packages/frontend/src/scripts/upload.ts
+++ b/packages/frontend/src/scripts/upload.ts
@@ -1,12 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { reactive, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { readAndCompressImage } from 'browser-image-resizer';
-import { getCompressionConfig } from './upload/compress-config';
-import { defaultStore } from '@/store';
-import { apiUrl } from '@/config';
-import { $i } from '@/account';
-import { alert } from '@/os';
-import { i18n } from '@/i18n';
+import { getCompressionConfig } from './upload/compress-config.js';
+import { defaultStore } from '@/store.js';
+import { apiUrl } from '@/config.js';
+import { $i } from '@/account.js';
+import { alert } from '@/os.js';
+import { i18n } from '@/i18n.js';
type Uploading = {
id: string;
diff --git a/packages/frontend/src/scripts/upload/compress-config.ts b/packages/frontend/src/scripts/upload/compress-config.ts
index 55d469c5e4..8fe64c8b76 100644
--- a/packages/frontend/src/scripts/upload/compress-config.ts
+++ b/packages/frontend/src/scripts/upload/compress-config.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import isAnimated from 'is-file-animated';
import { isWebpSupported } from './isWebpSupported';
import type { BrowserImageResizerConfig } from 'browser-image-resizer';
diff --git a/packages/frontend/src/scripts/upload/isWebpSupported.ts b/packages/frontend/src/scripts/upload/isWebpSupported.ts
index cde8b9d785..185c3e6b40 100644
--- a/packages/frontend/src/scripts/upload/isWebpSupported.ts
+++ b/packages/frontend/src/scripts/upload/isWebpSupported.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
let isWebpSupportedCache: boolean | undefined;
export function isWebpSupported() {
if (isWebpSupportedCache === undefined) {
diff --git a/packages/frontend/src/scripts/url.ts b/packages/frontend/src/scripts/url.ts
index 07737d6228..625f4ce057 100644
--- a/packages/frontend/src/scripts/url.ts
+++ b/packages/frontend/src/scripts/url.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
/* objを検査して
* 1. 配列に何も入っていない時はクエリを付けない
* 2. プロパティがundefinedの時はクエリを付けない
diff --git a/packages/frontend/src/scripts/use-chart-tooltip.ts b/packages/frontend/src/scripts/use-chart-tooltip.ts
index 6f40fd4a30..daf915c7e3 100644
--- a/packages/frontend/src/scripts/use-chart-tooltip.ts
+++ b/packages/frontend/src/scripts/use-chart-tooltip.ts
@@ -1,5 +1,10 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { onUnmounted, onDeactivated, ref } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
import MkChartTooltip from '@/components/MkChartTooltip.vue';
export function useChartTooltip(opts: { position: 'top' | 'middle' } = { position: 'top' }) {
diff --git a/packages/frontend/src/scripts/use-document-visibility.ts b/packages/frontend/src/scripts/use-document-visibility.ts
index 47e91dd937..a9e2512eb3 100644
--- a/packages/frontend/src/scripts/use-document-visibility.ts
+++ b/packages/frontend/src/scripts/use-document-visibility.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { onMounted, onUnmounted, ref, Ref } from 'vue';
export function useDocumentVisibility(): Ref<DocumentVisibilityState> {
diff --git a/packages/frontend/src/scripts/use-interval.ts b/packages/frontend/src/scripts/use-interval.ts
index 601dea6724..b8c5431fb6 100644
--- a/packages/frontend/src/scripts/use-interval.ts
+++ b/packages/frontend/src/scripts/use-interval.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { onMounted, onUnmounted } from 'vue';
export function useInterval(fn: () => void, interval: number, options: {
diff --git a/packages/frontend/src/scripts/use-leave-guard.ts b/packages/frontend/src/scripts/use-leave-guard.ts
index 146b012471..c9750c3923 100644
--- a/packages/frontend/src/scripts/use-leave-guard.ts
+++ b/packages/frontend/src/scripts/use-leave-guard.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { Ref } from 'vue';
export function useLeaveGuard(enabled: Ref<boolean>) {
diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts
index d057386b13..c618532570 100644
--- a/packages/frontend/src/scripts/use-note-capture.ts
+++ b/packages/frontend/src/scripts/use-note-capture.ts
@@ -1,11 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { onUnmounted, Ref } from 'vue';
-import * as misskey from 'misskey-js';
-import { useStream } from '@/stream';
-import { $i } from '@/account';
+import * as Misskey from 'misskey-js';
+import { useStream } from '@/stream.js';
+import { $i } from '@/account.js';
export function useNoteCapture(props: {
rootEl: Ref<HTMLElement>;
- note: Ref<misskey.entities.Note>;
+ note: Ref<Misskey.entities.Note>;
isDeletedRef: Ref<boolean>;
}) {
const note = props.note;
diff --git a/packages/frontend/src/scripts/use-tooltip.ts b/packages/frontend/src/scripts/use-tooltip.ts
index 1f6e0fb6ce..0a82997728 100644
--- a/packages/frontend/src/scripts/use-tooltip.ts
+++ b/packages/frontend/src/scripts/use-tooltip.ts
@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
import { Ref, ref, watch, onUnmounted } from 'vue';
export function useTooltip(
diff --git a/packages/frontend/src/scripts/worker-multi-dispatch.ts b/packages/frontend/src/scripts/worker-multi-dispatch.ts
index 1847a8ccff..1d184e99a1 100644
--- a/packages/frontend/src/scripts/worker-multi-dispatch.ts
+++ b/packages/frontend/src/scripts/worker-multi-dispatch.ts
@@ -1,75 +1,80 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
function defaultUseWorkerNumber(prev: number, totalWorkers: number) {
- return prev + 1;
+ return prev + 1;
}
export class WorkerMultiDispatch<POST = any, RETURN = any> {
- private symbol = Symbol('WorkerMultiDispatch');
- private workers: Worker[] = [];
- private terminated = false;
- private prevWorkerNumber = 0;
- private getUseWorkerNumber = defaultUseWorkerNumber;
- private finalizationRegistry: FinalizationRegistry<symbol>;
+ private symbol = Symbol('WorkerMultiDispatch');
+ private workers: Worker[] = [];
+ private terminated = false;
+ private prevWorkerNumber = 0;
+ private getUseWorkerNumber = defaultUseWorkerNumber;
+ private finalizationRegistry: FinalizationRegistry<symbol>;
- constructor(workerConstructor: () => Worker, concurrency: number, getUseWorkerNumber = defaultUseWorkerNumber) {
- this.getUseWorkerNumber = getUseWorkerNumber;
- for (let i = 0; i < concurrency; i++) {
- this.workers.push(workerConstructor());
- }
+ constructor(workerConstructor: () => Worker, concurrency: number, getUseWorkerNumber = defaultUseWorkerNumber) {
+ this.getUseWorkerNumber = getUseWorkerNumber;
+ for (let i = 0; i < concurrency; i++) {
+ this.workers.push(workerConstructor());
+ }
- this.finalizationRegistry = new FinalizationRegistry(() => {
- this.terminate();
- });
- this.finalizationRegistry.register(this, this.symbol);
+ this.finalizationRegistry = new FinalizationRegistry(() => {
+ this.terminate();
+ });
+ this.finalizationRegistry.register(this, this.symbol);
- if (_DEV_) console.log('WorkerMultiDispatch: Created', this);
- }
+ if (_DEV_) console.log('WorkerMultiDispatch: Created', this);
+ }
- public postMessage(message: POST, options?: Transferable[] | StructuredSerializeOptions, useWorkerNumber: typeof defaultUseWorkerNumber = this.getUseWorkerNumber) {
- let workerNumber = useWorkerNumber(this.prevWorkerNumber, this.workers.length);
- workerNumber = Math.abs(Math.round(workerNumber)) % this.workers.length;
- if (_DEV_) console.log('WorkerMultiDispatch: Posting message to worker', workerNumber, useWorkerNumber);
- this.prevWorkerNumber = workerNumber;
+ public postMessage(message: POST, options?: Transferable[] | StructuredSerializeOptions, useWorkerNumber: typeof defaultUseWorkerNumber = this.getUseWorkerNumber) {
+ let workerNumber = useWorkerNumber(this.prevWorkerNumber, this.workers.length);
+ workerNumber = Math.abs(Math.round(workerNumber)) % this.workers.length;
+ if (_DEV_) console.log('WorkerMultiDispatch: Posting message to worker', workerNumber, useWorkerNumber);
+ this.prevWorkerNumber = workerNumber;
- // 不毛だがunionをoverloadに突っ込めない
- // https://stackoverflow.com/questions/66507585/overload-signatures-union-types-and-no-overload-matches-this-call-error
- // https://github.com/microsoft/TypeScript/issues/14107
- if (Array.isArray(options)) {
- this.workers[workerNumber].postMessage(message, options);
- } else {
- this.workers[workerNumber].postMessage(message, options);
- }
- return workerNumber;
- }
+ // 不毛だがunionをoverloadに突っ込めない
+ // https://stackoverflow.com/questions/66507585/overload-signatures-union-types-and-no-overload-matches-this-call-error
+ // https://github.com/microsoft/TypeScript/issues/14107
+ if (Array.isArray(options)) {
+ this.workers[workerNumber].postMessage(message, options);
+ } else {
+ this.workers[workerNumber].postMessage(message, options);
+ }
+ return workerNumber;
+ }
- public addListener(callback: (this: Worker, ev: MessageEvent<RETURN>) => any, options?: boolean | AddEventListenerOptions) {
- this.workers.forEach(worker => {
- worker.addEventListener('message', callback, options);
- });
- }
+ public addListener(callback: (this: Worker, ev: MessageEvent<RETURN>) => any, options?: boolean | AddEventListenerOptions) {
+ this.workers.forEach(worker => {
+ worker.addEventListener('message', callback, options);
+ });
+ }
- public removeListener(callback: (this: Worker, ev: MessageEvent<RETURN>) => any, options?: boolean | AddEventListenerOptions) {
- this.workers.forEach(worker => {
- worker.removeEventListener('message', callback, options);
- });
- }
+ public removeListener(callback: (this: Worker, ev: MessageEvent<RETURN>) => any, options?: boolean | AddEventListenerOptions) {
+ this.workers.forEach(worker => {
+ worker.removeEventListener('message', callback, options);
+ });
+ }
- public terminate() {
- this.terminated = true;
- if (_DEV_) console.log('WorkerMultiDispatch: Terminating', this);
- this.workers.forEach(worker => {
- worker.terminate();
- });
- this.workers = [];
- this.finalizationRegistry.unregister(this);
- }
+ public terminate() {
+ this.terminated = true;
+ if (_DEV_) console.log('WorkerMultiDispatch: Terminating', this);
+ this.workers.forEach(worker => {
+ worker.terminate();
+ });
+ this.workers = [];
+ this.finalizationRegistry.unregister(this);
+ }
- public isTerminated() {
- return this.terminated;
- }
- public getWorkers() {
- return this.workers;
- }
- public getSymbol() {
- return this.symbol;
- }
+ public isTerminated() {
+ return this.terminated;
+ }
+ public getWorkers() {
+ return this.workers;
+ }
+ public getSymbol() {
+ return this.symbol;
+ }
}