summaryrefslogtreecommitdiff
path: root/src/client/sw/sw.ts
blob: ec4de17551c216d9e09e8d93a0d0f93bf40d1b9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
 * Service Worker
 */
declare var self: ServiceWorkerGlobalScope;

import { get, set } from 'idb-keyval';
import composeNotification from '@/sw/compose-notification';
import { I18n } from '../../misc/i18n';

//#region Variables
const version = _VERSION_;
const cacheName = `mk-cache-${version}`;

let lang: string;
let i18n: I18n<any>;
let pushesPool: any[] = [];
//#endregion

//#region Startup
get('lang').then(async prelang => {
	if (!prelang) return;
	lang = prelang;
	return fetchLocale();
});
//#endregion

//#region Lifecycle: Install
self.addEventListener('install', ev => {
	self.skipWaiting();
});
//#endregion

//#region Lifecycle: Activate
self.addEventListener('activate', ev => {
	ev.waitUntil(
		caches.keys()
			.then(cacheNames => Promise.all(
				cacheNames
					.filter((v) => v !== cacheName)
					.map(name => caches.delete(name))
			))
			.then(() => self.clients.claim())
	);
});
//#endregion

//#region When: Fetching
self.addEventListener('fetch', ev => {
	// Nothing to do
});
//#endregion

//#region When: Caught Notification
self.addEventListener('push', ev => {
	// クライアント取得
	ev.waitUntil(self.clients.matchAll({
		includeUncontrolled: true
	}).then(async clients => {
		// クライアントがあったらストリームに接続しているということなので通知しない
		if (clients.length != 0) return;

		const { type, body } = ev.data?.json();

		// localeを読み込めておらずi18nがundefinedだった場合はpushesPoolにためておく
		if (!i18n) return pushesPool.push({ type, body });

		const n = await composeNotification(type, body, i18n);
		if (n) return self.registration.showNotification(...n);
	}));
});
//#endregion

//#region When: Caught a message from the client
self.addEventListener('message', ev => {
	switch(ev.data) {
		case 'clear':
			return; // TODO
		default:
			break;
	}

	if (typeof ev.data === 'object') {
		// E.g. '[object Array]' → 'array'
		const otype = Object.prototype.toString.call(ev.data).slice(8, -1).toLowerCase();

		if (otype === 'object') {
			if (ev.data.msg === 'initialize') {
				lang = ev.data.lang;
				set('lang', lang);
				fetchLocale();
			}
		}
	}
});
//#endregion

//#region Function: (Re)Load i18n instance
async function fetchLocale() {
	//#region localeファイルの読み込み
	// Service Workerは何度も起動しそのたびにlocaleを読み込むので、CacheStorageを使う
	const localeUrl = `/assets/locales/${lang}.${version}.json`;
	let localeRes = await caches.match(localeUrl);

	if (!localeRes) {
		localeRes = await fetch(localeUrl);
		const clone = localeRes?.clone();
		if (!clone?.clone().ok) return;

		caches.open(cacheName).then(cache => cache.put(localeUrl, clone));
	}

	i18n = new I18n(await localeRes.json());
	//#endregion

	//#region i18nをきちんと読み込んだ後にやりたい処理
	for (const { type, body } of pushesPool) {
		const n = await composeNotification(type, body, i18n);
		if (n) self.registration.showNotification(...n);
	}
	pushesPool = [];
	//#endregion
}
//#endregion