mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-02 09:43:15 +00:00
feat: prompt-mode PWA update toast instead of autoUpdate reload (#102)
This commit is contained in:
parent
47a9a1ba89
commit
a7ddd79e98
3 changed files with 78 additions and 10 deletions
|
|
@ -1,21 +1,42 @@
|
|||
import { StartClient } from "@tanstack/react-start/client"
|
||||
import { StrictMode } from "react"
|
||||
import { StrictMode, useEffect, useState } from "react"
|
||||
import { hydrateRoot } from "react-dom/client"
|
||||
import { registerSW } from "virtual:pwa-register"
|
||||
|
||||
// autoUpdate: vite-plugin-pwa generates a SW that calls skipWaiting and
|
||||
// clientsClaim, so a new deploy activates for existing tabs. Open dashboard
|
||||
// tabs may reload when the service worker takes over; this is acceptable for
|
||||
// a dev/internal dashboard where silent updates are preferred over stale
|
||||
// bundles. Skip in dev — the SW precaches production-only build artifacts that
|
||||
// 404 against the dev server.
|
||||
if (import.meta.env.PROD) {
|
||||
registerSW()
|
||||
import { PwaUpdateToast } from "@/components/PwaUpdateToast"
|
||||
|
||||
// Prompt-mode PWA: vite-plugin-pwa still generates a SW that precaches the
|
||||
// production bundle, but it does not force skipWaiting/clientsClaim. Instead,
|
||||
// the new SW waits until the user accepts the update, so open dashboard tabs
|
||||
// are not reloaded mid-session. This protects in-flight agent runs and any
|
||||
// unsaved text in the compose box. Skip in dev — the SW precaches production-only
|
||||
// build artifacts that 404 against the dev server.
|
||||
function PwaUpdateProvider() {
|
||||
const [needRefresh, setNeedRefresh] = useState(false)
|
||||
const [updateSW, setUpdateSW] = useState<(() => Promise<void>) | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
if (!import.meta.env.PROD) return
|
||||
const updateSWRef = registerSW({
|
||||
onNeedRefresh() {
|
||||
setNeedRefresh(true)
|
||||
},
|
||||
})
|
||||
setUpdateSW(() => updateSWRef)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<PwaUpdateToast
|
||||
needRefresh={needRefresh}
|
||||
updateServiceWorker={updateSW}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
hydrateRoot(
|
||||
document,
|
||||
<StrictMode>
|
||||
<StartClient />
|
||||
<PwaUpdateProvider />
|
||||
</StrictMode>
|
||||
)
|
||||
|
|
|
|||
47
ui/src/components/PwaUpdateToast.tsx
Normal file
47
ui/src/components/PwaUpdateToast.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { useEffect, useState } from "react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
interface PwaUpdateToastProps {
|
||||
needRefresh: boolean
|
||||
updateServiceWorker: (() => Promise<void>) | undefined
|
||||
}
|
||||
|
||||
export function PwaUpdateToast({
|
||||
needRefresh,
|
||||
updateServiceWorker,
|
||||
}: PwaUpdateToastProps) {
|
||||
const [dismissed, setDismissed] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (needRefresh) {
|
||||
setDismissed(false)
|
||||
}
|
||||
}, [needRefresh])
|
||||
|
||||
if (!needRefresh || dismissed) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed right-4 bottom-4 z-50 flex max-w-sm items-center gap-3 rounded-lg border border-border bg-card p-3 shadow-lg">
|
||||
<div className="flex-1 text-xs">
|
||||
<p className="font-medium">A new version is available.</p>
|
||||
<p className="text-muted-foreground">Reload to update the dashboard.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="xs"
|
||||
onClick={() => setDismissed(true)}
|
||||
aria-label="Dismiss update notice"
|
||||
>
|
||||
Later
|
||||
</Button>
|
||||
<Button size="xs" onClick={() => void updateServiceWorker?.()}>
|
||||
Reload
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -173,7 +173,7 @@ const config = defineConfig({
|
|||
tanstackStart({ spa: { enabled: true } }),
|
||||
VitePWA({
|
||||
injectRegister: false,
|
||||
registerType: "autoUpdate",
|
||||
registerType: "prompt",
|
||||
outDir: ".output/public",
|
||||
devOptions: {
|
||||
enabled: true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue