Make PWA update notice detect build changes
This commit is contained in:
1 parent
ee5d0b9ca9
commit
858034cf8a
5 files changed
+64
-7
No files matched your search
@@ -3,6 +3,7 @@ import { CheckCircle2, RefreshCw, X } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
consumePwaBuildUpdate,
|
||||
consumePwaUpdateReloadPending,
|
||||
PWA_UPDATE_NOTICE_EVENT,
|
||||
type PwaUpdateNoticeEvent,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
} from "@/lib/pwa-update-notice";
|
||||
|
||||
type Notice = {
|
||||
buildLabel?: string;
|
||||
id: number;
|
||||
kind: PwaUpdateNoticeKind;
|
||||
};
|
||||
@@ -63,6 +65,14 @@ const noticeCopy: Record<PwaUpdateNoticeKind, { title: string; description: stri
|
||||
},
|
||||
};
|
||||
|
||||
const getNoticeDescription = (notice: Notice, fallbackDescription: string) => {
|
||||
if (notice.kind === "updated" && notice.buildLabel) {
|
||||
return `当前 PWA 已是最新版本(${notice.buildLabel})。`;
|
||||
}
|
||||
|
||||
return fallbackDescription;
|
||||
};
|
||||
|
||||
export const PwaUpdateNotice = () => {
|
||||
const [displayMode, setDisplayMode] = useState(() => getDisplayMode());
|
||||
const [notice, setNotice] = useState<Notice | null>(null);
|
||||
@@ -73,18 +83,27 @@ export const PwaUpdateNotice = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (consumePwaUpdateReloadPending()) {
|
||||
setNotice({ id: Date.now(), kind: "updated" });
|
||||
if (!displayMode.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
const buildLabel = __EDGEEVER_BUILD_LABEL__;
|
||||
|
||||
if (
|
||||
consumePwaUpdateReloadPending() ||
|
||||
consumePwaBuildUpdate(__EDGEEVER_BUILD_ID__, { notifyWhenMissingBaseline: true })
|
||||
) {
|
||||
setNotice({ buildLabel, id: Date.now(), kind: "updated" });
|
||||
}
|
||||
|
||||
const handleNotice = (event: Event) => {
|
||||
const { kind } = (event as PwaUpdateNoticeEvent).detail;
|
||||
setNotice({ id: Date.now(), kind });
|
||||
const { buildLabel: eventBuildLabel, kind } = (event as PwaUpdateNoticeEvent).detail;
|
||||
setNotice({ buildLabel: eventBuildLabel ?? buildLabel, id: Date.now(), kind });
|
||||
};
|
||||
|
||||
window.addEventListener(PWA_UPDATE_NOTICE_EVENT, handleNotice);
|
||||
return () => window.removeEventListener(PWA_UPDATE_NOTICE_EVENT, handleNotice);
|
||||
}, []);
|
||||
}, [displayMode.visible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!notice) {
|
||||
@@ -126,7 +145,7 @@ export const PwaUpdateNotice = () => {
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-sm font-semibold leading-5">{copy.title}</div>
|
||||
<div className="mt-0.5 text-xs leading-5 text-slate-500">{copy.description}</div>
|
||||
<div className="mt-0.5 text-xs leading-5 text-slate-500">{getNoticeDescription(notice, copy.description)}</div>
|
||||
{isReloadRequired ? (
|
||||
<Button className="mt-2" size="sm" variant="solid" onClick={() => window.location.reload()}>
|
||||
立即刷新
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
export const PWA_UPDATE_NOTICE_EVENT = "edgeever:pwa-update-notice";
|
||||
|
||||
const PWA_BUILD_ID_KEY = "edgeever:pwa-build-id";
|
||||
const PWA_UPDATE_RELOADED_AT_KEY = "edgeever:pwa-update-reloaded-at";
|
||||
|
||||
export type PwaUpdateNoticeKind = "checking" | "updated" | "reload-required";
|
||||
|
||||
export type PwaUpdateNoticeDetail = {
|
||||
buildLabel?: string;
|
||||
kind: PwaUpdateNoticeKind;
|
||||
};
|
||||
|
||||
@@ -31,3 +33,21 @@ export const consumePwaUpdateReloadPending = () => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const consumePwaBuildUpdate = (
|
||||
currentBuildId: string,
|
||||
{ notifyWhenMissingBaseline = false }: { notifyWhenMissingBaseline?: boolean } = {}
|
||||
) => {
|
||||
try {
|
||||
const previousBuildId = window.localStorage.getItem(PWA_BUILD_ID_KEY);
|
||||
window.localStorage.setItem(PWA_BUILD_ID_KEY, currentBuildId);
|
||||
|
||||
if (!previousBuildId) {
|
||||
return notifyWhenMissingBaseline;
|
||||
}
|
||||
|
||||
return previousBuildId !== currentBuildId;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -14,7 +14,7 @@ let updateServiceWorker: ReturnType<typeof registerSW>;
|
||||
updateServiceWorker = registerSW({
|
||||
immediate: true,
|
||||
onNeedRefresh() {
|
||||
emitPwaUpdateNotice({ kind: "checking" });
|
||||
emitPwaUpdateNotice({ buildLabel: __EDGEEVER_BUILD_LABEL__, kind: "checking" });
|
||||
void updateServiceWorker(true);
|
||||
},
|
||||
onNeedReload() {
|
||||
|
||||
Vendored
+3
@@ -1,2 +1,5 @@
|
||||
/// <reference types="vite/client" />
|
||||
/// <reference types="vite-plugin-pwa/client" />
|
||||
|
||||
declare const __EDGEEVER_BUILD_ID__: string;
|
||||
declare const __EDGEEVER_BUILD_LABEL__: string;
|
||||
@@ -1,10 +1,25 @@
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { execSync } from "node:child_process";
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
import { defineConfig } from "vite";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
|
||||
const readGitCommit = () => {
|
||||
try {
|
||||
return execSync("git rev-parse --short=12 HEAD", { encoding: "utf8" }).trim();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const buildId = process.env.CF_PAGES_COMMIT_SHA?.slice(0, 12) ?? process.env.GITHUB_SHA?.slice(0, 12) ?? readGitCommit() ?? "local";
|
||||
|
||||
export default defineConfig({
|
||||
root: "apps/web",
|
||||
define: {
|
||||
__EDGEEVER_BUILD_ID__: JSON.stringify(buildId),
|
||||
__EDGEEVER_BUILD_LABEL__: JSON.stringify(buildId === "local" ? "local" : buildId.slice(0, 7)),
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
VitePWA({
|
||||
|
||||
Reference in new issue
Block a user