aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-27 19:12:29 -0700
committerAdam Soltys <[email protected]>2026-06-27 19:12:29 -0700
commit0a17635c93b1e7ec1b2937ece25000c518881f64 (patch)
treee4a598bc28f8cb02e8b9ea23fd03887e3f0b04ac /src
parent803ccd12cded3f09086c715fffbc918a01575e09 (diff)
downloadcoinosv3-0a17635c93b1e7ec1b2937ece25000c518881f64.tar.gz
coinosv3-0a17635c93b1e7ec1b2937ece25000c518881f64.zip
Browser history / back-button navigation
Mirror the app's screen position (screen, tab, txDetail, bump, giftsAll, giftMode, claimStep) into the browser history, so Back/Forward (and Android system back) move between screens actually viewed. render() snapshots just these nav fields and pushState()s on change — incidental re-renders (typing, polling) don't add entries. popstate restores the snapshot and re-renders. History-API calls are guarded so they can never break a render. Verified via CDP: tab nav and tx-detail push entries; Back/Forward (incl. A→B→A) restore the right screen and the DOM follows. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/app.js50
1 files changed, 50 insertions, 0 deletions
diff --git a/src/app.js b/src/app.js
index 3a8f5fd..00bdbba 100644
--- a/src/app.js
+++ b/src/app.js
@@ -249,9 +249,59 @@ function render() {
// Remember where we are so a refresh restores it (only meaningful on the wallet).
try { sessionStorage.setItem(NAV_KEY, JSON.stringify({ tab: ui.tab, txDetail: ui.txDetail })); } catch {}
}
+ syncHistory(); // mirror the current screen into browser history (Back/Forward)
}
wallet.subscribe(render);
+// ---- browser-history navigation ------------------------------------------
+// Mirror the app's screen position into the browser history so the Back/Forward
+// buttons (and Android/system back) move between screens we've actually viewed.
+// We snapshot only the navigation-relevant `ui` fields, so incidental re-renders
+// (typing, polling, balance updates) don't create history entries.
+const NAV_FIELDS = ['screen', 'tab', 'txDetail', 'bump', 'giftsAll', 'giftMode', 'claimStep'];
+function navSnapshot() {
+ const s = {};
+ for (const f of NAV_FIELDS) s[f] = ui[f] ?? null;
+ return s;
+}
+const navSig = (s) => JSON.stringify(s);
+let navStack = []; // in-memory mirror of the history entries (to detect an in-app Back)
+let navIndex = -1;
+let restoringHistory = false; // true while applying a popstate (suppresses pushing)
+
+function syncHistory() {
+ if (restoringHistory) return;
+ try {
+ const snap = navSnapshot();
+ const sig = navSig(snap);
+ if (navIndex >= 0 && sig === navSig(navStack[navIndex])) return; // no navigation change
+ // Every screen change is a new history entry. (We deliberately don't try to
+ // detect in-app "back" navigations — an A→B→A pattern is indistinguishable
+ // from a genuine back, so guessing corrupts the stack. An in-app back just
+ // adds an entry; Back/Forward still walk the screens correctly.)
+ navStack = navStack.slice(0, navIndex + 1); // drop any forward entries
+ navStack.push(snap);
+ navIndex++;
+ const entry = { nav: snap, i: navIndex };
+ if (navIndex === 0) history.replaceState(entry, '');
+ else history.pushState(entry, '');
+ } catch {} // history API failures must never break a render
+}
+
+window.addEventListener('popstate', (e) => {
+ const st = e.state;
+ const snap = (st && st.nav) || navSnapshot();
+ restoringHistory = true;
+ try {
+ for (const f of NAV_FIELDS) ui[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; }
+ render();
+ } finally {
+ restoringHistory = false;
+ }
+});
+
// ---------------------------------------------------------------- utilities
let toastTimer;
function toast(msg) {