ps99-api 2.1.0 → 2.2.0
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/example-web/react2/package-lock.json +4954 -72
- package/example-web/react2/package.json +6 -1
- package/example-web/react2/public/manifest.json +1 -0
- package/example-web/react2/src/App.tsx +4 -8
- package/example-web/react2/src/components/CollectionConfigIndex.tsx +0 -3
- package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +6 -113
- package/example-web/react2/src/components/Footer.tsx +60 -0
- package/example-web/react2/src/components/GenericFetchComponent.tsx +6 -9
- package/example-web/react2/src/components/ImageComponent.tsx +15 -21
- package/example-web/react2/src/components/XPPotionsComponent.tsx +3 -1
- package/example-web/react2/src/hooks/useOnlineStatus.ts +20 -0
- package/example-web/react2/src/index.tsx +4 -0
- package/example-web/react2/src/serviceWorkerRegistration.ts +110 -0
- package/package.json +1 -1
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"author": "",
|
|
12
12
|
"license": "ISC",
|
|
13
13
|
"dependencies": {
|
|
14
|
+
"date-fns": "^3.6.0",
|
|
15
|
+
"idb-keyval": "^6.2.1",
|
|
14
16
|
"ps99-api": "file:../..",
|
|
15
17
|
"react": "^18.3.1",
|
|
16
18
|
"react-dom": "^18.3.1",
|
|
@@ -19,12 +21,15 @@
|
|
|
19
21
|
"devDependencies": {
|
|
20
22
|
"@types/react": "^18.3.3",
|
|
21
23
|
"@types/react-dom": "^18.3.0",
|
|
24
|
+
"clean-webpack-plugin": "^4.0.0",
|
|
22
25
|
"copy-webpack-plugin": "^12.0.2",
|
|
23
26
|
"css-loader": "^7.1.2",
|
|
27
|
+
"html-webpack-plugin": "^5.6.0",
|
|
24
28
|
"ts-loader": "^9.5.1",
|
|
25
29
|
"typescript": "^5.5.2",
|
|
26
30
|
"webpack": "^5.92.0",
|
|
27
31
|
"webpack-cli": "^5.1.4",
|
|
28
|
-
"webpack-dev-server": "^5.0.4"
|
|
32
|
+
"webpack-dev-server": "^5.0.4",
|
|
33
|
+
"workbox-webpack-plugin": "^7.1.0"
|
|
29
34
|
}
|
|
30
35
|
}
|
|
@@ -5,6 +5,7 @@ import Header from "./components/Header";
|
|
|
5
5
|
import CollectionsIndex from "./components/CollectionsIndex";
|
|
6
6
|
import CollectionConfigIndex from "./components/CollectionConfigIndex";
|
|
7
7
|
import DynamicCollectionConfigData from "./components/DynamicCollectionConfigData";
|
|
8
|
+
import Footer from "./components/Footer";
|
|
8
9
|
|
|
9
10
|
const App: React.FC = () => {
|
|
10
11
|
return (
|
|
@@ -13,15 +14,10 @@ const App: React.FC = () => {
|
|
|
13
14
|
<Routes>
|
|
14
15
|
<Route path="/" element={<HomePage />} />
|
|
15
16
|
<Route path="/collections" element={<CollectionsIndex />} />
|
|
16
|
-
<Route
|
|
17
|
-
|
|
18
|
-
element={<CollectionConfigIndex />}
|
|
19
|
-
/>
|
|
20
|
-
<Route
|
|
21
|
-
path="/collections/:collectionName/:configName"
|
|
22
|
-
element={<DynamicCollectionConfigData />}
|
|
23
|
-
/>
|
|
17
|
+
<Route path="/collections/:collectionName" element={<CollectionConfigIndex />} />
|
|
18
|
+
<Route path="/collections/:collectionName/:configName" element={<DynamicCollectionConfigData />} />
|
|
24
19
|
</Routes>
|
|
20
|
+
<Footer />
|
|
25
21
|
</Router>
|
|
26
22
|
);
|
|
27
23
|
};
|
|
@@ -33,9 +33,6 @@ const CollectionConfigIndex: React.FC = () => {
|
|
|
33
33
|
<div>
|
|
34
34
|
<h2>{collectionName} Configurations</h2>
|
|
35
35
|
<ul>
|
|
36
|
-
<li>
|
|
37
|
-
<Link to={`/collections/${collectionName}/all`}>All</Link>
|
|
38
|
-
</li>
|
|
39
36
|
{configNames.map((configName, index) => (
|
|
40
37
|
<li key={index}>
|
|
41
38
|
<Link
|
|
@@ -1,128 +1,21 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
lazy,
|
|
3
|
-
Suspense,
|
|
4
|
-
useEffect,
|
|
5
|
-
useState,
|
|
6
|
-
useRef,
|
|
7
|
-
useCallback,
|
|
8
|
-
} from "react";
|
|
1
|
+
import React, { lazy, Suspense } from "react";
|
|
9
2
|
import { useParams } from "react-router-dom";
|
|
10
|
-
import {
|
|
11
|
-
PetSimulator99API,
|
|
12
|
-
CollectionName,
|
|
13
|
-
Collection,
|
|
14
|
-
CollectionConfigData,
|
|
15
|
-
} from "ps99-api";
|
|
3
|
+
import { CollectionName } from "ps99-api";
|
|
16
4
|
|
|
17
5
|
const DynamicCollectionConfigData: React.FC = () => {
|
|
18
|
-
const { collectionName, configName } = useParams<{
|
|
19
|
-
collectionName: CollectionName;
|
|
20
|
-
configName: string;
|
|
21
|
-
}>();
|
|
6
|
+
const { collectionName, configName } = useParams<{ collectionName: CollectionName; configName: string }>();
|
|
22
7
|
|
|
23
|
-
if (!collectionName) {
|
|
24
|
-
return <div>Invalid collection name</div>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (configName === "all") {
|
|
28
|
-
return <RenderAllConfigs collectionName={collectionName} />;
|
|
8
|
+
if (!collectionName || !configName) {
|
|
9
|
+
return <div>Invalid collection or config name</div>;
|
|
29
10
|
}
|
|
30
11
|
|
|
31
12
|
const Component = lazy(() => import(`./${collectionName}Component`));
|
|
32
13
|
|
|
33
14
|
return (
|
|
34
15
|
<Suspense fallback={<div>Loading...</div>}>
|
|
35
|
-
<Component />
|
|
16
|
+
<Component configName={configName} />
|
|
36
17
|
</Suspense>
|
|
37
18
|
);
|
|
38
19
|
};
|
|
39
20
|
|
|
40
|
-
const RenderAllConfigs: React.FC<{ collectionName: CollectionName }> = ({
|
|
41
|
-
collectionName,
|
|
42
|
-
}) => {
|
|
43
|
-
const [configDataList, setConfigDataList] = useState<
|
|
44
|
-
Array<Collection<CollectionName>>
|
|
45
|
-
>([]);
|
|
46
|
-
const [page, setPage] = useState(0);
|
|
47
|
-
const [error, setError] = useState<string | null>(null);
|
|
48
|
-
const observer = useRef<IntersectionObserver | null>(null);
|
|
49
|
-
const loadMoreRef = useRef<HTMLDivElement | null>(null);
|
|
50
|
-
|
|
51
|
-
const fetchData = async (page: number) => {
|
|
52
|
-
try {
|
|
53
|
-
const api = new PetSimulator99API();
|
|
54
|
-
const response = await api.getCollection(collectionName);
|
|
55
|
-
if (response.status === "ok") {
|
|
56
|
-
const start = page * 20;
|
|
57
|
-
const end = start + 20;
|
|
58
|
-
setConfigDataList((prev) => [
|
|
59
|
-
...prev,
|
|
60
|
-
...response.data.slice(start, end),
|
|
61
|
-
]);
|
|
62
|
-
} else {
|
|
63
|
-
setError(response.error.message);
|
|
64
|
-
}
|
|
65
|
-
} catch (error) {
|
|
66
|
-
setError("Error fetching data");
|
|
67
|
-
console.error("Error fetching data:", error);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
fetchData(page);
|
|
73
|
-
}, [page]);
|
|
74
|
-
|
|
75
|
-
const lastElementRef = useCallback((node: HTMLDivElement | null) => {
|
|
76
|
-
if (observer.current) observer.current.disconnect();
|
|
77
|
-
observer.current = new IntersectionObserver((entries) => {
|
|
78
|
-
if (entries[0].isIntersecting) {
|
|
79
|
-
setPage((prevPage) => prevPage + 1);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
if (node) observer.current.observe(node);
|
|
83
|
-
}, []);
|
|
84
|
-
|
|
85
|
-
if (error) {
|
|
86
|
-
return <div>Error: {error}</div>;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<div>
|
|
91
|
-
{configDataList.map((configData, index) => {
|
|
92
|
-
const Component = lazy(() => import(`./${collectionName}Component`));
|
|
93
|
-
if (index === configDataList.length - 1) {
|
|
94
|
-
return (
|
|
95
|
-
<div ref={lastElementRef} key={index}>
|
|
96
|
-
<Suspense fallback={<div>Loading...</div>}>
|
|
97
|
-
<Component
|
|
98
|
-
configData={
|
|
99
|
-
configData.configData as CollectionConfigData<
|
|
100
|
-
typeof collectionName
|
|
101
|
-
>
|
|
102
|
-
}
|
|
103
|
-
/>
|
|
104
|
-
</Suspense>
|
|
105
|
-
</div>
|
|
106
|
-
);
|
|
107
|
-
} else {
|
|
108
|
-
return (
|
|
109
|
-
<div key={index}>
|
|
110
|
-
<Suspense fallback={<div>Loading...</div>}>
|
|
111
|
-
<Component
|
|
112
|
-
configData={
|
|
113
|
-
configData.configData as CollectionConfigData<
|
|
114
|
-
typeof collectionName
|
|
115
|
-
>
|
|
116
|
-
}
|
|
117
|
-
/>
|
|
118
|
-
</Suspense>
|
|
119
|
-
</div>
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
})}
|
|
123
|
-
<div ref={loadMoreRef}></div>
|
|
124
|
-
</div>
|
|
125
|
-
);
|
|
126
|
-
};
|
|
127
|
-
|
|
128
21
|
export default DynamicCollectionConfigData;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { useOnlineStatus } from "../hooks/useOnlineStatus";
|
|
3
|
+
|
|
4
|
+
const Footer: React.FC = () => {
|
|
5
|
+
const isOnline = useOnlineStatus();
|
|
6
|
+
const [lastUpdate, setLastUpdate] = useState<string | null>(null);
|
|
7
|
+
const [loading, setLoading] = useState(false);
|
|
8
|
+
|
|
9
|
+
const updateLastUpdate = () => {
|
|
10
|
+
setLastUpdate(new Date().toLocaleString());
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const fetchData = async () => {
|
|
15
|
+
setLoading(true);
|
|
16
|
+
// Simulate a fetch call to update lastUpdate time
|
|
17
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
18
|
+
updateLastUpdate();
|
|
19
|
+
setLoading(false);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
fetchData();
|
|
23
|
+
|
|
24
|
+
window.addEventListener("online", updateLastUpdate);
|
|
25
|
+
window.addEventListener("offline", updateLastUpdate);
|
|
26
|
+
|
|
27
|
+
return () => {
|
|
28
|
+
window.removeEventListener("online", updateLastUpdate);
|
|
29
|
+
window.removeEventListener("offline", updateLastUpdate);
|
|
30
|
+
};
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<footer
|
|
35
|
+
style={{
|
|
36
|
+
display: "flex",
|
|
37
|
+
justifyContent: "space-between",
|
|
38
|
+
padding: "1em",
|
|
39
|
+
borderTop: "1px solid #ccc",
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
<div>
|
|
43
|
+
{loading ? (
|
|
44
|
+
<span>♻️ Loading...</span>
|
|
45
|
+
) : (
|
|
46
|
+
<span>Last update: {lastUpdate}</span>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
<div>
|
|
50
|
+
{isOnline ? (
|
|
51
|
+
<span style={{ color: "green" }}>● Online</span>
|
|
52
|
+
) : (
|
|
53
|
+
<span style={{ color: "red" }}>● Offline</span>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
</footer>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default Footer;
|
|
@@ -9,10 +9,10 @@ interface GenericFetchComponentProps<T> {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const GenericFetchComponent = <T,>({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}: GenericFetchComponentProps<T>) => {
|
|
12
|
+
collectionName,
|
|
13
|
+
render,
|
|
14
|
+
configData,
|
|
15
|
+
}: GenericFetchComponentProps<T>) => {
|
|
16
16
|
const { configName } = useParams<{ configName: string }>();
|
|
17
17
|
const [data, setData] = useState<T | null>(configData || null);
|
|
18
18
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -23,12 +23,9 @@ export const GenericFetchComponent = <T,>({
|
|
|
23
23
|
const fetchData = async () => {
|
|
24
24
|
if (!configName) return;
|
|
25
25
|
const api = new PetSimulator99API();
|
|
26
|
-
const response: ApiResponseBody<any[]> =
|
|
27
|
-
await api.getCollection(collectionName);
|
|
26
|
+
const response: ApiResponseBody<any[]> = await api.getCollection(collectionName);
|
|
28
27
|
if (response.status === "ok") {
|
|
29
|
-
const item = response.data.find(
|
|
30
|
-
(item) => item.configName === configName,
|
|
31
|
-
);
|
|
28
|
+
const item = response.data.find((item) => item.configName === configName);
|
|
32
29
|
if (item) {
|
|
33
30
|
setData(item.configData);
|
|
34
31
|
} else {
|
|
@@ -7,14 +7,14 @@ interface ImageProps {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const MAX_CONCURRENT_REQUESTS = 5;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const requestQueue: Array<() => void> = [];
|
|
11
|
+
let activeRequests = 0;
|
|
12
12
|
|
|
13
13
|
const processQueue = () => {
|
|
14
|
-
if (
|
|
15
|
-
const nextRequest =
|
|
14
|
+
if (activeRequests < MAX_CONCURRENT_REQUESTS && requestQueue.length > 0) {
|
|
15
|
+
const nextRequest = requestQueue.shift();
|
|
16
16
|
if (nextRequest) {
|
|
17
|
-
|
|
17
|
+
activeRequests++;
|
|
18
18
|
nextRequest();
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -22,31 +22,27 @@ const processQueue = () => {
|
|
|
22
22
|
|
|
23
23
|
const ImageComponent: React.FC<ImageProps> = ({ src, alt }) => {
|
|
24
24
|
const [imageUrl, setImageUrl] = useState<string | null>(null);
|
|
25
|
-
const [error, setError] = useState<string | null>(null);
|
|
26
25
|
|
|
27
26
|
useEffect(() => {
|
|
28
27
|
const fetchImage = async () => {
|
|
28
|
+
const api = new PetSimulator99API();
|
|
29
29
|
try {
|
|
30
|
-
const api = new PetSimulator99API();
|
|
31
30
|
const imageBlob = await api.getImage(src);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
);
|
|
35
|
-
setImageUrl(imageUrl);
|
|
31
|
+
const url = URL.createObjectURL(new Blob([imageBlob], { type: "image/png" }));
|
|
32
|
+
setImageUrl(url);
|
|
36
33
|
} catch (error) {
|
|
37
|
-
setError("Error fetching image");
|
|
38
34
|
console.error("Error fetching image:", error);
|
|
39
35
|
} finally {
|
|
40
|
-
|
|
36
|
+
activeRequests--;
|
|
41
37
|
processQueue();
|
|
42
38
|
}
|
|
43
39
|
};
|
|
44
40
|
|
|
45
|
-
const
|
|
41
|
+
const requestImage = () => {
|
|
46
42
|
fetchImage();
|
|
47
43
|
};
|
|
48
44
|
|
|
49
|
-
|
|
45
|
+
requestQueue.push(requestImage);
|
|
50
46
|
processQueue();
|
|
51
47
|
|
|
52
48
|
return () => {
|
|
@@ -54,14 +50,12 @@ const ImageComponent: React.FC<ImageProps> = ({ src, alt }) => {
|
|
|
54
50
|
URL.revokeObjectURL(imageUrl);
|
|
55
51
|
}
|
|
56
52
|
};
|
|
57
|
-
}, [src
|
|
58
|
-
|
|
59
|
-
if (error) {
|
|
60
|
-
return <div>{error}</div>;
|
|
61
|
-
}
|
|
53
|
+
}, [src]);
|
|
62
54
|
|
|
63
55
|
return (
|
|
64
|
-
<div>
|
|
56
|
+
<div>
|
|
57
|
+
{imageUrl ? <img src={imageUrl} alt={alt} /> : <p>Loading...</p>}
|
|
58
|
+
</div>
|
|
65
59
|
);
|
|
66
60
|
};
|
|
67
61
|
|
|
@@ -3,7 +3,9 @@ import { CollectionConfigData } from "ps99-api";
|
|
|
3
3
|
import { GenericFetchComponent } from "./GenericFetchComponent";
|
|
4
4
|
import ImageComponent from "./ImageComponent";
|
|
5
5
|
|
|
6
|
-
const XPPotionsComponent: React.FC<{
|
|
6
|
+
const XPPotionsComponent: React.FC<{
|
|
7
|
+
configData?: CollectionConfigData<"XPPotions">;
|
|
8
|
+
}> = ({ configData }) => {
|
|
7
9
|
return (
|
|
8
10
|
<GenericFetchComponent<CollectionConfigData<"XPPotions">>
|
|
9
11
|
collectionName="XPPotions"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
export const useOnlineStatus = () => {
|
|
4
|
+
const [isOnline, setIsOnline] = useState(navigator.onLine);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const handleOnline = () => setIsOnline(true);
|
|
8
|
+
const handleOffline = () => setIsOnline(false);
|
|
9
|
+
|
|
10
|
+
window.addEventListener("online", handleOnline);
|
|
11
|
+
window.addEventListener("offline", handleOffline);
|
|
12
|
+
|
|
13
|
+
return () => {
|
|
14
|
+
window.removeEventListener("online", handleOnline);
|
|
15
|
+
window.removeEventListener("offline", handleOffline);
|
|
16
|
+
};
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
19
|
+
return isOnline;
|
|
20
|
+
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
3
|
import App from "./App";
|
|
4
|
+
import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
|
|
4
5
|
|
|
5
6
|
const container = document.getElementById("root");
|
|
6
7
|
const root = createRoot(container!);
|
|
7
8
|
root.render(<App />);
|
|
9
|
+
|
|
10
|
+
// Register the service worker
|
|
11
|
+
serviceWorkerRegistration.register();
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const isLocalhost = Boolean(
|
|
2
|
+
window.location.hostname === "localhost" ||
|
|
3
|
+
window.location.hostname === "[::1]" ||
|
|
4
|
+
window.location.hostname.match(/^127(?:\.\d+){0,2}\.\d+$/),
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
type Config = {
|
|
8
|
+
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
|
9
|
+
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function register(config?: Config) {
|
|
13
|
+
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
|
|
14
|
+
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
|
15
|
+
if (publicUrl.origin !== window.location.origin) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
window.addEventListener("load", () => {
|
|
20
|
+
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
|
21
|
+
|
|
22
|
+
if (isLocalhost) {
|
|
23
|
+
checkValidServiceWorker(swUrl, config);
|
|
24
|
+
|
|
25
|
+
navigator.serviceWorker.ready.then(() => {
|
|
26
|
+
console.log(
|
|
27
|
+
"This web app is being served cache-first by a service " +
|
|
28
|
+
"worker. To learn more, visit https://cra.link/PWA",
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
registerValidSW(swUrl, config);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function registerValidSW(swUrl: string, config?: Config) {
|
|
39
|
+
navigator.serviceWorker
|
|
40
|
+
.register(swUrl)
|
|
41
|
+
.then((registration) => {
|
|
42
|
+
registration.onupdatefound = () => {
|
|
43
|
+
const installingWorker = registration.installing;
|
|
44
|
+
if (installingWorker == null) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
installingWorker.onstatechange = () => {
|
|
48
|
+
if (installingWorker.state === "installed") {
|
|
49
|
+
if (navigator.serviceWorker.controller) {
|
|
50
|
+
console.log(
|
|
51
|
+
"New content is available and will be used when all " +
|
|
52
|
+
"tabs for this page are closed. See https://cra.link/PWA.",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (config && config.onUpdate) {
|
|
56
|
+
config.onUpdate(registration);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
console.log("Content is cached for offline use.");
|
|
60
|
+
|
|
61
|
+
if (config && config.onSuccess) {
|
|
62
|
+
config.onSuccess(registration);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
})
|
|
69
|
+
.catch((error) => {
|
|
70
|
+
console.error("Error during service worker registration:", error);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
|
75
|
+
fetch(swUrl, {
|
|
76
|
+
headers: { "Service-Worker": "script" },
|
|
77
|
+
})
|
|
78
|
+
.then((response) => {
|
|
79
|
+
const contentType = response.headers.get("content-type");
|
|
80
|
+
if (
|
|
81
|
+
response.status === 404 ||
|
|
82
|
+
(contentType != null && contentType.indexOf("javascript") === -1)
|
|
83
|
+
) {
|
|
84
|
+
navigator.serviceWorker.ready.then((registration) => {
|
|
85
|
+
registration.unregister().then(() => {
|
|
86
|
+
window.location.reload();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
registerValidSW(swUrl, config);
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
.catch(() => {
|
|
94
|
+
console.log(
|
|
95
|
+
"No internet connection found. App is running in offline mode.",
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function unregister() {
|
|
101
|
+
if ("serviceWorker" in navigator) {
|
|
102
|
+
navigator.serviceWorker.ready
|
|
103
|
+
.then((registration) => {
|
|
104
|
+
registration.unregister();
|
|
105
|
+
})
|
|
106
|
+
.catch((error) => {
|
|
107
|
+
console.error(error.message);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|