fix: auto-recover from stale-bundle chunk errors; responsive touch-ups
CI/CD / CI - API (dotnet build + engine sim) (push) Successful in 7m15s
CI/CD / CI - Web (tsc + next build) (push) Successful in 1m7s
CI/CD / Deploy - local stack (db + server + web) (push) Failing after 4m39s

- The "This page couldn't load" after a redeploy was a stale bundle: a tab open
  across a deploy requests JS chunks that no longer exist (ChunkLoadError). Added
  a global error/unhandledrejection guard that reloads once to fetch the fresh
  bundle (sessionStorage-guarded against loops, cleared after a healthy run).
- Reaction tray width → w-[min(270px,86vw)] so it never overflows narrow phones.

Verified: tsc + next build pass; web image rebuilt on :1500.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-07 00:51:36 +03:30
parent 0847d2c7cf
commit dcea0bc87c
2 changed files with 27 additions and 1 deletions
+26
View File
@@ -52,6 +52,32 @@ export default function Page() {
const init = useSessionStore((s) => s.init);
const loading = useSessionStore((s) => s.loading);
// Auto-recover from stale-bundle chunk errors (a tab open across a redeploy
// requests JS chunks that no longer exist → "page couldn't load"). Reload once
// to fetch the fresh bundle; clear the guard after a healthy run.
useEffect(() => {
const isChunkErr = (m: string) =>
/ChunkLoadError|Loading chunk|Failed to fetch dynamically imported|error loading dynamically imported|importing a module script failed/i.test(m);
const onErr = (e: ErrorEvent | PromiseRejectionEvent) => {
const msg =
((e as ErrorEvent).message ?? "") +
" " +
(((e as PromiseRejectionEvent).reason as { message?: string })?.message ?? "");
if (isChunkErr(msg) && !sessionStorage.getItem("hokm.chunkReload")) {
sessionStorage.setItem("hokm.chunkReload", "1");
window.location.reload();
}
};
window.addEventListener("error", onErr);
window.addEventListener("unhandledrejection", onErr);
const clear = setTimeout(() => sessionStorage.removeItem("hokm.chunkReload"), 5000);
return () => {
window.removeEventListener("error", onErr);
window.removeEventListener("unhandledrejection", onErr);
clearTimeout(clear);
};
}, []);
useEffect(() => {
init();
+1 -1
View File
@@ -822,7 +822,7 @@ function Reactions() {
initial={{ opacity: 0, y: 12, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 12, scale: 0.95 }}
className="absolute bottom-20 ltr:right-4 rtl:left-4 z-50 glass rounded-2xl p-2 w-[270px]"
className="absolute bottom-20 ltr:right-4 rtl:left-4 z-50 glass rounded-2xl p-2 w-[min(270px,86vw)]"
>
<div className="flex gap-1 p-1 rounded-xl bg-navy-900/70 mb-2">
<button