add service worker auto update prompt

This commit is contained in:
Zhang Minghan 2023-10-04 08:50:18 +08:00
parent b431754d71
commit c346c06804
7 changed files with 97 additions and 27 deletions

View File

@ -0,0 +1,53 @@
import { useRegisterSW } from 'virtual:pwa-register/react'
import {version} from "../conf.ts";
import {useTranslation} from "react-i18next";
import {useToast} from "./ui/use-toast.ts";
import {useEffect} from "react";
import {ToastAction} from "./ui/toast.tsx";
function ReloadPrompt() {
const { t } = useTranslation();
const { toast } = useToast();
const {
offlineReady: [offlineReady, setOfflineReady],
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
} = useRegisterSW({
onRegisteredSW() {
console.debug(`[service] service worker registered (version ${version})`);
},
onRegisterError(error) {
console.log(`[service] service worker registration failed: ${error.message}`);
},
});
useEffect(() => {
if (offlineReady) {
toast({
title: t('service.offline-title'),
description: t('service.offline'),
})
}
if (needRefresh) {
toast({
title: t('service.title'),
description: t('service.description'),
action: (
<ToastAction altText={t('service.update')} onClick={() => updateServiceWorker(true)}>
{t('service.update')}
</ToastAction>
),
});
setOfflineReady(false);
setNeedRefresh(false);
}
}, []);
return <></>;
}
export default ReloadPrompt;

View File

@ -1,6 +1,6 @@
import axios from "axios";
export const version: string = "3.2.2";
export const version: string = "3.2.3";
export const deploy: boolean = true;
export let rest_api: string = "http://localhost:8094";
export let ws_api: string = "ws://localhost:8094";

View File

@ -162,6 +162,13 @@ const resources = {
"copied-description": "API key has been copied to clipboard",
"learn-more": "Learn more",
},
service: {
"title": "New Version Available",
"description": "A new version is available. Do you want to update now?",
"update": "Update",
"offline-title": "Offline Mode",
"offline": "App is currently offline.",
}
},
},
cn: {
@ -310,6 +317,13 @@ const resources = {
"copied-description": "API 密钥已复制到剪贴板",
"learn-more": "了解更多",
},
service: {
"title": "发现新版本",
"description": "发现新版本,是否立即更新?",
"update": "更新",
"offline-title": "离线模式",
"offline": "应用当前处于离线状态。",
}
},
},
ru: {
@ -469,6 +483,13 @@ const resources = {
"copied-description": "Ключ API скопирован в буфер обмена",
"learn-more": "Узнать больше",
},
service: {
"title": "Доступна новая версия",
"description": "Доступна новая версия. Хотите обновить сейчас?",
"update": "Обновить",
"offline-title": "Режим оффлайн",
"offline": "Приложение в настоящее время находится в автономном режиме.",
}
},
},
};

View File

@ -5,11 +5,12 @@ import "./conf.ts";
import "./i18n.ts";
import "./assets/main.less";
import "./assets/globals.less";
import "./service.ts";
import "./conf.ts";
import ReloadPrompt from "./components/ReloadService.tsx";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<ReloadPrompt />
<App />
</React.StrictMode>,
);

View File

@ -1,24 +0,0 @@
// @ts-ignore
import { registerSW } from "virtual:pwa-register";
import { version } from "./conf.ts";
export const updateSW = registerSW({
onRegisteredSW(url: string, registration: ServiceWorkerRegistration) {
if (!(!registration.installing && navigator)) return;
if ("connection" in navigator && !navigator.onLine) return;
console.debug(
"[service] checking for update (current version: %s)",
version,
);
fetch(url, {
headers: { "Service-Worker": "script", "Cache-Control": "no-cache" },
cache: "no-store",
}).then(async (resp) => {
if (resp?.status === 200) {
await registration.update();
if (registration.onupdatefound) console.debug("[service] update found");
}
});
},
});

15
app/src/types/service.d.ts vendored Normal file
View File

@ -0,0 +1,15 @@
declare module 'virtual:pwa-register/react' {
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-expect-error ignore when React is not installed
import type { Dispatch, SetStateAction } from 'react'
import type { RegisterSWOptions } from 'vite-plugin-pwa/types'
export type { RegisterSWOptions }
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: [boolean, Dispatch<SetStateAction<boolean>>]
offlineReady: [boolean, Dispatch<SetStateAction<boolean>>]
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
onRegistered: (registration: ServiceWorkerRegistration) => void
}
}

View File

@ -5,6 +5,10 @@
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"types": [
"vite-plugin-pwa/react",
"./types/*.d.ts",
],
/* Bundler mode */
"moduleResolution": "bundler",
@ -25,5 +29,5 @@
"paths": {
"@/*": ["./src/*"]
},
"references": [{ "path": "./tsconfig.node.json" }]
"references": [{ "path": "./tsconfig.node.json" }],
}