diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2023-09-24 18:21:31 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-24 18:21:31 +0900 |
| commit | f32915b515f4cbc3b1a877cfb8e8e35bf6a31efa (patch) | |
| tree | 0f6f098cbb282e4b6619152b14b9e6f57e6b448f /packages/frontend/src/scripts | |
| parent | Merge pull request #11384 from misskey-dev/develop (diff) | |
| parent | 2023.9.0 (diff) | |
| download | misskey-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')
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; + } } |