@docusaurus/plugin-pwa 0.0.0-5012 → 0.0.0-5024
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/registerSw.d.ts +32 -0
- package/lib/registerSw.js +171 -248
- package/lib/renderReloadPopup.d.ts +9 -0
- package/lib/renderReloadPopup.js +8 -13
- package/lib/theme/PwaReloadPopup/index.js +33 -17
- package/package.json +11 -14
- package/src/{registerSw.js → registerSw.ts} +24 -5
- package/src/{renderReloadPopup.js → renderReloadPopup.tsx} +6 -4
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
declare global {
|
|
8
|
+
namespace NodeJS {
|
|
9
|
+
interface ProcessEnv {
|
|
10
|
+
PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES: (keyof typeof OfflineModeActivationStrategiesImplementations)[];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
declare global {
|
|
15
|
+
interface Navigator {
|
|
16
|
+
getInstalledRelatedApps: () => Promise<{
|
|
17
|
+
platform: string;
|
|
18
|
+
}[]>;
|
|
19
|
+
connection: {
|
|
20
|
+
saveData: boolean;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
declare const OfflineModeActivationStrategiesImplementations: {
|
|
25
|
+
always: () => boolean;
|
|
26
|
+
mobile: () => boolean;
|
|
27
|
+
saveData: () => boolean;
|
|
28
|
+
appInstalled: () => Promise<boolean>;
|
|
29
|
+
standalone: () => boolean;
|
|
30
|
+
queryString: () => boolean;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
package/lib/registerSw.js
CHANGED
|
@@ -4,55 +4,33 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
import {createStorageSlot} from '@docusaurus/theme-common';
|
|
9
|
-
|
|
7
|
+
import { createStorageSlot } from '@docusaurus/theme-common';
|
|
10
8
|
// First: read the env variables (provided by Webpack)
|
|
11
9
|
/* eslint-disable prefer-destructuring */
|
|
12
10
|
const PWA_SERVICE_WORKER_URL = process.env.PWA_SERVICE_WORKER_URL;
|
|
13
11
|
const PWA_RELOAD_POPUP = process.env.PWA_RELOAD_POPUP;
|
|
14
|
-
const PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES =
|
|
15
|
-
process.env.PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES;
|
|
12
|
+
const PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES = process.env.PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES;
|
|
16
13
|
const PWA_DEBUG = process.env.PWA_DEBUG;
|
|
17
14
|
/* eslint-enable prefer-destructuring */
|
|
18
|
-
|
|
19
15
|
const debug = PWA_DEBUG; // Shortcut
|
|
20
|
-
|
|
21
16
|
const MAX_MOBILE_WIDTH = 940;
|
|
22
|
-
|
|
23
|
-
const AppInstalledEventFiredStorage = createStorageSlot(
|
|
24
|
-
'docusaurus.pwa.event.appInstalled.fired',
|
|
25
|
-
);
|
|
26
|
-
|
|
17
|
+
const AppInstalledEventFiredStorage = createStorageSlot('docusaurus.pwa.event.appInstalled.fired');
|
|
27
18
|
async function clearRegistrations() {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.log(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
result,
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
}),
|
|
46
|
-
);
|
|
47
|
-
if (debug) {
|
|
48
|
-
console.log(
|
|
49
|
-
`[Docusaurus-PWA][registerSw]: unregistered all service worker registrations`,
|
|
50
|
-
registrations,
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
window.location.reload();
|
|
19
|
+
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
20
|
+
if (debug) {
|
|
21
|
+
console.log(`[Docusaurus-PWA][registerSw]: will unregister all service worker registrations`, registrations);
|
|
22
|
+
}
|
|
23
|
+
await Promise.all(registrations.map(async (registration) => {
|
|
24
|
+
const result = await registration?.unregister();
|
|
25
|
+
if (debug) {
|
|
26
|
+
console.log(`[Docusaurus-PWA][registerSw]: unregister() service worker registration`, registrations, result);
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
if (debug) {
|
|
30
|
+
console.log(`[Docusaurus-PWA][registerSw]: unregistered all service worker registrations`, registrations);
|
|
31
|
+
}
|
|
32
|
+
window.location.reload();
|
|
54
33
|
}
|
|
55
|
-
|
|
56
34
|
/*
|
|
57
35
|
As of 2021:
|
|
58
36
|
It is complicated and not very reliable to detect an app is actually installed.
|
|
@@ -63,244 +41,189 @@ https://stackoverflow.com/questions/51735869/check-if-user-has-already-installed
|
|
|
63
41
|
- display-mode: standalone is not exactly the same concept, but looks like a decent fallback https://petelepage.com/blog/2019/07/is-my-pwa-installed/
|
|
64
42
|
*/
|
|
65
43
|
async function isAppInstalledEventFired() {
|
|
66
|
-
|
|
44
|
+
return AppInstalledEventFiredStorage.get() === 'true';
|
|
67
45
|
}
|
|
68
46
|
async function isAppInstalledRelatedApps() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
47
|
+
if ('getInstalledRelatedApps' in window.navigator) {
|
|
48
|
+
const relatedApps = await navigator.getInstalledRelatedApps();
|
|
49
|
+
return relatedApps.some((app) => app.platform === 'webapp');
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
74
52
|
}
|
|
75
53
|
function isStandaloneDisplayMode() {
|
|
76
|
-
|
|
54
|
+
return window.matchMedia('(display-mode: standalone)').matches;
|
|
77
55
|
}
|
|
78
|
-
|
|
79
56
|
const OfflineModeActivationStrategiesImplementations = {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
new URLSearchParams(window.location.search).get('offlineMode') === 'true',
|
|
57
|
+
always: () => true,
|
|
58
|
+
mobile: () => window.innerWidth <= MAX_MOBILE_WIDTH,
|
|
59
|
+
saveData: () => !!navigator.connection?.saveData,
|
|
60
|
+
appInstalled: async () => {
|
|
61
|
+
const installedEventFired = await isAppInstalledEventFired();
|
|
62
|
+
const installedRelatedApps = await isAppInstalledRelatedApps();
|
|
63
|
+
return installedEventFired || installedRelatedApps;
|
|
64
|
+
},
|
|
65
|
+
standalone: () => isStandaloneDisplayMode(),
|
|
66
|
+
queryString: () => new URLSearchParams(window.location.search).get('offlineMode') === 'true',
|
|
91
67
|
};
|
|
92
|
-
|
|
93
68
|
async function isStrategyActive(strategyName) {
|
|
94
|
-
|
|
69
|
+
return OfflineModeActivationStrategiesImplementations[strategyName]();
|
|
95
70
|
}
|
|
96
|
-
|
|
97
71
|
async function getActiveStrategies() {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
);
|
|
104
|
-
return activeStrategies.filter(Boolean);
|
|
72
|
+
const activeStrategies = await Promise.all(PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES.map(async (strategyName) => {
|
|
73
|
+
const isActive = await isStrategyActive(strategyName);
|
|
74
|
+
return isActive ? strategyName : undefined;
|
|
75
|
+
}));
|
|
76
|
+
return activeStrategies.filter(Boolean);
|
|
105
77
|
}
|
|
106
|
-
|
|
107
78
|
async function isOfflineModeEnabled() {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.log(
|
|
122
|
-
'[Docusaurus-PWA][registerSw]: offline mode disabled, because none of the offlineModeActivationStrategies could be used',
|
|
123
|
-
logObject,
|
|
124
|
-
);
|
|
79
|
+
const activeStrategies = await getActiveStrategies();
|
|
80
|
+
const enabled = activeStrategies.length > 0;
|
|
81
|
+
if (debug) {
|
|
82
|
+
const logObject = {
|
|
83
|
+
activeStrategies,
|
|
84
|
+
availableStrategies: PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES,
|
|
85
|
+
};
|
|
86
|
+
if (enabled) {
|
|
87
|
+
console.log('[Docusaurus-PWA][registerSw]: offline mode enabled, because of activation strategies', logObject);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log('[Docusaurus-PWA][registerSw]: offline mode disabled, because none of the offlineModeActivationStrategies could be used', logObject);
|
|
91
|
+
}
|
|
125
92
|
}
|
|
126
|
-
|
|
127
|
-
return enabled;
|
|
93
|
+
return enabled;
|
|
128
94
|
}
|
|
129
|
-
|
|
130
95
|
function createServiceWorkerUrl(params) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
paramsQueryString,
|
|
134
|
-
)}`;
|
|
135
|
-
if (debug) {
|
|
136
|
-
console.log(`[Docusaurus-PWA][registerSw]: service worker url`, {
|
|
137
|
-
url,
|
|
138
|
-
params,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
return url;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async function registerSW() {
|
|
145
|
-
const {Workbox} = await import('workbox-window');
|
|
146
|
-
|
|
147
|
-
const offlineMode = await isOfflineModeEnabled();
|
|
148
|
-
|
|
149
|
-
const url = createServiceWorkerUrl({offlineMode, debug});
|
|
150
|
-
const wb = new Workbox(url);
|
|
151
|
-
|
|
152
|
-
const registration = await wb.register();
|
|
153
|
-
|
|
154
|
-
const sendSkipWaiting = () => wb.messageSW({type: 'SKIP_WAITING'});
|
|
155
|
-
|
|
156
|
-
const handleServiceWorkerWaiting = async () => {
|
|
96
|
+
const paramsQueryString = JSON.stringify(params);
|
|
97
|
+
const url = `${PWA_SERVICE_WORKER_URL}?params=${encodeURIComponent(paramsQueryString)}`;
|
|
157
98
|
if (debug) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
sendSkipWaiting();
|
|
163
|
-
} else if (PWA_RELOAD_POPUP) {
|
|
164
|
-
const renderReloadPopup = (await import('./renderReloadPopup')).default;
|
|
165
|
-
await renderReloadPopup({
|
|
166
|
-
onReload() {
|
|
167
|
-
wb.addEventListener('controlling', () => {
|
|
168
|
-
window.location.reload();
|
|
169
|
-
});
|
|
170
|
-
sendSkipWaiting();
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
if (debug) {
|
|
177
|
-
if (registration.active) {
|
|
178
|
-
console.log(
|
|
179
|
-
'[Docusaurus-PWA][registerSw]: registration.active',
|
|
180
|
-
registration,
|
|
181
|
-
);
|
|
99
|
+
console.log(`[Docusaurus-PWA][registerSw]: service worker url`, {
|
|
100
|
+
url,
|
|
101
|
+
params,
|
|
102
|
+
});
|
|
182
103
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
104
|
+
return url;
|
|
105
|
+
}
|
|
106
|
+
async function registerSW() {
|
|
107
|
+
const { Workbox } = await import('workbox-window');
|
|
108
|
+
const offlineMode = await isOfflineModeEnabled();
|
|
109
|
+
const url = createServiceWorkerUrl({ offlineMode, debug });
|
|
110
|
+
const wb = new Workbox(url);
|
|
111
|
+
const registration = await wb.register();
|
|
112
|
+
const sendSkipWaiting = () => wb.messageSW({ type: 'SKIP_WAITING' });
|
|
113
|
+
const handleServiceWorkerWaiting = async () => {
|
|
114
|
+
if (debug) {
|
|
115
|
+
console.log('[Docusaurus-PWA][registerSw]: handleServiceWorkerWaiting');
|
|
116
|
+
}
|
|
117
|
+
// Immediately load new service worker when files aren't cached
|
|
118
|
+
if (!offlineMode) {
|
|
119
|
+
sendSkipWaiting();
|
|
120
|
+
}
|
|
121
|
+
else if (PWA_RELOAD_POPUP) {
|
|
122
|
+
const renderReloadPopup = (await import('./renderReloadPopup')).default;
|
|
123
|
+
await renderReloadPopup({
|
|
124
|
+
onReload() {
|
|
125
|
+
wb.addEventListener('controlling', () => {
|
|
126
|
+
window.location.reload();
|
|
127
|
+
});
|
|
128
|
+
sendSkipWaiting();
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
if (debug && registration) {
|
|
134
|
+
if (registration.active) {
|
|
135
|
+
console.log('[Docusaurus-PWA][registerSw]: registration.active', registration);
|
|
136
|
+
}
|
|
137
|
+
if (registration.installing) {
|
|
138
|
+
console.log('[Docusaurus-PWA][registerSw]: registration.installing', registration);
|
|
139
|
+
}
|
|
140
|
+
if (registration.waiting) {
|
|
141
|
+
console.log('[Docusaurus-PWA][registerSw]: registration.waiting', registration);
|
|
142
|
+
}
|
|
202
143
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
144
|
+
// Update the current service worker when the next one has finished
|
|
145
|
+
// installing and transitions to waiting state.
|
|
146
|
+
wb.addEventListener('waiting', (event) => {
|
|
147
|
+
if (debug) {
|
|
148
|
+
console.log('[Docusaurus-PWA][registerSw]: event waiting', event);
|
|
149
|
+
}
|
|
150
|
+
handleServiceWorkerWaiting();
|
|
151
|
+
});
|
|
152
|
+
// Update current service worker if the next one finishes installing and
|
|
153
|
+
// moves to waiting state in another tab.
|
|
154
|
+
// @ts-expect-error: not present in the API typings anymore
|
|
155
|
+
wb.addEventListener('externalwaiting', (event) => {
|
|
156
|
+
if (debug) {
|
|
157
|
+
console.log('[Docusaurus-PWA][registerSw]: event externalwaiting', event);
|
|
158
|
+
}
|
|
159
|
+
handleServiceWorkerWaiting();
|
|
160
|
+
});
|
|
161
|
+
// Update service worker if the next one is already in the waiting state.
|
|
162
|
+
// This happens when the user doesn't click on `reload` in the popup.
|
|
163
|
+
if (registration?.waiting) {
|
|
164
|
+
await handleServiceWorkerWaiting();
|
|
211
165
|
}
|
|
212
|
-
handleServiceWorkerWaiting();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// Update service worker if the next one is already in the waiting state.
|
|
216
|
-
// This happens when the user doesn't click on `reload` in the popup.
|
|
217
|
-
if (registration.waiting) {
|
|
218
|
-
await handleServiceWorkerWaiting();
|
|
219
|
-
}
|
|
220
166
|
}
|
|
221
|
-
|
|
222
167
|
// TODO these events still works in chrome but have been removed from the spec
|
|
223
168
|
// in 2019! See https://github.com/w3c/manifest/pull/836
|
|
224
169
|
function addLegacyAppInstalledEventsListeners() {
|
|
225
|
-
|
|
226
|
-
if (debug) {
|
|
227
|
-
console.log(
|
|
228
|
-
'[Docusaurus-PWA][registerSw]: addLegacyAppInstalledEventsListeners',
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
window.addEventListener('appinstalled', async (event) => {
|
|
233
|
-
if (debug) {
|
|
234
|
-
console.log('[Docusaurus-PWA][registerSw]: event appinstalled', event);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
AppInstalledEventFiredStorage.set('true');
|
|
238
|
-
if (debug) {
|
|
239
|
-
console.log(
|
|
240
|
-
"[Docusaurus-PWA][registerSw]: AppInstalledEventFiredStorage.set('true')",
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// After the app is installed, we register a service worker with the path
|
|
245
|
-
// `/sw?enabled`. Since the previous service worker was `/sw`, it'll be
|
|
246
|
-
// treated as a new one. The previous registration will need to be
|
|
247
|
-
// cleared, otherwise the reload popup will show.
|
|
248
|
-
await clearRegistrations();
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// TODO this event still works in chrome but has been removed from the spec
|
|
252
|
-
// in 2019!!!
|
|
253
|
-
window.addEventListener('beforeinstallprompt', async (event) => {
|
|
254
|
-
if (debug) {
|
|
255
|
-
console.log(
|
|
256
|
-
'[Docusaurus-PWA][registerSw]: event beforeinstallprompt',
|
|
257
|
-
event,
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
// TODO instead of default browser install UI, show custom docusaurus
|
|
261
|
-
// prompt?
|
|
262
|
-
// event.preventDefault();
|
|
263
|
-
if (debug) {
|
|
264
|
-
console.log(
|
|
265
|
-
'[Docusaurus-PWA][registerSw]: AppInstalledEventFiredStorage.get()',
|
|
266
|
-
AppInstalledEventFiredStorage.get(),
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
if (AppInstalledEventFiredStorage.get()) {
|
|
270
|
-
AppInstalledEventFiredStorage.del();
|
|
170
|
+
if (typeof window !== 'undefined') {
|
|
271
171
|
if (debug) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
172
|
+
console.log('[Docusaurus-PWA][registerSw]: addLegacyAppInstalledEventsListeners');
|
|
173
|
+
}
|
|
174
|
+
window.addEventListener('appinstalled', async (event) => {
|
|
175
|
+
if (debug) {
|
|
176
|
+
console.log('[Docusaurus-PWA][registerSw]: event appinstalled', event);
|
|
177
|
+
}
|
|
178
|
+
AppInstalledEventFiredStorage.set('true');
|
|
179
|
+
if (debug) {
|
|
180
|
+
console.log("[Docusaurus-PWA][registerSw]: AppInstalledEventFiredStorage.set('true')");
|
|
181
|
+
}
|
|
182
|
+
// After the app is installed, we register a service worker with the path
|
|
183
|
+
// `/sw?enabled`. Since the previous service worker was `/sw`, it'll be
|
|
184
|
+
// treated as a new one. The previous registration will need to be
|
|
185
|
+
// cleared, otherwise the reload popup will show.
|
|
186
|
+
await clearRegistrations();
|
|
187
|
+
});
|
|
188
|
+
// TODO this event still works in chrome but has been removed from the spec
|
|
189
|
+
// in 2019!!!
|
|
190
|
+
window.addEventListener('beforeinstallprompt', async (event) => {
|
|
191
|
+
if (debug) {
|
|
192
|
+
console.log('[Docusaurus-PWA][registerSw]: event beforeinstallprompt', event);
|
|
193
|
+
}
|
|
194
|
+
// TODO instead of default browser install UI, show custom docusaurus
|
|
195
|
+
// prompt?
|
|
196
|
+
// event.preventDefault();
|
|
197
|
+
if (debug) {
|
|
198
|
+
console.log('[Docusaurus-PWA][registerSw]: AppInstalledEventFiredStorage.get()', AppInstalledEventFiredStorage.get());
|
|
199
|
+
}
|
|
200
|
+
if (AppInstalledEventFiredStorage.get()) {
|
|
201
|
+
AppInstalledEventFiredStorage.del();
|
|
202
|
+
if (debug) {
|
|
203
|
+
console.log('[Docusaurus-PWA][registerSw]: AppInstalledEventFiredStorage.del()');
|
|
204
|
+
}
|
|
205
|
+
// After uninstalling the app, if the user doesn't clear all data, then
|
|
206
|
+
// the previous service worker will continue serving cached files. We
|
|
207
|
+
// need to clear registrations and reload, otherwise the popup shows.
|
|
208
|
+
await clearRegistrations();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
if (debug) {
|
|
212
|
+
console.log('[Docusaurus-PWA][registerSw]: legacy appinstalled and beforeinstallprompt event listeners installed');
|
|
275
213
|
}
|
|
276
|
-
// After uninstalling the app, if the user doesn't clear all data, then
|
|
277
|
-
// the previous service worker will continue serving cached files. We
|
|
278
|
-
// need to clear registrations and reload, otherwise the popup shows.
|
|
279
|
-
await clearRegistrations();
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
if (debug) {
|
|
284
|
-
console.log(
|
|
285
|
-
'[Docusaurus-PWA][registerSw]: legacy appinstalled and beforeinstallprompt event listeners installed',
|
|
286
|
-
);
|
|
287
214
|
}
|
|
288
|
-
}
|
|
289
215
|
}
|
|
290
|
-
|
|
291
216
|
/*
|
|
292
217
|
Init code to run on the client!
|
|
293
218
|
*/
|
|
294
219
|
if (typeof window !== 'undefined') {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
registerSW().catch((e) => console.error('registerSW failed', e));
|
|
305
|
-
}
|
|
220
|
+
if (debug) {
|
|
221
|
+
console.log('[Docusaurus-PWA][registerSw]: debug mode enabled');
|
|
222
|
+
}
|
|
223
|
+
if ('serviceWorker' in navigator) {
|
|
224
|
+
// First: add the listeners asap/synchronously
|
|
225
|
+
addLegacyAppInstalledEventsListeners();
|
|
226
|
+
// Then try to register the SW using lazy/dynamic imports
|
|
227
|
+
registerSW().catch((e) => console.error('registerSW failed', e));
|
|
228
|
+
}
|
|
306
229
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
export default function renderReloadPopup(props: {
|
|
8
|
+
onReload: () => void;
|
|
9
|
+
}): Promise<void>;
|
package/lib/renderReloadPopup.js
CHANGED
|
@@ -4,23 +4,18 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
7
|
import React from 'react';
|
|
9
|
-
import
|
|
10
|
-
|
|
8
|
+
import ReactDOM from 'react-dom';
|
|
11
9
|
const POPUP_CONTAINER_ID = 'pwa-popup-container';
|
|
12
|
-
|
|
13
10
|
const getContainer = () => document.getElementById(POPUP_CONTAINER_ID);
|
|
14
|
-
|
|
15
11
|
const createContainer = () => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
const container = document.createElement('div');
|
|
13
|
+
container.id = POPUP_CONTAINER_ID;
|
|
14
|
+
document.body.appendChild(container);
|
|
15
|
+
return container;
|
|
20
16
|
};
|
|
21
|
-
|
|
22
17
|
export default async function renderReloadPopup(props) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
const container = getContainer() || createContainer();
|
|
19
|
+
const { default: ReloadPopup } = await import(process.env.PWA_RELOAD_POPUP);
|
|
20
|
+
ReactDOM.render(<ReloadPopup {...props}/>, container);
|
|
26
21
|
}
|
|
@@ -4,35 +4,51 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import React, {
|
|
7
|
+
import React, {useState} from 'react';
|
|
8
8
|
import clsx from 'clsx';
|
|
9
|
-
import Translate, {
|
|
9
|
+
import Translate, {translate} from '@docusaurus/Translate';
|
|
10
10
|
import styles from './styles.module.css';
|
|
11
|
-
export default function PwaReloadPopup({
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
export default function PwaReloadPopup({onReload}) {
|
|
12
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
13
|
+
return (
|
|
14
|
+
isVisible && (
|
|
15
|
+
<div className={clsx('alert', 'alert--secondary', styles.popup)}>
|
|
14
16
|
<p>
|
|
15
|
-
<Translate
|
|
17
|
+
<Translate
|
|
18
|
+
id="theme.PwaReloadPopup.info"
|
|
19
|
+
description="The text for PWA reload popup">
|
|
16
20
|
New version available
|
|
17
21
|
</Translate>
|
|
18
22
|
</p>
|
|
19
23
|
<div className={styles.buttonContainer}>
|
|
20
|
-
<button
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
<button
|
|
25
|
+
className="button button--link"
|
|
26
|
+
type="button"
|
|
27
|
+
onClick={() => {
|
|
28
|
+
setIsVisible(false);
|
|
29
|
+
onReload();
|
|
30
|
+
}}>
|
|
31
|
+
<Translate
|
|
32
|
+
id="theme.PwaReloadPopup.refreshButtonText"
|
|
33
|
+
description="The text for PWA reload button">
|
|
25
34
|
Refresh
|
|
26
35
|
</Translate>
|
|
27
36
|
</button>
|
|
28
37
|
|
|
29
|
-
<button
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
<button
|
|
39
|
+
aria-label={translate({
|
|
40
|
+
id: 'theme.PwaReloadPopup.closeButtonAriaLabel',
|
|
41
|
+
message: 'Close',
|
|
42
|
+
description:
|
|
43
|
+
'The ARIA label for close button of PWA reload popup',
|
|
44
|
+
})}
|
|
45
|
+
className="close"
|
|
46
|
+
type="button"
|
|
47
|
+
onClick={() => setIsVisible(false)}>
|
|
34
48
|
<span aria-hidden="true">×</span>
|
|
35
49
|
</button>
|
|
36
50
|
</div>
|
|
37
|
-
</div>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
);
|
|
38
54
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/plugin-pwa",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-5024",
|
|
4
4
|
"description": "Docusaurus Plugin to add PWA support.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/plugin-pwa.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "
|
|
9
|
-
"build:server": "tsc --project tsconfig.server.json",
|
|
10
|
-
"build:browser": "tsc --project tsconfig.browser.json",
|
|
11
|
-
"build:copy": "node copyUntypedFiles.mjs"
|
|
8
|
+
"build": "tsc --build && node copyUntypedFiles.mjs && prettier --config ../../.prettierrc --write \"lib/theme/**/*.js\""
|
|
12
9
|
},
|
|
13
10
|
"publishConfig": {
|
|
14
11
|
"access": "public"
|
|
@@ -20,13 +17,13 @@
|
|
|
20
17
|
},
|
|
21
18
|
"license": "MIT",
|
|
22
19
|
"dependencies": {
|
|
23
|
-
"@babel/core": "^7.17.
|
|
24
|
-
"@babel/preset-env": "^7.17.
|
|
25
|
-
"@docusaurus/core": "0.0.0-
|
|
26
|
-
"@docusaurus/theme-common": "0.0.0-
|
|
27
|
-
"@docusaurus/theme-translations": "0.0.0-
|
|
28
|
-
"@docusaurus/utils": "0.0.0-
|
|
29
|
-
"@docusaurus/utils-validation": "0.0.0-
|
|
20
|
+
"@babel/core": "^7.17.12",
|
|
21
|
+
"@babel/preset-env": "^7.17.12",
|
|
22
|
+
"@docusaurus/core": "0.0.0-5024",
|
|
23
|
+
"@docusaurus/theme-common": "0.0.0-5024",
|
|
24
|
+
"@docusaurus/theme-translations": "0.0.0-5024",
|
|
25
|
+
"@docusaurus/utils": "0.0.0-5024",
|
|
26
|
+
"@docusaurus/utils-validation": "0.0.0-5024",
|
|
30
27
|
"babel-loader": "^8.2.5",
|
|
31
28
|
"clsx": "^1.1.1",
|
|
32
29
|
"core-js": "^3.22.5",
|
|
@@ -39,12 +36,12 @@
|
|
|
39
36
|
"workbox-window": "^6.5.3"
|
|
40
37
|
},
|
|
41
38
|
"devDependencies": {
|
|
42
|
-
"@docusaurus/module-type-aliases": "0.0.0-
|
|
39
|
+
"@docusaurus/module-type-aliases": "0.0.0-5024",
|
|
43
40
|
"fs-extra": "^10.1.0"
|
|
44
41
|
},
|
|
45
42
|
"peerDependencies": {
|
|
46
43
|
"react": "^16.8.4 || ^17.0.0",
|
|
47
44
|
"react-dom": "^16.8.4 || ^17.0.0"
|
|
48
45
|
},
|
|
49
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "9d61f0a1f0c1121d25211afbb0fc45a92ca3b2ec"
|
|
50
47
|
}
|
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import {createStorageSlot} from '@docusaurus/theme-common';
|
|
9
9
|
|
|
10
|
+
declare global {
|
|
11
|
+
namespace NodeJS {
|
|
12
|
+
interface ProcessEnv {
|
|
13
|
+
PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES: (keyof typeof OfflineModeActivationStrategiesImplementations)[];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
// First: read the env variables (provided by Webpack)
|
|
11
19
|
/* eslint-disable prefer-destructuring */
|
|
12
20
|
const PWA_SERVICE_WORKER_URL = process.env.PWA_SERVICE_WORKER_URL;
|
|
@@ -34,7 +42,7 @@ async function clearRegistrations() {
|
|
|
34
42
|
}
|
|
35
43
|
await Promise.all(
|
|
36
44
|
registrations.map(async (registration) => {
|
|
37
|
-
const result = await registration?.
|
|
45
|
+
const result = await registration?.unregister();
|
|
38
46
|
if (debug) {
|
|
39
47
|
console.log(
|
|
40
48
|
`[Docusaurus-PWA][registerSw]: unregister() service worker registration`,
|
|
@@ -65,6 +73,14 @@ https://stackoverflow.com/questions/51735869/check-if-user-has-already-installed
|
|
|
65
73
|
async function isAppInstalledEventFired() {
|
|
66
74
|
return AppInstalledEventFiredStorage.get() === 'true';
|
|
67
75
|
}
|
|
76
|
+
|
|
77
|
+
declare global {
|
|
78
|
+
interface Navigator {
|
|
79
|
+
getInstalledRelatedApps: () => Promise<{platform: string}[]>;
|
|
80
|
+
connection: {saveData: boolean};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
68
84
|
async function isAppInstalledRelatedApps() {
|
|
69
85
|
if ('getInstalledRelatedApps' in window.navigator) {
|
|
70
86
|
const relatedApps = await navigator.getInstalledRelatedApps();
|
|
@@ -90,7 +106,9 @@ const OfflineModeActivationStrategiesImplementations = {
|
|
|
90
106
|
new URLSearchParams(window.location.search).get('offlineMode') === 'true',
|
|
91
107
|
};
|
|
92
108
|
|
|
93
|
-
async function isStrategyActive(
|
|
109
|
+
async function isStrategyActive(
|
|
110
|
+
strategyName: keyof typeof OfflineModeActivationStrategiesImplementations,
|
|
111
|
+
) {
|
|
94
112
|
return OfflineModeActivationStrategiesImplementations[strategyName]();
|
|
95
113
|
}
|
|
96
114
|
|
|
@@ -127,7 +145,7 @@ async function isOfflineModeEnabled() {
|
|
|
127
145
|
return enabled;
|
|
128
146
|
}
|
|
129
147
|
|
|
130
|
-
function createServiceWorkerUrl(params) {
|
|
148
|
+
function createServiceWorkerUrl(params: object) {
|
|
131
149
|
const paramsQueryString = JSON.stringify(params);
|
|
132
150
|
const url = `${PWA_SERVICE_WORKER_URL}?params=${encodeURIComponent(
|
|
133
151
|
paramsQueryString,
|
|
@@ -173,7 +191,7 @@ async function registerSW() {
|
|
|
173
191
|
}
|
|
174
192
|
};
|
|
175
193
|
|
|
176
|
-
if (debug) {
|
|
194
|
+
if (debug && registration) {
|
|
177
195
|
if (registration.active) {
|
|
178
196
|
console.log(
|
|
179
197
|
'[Docusaurus-PWA][registerSw]: registration.active',
|
|
@@ -205,6 +223,7 @@ async function registerSW() {
|
|
|
205
223
|
|
|
206
224
|
// Update current service worker if the next one finishes installing and
|
|
207
225
|
// moves to waiting state in another tab.
|
|
226
|
+
// @ts-expect-error: not present in the API typings anymore
|
|
208
227
|
wb.addEventListener('externalwaiting', (event) => {
|
|
209
228
|
if (debug) {
|
|
210
229
|
console.log('[Docusaurus-PWA][registerSw]: event externalwaiting', event);
|
|
@@ -214,7 +233,7 @@ async function registerSW() {
|
|
|
214
233
|
|
|
215
234
|
// Update service worker if the next one is already in the waiting state.
|
|
216
235
|
// This happens when the user doesn't click on `reload` in the popup.
|
|
217
|
-
if (registration
|
|
236
|
+
if (registration?.waiting) {
|
|
218
237
|
await handleServiceWorkerWaiting();
|
|
219
238
|
}
|
|
220
239
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React from 'react';
|
|
9
|
-
import
|
|
9
|
+
import ReactDOM from 'react-dom';
|
|
10
10
|
|
|
11
11
|
const POPUP_CONTAINER_ID = 'pwa-popup-container';
|
|
12
12
|
|
|
@@ -19,8 +19,10 @@ const createContainer = () => {
|
|
|
19
19
|
return container;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
export default async function renderReloadPopup(props
|
|
22
|
+
export default async function renderReloadPopup(props: {
|
|
23
|
+
onReload: () => void;
|
|
24
|
+
}): Promise<void> {
|
|
23
25
|
const container = getContainer() || createContainer();
|
|
24
|
-
const {default: ReloadPopup} = await import(process.env.PWA_RELOAD_POPUP);
|
|
25
|
-
render(<ReloadPopup {...props} />, container);
|
|
26
|
+
const {default: ReloadPopup} = await import(process.env.PWA_RELOAD_POPUP!);
|
|
27
|
+
ReactDOM.render(<ReloadPopup {...props} />, container);
|
|
26
28
|
}
|