fix: PWA-Update-Banner nach Reload zuverlässig ausblenden
needRefresh zurücksetzen, Reload-Fallback ergänzen und kurze Suppression nach Update, damit die Benachrichtigung nicht erneut erscheint. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,17 +5,16 @@ import { usePwaUpdate } from '../hooks/usePwaUpdate.js'
|
|||||||
|
|
||||||
export default function PwaUpdatePrompt() {
|
export default function PwaUpdatePrompt() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { needRefresh, updateApp } = usePwaUpdate()
|
const { needRefresh, updateApp, dismissUpdate } = usePwaUpdate()
|
||||||
const [updating, setUpdating] = useState(false)
|
const [updating, setUpdating] = useState(false)
|
||||||
const [dismissed, setDismissed] = useState(false)
|
|
||||||
|
|
||||||
if (!needRefresh || dismissed) return null
|
if (!needRefresh) return null
|
||||||
|
|
||||||
const handleUpdate = async () => {
|
const handleUpdate = async () => {
|
||||||
setUpdating(true)
|
setUpdating(true)
|
||||||
try {
|
try {
|
||||||
await updateApp()
|
await updateApp()
|
||||||
} finally {
|
} catch {
|
||||||
setUpdating(false)
|
setUpdating(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +42,7 @@ export default function PwaUpdatePrompt() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="pwa-update-link"
|
className="pwa-update-link"
|
||||||
onClick={() => setDismissed(true)}
|
onClick={dismissUpdate}
|
||||||
>
|
>
|
||||||
{t('pwa.later')}
|
{t('pwa.later')}
|
||||||
</button>
|
</button>
|
||||||
@@ -52,7 +51,7 @@ export default function PwaUpdatePrompt() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="pwa-update-close"
|
className="pwa-update-close"
|
||||||
onClick={() => setDismissed(true)}
|
onClick={dismissUpdate}
|
||||||
aria-label={t('pwa.later')}
|
aria-label={t('pwa.later')}
|
||||||
>
|
>
|
||||||
<X size={18} />
|
<X size={18} />
|
||||||
|
|||||||
@@ -2,9 +2,26 @@ import { useEffect, useRef } from 'react'
|
|||||||
import { useRegisterSW } from 'virtual:pwa-register/react'
|
import { useRegisterSW } from 'virtual:pwa-register/react'
|
||||||
|
|
||||||
const UPDATE_CHECK_INTERVAL_MS = 60 * 60 * 1000
|
const UPDATE_CHECK_INTERVAL_MS = 60 * 60 * 1000
|
||||||
|
const UPDATE_SUPPRESS_KEY = 'pwa_update_suppress_until'
|
||||||
|
const UPDATE_SUPPRESS_MS = 30_000
|
||||||
|
const UPDATE_RELOAD_FALLBACK_MS = 2000
|
||||||
|
|
||||||
|
function isUpdateSuppressed(): boolean {
|
||||||
|
const suppressUntil = Number(sessionStorage.getItem(UPDATE_SUPPRESS_KEY) || '0')
|
||||||
|
return Date.now() < suppressUntil
|
||||||
|
}
|
||||||
|
|
||||||
|
function suppressUpdatePrompt(): void {
|
||||||
|
sessionStorage.setItem(UPDATE_SUPPRESS_KEY, String(Date.now() + UPDATE_SUPPRESS_MS))
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearUpdateSuppression(): void {
|
||||||
|
sessionStorage.removeItem(UPDATE_SUPPRESS_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
function scheduleUpdateChecks(registration: ServiceWorkerRegistration): () => void {
|
function scheduleUpdateChecks(registration: ServiceWorkerRegistration): () => void {
|
||||||
const checkForUpdate = () => {
|
const checkForUpdate = () => {
|
||||||
|
if (isUpdateSuppressed()) return
|
||||||
registration.update().catch(() => {})
|
registration.update().catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,28 +44,57 @@ export function usePwaUpdate() {
|
|||||||
const cleanupRef = useRef<(() => void) | null>(null)
|
const cleanupRef = useRef<(() => void) | null>(null)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
needRefresh: [needRefresh],
|
needRefresh: [needRefresh, setNeedRefresh],
|
||||||
updateServiceWorker
|
updateServiceWorker
|
||||||
} = useRegisterSW({
|
} = useRegisterSW({
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
onNeedReload() {
|
||||||
|
clearUpdateSuppression()
|
||||||
|
setNeedRefresh(false)
|
||||||
|
window.location.reload()
|
||||||
|
},
|
||||||
|
onNeedRefresh() {
|
||||||
|
if (isUpdateSuppressed()) return
|
||||||
|
setNeedRefresh(true)
|
||||||
|
},
|
||||||
onRegisteredSW(_swUrl: string, registration: ServiceWorkerRegistration | undefined) {
|
onRegisteredSW(_swUrl: string, registration: ServiceWorkerRegistration | undefined) {
|
||||||
if (!registration) return
|
if (!registration) return
|
||||||
|
|
||||||
|
if (isUpdateSuppressed() || !registration.waiting) {
|
||||||
|
setNeedRefresh(false)
|
||||||
|
}
|
||||||
|
|
||||||
cleanupRef.current?.()
|
cleanupRef.current?.()
|
||||||
cleanupRef.current = scheduleUpdateChecks(registration)
|
cleanupRef.current = scheduleUpdateChecks(registration)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (isUpdateSuppressed()) {
|
||||||
|
setNeedRefresh(false)
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cleanupRef.current?.()
|
cleanupRef.current?.()
|
||||||
cleanupRef.current = null
|
cleanupRef.current = null
|
||||||
}
|
}
|
||||||
}, [])
|
}, [setNeedRefresh])
|
||||||
|
|
||||||
const updateApp = async () => {
|
const updateApp = async () => {
|
||||||
|
setNeedRefresh(false)
|
||||||
|
suppressUpdatePrompt()
|
||||||
|
|
||||||
await updateServiceWorker(true)
|
await updateServiceWorker(true)
|
||||||
|
|
||||||
|
// vite-plugin-pwa reloads via the "controlling" event; fallback if that does not fire.
|
||||||
|
window.setTimeout(() => {
|
||||||
|
window.location.reload()
|
||||||
|
}, UPDATE_RELOAD_FALLBACK_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { needRefresh, updateApp }
|
const dismissUpdate = () => {
|
||||||
|
setNeedRefresh(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { needRefresh, updateApp, dismissUpdate }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user