From 5b6e5db306f205ecc7b64957949aa4ae3f6368e7 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Mon, 30 Mar 2026 17:02:18 +0200 Subject: [PATCH] bug-fix --- package-lock.json | 4 +-- package.json | 2 +- src/components/ErrorBoundary.tsx | 8 +++++ src/lib/stale-chunk-recovery.ts | 50 ++++++++++++++++++++++++++++++++ src/main.tsx | 31 +------------------- 5 files changed, 62 insertions(+), 33 deletions(-) create mode 100644 src/lib/stale-chunk-recovery.ts diff --git a/package-lock.json b/package-lock.json index 2ea3d8d3..0301af1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jumble-imwald", - "version": "21.2.0", + "version": "21.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jumble-imwald", - "version": "21.2.0", + "version": "21.2.1", "license": "MIT", "dependencies": { "@asciidoctor/core": "^3.0.4", diff --git a/package.json b/package.json index d553f7cf..34ff1039 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jumble-imwald", - "version": "21.2.0", + "version": "21.2.1", "description": "A user-friendly Nostr client focused on relay feed browsing and relay discovery, forked from Jumble", "private": true, "type": "module", diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index 447cd4d7..30fd6fb6 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -3,6 +3,7 @@ import { MessageCircle, RotateCw } from 'lucide-react' import React, { Component, ReactNode } from 'react' import { toast } from 'sonner' import logger from '@/lib/logger' +import { isChunkLoadFailureMessage, tryStaleChunkReloadOnce } from '@/lib/stale-chunk-recovery' const ISSUES_URL = 'https://gitrepublic.imwald.eu/repos/npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z/jumble-imwald-edition?tab=issues' @@ -66,6 +67,13 @@ export class ErrorBoundary extends Component + Reloading to pick up the latest app version… + + ) + } if (isLikelyBrokenReactContextFromHmr(msg) && tryContextRecoveryReload()) { return (
diff --git a/src/lib/stale-chunk-recovery.ts b/src/lib/stale-chunk-recovery.ts new file mode 100644 index 00000000..d5a2b7e2 --- /dev/null +++ b/src/lib/stale-chunk-recovery.ts @@ -0,0 +1,50 @@ +/** + * After a deploy, hashed chunks from the previous build are removed. A tab that still runs old JS + * (HTTP cache, or a service worker precache race) can 404 on `import()`. One reload usually picks + * up fresh `index.html` and the new asset graph. + */ +const SESSION_KEY = 'jumble:stale-chunk-reload' + +export function isChunkLoadFailureMessage(message: string): boolean { + const m = message.toLowerCase() + return ( + m.includes('failed to fetch dynamically imported module') || + m.includes('error loading dynamically imported module') || + m.includes('importing a module script failed') || + // Safari / some WebKit builds + (m.includes('dynamically imported module') && (m.includes('failed') || m.includes('error'))) + ) +} + +/** Returns true if a reload was scheduled (at most once per session). */ +export function tryStaleChunkReloadOnce(): boolean { + if (typeof window === 'undefined') return false + try { + if (sessionStorage.getItem(SESSION_KEY)) return false + sessionStorage.setItem(SESSION_KEY, '1') + } catch { + return false + } + window.location.reload() + return true +} + +export function installStaleBuildChunkRecovery(): void { + if (typeof window === 'undefined') return + + window.addEventListener('unhandledrejection', (event) => { + const r = event.reason + const msg = + typeof r === 'string' ? r : r instanceof Error ? r.message : String(r ?? '') + if (!isChunkLoadFailureMessage(msg)) return + event.preventDefault() + tryStaleChunkReloadOnce() + }) + + window.addEventListener('error', (event) => { + const msg = event.message ?? '' + if (!isChunkLoadFailureMessage(msg)) return + event.preventDefault() + tryStaleChunkReloadOnce() + }) +} diff --git a/src/main.tsx b/src/main.tsx index d19f5307..6700b6f9 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -12,36 +12,7 @@ import { ErrorBoundary } from './components/ErrorBoundary.tsx' import { initI18n } from './i18n' import storage from './services/local-storage.service' import { restoreSessionFeedSnapshotsAfterHardRefresh } from './services/session-feed-snapshot.service' - -/** - * After a deploy, hashed chunks from the previous build are removed. A tab that still runs old JS - * (HTTP cache, or a service worker that just dropped the old precache) can hit 404 on import(). - * One reload usually picks up the new index.html and asset graph. - */ -function installStaleBuildChunkRecovery() { - if (typeof window === 'undefined') return - - const isChunkLoadFailure = (msg: string) => - msg.includes('Failed to fetch dynamically imported module') || - msg.includes('error loading dynamically imported module') || - msg.includes('Importing a module script failed') - - window.addEventListener('unhandledrejection', (event) => { - const r = event.reason - const msg = - typeof r === 'string' ? r : r instanceof Error ? r.message : String(r ?? '') - if (!isChunkLoadFailure(msg)) return - event.preventDefault() - try { - const key = 'jumble:stale-chunk-reload' - if (sessionStorage.getItem(key)) return - sessionStorage.setItem(key, '1') - } catch { - return - } - window.location.reload() - }) -} +import { installStaleBuildChunkRecovery } from './lib/stale-chunk-recovery' installStaleBuildChunkRecovery()