@develemit/web-hooks 0.3.0 → 0.3.1
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/CHANGELOG.md +6 -0
- package/README.md +12 -0
- package/dist/{chunk-UJ366T3Y.js → chunk-R2PFNE3U.js} +12 -4
- package/dist/{chunk-JG3GPTMY.js → chunk-UVJBJETY.js} +26 -6
- package/dist/index.cjs +38 -10
- package/dist/index.js +2 -2
- package/dist/push.cjs +12 -4
- package/dist/push.d.cts +2 -1
- package/dist/push.d.ts +2 -1
- package/dist/push.js +1 -1
- package/dist/service-worker.cjs +26 -6
- package/dist/service-worker.d.cts +2 -1
- package/dist/service-worker.d.ts +2 -1
- package/dist/service-worker.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1 - 2026-11-06
|
|
4
|
+
|
|
5
|
+
- `service-worker`: `useServiceWorker({ onError })` and `registerServiceWorker({ onError })`. Called with the rejection when `register()` fails (not after unmount or dispose). The latest callback is used; a throwing callback is swallowed. Registration failure still never throws into React.
|
|
6
|
+
- `push`: an empty or whitespace `vapidPublicKey` is "not ready". The on-load re-subscribe waits for the key and runs once when it arrives; `subscribe()` sets `error` to `'push key not ready'` and never prompts or calls `pushManager.subscribe`. `shouldResubscribe` takes an optional `hasKey` (default `true`); new `isKeyReady`.
|
|
7
|
+
- `service-worker`: a worker that skips waiting no longer makes the tab reload unasked on `controllerchange`. The hook reports `updateAvailable` and `applyUpdate()` reloads once. **Behaviour change:** this also replaces the old behaviour of following another tab's update with an automatic reload; that tab now shows the update prompt instead. No app relied on the old behaviour, so it ships as a patch.
|
|
8
|
+
|
|
3
9
|
## 0.3.0 - 2026-11-06
|
|
4
10
|
|
|
5
11
|
- `push`: `usePushSubscription({ vapidPublicKey, onSubscribe, onUnsubscribe, resubscribeOnLoad = true })` returning `{ support, permission, subscribed, busy, error, subscribe, unsubscribe }`, plus `urlBase64ToUint8Array`, `getPushSupport` (`'supported' | 'unsupported' | 'ios-needs-install'`) and the pure `shouldResubscribe`. The VAPID key is always converted to a `Uint8Array`, the browser's `pushManager.getSubscription()` is the source of truth, mounting never asks for permission, a dropped iOS subscription is re-made silently when permission is already granted, and an `onSubscribe` rejection with `status` 410 unsubscribes and subscribes once more. The package makes no HTTP requests; the app's callbacks do.
|
package/README.md
CHANGED
|
@@ -301,6 +301,7 @@ const { updateAvailable, applyUpdate, supported } = useServiceWorker({
|
|
|
301
301
|
url: '/sw.js',
|
|
302
302
|
scope: '/',
|
|
303
303
|
enabled: process.env.NODE_ENV === 'production',
|
|
304
|
+
onError: (error) => captureError(error), // registration failed; never throws into React
|
|
304
305
|
});
|
|
305
306
|
if (updateAvailable) showBanner({ onTap: applyUpdate });
|
|
306
307
|
```
|
|
@@ -315,6 +316,12 @@ self.addEventListener('message', (event) => {
|
|
|
315
316
|
});
|
|
316
317
|
```
|
|
317
318
|
|
|
319
|
+
Workers that call `self.skipWaiting()` on install (as emit-infra's does) take
|
|
320
|
+
control with no waiting stage. The page is never reloaded unasked: when a new
|
|
321
|
+
worker takes control of a tab that was already controlled, `updateAvailable`
|
|
322
|
+
becomes `true` and `applyUpdate()` reloads the page once. The same applies when
|
|
323
|
+
another tab applied the update.
|
|
324
|
+
|
|
318
325
|
A first install (no controller yet) is not reported as an update. In dev, call
|
|
319
326
|
`unregisterAll()` to clear workers left by a production build. Browsers without
|
|
320
327
|
service workers get `supported: false`.
|
|
@@ -373,6 +380,11 @@ if (push.support === 'ios-needs-install') return <InstallHint />;
|
|
|
373
380
|
<button onClick={push.subscribe} disabled={push.busy}>Enable</button>
|
|
374
381
|
```
|
|
375
382
|
|
|
383
|
+
`vapidPublicKey` may be `''` while the app is still fetching it. Until it is
|
|
384
|
+
non-blank, the on-load re-subscribe is skipped (and runs once when the key
|
|
385
|
+
arrives) and `subscribe()` sets `error` to `'push key not ready'` without
|
|
386
|
+
asking for permission or touching the push manager.
|
|
387
|
+
|
|
376
388
|
Returns `{ support, permission, subscribed, busy, error, subscribe, unsubscribe }`.
|
|
377
389
|
`support` is `'supported'`, `'unsupported'` or `'ios-needs-install'`: iOS only
|
|
378
390
|
allows Web Push in an installed PWA (16.4+), so an iPhone Safari tab (or any
|
|
@@ -27,8 +27,11 @@ function readPermission() {
|
|
|
27
27
|
if (typeof window === "undefined" || !("Notification" in window)) return "unsupported";
|
|
28
28
|
return Notification.permission;
|
|
29
29
|
}
|
|
30
|
-
function
|
|
31
|
-
return
|
|
30
|
+
function isKeyReady(key) {
|
|
31
|
+
return key.trim().length > 0;
|
|
32
|
+
}
|
|
33
|
+
function shouldResubscribe({ permission, hasSubscription, resubscribeOnLoad, hasKey = true }) {
|
|
34
|
+
return hasKey && resubscribeOnLoad && permission === "granted" && !hasSubscription;
|
|
32
35
|
}
|
|
33
36
|
function isGone(error) {
|
|
34
37
|
return typeof error === "object" && error !== null && error.status === 410;
|
|
@@ -74,6 +77,7 @@ function usePushSubscription(options) {
|
|
|
74
77
|
const [subscribed, setSubscribed] = useState(false);
|
|
75
78
|
const [busy, setBusy] = useState(false);
|
|
76
79
|
const [error, setError] = useState(null);
|
|
80
|
+
const hasKey = isKeyReady(options.vapidPublicKey);
|
|
77
81
|
const latest = useRef(options);
|
|
78
82
|
latest.current = options;
|
|
79
83
|
useEffect(() => {
|
|
@@ -90,7 +94,7 @@ function usePushSubscription(options) {
|
|
|
90
94
|
const permissionNow = readPermission();
|
|
91
95
|
setPermission(permissionNow);
|
|
92
96
|
if (existing) return setSubscribed(true);
|
|
93
|
-
if (!shouldResubscribe({ permission: permissionNow, hasSubscription: false, resubscribeOnLoad })) return;
|
|
97
|
+
if (!shouldResubscribe({ permission: permissionNow, hasSubscription: false, resubscribeOnLoad, hasKey })) return;
|
|
94
98
|
await ensureSubscribed(reg, latest.current);
|
|
95
99
|
if (!cancelled) setSubscribed(true);
|
|
96
100
|
} catch {
|
|
@@ -99,9 +103,13 @@ function usePushSubscription(options) {
|
|
|
99
103
|
return () => {
|
|
100
104
|
cancelled = true;
|
|
101
105
|
};
|
|
102
|
-
}, [resubscribeOnLoad]);
|
|
106
|
+
}, [resubscribeOnLoad, hasKey]);
|
|
103
107
|
const subscribe = useCallback(async () => {
|
|
104
108
|
if (getPushSupport() !== "supported") return;
|
|
109
|
+
if (!isKeyReady(latest.current.vapidPublicKey)) {
|
|
110
|
+
setError("push key not ready");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
105
113
|
setError(null);
|
|
106
114
|
setBusy(true);
|
|
107
115
|
try {
|
|
@@ -23,17 +23,24 @@ async function unregisterAll() {
|
|
|
23
23
|
} catch {
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = () => window.location.reload() } = {}) {
|
|
26
|
+
function registerServiceWorker({ url, scope, onUpdateAvailable, onError }, { reload = () => window.location.reload() } = {}) {
|
|
27
27
|
let registration;
|
|
28
28
|
let disposed = false;
|
|
29
29
|
let applying = false;
|
|
30
30
|
const cleanups = [];
|
|
31
31
|
const controlledAtStart = hasController();
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
let activatedUnasked = false;
|
|
33
|
+
const reloadOnce = () => {
|
|
34
|
+
if (reloaded) return;
|
|
34
35
|
reloaded = true;
|
|
35
36
|
reload();
|
|
36
37
|
};
|
|
38
|
+
const onControllerChange = () => {
|
|
39
|
+
if (applying) return reloadOnce();
|
|
40
|
+
if (!controlledAtStart || activatedUnasked) return;
|
|
41
|
+
activatedUnasked = true;
|
|
42
|
+
onUpdateAvailable();
|
|
43
|
+
};
|
|
37
44
|
navigator.serviceWorker.addEventListener("controllerchange", onControllerChange);
|
|
38
45
|
cleanups.push(() => navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange));
|
|
39
46
|
const watchInstalling = (reg) => {
|
|
@@ -56,10 +63,16 @@ function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = ()
|
|
|
56
63
|
const onUpdateFound = () => watchInstalling(reg);
|
|
57
64
|
reg.addEventListener("updatefound", onUpdateFound);
|
|
58
65
|
cleanups.push(() => reg.removeEventListener("updatefound", onUpdateFound));
|
|
59
|
-
}).catch(() => {
|
|
66
|
+
}).catch((error) => {
|
|
67
|
+
if (disposed) return;
|
|
68
|
+
try {
|
|
69
|
+
onError?.(error);
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
60
72
|
});
|
|
61
73
|
return {
|
|
62
74
|
applyUpdate() {
|
|
75
|
+
if (activatedUnasked) return reloadOnce();
|
|
63
76
|
const waiting = registration?.waiting;
|
|
64
77
|
if (!waiting) return;
|
|
65
78
|
applying = true;
|
|
@@ -73,9 +86,11 @@ function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = ()
|
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
// src/service-worker/use-service-worker.ts
|
|
76
|
-
function useServiceWorker({ url, scope, enabled = true }) {
|
|
89
|
+
function useServiceWorker({ url, scope, enabled = true, onError }) {
|
|
77
90
|
const [supported, setSupported] = useState(true);
|
|
78
91
|
const [updateAvailable, setUpdateAvailable] = useState(false);
|
|
92
|
+
const onErrorRef = useRef(onError);
|
|
93
|
+
onErrorRef.current = onError;
|
|
79
94
|
const handle = useRef(void 0);
|
|
80
95
|
useEffect(() => {
|
|
81
96
|
if (!enabled) return;
|
|
@@ -83,7 +98,12 @@ function useServiceWorker({ url, scope, enabled = true }) {
|
|
|
83
98
|
setSupported(false);
|
|
84
99
|
return;
|
|
85
100
|
}
|
|
86
|
-
const registered = registerServiceWorker({
|
|
101
|
+
const registered = registerServiceWorker({
|
|
102
|
+
url,
|
|
103
|
+
scope,
|
|
104
|
+
onUpdateAvailable: () => setUpdateAvailable(true),
|
|
105
|
+
onError: (error) => onErrorRef.current?.(error)
|
|
106
|
+
});
|
|
87
107
|
handle.current = registered;
|
|
88
108
|
return () => {
|
|
89
109
|
registered.dispose();
|
package/dist/index.cjs
CHANGED
|
@@ -1089,17 +1089,24 @@ async function unregisterAll() {
|
|
|
1089
1089
|
} catch {
|
|
1090
1090
|
}
|
|
1091
1091
|
}
|
|
1092
|
-
function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = () => window.location.reload() } = {}) {
|
|
1092
|
+
function registerServiceWorker({ url, scope, onUpdateAvailable, onError }, { reload = () => window.location.reload() } = {}) {
|
|
1093
1093
|
let registration;
|
|
1094
1094
|
let disposed = false;
|
|
1095
1095
|
let applying = false;
|
|
1096
1096
|
const cleanups = [];
|
|
1097
1097
|
const controlledAtStart = hasController();
|
|
1098
|
-
|
|
1099
|
-
|
|
1098
|
+
let activatedUnasked = false;
|
|
1099
|
+
const reloadOnce = () => {
|
|
1100
|
+
if (reloaded) return;
|
|
1100
1101
|
reloaded = true;
|
|
1101
1102
|
reload();
|
|
1102
1103
|
};
|
|
1104
|
+
const onControllerChange = () => {
|
|
1105
|
+
if (applying) return reloadOnce();
|
|
1106
|
+
if (!controlledAtStart || activatedUnasked) return;
|
|
1107
|
+
activatedUnasked = true;
|
|
1108
|
+
onUpdateAvailable();
|
|
1109
|
+
};
|
|
1103
1110
|
navigator.serviceWorker.addEventListener("controllerchange", onControllerChange);
|
|
1104
1111
|
cleanups.push(() => navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange));
|
|
1105
1112
|
const watchInstalling = (reg) => {
|
|
@@ -1122,10 +1129,16 @@ function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = ()
|
|
|
1122
1129
|
const onUpdateFound = () => watchInstalling(reg);
|
|
1123
1130
|
reg.addEventListener("updatefound", onUpdateFound);
|
|
1124
1131
|
cleanups.push(() => reg.removeEventListener("updatefound", onUpdateFound));
|
|
1125
|
-
}).catch(() => {
|
|
1132
|
+
}).catch((error) => {
|
|
1133
|
+
if (disposed) return;
|
|
1134
|
+
try {
|
|
1135
|
+
onError?.(error);
|
|
1136
|
+
} catch {
|
|
1137
|
+
}
|
|
1126
1138
|
});
|
|
1127
1139
|
return {
|
|
1128
1140
|
applyUpdate() {
|
|
1141
|
+
if (activatedUnasked) return reloadOnce();
|
|
1129
1142
|
const waiting = registration?.waiting;
|
|
1130
1143
|
if (!waiting) return;
|
|
1131
1144
|
applying = true;
|
|
@@ -1139,9 +1152,11 @@ function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = ()
|
|
|
1139
1152
|
}
|
|
1140
1153
|
|
|
1141
1154
|
// src/service-worker/use-service-worker.ts
|
|
1142
|
-
function useServiceWorker({ url, scope, enabled = true }) {
|
|
1155
|
+
function useServiceWorker({ url, scope, enabled = true, onError }) {
|
|
1143
1156
|
const [supported, setSupported] = (0, import_react18.useState)(true);
|
|
1144
1157
|
const [updateAvailable, setUpdateAvailable] = (0, import_react18.useState)(false);
|
|
1158
|
+
const onErrorRef = (0, import_react18.useRef)(onError);
|
|
1159
|
+
onErrorRef.current = onError;
|
|
1145
1160
|
const handle = (0, import_react18.useRef)(void 0);
|
|
1146
1161
|
(0, import_react18.useEffect)(() => {
|
|
1147
1162
|
if (!enabled) return;
|
|
@@ -1149,7 +1164,12 @@ function useServiceWorker({ url, scope, enabled = true }) {
|
|
|
1149
1164
|
setSupported(false);
|
|
1150
1165
|
return;
|
|
1151
1166
|
}
|
|
1152
|
-
const registered = registerServiceWorker({
|
|
1167
|
+
const registered = registerServiceWorker({
|
|
1168
|
+
url,
|
|
1169
|
+
scope,
|
|
1170
|
+
onUpdateAvailable: () => setUpdateAvailable(true),
|
|
1171
|
+
onError: (error) => onErrorRef.current?.(error)
|
|
1172
|
+
});
|
|
1153
1173
|
handle.current = registered;
|
|
1154
1174
|
return () => {
|
|
1155
1175
|
registered.dispose();
|
|
@@ -1324,8 +1344,11 @@ function readPermission() {
|
|
|
1324
1344
|
if (typeof window === "undefined" || !("Notification" in window)) return "unsupported";
|
|
1325
1345
|
return Notification.permission;
|
|
1326
1346
|
}
|
|
1327
|
-
function
|
|
1328
|
-
return
|
|
1347
|
+
function isKeyReady(key) {
|
|
1348
|
+
return key.trim().length > 0;
|
|
1349
|
+
}
|
|
1350
|
+
function shouldResubscribe({ permission, hasSubscription, resubscribeOnLoad, hasKey = true }) {
|
|
1351
|
+
return hasKey && resubscribeOnLoad && permission === "granted" && !hasSubscription;
|
|
1329
1352
|
}
|
|
1330
1353
|
function isGone(error) {
|
|
1331
1354
|
return typeof error === "object" && error !== null && error.status === 410;
|
|
@@ -1371,6 +1394,7 @@ function usePushSubscription(options) {
|
|
|
1371
1394
|
const [subscribed, setSubscribed] = (0, import_react21.useState)(false);
|
|
1372
1395
|
const [busy, setBusy] = (0, import_react21.useState)(false);
|
|
1373
1396
|
const [error, setError] = (0, import_react21.useState)(null);
|
|
1397
|
+
const hasKey = isKeyReady(options.vapidPublicKey);
|
|
1374
1398
|
const latest = (0, import_react21.useRef)(options);
|
|
1375
1399
|
latest.current = options;
|
|
1376
1400
|
(0, import_react21.useEffect)(() => {
|
|
@@ -1387,7 +1411,7 @@ function usePushSubscription(options) {
|
|
|
1387
1411
|
const permissionNow = readPermission();
|
|
1388
1412
|
setPermission(permissionNow);
|
|
1389
1413
|
if (existing) return setSubscribed(true);
|
|
1390
|
-
if (!shouldResubscribe({ permission: permissionNow, hasSubscription: false, resubscribeOnLoad })) return;
|
|
1414
|
+
if (!shouldResubscribe({ permission: permissionNow, hasSubscription: false, resubscribeOnLoad, hasKey })) return;
|
|
1391
1415
|
await ensureSubscribed(reg, latest.current);
|
|
1392
1416
|
if (!cancelled) setSubscribed(true);
|
|
1393
1417
|
} catch {
|
|
@@ -1396,9 +1420,13 @@ function usePushSubscription(options) {
|
|
|
1396
1420
|
return () => {
|
|
1397
1421
|
cancelled = true;
|
|
1398
1422
|
};
|
|
1399
|
-
}, [resubscribeOnLoad]);
|
|
1423
|
+
}, [resubscribeOnLoad, hasKey]);
|
|
1400
1424
|
const subscribe3 = (0, import_react21.useCallback)(async () => {
|
|
1401
1425
|
if (getPushSupport() !== "supported") return;
|
|
1426
|
+
if (!isKeyReady(latest.current.vapidPublicKey)) {
|
|
1427
|
+
setError("push key not ready");
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1402
1430
|
setError(null);
|
|
1403
1431
|
setBusy(true);
|
|
1404
1432
|
try {
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
deriveUpdateState,
|
|
4
4
|
unregisterAll,
|
|
5
5
|
useServiceWorker
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UVJBJETY.js";
|
|
7
7
|
import {
|
|
8
8
|
canShareFiles,
|
|
9
9
|
isShareCancel,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
shouldResubscribe,
|
|
22
22
|
urlBase64ToUint8Array,
|
|
23
23
|
usePushSubscription
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-R2PFNE3U.js";
|
|
25
25
|
import {
|
|
26
26
|
nextDelay,
|
|
27
27
|
useVisibilityPolling
|
package/dist/push.cjs
CHANGED
|
@@ -82,8 +82,11 @@ function readPermission() {
|
|
|
82
82
|
if (typeof window === "undefined" || !("Notification" in window)) return "unsupported";
|
|
83
83
|
return Notification.permission;
|
|
84
84
|
}
|
|
85
|
-
function
|
|
86
|
-
return
|
|
85
|
+
function isKeyReady(key) {
|
|
86
|
+
return key.trim().length > 0;
|
|
87
|
+
}
|
|
88
|
+
function shouldResubscribe({ permission, hasSubscription, resubscribeOnLoad, hasKey = true }) {
|
|
89
|
+
return hasKey && resubscribeOnLoad && permission === "granted" && !hasSubscription;
|
|
87
90
|
}
|
|
88
91
|
function isGone(error) {
|
|
89
92
|
return typeof error === "object" && error !== null && error.status === 410;
|
|
@@ -129,6 +132,7 @@ function usePushSubscription(options) {
|
|
|
129
132
|
const [subscribed, setSubscribed] = (0, import_react.useState)(false);
|
|
130
133
|
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
131
134
|
const [error, setError] = (0, import_react.useState)(null);
|
|
135
|
+
const hasKey = isKeyReady(options.vapidPublicKey);
|
|
132
136
|
const latest = (0, import_react.useRef)(options);
|
|
133
137
|
latest.current = options;
|
|
134
138
|
(0, import_react.useEffect)(() => {
|
|
@@ -145,7 +149,7 @@ function usePushSubscription(options) {
|
|
|
145
149
|
const permissionNow = readPermission();
|
|
146
150
|
setPermission(permissionNow);
|
|
147
151
|
if (existing) return setSubscribed(true);
|
|
148
|
-
if (!shouldResubscribe({ permission: permissionNow, hasSubscription: false, resubscribeOnLoad })) return;
|
|
152
|
+
if (!shouldResubscribe({ permission: permissionNow, hasSubscription: false, resubscribeOnLoad, hasKey })) return;
|
|
149
153
|
await ensureSubscribed(reg, latest.current);
|
|
150
154
|
if (!cancelled) setSubscribed(true);
|
|
151
155
|
} catch {
|
|
@@ -154,9 +158,13 @@ function usePushSubscription(options) {
|
|
|
154
158
|
return () => {
|
|
155
159
|
cancelled = true;
|
|
156
160
|
};
|
|
157
|
-
}, [resubscribeOnLoad]);
|
|
161
|
+
}, [resubscribeOnLoad, hasKey]);
|
|
158
162
|
const subscribe = (0, import_react.useCallback)(async () => {
|
|
159
163
|
if (getPushSupport() !== "supported") return;
|
|
164
|
+
if (!isKeyReady(latest.current.vapidPublicKey)) {
|
|
165
|
+
setError("push key not ready");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
160
168
|
setError(null);
|
|
161
169
|
setBusy(true);
|
|
162
170
|
try {
|
package/dist/push.d.cts
CHANGED
|
@@ -12,8 +12,9 @@ interface ResubscribeInput {
|
|
|
12
12
|
permission: PushPermission;
|
|
13
13
|
hasSubscription: boolean;
|
|
14
14
|
resubscribeOnLoad: boolean;
|
|
15
|
+
hasKey?: boolean;
|
|
15
16
|
}
|
|
16
|
-
declare function shouldResubscribe({ permission, hasSubscription, resubscribeOnLoad }: ResubscribeInput): boolean;
|
|
17
|
+
declare function shouldResubscribe({ permission, hasSubscription, resubscribeOnLoad, hasKey }: ResubscribeInput): boolean;
|
|
17
18
|
|
|
18
19
|
interface UsePushSubscriptionOptions {
|
|
19
20
|
vapidPublicKey: string;
|
package/dist/push.d.ts
CHANGED
|
@@ -12,8 +12,9 @@ interface ResubscribeInput {
|
|
|
12
12
|
permission: PushPermission;
|
|
13
13
|
hasSubscription: boolean;
|
|
14
14
|
resubscribeOnLoad: boolean;
|
|
15
|
+
hasKey?: boolean;
|
|
15
16
|
}
|
|
16
|
-
declare function shouldResubscribe({ permission, hasSubscription, resubscribeOnLoad }: ResubscribeInput): boolean;
|
|
17
|
+
declare function shouldResubscribe({ permission, hasSubscription, resubscribeOnLoad, hasKey }: ResubscribeInput): boolean;
|
|
17
18
|
|
|
18
19
|
interface UsePushSubscriptionOptions {
|
|
19
20
|
vapidPublicKey: string;
|
package/dist/push.js
CHANGED
package/dist/service-worker.cjs
CHANGED
|
@@ -50,17 +50,24 @@ async function unregisterAll() {
|
|
|
50
50
|
} catch {
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = () => window.location.reload() } = {}) {
|
|
53
|
+
function registerServiceWorker({ url, scope, onUpdateAvailable, onError }, { reload = () => window.location.reload() } = {}) {
|
|
54
54
|
let registration;
|
|
55
55
|
let disposed = false;
|
|
56
56
|
let applying = false;
|
|
57
57
|
const cleanups = [];
|
|
58
58
|
const controlledAtStart = hasController();
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
let activatedUnasked = false;
|
|
60
|
+
const reloadOnce = () => {
|
|
61
|
+
if (reloaded) return;
|
|
61
62
|
reloaded = true;
|
|
62
63
|
reload();
|
|
63
64
|
};
|
|
65
|
+
const onControllerChange = () => {
|
|
66
|
+
if (applying) return reloadOnce();
|
|
67
|
+
if (!controlledAtStart || activatedUnasked) return;
|
|
68
|
+
activatedUnasked = true;
|
|
69
|
+
onUpdateAvailable();
|
|
70
|
+
};
|
|
64
71
|
navigator.serviceWorker.addEventListener("controllerchange", onControllerChange);
|
|
65
72
|
cleanups.push(() => navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange));
|
|
66
73
|
const watchInstalling = (reg) => {
|
|
@@ -83,10 +90,16 @@ function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = ()
|
|
|
83
90
|
const onUpdateFound = () => watchInstalling(reg);
|
|
84
91
|
reg.addEventListener("updatefound", onUpdateFound);
|
|
85
92
|
cleanups.push(() => reg.removeEventListener("updatefound", onUpdateFound));
|
|
86
|
-
}).catch(() => {
|
|
93
|
+
}).catch((error) => {
|
|
94
|
+
if (disposed) return;
|
|
95
|
+
try {
|
|
96
|
+
onError?.(error);
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
87
99
|
});
|
|
88
100
|
return {
|
|
89
101
|
applyUpdate() {
|
|
102
|
+
if (activatedUnasked) return reloadOnce();
|
|
90
103
|
const waiting = registration?.waiting;
|
|
91
104
|
if (!waiting) return;
|
|
92
105
|
applying = true;
|
|
@@ -100,9 +113,11 @@ function registerServiceWorker({ url, scope, onUpdateAvailable }, { reload = ()
|
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
// src/service-worker/use-service-worker.ts
|
|
103
|
-
function useServiceWorker({ url, scope, enabled = true }) {
|
|
116
|
+
function useServiceWorker({ url, scope, enabled = true, onError }) {
|
|
104
117
|
const [supported, setSupported] = (0, import_react.useState)(true);
|
|
105
118
|
const [updateAvailable, setUpdateAvailable] = (0, import_react.useState)(false);
|
|
119
|
+
const onErrorRef = (0, import_react.useRef)(onError);
|
|
120
|
+
onErrorRef.current = onError;
|
|
106
121
|
const handle = (0, import_react.useRef)(void 0);
|
|
107
122
|
(0, import_react.useEffect)(() => {
|
|
108
123
|
if (!enabled) return;
|
|
@@ -110,7 +125,12 @@ function useServiceWorker({ url, scope, enabled = true }) {
|
|
|
110
125
|
setSupported(false);
|
|
111
126
|
return;
|
|
112
127
|
}
|
|
113
|
-
const registered = registerServiceWorker({
|
|
128
|
+
const registered = registerServiceWorker({
|
|
129
|
+
url,
|
|
130
|
+
scope,
|
|
131
|
+
onUpdateAvailable: () => setUpdateAvailable(true),
|
|
132
|
+
onError: (error) => onErrorRef.current?.(error)
|
|
133
|
+
});
|
|
114
134
|
handle.current = registered;
|
|
115
135
|
return () => {
|
|
116
136
|
registered.dispose();
|
|
@@ -2,13 +2,14 @@ interface UseServiceWorkerOptions {
|
|
|
2
2
|
url: string;
|
|
3
3
|
scope?: string;
|
|
4
4
|
enabled?: boolean;
|
|
5
|
+
onError?: (error: unknown) => void;
|
|
5
6
|
}
|
|
6
7
|
interface ServiceWorkerState$1 {
|
|
7
8
|
supported: boolean;
|
|
8
9
|
updateAvailable: boolean;
|
|
9
10
|
applyUpdate: () => void;
|
|
10
11
|
}
|
|
11
|
-
declare function useServiceWorker({ url, scope, enabled }: UseServiceWorkerOptions): ServiceWorkerState$1;
|
|
12
|
+
declare function useServiceWorker({ url, scope, enabled, onError }: UseServiceWorkerOptions): ServiceWorkerState$1;
|
|
12
13
|
|
|
13
14
|
declare function unregisterAll(): Promise<void>;
|
|
14
15
|
|
package/dist/service-worker.d.ts
CHANGED
|
@@ -2,13 +2,14 @@ interface UseServiceWorkerOptions {
|
|
|
2
2
|
url: string;
|
|
3
3
|
scope?: string;
|
|
4
4
|
enabled?: boolean;
|
|
5
|
+
onError?: (error: unknown) => void;
|
|
5
6
|
}
|
|
6
7
|
interface ServiceWorkerState$1 {
|
|
7
8
|
supported: boolean;
|
|
8
9
|
updateAvailable: boolean;
|
|
9
10
|
applyUpdate: () => void;
|
|
10
11
|
}
|
|
11
|
-
declare function useServiceWorker({ url, scope, enabled }: UseServiceWorkerOptions): ServiceWorkerState$1;
|
|
12
|
+
declare function useServiceWorker({ url, scope, enabled, onError }: UseServiceWorkerOptions): ServiceWorkerState$1;
|
|
12
13
|
|
|
13
14
|
declare function unregisterAll(): Promise<void>;
|
|
14
15
|
|
package/dist/service-worker.js
CHANGED
package/package.json
CHANGED