diff options
| author | Adam Soltys <[email protected]> | 2026-10-14 00:34:46 +0000 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-14 00:34:46 +0000 |
| commit | 928da70638fceb622d5e83ed0792817e1bcd908e (patch) | |
| tree | c7c819d957fd40eba8259e28f48e90ee62a722fc /src/app.js | |
| parent | 8a5118839502adb52cd504867bb8ac602160a48c (diff) | |
| download | coinosv3-928da70638fceb622d5e83ed0792817e1bcd908e.tar.gz coinosv3-928da70638fceb622d5e83ed0792817e1bcd908e.zip | |
The × was a blind history.back(), and the entry under the pager isn't
always the feed: the browser's older entries outlive a background reload of
the app, and boot merges entries. It took the reader to the wallet, then the
chat list. closeLayer still steps Back (so the phone's Back doesn't reopen
it), but if Back lands anywhere but the page under the layer, the reader is
put on that page and that entry rewritten to it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 40 |
1 files changed, 37 insertions, 3 deletions
@@ -600,7 +600,7 @@ function imageViewer() { // mounted viewer: morphing can replace handlers with a new render's ones. document.querySelector('.lightbox')?.remove(); document.documentElement.classList.remove('no-scroll'); - goBack(() => { ui.lightbox = null; }); + closeLayer('lightbox'); }; // A tap on the picture closes it at every zoom level; gestures are filtered // by attachZoom before this click handler runs. @@ -702,7 +702,7 @@ function attachZoom(box, img, { wheel = true } = {}) { }, { passive: false }); } if (typeof window !== 'undefined') { - window.addEventListener('keydown', (e) => { if (e.key === 'Escape' && ui.lightbox) goBack(() => { ui.lightbox = null; }); }); + window.addEventListener('keydown', (e) => { if (e.key === 'Escape' && ui.lightbox) closeLayer('lightbox'); }); // Enter walks the Send flow: from an amount (or any field) it presses the // screen's main button — Review, Pay, Send — and with nothing focused it // presses it again, so amount ⏎ reviews and ⏎ again confirms. A held key @@ -1031,11 +1031,23 @@ window.addEventListener('popstate', (e) => { snap.screen = 'wallet'; snap.tab = ui.tab ?? null; try { history.replaceState({ nav: snap, i: st && typeof st.i === 'number' ? st.i : navIndex }, ''); } catch {} } + let corrected = false; + if (layerClose) { + const want = layerClose.expect; + clearTimeout(layerClose.timer); + layerClose = null; + if (navSig(snap) !== navSig(want)) { + snap = want; + corrected = true; + try { history.replaceState({ nav: snap, i: st && typeof st.i === 'number' ? st.i : navIndex }, '', navUrl(snap)); } catch {} + } + } restoringHistory = true; try { for (const f of NAV_FIELDS) ui[f] = (f === 'profEdit' || f === 'feedEdit') && snap[f] ? structuredClone(snap[f]) : f in snap ? snap[f] : null; if (st && typeof st.i === 'number') navIndex = st.i; else { const i = navStack.findIndex((s) => navSig(s) === navSig(snap)); if (i >= 0) navIndex = i; } + if (corrected && navIndex >= 0) navStack[navIndex] = snap; render(); } finally { restoringHistory = false; @@ -1050,6 +1062,28 @@ function goBack(toParent) { if (navIndex > 0) history.back(); else { toParent(); render(); } } +// Close something laid over the page (the media pager, the lightbox): Back, +// so the browser's Back doesn't reopen it — but landing on the page that +// was under it, whatever the entry Back reaches says. The entry under isn't +// always that page (after the app was reloaded in the background the +// browser's older entries are still there, or entries merged at boot), and +// blind Back walked off to the wallet, the chat list. That entry becomes the +// page under the layer. No Back to take (nothing of ours under it): shut in place. +let layerClose = null; // { expect: the snapshot to land on, timer } +function closeLayer(field) { + const cur = navStack[navIndex] || navSnapshot(); + const expect = { ...navSnapshot(), [field]: null }; + if (navIndex > 0 && cur) { + clearTimeout(layerClose && layerClose.timer); + // (the layer stays up until Back lands, as before; no popstate in time: + // a Back that went nowhere — shut where we are) + layerClose = { expect, timer: setTimeout(() => { if (layerClose && layerClose.expect === expect) { layerClose = null; ui[field] = null; render(); } }, 600) }; + history.back(); + return; + } + ui[field] = null; + render(); +} // Whether an in-app Back has anywhere of ours to go. A page opened straight // from a link has nothing under it; its Back would invent a parent. const canGoBack = () => navIndex > 0; @@ -6236,7 +6270,7 @@ async function importSnapshotFile(e) { // sits just before boot so every helper it captures is defined; the hooks are // only invoked at runtime (first render happens after loadLocale below). const ctx = { - h, ui, render, wallet, toast, copy, copyBtn, pasteBtn, blankSend, goBack, canGoBack, goHome, openExternal, showSend, + h, ui, render, wallet, toast, copy, copyBtn, pasteBtn, blankSend, goBack, canGoBack, closeLayer, goHome, openExternal, showSend, openWalletSettings: (view) => openWalletSettings(view), // one page of the Settings hub, from anywhere (Notifications' gear) openSettingsPage: (page) => { clearFeatureNav(); ui.screen = 'wallet'; ui.tab = 'settings'; ui.settingsPage = page; render(); try { window.scrollTo(0, 0); } catch {} }, |
