aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/frontend/src/PWAStore.ts
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/frontend/src/PWAStore.ts')
-rw-r--r--subprojects/frontend/src/PWAStore.ts94
1 files changed, 94 insertions, 0 deletions
diff --git a/subprojects/frontend/src/PWAStore.ts b/subprojects/frontend/src/PWAStore.ts
new file mode 100644
index 00000000..e9f99e2a
--- /dev/null
+++ b/subprojects/frontend/src/PWAStore.ts
@@ -0,0 +1,94 @@
1import { makeAutoObservable, observable } from 'mobx';
2import ms from 'ms';
3// eslint-disable-next-line import/no-unresolved -- Importing virtual module.
4import { registerSW } from 'virtual:pwa-register';
5
6import getLogger from './utils/getLogger';
7
8const log = getLogger('PWAStore');
9
10const UPDATE_INTERVAL = ms('30m');
11
12export default class PWAStore {
13 needsUpdate = false;
14
15 updateError = false;
16
17 private readonly updateSW: (
18 reloadPage?: boolean | undefined,
19 ) => Promise<void>;
20
21 private registration: ServiceWorkerRegistration | undefined;
22
23 constructor() {
24 if (window.location.host === 'localhost') {
25 // Do not register service worker during local development.
26 this.updateSW = () => Promise.resolve();
27 } else {
28 this.updateSW = registerSW({
29 onNeedRefresh: () => this.requestUpdate(),
30 onOfflineReady() {
31 log.debug('Service worker is ready for offline use');
32 },
33 onRegistered: (registration) => {
34 log.debug('Registered service worker');
35 this.setRegistration(registration);
36 },
37 onRegisterError(error) {
38 log.error('Failed to register service worker', error);
39 },
40 });
41 setInterval(() => this.checkForUpdates(), UPDATE_INTERVAL);
42 }
43 makeAutoObservable<PWAStore, 'updateSW' | 'registration'>(this, {
44 updateSW: false,
45 registration: observable.ref,
46 });
47 }
48
49 private requestUpdate(): void {
50 this.needsUpdate = true;
51 }
52
53 private setRegistration(
54 registration: ServiceWorkerRegistration | undefined,
55 ): void {
56 this.registration = registration;
57 }
58
59 private signalError(): void {
60 this.updateError = true;
61 }
62
63 private update(reloadPage?: boolean | undefined): void {
64 this.updateSW(reloadPage).catch((error) => {
65 log.error('Error while reloading page with updates', error);
66 this.signalError();
67 });
68 }
69
70 checkForUpdates(): void {
71 this.dismissError();
72 // In development mode, the service worker deactives itself,
73 // so we must watch out for a deactivated service worker before updating.
74 if (this.registration !== undefined && this.registration.active) {
75 this.registration.update().catch((error) => {
76 log.error('Error while updating service worker', error);
77 this.signalError();
78 });
79 }
80 }
81
82 reloadWithUpdate(): void {
83 this.dismissUpdate();
84 this.update(true);
85 }
86
87 dismissUpdate(): void {
88 this.needsUpdate = false;
89 }
90
91 dismissError(): void {
92 this.updateError = false;
93 }
94}