nav resilience after pool restart, thinner marker rings, drop dead marker popup
This commit is contained in:
@@ -156,6 +156,42 @@ router.afterEach(async (to) => {
|
||||
document.title = page && page !== 'Home' ? `${siteTitle} - ${page}` : siteTitle
|
||||
})
|
||||
|
||||
// A deploy replaces the hashed chunk files, so a tab that was open across it
|
||||
// still asks for chunks that no longer exist. The dynamic import rejects, the
|
||||
// navigation aborts, and to the user the site simply stops navigating. Reload
|
||||
// once to pick up the new index.html; the sessionStorage flag stops a reload
|
||||
// loop if the chunk is missing for any other reason.
|
||||
const CHUNK_RELOAD_KEY = 'shopdb-chunk-reloaded'
|
||||
|
||||
function isChunkLoadError(error) {
|
||||
const text = `${error?.message || ''} ${error?.name || ''}`
|
||||
return /dynamically imported module|Importing a module script failed|ChunkLoadError|Loading chunk/i.test(text)
|
||||
}
|
||||
|
||||
function reloadOnceForStaleChunk(error) {
|
||||
if (!isChunkLoadError(error)) return false
|
||||
if (sessionStorage.getItem(CHUNK_RELOAD_KEY)) return false
|
||||
sessionStorage.setItem(CHUNK_RELOAD_KEY, '1')
|
||||
window.location.reload()
|
||||
return true
|
||||
}
|
||||
|
||||
router.onError(error => {
|
||||
reloadOnceForStaleChunk(error)
|
||||
})
|
||||
|
||||
// Vite raises this for a failed module preload, which does not always surface
|
||||
// as a router error.
|
||||
window.addEventListener('vite:preloadError', event => {
|
||||
if (reloadOnceForStaleChunk(event?.payload || event)) event.preventDefault?.()
|
||||
})
|
||||
|
||||
// A navigation that completes proves the current chunks are good, so clear the
|
||||
// flag - otherwise the one allowed reload is spent for the rest of the session.
|
||||
router.afterEach(() => {
|
||||
sessionStorage.removeItem(CHUNK_RELOAD_KEY)
|
||||
})
|
||||
|
||||
// Navigation guard
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const authStore = useAuthStore()
|
||||
@@ -197,7 +233,12 @@ router.beforeEach(async (to, from, next) => {
|
||||
// First-run: steer a fresh admin into the setup wizard (once, until finished).
|
||||
if (authStore.isAuthenticated && authStore.isAdmin && to.path !== '/setup') {
|
||||
if (!isSetupLoaded()) {
|
||||
await refreshSetupState()
|
||||
// Bounded like the plugin gate: a hung request during an app-pool restart
|
||||
// must not hold the navigation open. Its own catch already fails open.
|
||||
await Promise.race([
|
||||
refreshSetupState(),
|
||||
new Promise(resolve => setTimeout(resolve, 4000)),
|
||||
])
|
||||
}
|
||||
if (!setupComplete.value && !setupSkipped.value) {
|
||||
return next('/setup')
|
||||
|
||||
Reference in New Issue
Block a user