@swoff/cli 0.4.1 → 0.4.4
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/dist/lib/commands/generate-guide.js +15 -15
- package/dist/lib/commands/generate-guide.js.map +1 -1
- package/dist/lib/commands/init.js +52 -6
- package/dist/lib/commands/init.js.map +1 -1
- package/dist/lib/config/validator.js +1 -1
- package/dist/lib/config/validator.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js +2 -1
- package/dist/lib/generators/file-generators/client-injector-bundle.js.map +1 -1
- package/dist/lib/generators/file-generators/client-injector.js +2 -1
- package/dist/lib/generators/file-generators/client-injector.js.map +1 -1
- package/dist/lib/generators/file-generators/context.js +4 -1
- package/dist/lib/generators/file-generators/context.js.map +1 -1
- package/dist/lib/generators/file-generators/generate-framework-adapters.js +28 -22
- package/dist/lib/generators/file-generators/generate-framework-adapters.js.map +1 -1
- package/dist/lib/generators/sw-sections/activate-handler.js +1 -5
- package/dist/lib/generators/sw-sections/activate-handler.js.map +1 -1
- package/dist/lib/generators/sw-sections/background-precache.js +8 -8
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js +2 -5
- package/dist/lib/generators/sw-sections/batch-refresh-queue.js.map +1 -1
- package/dist/lib/generators/sw-sections/fetch-handler.js +14 -3
- package/dist/lib/generators/sw-sections/fetch-handler.js.map +1 -1
- package/dist/lib/shared/config-types.js +1 -0
- package/dist/lib/shared/config-types.js.map +1 -1
- package/dist/lib/utils/detect-framework.js +16 -0
- package/dist/lib/utils/detect-framework.js.map +1 -1
- package/dist/runtime/client-injector-bundle.js +2 -2
- package/dist/runtime/client-injector-bundle.js.map +1 -1
- package/dist/runtime/client-injector.js +3 -2
- package/dist/runtime/client-injector.js.map +1 -1
- package/dist/runtime/gql-wrapper.js +3 -1
- package/dist/runtime/gql-wrapper.js.map +1 -1
- package/dist/runtime/swoff-api-bundle.js +2 -1
- package/dist/runtime/swoff-api-bundle.js.map +1 -1
- package/package.json +1 -1
- package/templates/react/{useOfflineAnalytics.jsx → useSwoffAnalytics.jsx} +3 -3
- package/templates/react/{useOfflineAnalytics.tsx → useSwoffAnalytics.tsx} +3 -3
- package/templates/react/{useAuth.jsx → useSwoffAuth.jsx} +2 -2
- package/templates/react/{useAuth.tsx → useSwoffAuth.tsx} +2 -2
- package/templates/react/{useCachedFetch.jsx → useSwoffFetch.jsx} +6 -6
- package/templates/react/{useCachedFetch.tsx → useSwoffFetch.tsx} +7 -7
- package/templates/react/{useMutation.jsx → useSwoffMutation.jsx} +3 -3
- package/templates/react/{useMutation.tsx → useSwoffMutation.tsx} +3 -3
- package/templates/react/{useMutationState.jsx → useSwoffMutationState.jsx} +1 -1
- package/templates/react/{useMutationState.tsx → useSwoffMutationState.tsx} +2 -2
- package/templates/react/{useNetworkStatus.jsx → useSwoffNetwork.jsx} +2 -2
- package/templates/react/{useNetworkStatus.tsx → useSwoffNetwork.tsx} +2 -2
- package/templates/react/{usePrecacheProgress.jsx → useSwoffPrecache.jsx} +4 -7
- package/templates/react/{usePrecacheProgress.tsx → useSwoffPrecache.tsx} +4 -7
- package/templates/react/{usePrefetch.jsx → useSwoffPrefetch.jsx} +2 -2
- package/templates/react/{usePrefetch.tsx → useSwoffPrefetch.tsx} +2 -2
- package/templates/react/{usePushSubscription.jsx → useSwoffPush.jsx} +1 -1
- package/templates/react/{usePushSubscription.tsx → useSwoffPush.tsx} +1 -1
- package/templates/react/{usePwaInstall.jsx → useSwoffPwa.jsx} +1 -1
- package/templates/react/{usePwaInstall.tsx → useSwoffPwa.tsx} +1 -1
- package/templates/react/{useMutationQueue.jsx → useSwoffQueue.jsx} +2 -2
- package/templates/react/{useMutationQueue.tsx → useSwoffQueue.tsx} +2 -2
- package/templates/react/{useStorageEstimate.jsx → useSwoffStorage.jsx} +2 -2
- package/templates/react/{useStorageEstimate.tsx → useSwoffStorage.tsx} +2 -2
- package/templates/react/{useBackgroundSync.jsx → useSwoffSync.jsx} +1 -1
- package/templates/react/{useBackgroundSync.tsx → useSwoffSync.tsx} +1 -1
- package/templates/svelte/useSwoffAnalytics.js +31 -0
- package/templates/svelte/useSwoffAnalytics.ts +37 -0
- package/templates/svelte/useSwoffAuth.js +95 -0
- package/templates/svelte/useSwoffAuth.ts +96 -0
- package/templates/svelte/useSwoffFetch.js +97 -0
- package/templates/svelte/useSwoffFetch.ts +112 -0
- package/templates/svelte/useSwoffMutation.js +110 -0
- package/templates/svelte/useSwoffMutation.ts +141 -0
- package/templates/svelte/useSwoffMutationState.js +27 -0
- package/templates/svelte/useSwoffMutationState.ts +28 -0
- package/templates/svelte/useSwoffNetwork.js +75 -0
- package/templates/svelte/useSwoffNetwork.ts +75 -0
- package/templates/svelte/useSwoffPrecache.js +19 -0
- package/templates/svelte/useSwoffPrecache.ts +19 -0
- package/templates/svelte/useSwoffPrefetch.js +19 -0
- package/templates/svelte/useSwoffPrefetch.ts +20 -0
- package/templates/svelte/useSwoffPush.js +82 -0
- package/templates/svelte/useSwoffPush.ts +82 -0
- package/templates/svelte/useSwoffPwa.js +27 -0
- package/templates/svelte/useSwoffPwa.ts +27 -0
- package/templates/svelte/useSwoffQueue.js +57 -0
- package/templates/svelte/useSwoffQueue.ts +67 -0
- package/templates/svelte/useSwoffReset.js +21 -0
- package/templates/svelte/useSwoffReset.ts +32 -0
- package/templates/svelte/useSwoffStorage.js +47 -0
- package/templates/svelte/useSwoffStorage.ts +47 -0
- package/templates/svelte/useSwoffSync.js +31 -0
- package/templates/svelte/useSwoffSync.ts +31 -0
- package/templates/vue/useSwoffAnalytics.js +46 -0
- package/templates/vue/useSwoffAnalytics.ts +54 -0
- package/templates/vue/useSwoffAuth.js +106 -0
- package/templates/vue/useSwoffAuth.ts +118 -0
- package/templates/vue/useSwoffFetch.js +127 -0
- package/templates/vue/useSwoffFetch.ts +143 -0
- package/templates/vue/useSwoffMutation.js +124 -0
- package/templates/vue/useSwoffMutation.ts +155 -0
- package/templates/vue/useSwoffMutationState.js +35 -0
- package/templates/vue/useSwoffMutationState.ts +36 -0
- package/templates/vue/useSwoffNetwork.js +86 -0
- package/templates/vue/useSwoffNetwork.ts +89 -0
- package/templates/vue/useSwoffPrecache.js +19 -0
- package/templates/vue/useSwoffPrecache.ts +19 -0
- package/templates/vue/useSwoffPrefetch.js +33 -0
- package/templates/vue/useSwoffPrefetch.ts +41 -0
- package/templates/vue/useSwoffPush.js +81 -0
- package/templates/vue/useSwoffPush.ts +81 -0
- package/templates/vue/useSwoffPwa.js +27 -0
- package/templates/vue/useSwoffPwa.ts +27 -0
- package/templates/vue/useSwoffQueue.js +70 -0
- package/templates/vue/useSwoffQueue.ts +82 -0
- package/templates/vue/useSwoffReset.js +32 -0
- package/templates/vue/useSwoffReset.ts +39 -0
- package/templates/vue/useSwoffStorage.js +56 -0
- package/templates/vue/useSwoffStorage.ts +57 -0
- package/templates/vue/useSwoffSync.js +30 -0
- package/templates/vue/useSwoffSync.ts +30 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { fetchWithCache } from "../fetch/core.js";
|
|
3
|
+
import {
|
|
4
|
+
trackMutation,
|
|
5
|
+
resolveMutation,
|
|
6
|
+
rejectMutation,
|
|
7
|
+
} from "../mutation/state.js";
|
|
8
|
+
|
|
9
|
+
export function useSwoffMutation(url, options = {}) {
|
|
10
|
+
const lastResult = writable(null);
|
|
11
|
+
const isLoading = writable(false);
|
|
12
|
+
const mutationId = writable(null);
|
|
13
|
+
|
|
14
|
+
const inFlightKeys = new Set();
|
|
15
|
+
|
|
16
|
+
async function mutate(body, callbacks) {
|
|
17
|
+
const key = options.mutationKey;
|
|
18
|
+
if (key && inFlightKeys.has(key)) return { status: "skipped" };
|
|
19
|
+
if (key) inFlightKeys.add(key);
|
|
20
|
+
|
|
21
|
+
let retriesLeft =
|
|
22
|
+
options.retry === true
|
|
23
|
+
? Infinity
|
|
24
|
+
: typeof options.retry === "number"
|
|
25
|
+
? options.retry
|
|
26
|
+
: 0;
|
|
27
|
+
|
|
28
|
+
const mid = "mut-" + crypto.randomUUID();
|
|
29
|
+
mutationId.set(mid);
|
|
30
|
+
trackMutation(mid, "pending");
|
|
31
|
+
isLoading.set(true);
|
|
32
|
+
|
|
33
|
+
options.onMutate?.();
|
|
34
|
+
|
|
35
|
+
const attempt = async () => {
|
|
36
|
+
try {
|
|
37
|
+
const {
|
|
38
|
+
mutationKey,
|
|
39
|
+
onMutate,
|
|
40
|
+
onSuccess,
|
|
41
|
+
onError,
|
|
42
|
+
onSettled,
|
|
43
|
+
retry,
|
|
44
|
+
...fetchOnlyOptions
|
|
45
|
+
} = options;
|
|
46
|
+
const fetchOptions = { ...fetchOnlyOptions, body };
|
|
47
|
+
const { response, queued } = await fetchWithCache(url, fetchOptions);
|
|
48
|
+
if (queued) {
|
|
49
|
+
resolveMutation(mid, null);
|
|
50
|
+
isLoading.set(false);
|
|
51
|
+
const r = { status: "queued" };
|
|
52
|
+
lastResult.set(r);
|
|
53
|
+
options.onSettled?.();
|
|
54
|
+
callbacks?.onSettled?.();
|
|
55
|
+
if (key) inFlightKeys.delete(key);
|
|
56
|
+
return r;
|
|
57
|
+
}
|
|
58
|
+
const data = await response.json();
|
|
59
|
+
resolveMutation(mid, data);
|
|
60
|
+
isLoading.set(false);
|
|
61
|
+
const r = { status: "success", data };
|
|
62
|
+
lastResult.set(r);
|
|
63
|
+
options.onSuccess?.(data);
|
|
64
|
+
callbacks?.onSuccess?.(data);
|
|
65
|
+
options.onSettled?.();
|
|
66
|
+
callbacks?.onSettled?.();
|
|
67
|
+
if (key) inFlightKeys.delete(key);
|
|
68
|
+
return r;
|
|
69
|
+
} catch (err) {
|
|
70
|
+
if (retriesLeft > 0) {
|
|
71
|
+
retriesLeft--;
|
|
72
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
73
|
+
return attempt();
|
|
74
|
+
}
|
|
75
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
76
|
+
rejectMutation(mid, error);
|
|
77
|
+
isLoading.set(false);
|
|
78
|
+
const r = { status: "error", error };
|
|
79
|
+
lastResult.set(r);
|
|
80
|
+
options.onError?.(error);
|
|
81
|
+
callbacks?.onError?.(error);
|
|
82
|
+
options.onSettled?.();
|
|
83
|
+
callbacks?.onSettled?.();
|
|
84
|
+
if (key) inFlightKeys.delete(key);
|
|
85
|
+
return r;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return attempt();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function reset() {
|
|
93
|
+
lastResult.set(null);
|
|
94
|
+
isLoading.set(false);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
mutate,
|
|
99
|
+
isLoading,
|
|
100
|
+
isIdle: lastResult,
|
|
101
|
+
isQueued: lastResult,
|
|
102
|
+
isSuccess: lastResult,
|
|
103
|
+
isError: lastResult,
|
|
104
|
+
data: lastResult,
|
|
105
|
+
error: lastResult,
|
|
106
|
+
lastResult,
|
|
107
|
+
reset,
|
|
108
|
+
mutationId,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { fetchWithCache } from "../fetch/core";
|
|
3
|
+
import {
|
|
4
|
+
trackMutation,
|
|
5
|
+
resolveMutation,
|
|
6
|
+
rejectMutation,
|
|
7
|
+
} from "../mutation/state";
|
|
8
|
+
import type { FetchWithCacheOptions } from "../fetch/core";
|
|
9
|
+
|
|
10
|
+
export interface MutateCallbacks<TData> {
|
|
11
|
+
onSuccess?: (data: TData) => void;
|
|
12
|
+
onError?: (error: Error) => void;
|
|
13
|
+
onSettled?: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UseMutationOptions<TData> extends FetchWithCacheOptions {
|
|
17
|
+
onMutate?: () => void;
|
|
18
|
+
onSuccess?: (data: TData) => void;
|
|
19
|
+
onError?: (error: Error) => void;
|
|
20
|
+
onSettled?: () => void;
|
|
21
|
+
mutationKey?: string;
|
|
22
|
+
retry?: number | boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type MutateResult<T> =
|
|
26
|
+
| { status: "skipped" }
|
|
27
|
+
| { status: "queued" }
|
|
28
|
+
| { status: "success"; data: T }
|
|
29
|
+
| { status: "error"; error: Error };
|
|
30
|
+
|
|
31
|
+
export function useSwoffMutation<TData = unknown>(
|
|
32
|
+
url: string,
|
|
33
|
+
options: UseMutationOptions<TData> = {},
|
|
34
|
+
) {
|
|
35
|
+
const lastResult = writable<MutateResult<TData> | null>(null);
|
|
36
|
+
const isLoading = writable(false);
|
|
37
|
+
const mutationId = writable<string | null>(null);
|
|
38
|
+
|
|
39
|
+
const inFlightKeys = new Set<string>();
|
|
40
|
+
|
|
41
|
+
async function mutate(
|
|
42
|
+
body?: unknown,
|
|
43
|
+
callbacks?: MutateCallbacks<TData>,
|
|
44
|
+
): Promise<MutateResult<TData>> {
|
|
45
|
+
const key = options.mutationKey;
|
|
46
|
+
if (key && inFlightKeys.has(key)) return { status: "skipped" };
|
|
47
|
+
if (key) inFlightKeys.add(key);
|
|
48
|
+
|
|
49
|
+
let retriesLeft: number =
|
|
50
|
+
options.retry === true
|
|
51
|
+
? Infinity
|
|
52
|
+
: typeof options.retry === "number"
|
|
53
|
+
? options.retry
|
|
54
|
+
: 0;
|
|
55
|
+
|
|
56
|
+
const mid = "mut-" + crypto.randomUUID();
|
|
57
|
+
mutationId.set(mid);
|
|
58
|
+
trackMutation(mid, "pending");
|
|
59
|
+
isLoading.set(true);
|
|
60
|
+
|
|
61
|
+
options.onMutate?.();
|
|
62
|
+
|
|
63
|
+
const attempt = async (): Promise<MutateResult<TData>> => {
|
|
64
|
+
try {
|
|
65
|
+
const {
|
|
66
|
+
mutationKey,
|
|
67
|
+
onMutate,
|
|
68
|
+
onSuccess,
|
|
69
|
+
onError,
|
|
70
|
+
onSettled,
|
|
71
|
+
retry,
|
|
72
|
+
...fetchOnlyOptions
|
|
73
|
+
} = options;
|
|
74
|
+
const fetchOptions = { ...fetchOnlyOptions, body };
|
|
75
|
+
const { response, queued } = await fetchWithCache<TData>(
|
|
76
|
+
url,
|
|
77
|
+
fetchOptions as FetchWithCacheOptions,
|
|
78
|
+
);
|
|
79
|
+
if (queued) {
|
|
80
|
+
resolveMutation(mid, null);
|
|
81
|
+
isLoading.set(false);
|
|
82
|
+
const r: MutateResult<TData> = { status: "queued" };
|
|
83
|
+
lastResult.set(r);
|
|
84
|
+
options.onSettled?.();
|
|
85
|
+
callbacks?.onSettled?.();
|
|
86
|
+
if (key) inFlightKeys.delete(key);
|
|
87
|
+
return r;
|
|
88
|
+
}
|
|
89
|
+
const data: TData = await response.json();
|
|
90
|
+
resolveMutation(mid, data);
|
|
91
|
+
isLoading.set(false);
|
|
92
|
+
const r: MutateResult<TData> = { status: "success", data };
|
|
93
|
+
lastResult.set(r);
|
|
94
|
+
options.onSuccess?.(data);
|
|
95
|
+
callbacks?.onSuccess?.(data);
|
|
96
|
+
options.onSettled?.();
|
|
97
|
+
callbacks?.onSettled?.();
|
|
98
|
+
if (key) inFlightKeys.delete(key);
|
|
99
|
+
return r;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if (retriesLeft > 0) {
|
|
102
|
+
retriesLeft--;
|
|
103
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
104
|
+
return attempt();
|
|
105
|
+
}
|
|
106
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
107
|
+
rejectMutation(mid, error);
|
|
108
|
+
isLoading.set(false);
|
|
109
|
+
const r: MutateResult<TData> = { status: "error", error };
|
|
110
|
+
lastResult.set(r);
|
|
111
|
+
options.onError?.(error);
|
|
112
|
+
callbacks?.onError?.(error);
|
|
113
|
+
options.onSettled?.();
|
|
114
|
+
callbacks?.onSettled?.();
|
|
115
|
+
if (key) inFlightKeys.delete(key);
|
|
116
|
+
return r;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return attempt();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function reset() {
|
|
124
|
+
lastResult.set(null);
|
|
125
|
+
isLoading.set(false);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
mutate,
|
|
130
|
+
isLoading,
|
|
131
|
+
isIdle: lastResult,
|
|
132
|
+
isQueued: lastResult,
|
|
133
|
+
isSuccess: lastResult,
|
|
134
|
+
isError: lastResult,
|
|
135
|
+
data: lastResult,
|
|
136
|
+
error: lastResult,
|
|
137
|
+
lastResult,
|
|
138
|
+
reset,
|
|
139
|
+
mutationId,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { getMutationState, onMutationStateChange } from "../mutation/state.js";
|
|
4
|
+
|
|
5
|
+
export function useSwoffMutationState(id) {
|
|
6
|
+
const state = writable(null);
|
|
7
|
+
|
|
8
|
+
function update() {
|
|
9
|
+
state.set(id ? (getMutationState(id) ?? null) : null);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
onMount(update);
|
|
13
|
+
|
|
14
|
+
let cleanup;
|
|
15
|
+
|
|
16
|
+
onMount(() => {
|
|
17
|
+
const unsub = onMutationStateChange(update);
|
|
18
|
+
if (typeof unsub === "function") {
|
|
19
|
+
cleanup = unsub;
|
|
20
|
+
}
|
|
21
|
+
return () => {
|
|
22
|
+
cleanup?.();
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return state;
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { getMutationState, onMutationStateChange } from "../mutation/state";
|
|
4
|
+
import type { MutationState } from "../mutation/state";
|
|
5
|
+
|
|
6
|
+
export function useSwoffMutationState(id: string | null) {
|
|
7
|
+
const state = writable<MutationState | null>(null);
|
|
8
|
+
|
|
9
|
+
function update() {
|
|
10
|
+
state.set(id ? (getMutationState(id) ?? null) : null);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
onMount(update);
|
|
14
|
+
|
|
15
|
+
let cleanup: (() => void) | undefined;
|
|
16
|
+
|
|
17
|
+
onMount(() => {
|
|
18
|
+
const unsub = onMutationStateChange(update);
|
|
19
|
+
if (typeof unsub === "function") {
|
|
20
|
+
cleanup = unsub;
|
|
21
|
+
}
|
|
22
|
+
return () => {
|
|
23
|
+
cleanup?.();
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return state;
|
|
28
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import {
|
|
4
|
+
CONNECTIVITY_EVENT,
|
|
5
|
+
forceRetry,
|
|
6
|
+
getCurrentOnlineStatus,
|
|
7
|
+
} from "../connectivity.js";
|
|
8
|
+
|
|
9
|
+
export function useSwoffNetwork() {
|
|
10
|
+
const online = writable(true);
|
|
11
|
+
const wasOffline = writable(false);
|
|
12
|
+
const lastChangedAt = writable(null);
|
|
13
|
+
const effectiveType = writable(null);
|
|
14
|
+
const downlink = writable(null);
|
|
15
|
+
const isRetrying = writable(false);
|
|
16
|
+
|
|
17
|
+
let wasOfflineFlag = false;
|
|
18
|
+
|
|
19
|
+
onMount(() => {
|
|
20
|
+
if (!getCurrentOnlineStatus()) {
|
|
21
|
+
wasOfflineFlag = true;
|
|
22
|
+
online.set(false);
|
|
23
|
+
wasOffline.set(true);
|
|
24
|
+
lastChangedAt.set(Date.now());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const connection = navigator.connection;
|
|
28
|
+
|
|
29
|
+
function handleConnectivityChange(e) {
|
|
30
|
+
const isTrulyOnline = e.detail.online;
|
|
31
|
+
if (!isTrulyOnline) {
|
|
32
|
+
wasOfflineFlag = true;
|
|
33
|
+
} else {
|
|
34
|
+
wasOfflineFlag = false;
|
|
35
|
+
}
|
|
36
|
+
online.set(isTrulyOnline);
|
|
37
|
+
wasOffline.set(wasOfflineFlag);
|
|
38
|
+
lastChangedAt.set(Date.now());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function onTypeChange() {
|
|
42
|
+
if (!connection) return;
|
|
43
|
+
effectiveType.set(connection.effectiveType);
|
|
44
|
+
downlink.set(connection.downlink);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
window.addEventListener(CONNECTIVITY_EVENT, handleConnectivityChange);
|
|
48
|
+
if (connection) {
|
|
49
|
+
connection.addEventListener("change", onTypeChange);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
window.removeEventListener(CONNECTIVITY_EVENT, handleConnectivityChange);
|
|
54
|
+
if (connection) {
|
|
55
|
+
connection.removeEventListener("change", onTypeChange);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
async function retry() {
|
|
61
|
+
isRetrying.set(true);
|
|
62
|
+
await forceRetry();
|
|
63
|
+
isRetrying.set(false);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
online,
|
|
68
|
+
wasOffline,
|
|
69
|
+
lastChangedAt,
|
|
70
|
+
effectiveType,
|
|
71
|
+
downlink,
|
|
72
|
+
isRetrying,
|
|
73
|
+
retry,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import {
|
|
4
|
+
CONNECTIVITY_EVENT,
|
|
5
|
+
forceRetry,
|
|
6
|
+
getCurrentOnlineStatus,
|
|
7
|
+
} from "../connectivity";
|
|
8
|
+
|
|
9
|
+
export function useSwoffNetwork() {
|
|
10
|
+
const online = writable(true);
|
|
11
|
+
const wasOffline = writable(false);
|
|
12
|
+
const lastChangedAt = writable<number | null>(null);
|
|
13
|
+
const effectiveType = writable<string | null>(null);
|
|
14
|
+
const downlink = writable<number | null>(null);
|
|
15
|
+
const isRetrying = writable(false);
|
|
16
|
+
|
|
17
|
+
let wasOfflineFlag = false;
|
|
18
|
+
|
|
19
|
+
onMount(() => {
|
|
20
|
+
if (!getCurrentOnlineStatus()) {
|
|
21
|
+
wasOfflineFlag = true;
|
|
22
|
+
online.set(false);
|
|
23
|
+
wasOffline.set(true);
|
|
24
|
+
lastChangedAt.set(Date.now());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const connection = navigator.connection;
|
|
28
|
+
|
|
29
|
+
function handleConnectivityChange(e: Event) {
|
|
30
|
+
const isTrulyOnline = (e as CustomEvent).detail.online;
|
|
31
|
+
if (!isTrulyOnline) {
|
|
32
|
+
wasOfflineFlag = true;
|
|
33
|
+
} else {
|
|
34
|
+
wasOfflineFlag = false;
|
|
35
|
+
}
|
|
36
|
+
online.set(isTrulyOnline);
|
|
37
|
+
wasOffline.set(wasOfflineFlag);
|
|
38
|
+
lastChangedAt.set(Date.now());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function onTypeChange() {
|
|
42
|
+
if (!connection) return;
|
|
43
|
+
effectiveType.set(connection.effectiveType);
|
|
44
|
+
downlink.set(connection.downlink);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
window.addEventListener(CONNECTIVITY_EVENT, handleConnectivityChange);
|
|
48
|
+
if (connection) {
|
|
49
|
+
connection.addEventListener("change", onTypeChange);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return () => {
|
|
53
|
+
window.removeEventListener(CONNECTIVITY_EVENT, handleConnectivityChange);
|
|
54
|
+
if (connection) {
|
|
55
|
+
connection.removeEventListener("change", onTypeChange);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
async function retry() {
|
|
61
|
+
isRetrying.set(true);
|
|
62
|
+
await forceRetry();
|
|
63
|
+
isRetrying.set(false);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
online,
|
|
68
|
+
wasOffline,
|
|
69
|
+
lastChangedAt,
|
|
70
|
+
effectiveType,
|
|
71
|
+
downlink,
|
|
72
|
+
isRetrying,
|
|
73
|
+
retry,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
|
|
4
|
+
export function useSwoffPrecache() {
|
|
5
|
+
const progress = writable(0);
|
|
6
|
+
|
|
7
|
+
function onProgress(e) {
|
|
8
|
+
progress.set(e.detail.percent);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
onMount(() => {
|
|
12
|
+
window.addEventListener("sw-progress", onProgress);
|
|
13
|
+
return () => {
|
|
14
|
+
window.removeEventListener("sw-progress", onProgress);
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return { progress };
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
|
|
4
|
+
export function useSwoffPrecache() {
|
|
5
|
+
const progress = writable(0);
|
|
6
|
+
|
|
7
|
+
function onProgress(e: WindowEventMap["sw-progress"]) {
|
|
8
|
+
progress.set(e.detail.percent);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
onMount(() => {
|
|
12
|
+
window.addEventListener("sw-progress", onProgress);
|
|
13
|
+
return () => {
|
|
14
|
+
window.removeEventListener("sw-progress", onProgress);
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return { progress };
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { prefetchCache } from "../fetch/core.js";
|
|
3
|
+
|
|
4
|
+
export function useSwoffPrefetch() {
|
|
5
|
+
const prefetchList = writable([]);
|
|
6
|
+
|
|
7
|
+
function prefetch(url, options) {
|
|
8
|
+
prefetchCache(url, options);
|
|
9
|
+
prefetchList.update((list) =>
|
|
10
|
+
list.includes(url) ? list : [...list, url],
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function clear() {
|
|
15
|
+
prefetchList.set([]);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return { prefetch, prefetchList, clear };
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { prefetchCache } from "../fetch/core";
|
|
3
|
+
import type { FetchWithCacheOptions } from "../fetch/core";
|
|
4
|
+
|
|
5
|
+
export function useSwoffPrefetch() {
|
|
6
|
+
const prefetchList = writable<string[]>([]);
|
|
7
|
+
|
|
8
|
+
function prefetch(url: string, options?: FetchWithCacheOptions) {
|
|
9
|
+
prefetchCache(url, options);
|
|
10
|
+
prefetchList.update((list) =>
|
|
11
|
+
list.includes(url) ? list : [...list, url],
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function clear() {
|
|
16
|
+
prefetchList.set([]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { prefetch, prefetchList, clear };
|
|
20
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import {
|
|
4
|
+
subscribeToPush,
|
|
5
|
+
unsubscribeFromPush,
|
|
6
|
+
isSubscribed,
|
|
7
|
+
getPushSubscription,
|
|
8
|
+
} from "../realtime/notifications.js";
|
|
9
|
+
|
|
10
|
+
export function useSwoffPush() {
|
|
11
|
+
const subscribed = writable(false);
|
|
12
|
+
const subscription = writable(null);
|
|
13
|
+
const permission = writable(
|
|
14
|
+
typeof Notification !== "undefined" ? Notification.permission : "denied",
|
|
15
|
+
);
|
|
16
|
+
const loading = writable(true);
|
|
17
|
+
|
|
18
|
+
onMount(() => {
|
|
19
|
+
let cancelled = false;
|
|
20
|
+
|
|
21
|
+
isSubscribed().then((subd) => {
|
|
22
|
+
if (cancelled) return;
|
|
23
|
+
if (subd) {
|
|
24
|
+
getPushSubscription().then((sub) => {
|
|
25
|
+
if (cancelled) return;
|
|
26
|
+
subscribed.set(true);
|
|
27
|
+
subscription.set(sub);
|
|
28
|
+
permission.set(Notification.permission);
|
|
29
|
+
loading.set(false);
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
subscribed.set(false);
|
|
33
|
+
subscription.set(null);
|
|
34
|
+
permission.set(Notification.permission);
|
|
35
|
+
loading.set(false);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function onSubChanged(e) {
|
|
40
|
+
if (cancelled) return;
|
|
41
|
+
if (!e.detail.subscribed) {
|
|
42
|
+
subscribed.set(false);
|
|
43
|
+
subscription.set(null);
|
|
44
|
+
permission.set(Notification.permission);
|
|
45
|
+
loading.set(false);
|
|
46
|
+
} else {
|
|
47
|
+
getPushSubscription().then((sub) => {
|
|
48
|
+
if (cancelled) return;
|
|
49
|
+
subscribed.set(true);
|
|
50
|
+
subscription.set(sub);
|
|
51
|
+
permission.set(Notification.permission);
|
|
52
|
+
loading.set(false);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function onPermissionChanged(e) {
|
|
58
|
+
if (cancelled) return;
|
|
59
|
+
permission.set(e.detail.permission);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
window.addEventListener("push-subscription-changed", onSubChanged);
|
|
63
|
+
window.addEventListener("push-permission-changed", onPermissionChanged);
|
|
64
|
+
|
|
65
|
+
return () => {
|
|
66
|
+
cancelled = true;
|
|
67
|
+
window.removeEventListener("push-subscription-changed", onSubChanged);
|
|
68
|
+
window.removeEventListener("push-permission-changed", onPermissionChanged);
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
async function subscribe() {
|
|
73
|
+
const sub = await subscribeToPush();
|
|
74
|
+
return sub !== null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function unsubscribe() {
|
|
78
|
+
await unsubscribeFromPush();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { subscribed, subscription, permission, loading, subscribe, unsubscribe };
|
|
82
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { writable } from "svelte/store";
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import {
|
|
4
|
+
subscribeToPush,
|
|
5
|
+
unsubscribeFromPush,
|
|
6
|
+
isSubscribed,
|
|
7
|
+
getPushSubscription,
|
|
8
|
+
} from "../realtime/notifications";
|
|
9
|
+
|
|
10
|
+
export function useSwoffPush() {
|
|
11
|
+
const subscribed = writable(false);
|
|
12
|
+
const subscription = writable<PushSubscription | null>(null);
|
|
13
|
+
const permission = writable(
|
|
14
|
+
typeof Notification !== "undefined" ? Notification.permission : "denied",
|
|
15
|
+
);
|
|
16
|
+
const loading = writable(true);
|
|
17
|
+
|
|
18
|
+
onMount(() => {
|
|
19
|
+
let cancelled = false;
|
|
20
|
+
|
|
21
|
+
isSubscribed().then((subd) => {
|
|
22
|
+
if (cancelled) return;
|
|
23
|
+
if (subd) {
|
|
24
|
+
getPushSubscription().then((sub) => {
|
|
25
|
+
if (cancelled) return;
|
|
26
|
+
subscribed.set(true);
|
|
27
|
+
subscription.set(sub);
|
|
28
|
+
permission.set(Notification.permission);
|
|
29
|
+
loading.set(false);
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
subscribed.set(false);
|
|
33
|
+
subscription.set(null);
|
|
34
|
+
permission.set(Notification.permission);
|
|
35
|
+
loading.set(false);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function onSubChanged(e: CustomEvent) {
|
|
40
|
+
if (cancelled) return;
|
|
41
|
+
if (!e.detail.subscribed) {
|
|
42
|
+
subscribed.set(false);
|
|
43
|
+
subscription.set(null);
|
|
44
|
+
permission.set(Notification.permission);
|
|
45
|
+
loading.set(false);
|
|
46
|
+
} else {
|
|
47
|
+
getPushSubscription().then((sub) => {
|
|
48
|
+
if (cancelled) return;
|
|
49
|
+
subscribed.set(true);
|
|
50
|
+
subscription.set(sub);
|
|
51
|
+
permission.set(Notification.permission);
|
|
52
|
+
loading.set(false);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function onPermissionChanged(e: CustomEvent) {
|
|
58
|
+
if (cancelled) return;
|
|
59
|
+
permission.set(e.detail.permission);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
window.addEventListener("push-subscription-changed", onSubChanged);
|
|
63
|
+
window.addEventListener("push-permission-changed", onPermissionChanged);
|
|
64
|
+
|
|
65
|
+
return () => {
|
|
66
|
+
cancelled = true;
|
|
67
|
+
window.removeEventListener("push-subscription-changed", onSubChanged);
|
|
68
|
+
window.removeEventListener("push-permission-changed", onPermissionChanged);
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
async function subscribe() {
|
|
73
|
+
const sub = await subscribeToPush();
|
|
74
|
+
return sub !== null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function unsubscribe() {
|
|
78
|
+
await unsubscribeFromPush();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { subscribed, subscription, permission, loading, subscribe, unsubscribe };
|
|
82
|
+
}
|