summaryrefslogtreecommitdiff
path: root/packages/sw/src/scripts
diff options
context:
space:
mode:
authorokayurisotto <aytkzm@gmail.com>2023-04-12 01:07:24 +0900
committerGitHub <noreply@github.com>2023-04-12 01:07:24 +0900
commit5002effd656abc8c82dbc1bdd904614b8157d8c1 (patch)
tree9ed645f690b6a6fc1fc0c40e9a76ed547c92c3de /packages/sw/src/scripts
parentfix(client): noPaging: true with gallery/featured (diff)
downloadsharkey-5002effd656abc8c82dbc1bdd904614b8157d8c1.tar.gz
sharkey-5002effd656abc8c82dbc1bdd904614b8157d8c1.tar.bz2
sharkey-5002effd656abc8c82dbc1bdd904614b8157d8c1.zip
Refactor sw (#10579)
* refactor(sw): remove dead code * refactor(sw): remove dead code * refactor(sw): remove dead code * refactor(sw): remove dead code * refactor(sw): remove dead code * refactor(sw): remove dead code * refactor(sw): 冗長な部分を変更 * refactor(sw): 使われていない煩雑な機能を削除 * refactor(sw): remove dead code * refactor(sw): URL文字列の作成に`URL`を使うように * refactor(sw): 型アサーションの削除とそれに伴い露呈したエラーへの対処 * refactor(sw): `append` -> `set` in `URLSearchParams` * refactor(sw): `any`の削除とそれに伴い露呈したエラーへの対処 * refactor(sw): 型アサーションの削除とそれに伴い露呈したエラーへの対処 対処と言っても`throw`するだけ。いままでもこの状況ではエラーが投げられていたはずなので、この対処により新たな問題が起きることはないはず。 * refactor(sw): i18n loading * refactor(sw): 型推論がうまくできる書き方に変更 `codes`が`(string | undefined)[]`から`string[]`になった * refactor(sw): クエリ文字列の作成に`URLSearchParams`を使うように * refactor(sw): `findClient` * refactor(sw): `openClient`における`any`や`as`の書き換え * refactor(sw): `openPost`における`any`の書き換え * refactor(sw): `let` -> `const` * refactor(sw): `any` -> `unknown` * cleanup(sw): import * cleanup(sw) * cleanup(sw): `?.` * cleanup(sw/.eslintrc.js) * refactor(sw): `@typescript-eslint/explicit-function-return-type` * refactor(sw): `@typescript-eslint/no-unused-vars` * refactor(sw): どうしようもないところに`eslint-disable-next-line`を * refactor(sw): `import/no-default-export` * update operations.ts * throw new Error --------- Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Diffstat (limited to 'packages/sw/src/scripts')
-rw-r--r--packages/sw/src/scripts/create-notification.ts32
-rw-r--r--packages/sw/src/scripts/get-account-from-id.ts9
-rw-r--r--packages/sw/src/scripts/get-user-name.ts2
-rw-r--r--packages/sw/src/scripts/i18n.ts7
-rw-r--r--packages/sw/src/scripts/lang.ts18
-rw-r--r--packages/sw/src/scripts/login-id.ts10
-rw-r--r--packages/sw/src/scripts/operations.ts49
-rw-r--r--packages/sw/src/scripts/twemoji-base.ts12
-rw-r--r--packages/sw/src/scripts/url.ts18
9 files changed, 66 insertions, 91 deletions
diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts
index 4d27858253..61ffa7ab0f 100644
--- a/packages/sw/src/scripts/create-notification.ts
+++ b/packages/sw/src/scripts/create-notification.ts
@@ -1,22 +1,20 @@
/*
* Notification manager for SW
*/
-import { swLang } from '@/scripts/lang';
+import type { BadgeNames, PushNotificationDataMap } from '@/types';
+import { char2fileName } from '@/scripts/twemoji-base';
import { cli } from '@/scripts/operations';
-import { BadgeNames, PushNotificationDataMap } from '@/types';
-import getUserName from '@/scripts/get-user-name';
-import { I18n } from '@/scripts/i18n';
import { getAccountFromId } from '@/scripts/get-account-from-id';
-import { char2fileName } from '@/scripts/twemoji-base';
-import * as url from '@/scripts/url';
+import { swLang } from '@/scripts/lang';
+import { getUserName } from '@/scripts/get-user-name';
-const closeNotificationsByTags = async (tags: string[]) => {
+const closeNotificationsByTags = async (tags: string[]): Promise<void> => {
for (const n of (await Promise.all(tags.map(tag => globalThis.registration.getNotifications({ tag })))).flat()) {
n.close();
}
};
-const iconUrl = (name: BadgeNames) => `/static-assets/tabler-badges/${name}.png`;
+const iconUrl = (name: BadgeNames): string => `/static-assets/tabler-badges/${name}.png`;
/* How to add a new badge:
* 1. Find the icon and download png from https://tabler-icons.io/
* 2. vips resize ~/Downloads/icon-name.png vipswork.png 0.4; vips scRGB2BW vipswork.png ~/icon-name.png"[compression=9,strip]"; rm vipswork.png;
@@ -25,7 +23,7 @@ const iconUrl = (name: BadgeNames) => `/static-assets/tabler-badges/${name}.png`
* 5. Add `badge: iconUrl('icon-name'),`
*/
-export async function createNotification<K extends keyof PushNotificationDataMap>(data: PushNotificationDataMap[K]) {
+export async function createNotification<K extends keyof PushNotificationDataMap>(data: PushNotificationDataMap[K]): Promise<void> {
const n = await composeNotification(data);
if (n) {
@@ -37,8 +35,7 @@ export async function createNotification<K extends keyof PushNotificationDataMap
}
async function composeNotification(data: PushNotificationDataMap[keyof PushNotificationDataMap]): Promise<[string, NotificationOptions] | null> {
- if (!swLang.i18n) swLang.fetchLocale();
- const i18n = await swLang.i18n as I18n<any>;
+ const i18n = await (swLang.i18n ?? swLang.fetchLocale());
const { t } = i18n;
switch (data.type) {
/*
@@ -139,16 +136,16 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif
if (reaction.startsWith(':')) {
// カスタム絵文字の場合
const name = reaction.substring(1, reaction.length - 1);
- badge = `${origin}/emoji/${name}.webp?${url.query({
- badge: '1',
- })}`;
+ const badgeUrl = new URL(`/emoji/${name}.webp`, origin);
+ badgeUrl.searchParams.set('badge', '1');
+ badge = badgeUrl.href;
reaction = name.split('@')[0];
} else {
// Unicode絵文字の場合
badge = `/twemoji-badge/${char2fileName(reaction)}.png`;
}
- if (badge ? await fetch(badge).then(res => res.status !== 200).catch(() => true) : true) {
+ if (await fetch(badge).then(res => res.status !== 200).catch(() => true)) {
badge = iconUrl('plus');
}
@@ -226,10 +223,9 @@ async function composeNotification(data: PushNotificationDataMap[keyof PushNotif
}
}
-export async function createEmptyNotification() {
+export async function createEmptyNotification(): Promise<void> {
return new Promise<void>(async res => {
- if (!swLang.i18n) swLang.fetchLocale();
- const i18n = await swLang.i18n as I18n<any>;
+ const i18n = await (swLang.i18n ?? swLang.fetchLocale());
const { t } = i18n;
await globalThis.registration.showNotification(
diff --git a/packages/sw/src/scripts/get-account-from-id.ts b/packages/sw/src/scripts/get-account-from-id.ts
index be4cfaeba4..825829009f 100644
--- a/packages/sw/src/scripts/get-account-from-id.ts
+++ b/packages/sw/src/scripts/get-account-from-id.ts
@@ -1,7 +1,10 @@
import { get } from 'idb-keyval';
-export async function getAccountFromId(id: string) {
- const accounts = await get('accounts') as { token: string; id: string; }[];
- if (!accounts) console.log('Accounts are not recorded');
+export async function getAccountFromId(id: string): Promise<{ token: string; id: string } | void> {
+ const accounts = await get<{ token: string; id: string }[]>('accounts');
+ if (!accounts) {
+ console.log('Accounts are not recorded');
+ return;
+ }
return accounts.find(e => e.id === id);
}
diff --git a/packages/sw/src/scripts/get-user-name.ts b/packages/sw/src/scripts/get-user-name.ts
index 4daf203e06..e7e75ee25f 100644
--- a/packages/sw/src/scripts/get-user-name.ts
+++ b/packages/sw/src/scripts/get-user-name.ts
@@ -1,3 +1,3 @@
-export default function(user: { name?: string | null, username: string }): string {
+export function getUserName(user: { name?: string | null; username: string }): string {
return user.name === '' ? user.username : user.name ?? user.username;
}
diff --git a/packages/sw/src/scripts/i18n.ts b/packages/sw/src/scripts/i18n.ts
index 3fe88e5514..33e8e4267b 100644
--- a/packages/sw/src/scripts/i18n.ts
+++ b/packages/sw/src/scripts/i18n.ts
@@ -1,4 +1,6 @@
-export class I18n<T extends Record<string, any>> {
+export type Locale = { [key: string]: string | Locale };
+
+export class I18n<T extends Locale = Locale> {
public ts: T;
constructor(locale: T) {
@@ -13,7 +15,8 @@ export class I18n<T extends Record<string, any>> {
// なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも
public t(key: string, args?: Record<string, string>): string {
try {
- let str = key.split('.').reduce((o, i) => o[i], this.ts) as unknown as string;
+ let str = key.split('.').reduce<Locale | Locale[keyof Locale]>((o, i) => o[i], this.ts);
+ if (typeof str !== 'string') throw new Error();
if (args) {
for (const [k, v] of Object.entries(args)) {
diff --git a/packages/sw/src/scripts/lang.ts b/packages/sw/src/scripts/lang.ts
index 39bd333aba..063059908e 100644
--- a/packages/sw/src/scripts/lang.ts
+++ b/packages/sw/src/scripts/lang.ts
@@ -2,7 +2,7 @@
* Language manager for SW
*/
import { get, set } from 'idb-keyval';
-import { I18n } from '@/scripts/i18n';
+import { I18n, type Locale } from '@/scripts/i18n';
class SwLang {
public cacheName = `mk-cache-${_VERSION_}`;
@@ -12,19 +12,19 @@ class SwLang {
return prelang;
});
- public setLang(newLang: string) {
+ public setLang(newLang: string): Promise<I18n<Locale>> {
this.lang = Promise.resolve(newLang);
set('lang', newLang);
return this.fetchLocale();
}
- public i18n: Promise<I18n<any>> | null = null;
+ public i18n: Promise<I18n> | null = null;
- public fetchLocale() {
- return this.i18n = this._fetch();
+ public fetchLocale(): Promise<I18n<Locale>> {
+ return (this.i18n = this._fetch());
}
- private async _fetch() {
+ private async _fetch(): Promise<I18n<Locale>> {
// Service Workerは何度も起動しそのたびにlocaleを読み込むので、CacheStorageを使う
const localeUrl = `/assets/locales/${await this.lang}.${_VERSION_}.json`;
let localeRes = await caches.match(localeUrl);
@@ -32,13 +32,13 @@ class SwLang {
// _DEV_がtrueの場合は常に最新化
if (!localeRes || _DEV_) {
localeRes = await fetch(localeUrl);
- const clone = localeRes?.clone();
- if (!clone?.clone().ok) Error('locale fetching error');
+ const clone = localeRes.clone();
+ if (!clone.clone().ok) throw new Error('locale fetching error');
caches.open(this.cacheName).then(cache => cache.put(localeUrl, clone));
}
- return new I18n(await localeRes.json());
+ return new I18n<Locale>(await localeRes.json());
}
}
diff --git a/packages/sw/src/scripts/login-id.ts b/packages/sw/src/scripts/login-id.ts
index 0f9c6be4a9..ce04afd601 100644
--- a/packages/sw/src/scripts/login-id.ts
+++ b/packages/sw/src/scripts/login-id.ts
@@ -1,11 +1,5 @@
-export function getUrlWithLoginId(url: string, loginId: string) {
+export function getUrlWithLoginId(url: string, loginId: string): string {
const u = new URL(url, origin);
- u.searchParams.append('loginId', loginId);
- return u.toString();
-}
-
-export function getUrlWithoutLoginId(url: string) {
- const u = new URL(url);
- u.searchParams.delete('loginId');
+ u.searchParams.set('loginId', loginId);
return u.toString();
}
diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts
index 2fd02f9dcb..428f243ffe 100644
--- a/packages/sw/src/scripts/operations.ts
+++ b/packages/sw/src/scripts/operations.ts
@@ -3,17 +3,21 @@
* 各種操作
*/
import * as Misskey from 'misskey-js';
-import { SwMessage, SwMessageOrderType } from '@/types';
+import type { SwMessage, SwMessageOrderType } from '@/types';
import { getAccountFromId } from '@/scripts/get-account-from-id';
import { getUrlWithLoginId } from '@/scripts/login-id';
-export const cli = new Misskey.api.APIClient({ origin, fetch: (...args) => fetch(...args) });
+export const cli = new Misskey.api.APIClient({ origin, fetch: (...args): Promise<Response> => fetch(...args) });
-export async function api<E extends keyof Misskey.Endpoints>(endpoint: E, userId: string, options?: Misskey.Endpoints[E]['req']) {
- const account = await getAccountFromId(userId);
- if (!account) return;
+export async function api<E extends keyof Misskey.Endpoints, O extends Misskey.Endpoints[E]['req']>(endpoint: E, userId?: string, options?: O): Promise<void | ReturnType<typeof cli.request<E, O>>> {
+ let account: { token: string; id: string } | void;
- return cli.request(endpoint, options, account.token);
+ if (userId) {
+ account = await getAccountFromId(userId);
+ if (!account) return;
+ }
+
+ return cli.request(endpoint, options, account?.token);
}
// mark-all-as-read送出を1秒間隔に制限する
@@ -24,55 +28,52 @@ export function sendMarkAllAsRead(userId: string): Promise<null | undefined | vo
return new Promise(resolve => {
setTimeout(() => {
readBlockingStatus.set(userId, false);
- api('notifications/mark-all-as-read', userId)
- .then(resolve, resolve);
+ api('notifications/mark-all-as-read', userId).then(resolve, resolve);
}, 1000);
});
}
// rendered acctからユーザーを開く
-export function openUser(acct: string, loginId?: string) {
+export function openUser(acct: string, loginId?: string): ReturnType<typeof openClient> {
return openClient('push', `/@${acct}`, loginId, { acct });
}
// noteIdからノートを開く
-export function openNote(noteId: string, loginId?: string) {
+export function openNote(noteId: string, loginId?: string): ReturnType<typeof openClient> {
return openClient('push', `/notes/${noteId}`, loginId, { noteId });
}
// noteIdからノートを開く
-export function openAntenna(antennaId: string, loginId: string) {
+export function openAntenna(antennaId: string, loginId: string): ReturnType<typeof openClient> {
return openClient('push', `/timeline/antenna/${antennaId}`, loginId, { antennaId });
}
// post-formのオプションから投稿フォームを開く
-export async function openPost(options: any, loginId?: string) {
+export async function openPost(options: { initialText?: string; reply?: Misskey.entities.Note; renote?: Misskey.entities.Note }, loginId?: string): ReturnType<typeof openClient> {
// クエリを作成しておく
- let url = '/share?';
- if (options.initialText) url += `text=${options.initialText}&`;
- if (options.reply) url += `replyId=${options.reply.id}&`;
- if (options.renote) url += `renoteId=${options.renote.id}&`;
+ const url = '/share';
+ const query = new URLSearchParams();
+ if (options.initialText) query.set('text', options.initialText);
+ if (options.reply) query.set('replyId', options.reply.id);
+ if (options.renote) query.set('renoteId', options.renote.id);
- return openClient('post', url, loginId, { options });
+ return openClient('post', `${url}?${query}`, loginId, { options });
}
-export async function openClient(order: SwMessageOrderType, url: string, loginId?: string, query: any = {}) {
+export async function openClient(order: SwMessageOrderType, url: string, loginId?: string, query: Record<string, SwMessage[string]> = {}): Promise<WindowClient | null> {
const client = await findClient();
if (client) {
- client.postMessage({ type: 'order', ...query, order, loginId, url } as SwMessage);
+ client.postMessage({ type: 'order', ...query, order, loginId, url } satisfies SwMessage);
return client;
}
return globalThis.clients.openWindow(loginId ? getUrlWithLoginId(url, loginId) : url);
}
-export async function findClient() {
+export async function findClient(): Promise<WindowClient | null> {
const clients = await globalThis.clients.matchAll({
type: 'window',
});
- for (const c of clients) {
- if (!(new URL(c.url)).searchParams.has('zen')) return c;
- }
- return null;
+ return clients.find(c => !(new URL(c.url)).searchParams.has('zen')) ?? null;
}
diff --git a/packages/sw/src/scripts/twemoji-base.ts b/packages/sw/src/scripts/twemoji-base.ts
index 638aae3284..4b6a5353ea 100644
--- a/packages/sw/src/scripts/twemoji-base.ts
+++ b/packages/sw/src/scripts/twemoji-base.ts
@@ -1,12 +1,8 @@
-export const twemojiSvgBase = '/twemoji';
-
export function char2fileName(char: string): string {
- let codes = Array.from(char).map(x => x.codePointAt(0)?.toString(16));
+ let codes = Array.from(char)
+ .map(x => x.codePointAt(0)?.toString(16))
+ .filter(<T>(x: T | undefined): x is T => x !== undefined);
if (!codes.includes('200d')) codes = codes.filter(x => x !== 'fe0f');
- codes = codes.filter(x => x && x.length);
+ codes = codes.filter(x => x.length !== 0);
return codes.join('-');
}
-
-export function char2filePath(char: string): string {
- return `${twemojiSvgBase}/${char2fileName(char)}.svg`;
-}
diff --git a/packages/sw/src/scripts/url.ts b/packages/sw/src/scripts/url.ts
deleted file mode 100644
index 5255076156..0000000000
--- a/packages/sw/src/scripts/url.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/* objを検査して
- * 1. 配列に何も入っていない時はクエリを付けない
- * 2. プロパティがundefinedの時はクエリを付けない
- * (new URLSearchParams(obj)ではそこまで丁寧なことをしてくれない)
- */
-export function query(obj: object): string {
- const params = Object.entries(obj)
- .filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined)
- .reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>);
-
- return Object.entries(params)
- .map((e) => `${e[0]}=${encodeURIComponent(e[1])}`)
- .join('&');
-}
-
-export function appendQuery(url: string, query: string): string {
- return `${url}${/\?/.test(url) ? url.endsWith('?') ? '' : '&' : '?'}${query}`;
-}