diff options
Diffstat (limited to 'subprojects/frontend/src/PWAStore.ts')
-rw-r--r-- | subprojects/frontend/src/PWAStore.ts | 94 |
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 @@ | |||
1 | import { makeAutoObservable, observable } from 'mobx'; | ||
2 | import ms from 'ms'; | ||
3 | // eslint-disable-next-line import/no-unresolved -- Importing virtual module. | ||
4 | import { registerSW } from 'virtual:pwa-register'; | ||
5 | |||
6 | import getLogger from './utils/getLogger'; | ||
7 | |||
8 | const log = getLogger('PWAStore'); | ||
9 | |||
10 | const UPDATE_INTERVAL = ms('30m'); | ||
11 | |||
12 | export 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 | } | ||