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>
This commit is contained in:
		| @@ -1,22 +1,20 @@ | ||||
| module.exports = { | ||||
| 	root: true, | ||||
| 	env: { | ||||
| 		"node": false | ||||
| 		node: false, | ||||
| 	}, | ||||
| 	parserOptions: { | ||||
| 		"parser": "@typescript-eslint/parser", | ||||
| 		parser: '@typescript-eslint/parser', | ||||
| 		tsconfigRootDir: __dirname, | ||||
| 		project: ['./tsconfig.json'], | ||||
| 	}, | ||||
| 	extends: [ | ||||
| 		"../shared/.eslintrc.js", | ||||
| 	], | ||||
| 	extends: ['../shared/.eslintrc.js'], | ||||
| 	globals: { | ||||
| 		"require": false, | ||||
| 		"_DEV_": false, | ||||
| 		"_LANGS_": false, | ||||
| 		"_VERSION_": false, | ||||
| 		"_ENV_": false, | ||||
| 		"_PERF_PREFIX_": false, | ||||
| 	} | ||||
| } | ||||
| 		require: false, | ||||
| 		_DEV_: false, | ||||
| 		_LANGS_: false, | ||||
| 		_VERSION_: false, | ||||
| 		_ENV_: false, | ||||
| 		_PERF_PREFIX_: false, | ||||
| 	}, | ||||
| }; | ||||
|   | ||||
							
								
								
									
										1
									
								
								packages/sw/src/@types/global.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								packages/sw/src/@types/global.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,4 @@ | ||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||
| type FIXME = any; | ||||
|  | ||||
| declare const _LANGS_: string[][]; | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| import * as misskey from 'misskey-js'; | ||||
| import * as Acct from 'misskey-js/built/acct'; | ||||
|  | ||||
| export const acct = (user: misskey.Acct) => { | ||||
| 	return Acct.toString(user); | ||||
| }; | ||||
|  | ||||
| export const userName = (user: misskey.entities.User) => { | ||||
| 	return user.name || user.username; | ||||
| }; | ||||
|  | ||||
| export const userPage = (user: misskey.Acct, path?, absolute = false) => { | ||||
| 	return `${absolute ? origin : ''}/@${acct(user)}${(path ? `/${path}` : '')}`; | ||||
| }; | ||||
| @@ -1,22 +1,20 @@ | ||||
| /* | ||||
|  * Notification manager for SW | ||||
|  */ | ||||
| import { swLang } from '@/scripts/lang'; | ||||
| 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 type { BadgeNames, PushNotificationDataMap } from '@/types'; | ||||
| import { char2fileName } from '@/scripts/twemoji-base'; | ||||
| import * as url from '@/scripts/url'; | ||||
| import { cli } from '@/scripts/operations'; | ||||
| import { getAccountFromId } from '@/scripts/get-account-from-id'; | ||||
| 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( | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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)) { | ||||
|   | ||||
| @@ -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()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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(); | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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`; | ||||
| } | ||||
|   | ||||
| @@ -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}`; | ||||
| } | ||||
| @@ -1,12 +1,12 @@ | ||||
| import { get } from 'idb-keyval'; | ||||
| import * as Acct from 'misskey-js/built/acct'; | ||||
| import type { PushNotificationDataMap } from '@/types'; | ||||
| import { createEmptyNotification, createNotification } from '@/scripts/create-notification'; | ||||
| import { swLang } from '@/scripts/lang'; | ||||
| import { PushNotificationDataMap } from '@/types'; | ||||
| import * as swos from '@/scripts/operations'; | ||||
| import { acct as getAcct } from '@/filters/user'; | ||||
| import { get } from 'idb-keyval'; | ||||
|  | ||||
| globalThis.addEventListener('install', ev => { | ||||
| 	//ev.waitUntil(globalThis.skipWaiting()); | ||||
| globalThis.addEventListener('install', () => { | ||||
| 	// ev.waitUntil(globalThis.skipWaiting()); | ||||
| }); | ||||
|  | ||||
| globalThis.addEventListener('activate', ev => { | ||||
| @@ -43,7 +43,7 @@ globalThis.addEventListener('push', ev => { | ||||
| 	ev.waitUntil(globalThis.clients.matchAll({ | ||||
| 		includeUncontrolled: true, | ||||
| 		type: 'window', | ||||
| 	}).then(async (clients: readonly WindowClient[]) => { | ||||
| 	}).then(async () => { | ||||
| 		const data: PushNotificationDataMap[keyof PushNotificationDataMap] = ev.data?.json(); | ||||
|  | ||||
| 		switch (data.type) { | ||||
| @@ -66,7 +66,7 @@ globalThis.addEventListener('push', ev => { | ||||
| }); | ||||
|  | ||||
| globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEventMap['notificationclick']) => { | ||||
| 	ev.waitUntil((async () => { | ||||
| 	ev.waitUntil((async (): Promise<void> => { | ||||
| 		if (_DEV_) { | ||||
| 			console.log('notificationclick', ev.action, ev.notification.data); | ||||
| 		} | ||||
| @@ -83,7 +83,7 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv | ||||
| 						if ('userId' in data.body) await swos.api('following/create', loginId, { userId: data.body.userId }); | ||||
| 						break; | ||||
| 					case 'showUser': | ||||
| 						if ('user' in data.body) client = await swos.openUser(getAcct(data.body.user), loginId); | ||||
| 						if ('user' in data.body) client = await swos.openUser(Acct.toString(data.body.user), loginId); | ||||
| 						break; | ||||
| 					case 'reply': | ||||
| 						if ('note' in data.body) client = await swos.openPost({ reply: data.body.note }, loginId); | ||||
| @@ -120,7 +120,7 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv | ||||
| 								if ('note' in data.body) { | ||||
| 									client = await swos.openNote(data.body.note.id, loginId); | ||||
| 								} else if ('user' in data.body) { | ||||
| 									client = await swos.openUser(getAcct(data.body.user), loginId); | ||||
| 									client = await swos.openUser(Acct.toString(data.body.user), loginId); | ||||
| 								} | ||||
| 								break; | ||||
| 						} | ||||
| @@ -160,7 +160,7 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv | ||||
| globalThis.addEventListener('notificationclose', (ev: ServiceWorkerGlobalScopeEventMap['notificationclose']) => { | ||||
| 	const data: PushNotificationDataMap[keyof PushNotificationDataMap] = ev.notification.data; | ||||
|  | ||||
| 	ev.waitUntil((async () => { | ||||
| 	ev.waitUntil((async (): Promise<void> => { | ||||
| 		if (data.type === 'notification') { | ||||
| 			await swos.sendMarkAllAsRead(data.userId); | ||||
| 		} | ||||
| @@ -169,7 +169,7 @@ globalThis.addEventListener('notificationclose', (ev: ServiceWorkerGlobalScopeEv | ||||
| }); | ||||
|  | ||||
| globalThis.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message']) => { | ||||
| 	ev.waitUntil((async () => { | ||||
| 	ev.waitUntil((async (): Promise<void> => { | ||||
| 		switch (ev.data) { | ||||
| 			case 'clear': | ||||
| 				// Cache Storage全削除 | ||||
|   | ||||
| @@ -1,20 +1,20 @@ | ||||
| import * as Misskey from 'misskey-js'; | ||||
| import type * as Misskey from 'misskey-js'; | ||||
|  | ||||
| export type SwMessageOrderType = 'post' | 'push'; | ||||
|  | ||||
| export type SwMessage = { | ||||
| 	type: 'order'; | ||||
| 	order: SwMessageOrderType; | ||||
| 	loginId: string; | ||||
| 	loginId?: string; | ||||
| 	url: string; | ||||
| 	[x: string]: any; | ||||
| 	[x: string]: unknown; | ||||
| }; | ||||
|  | ||||
| // Defined also @/core/PushNotificationService.ts#L12 | ||||
| type PushNotificationDataSourceMap = { | ||||
| 	notification: Misskey.entities.Notification; | ||||
| 	unreadAntennaNote: { | ||||
| 		antenna: { id: string, name: string }; | ||||
| 		antenna: { id: string; name: string }; | ||||
| 		note: Misskey.entities.Note; | ||||
| 	}; | ||||
| 	readAllNotifications: undefined; | ||||
| @@ -31,8 +31,8 @@ export type PushNotificationDataMap = { | ||||
| 	[K in keyof PushNotificationDataSourceMap]: PushNotificationData<K>; | ||||
| }; | ||||
|  | ||||
| export type BadgeNames =  | ||||
| 	'null' | ||||
| export type BadgeNames = | ||||
| 	| 'null' | ||||
| 	| 'antenna' | ||||
| 	| 'arrow-back-up' | ||||
| 	| 'at' | ||||
| @@ -44,5 +44,4 @@ export type BadgeNames = | ||||
| 	| 'quote' | ||||
| 	| 'repeat' | ||||
| 	| 'user-plus' | ||||
| 	| 'users' | ||||
| 	; | ||||
| 	| 'users'; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 okayurisotto
					okayurisotto