From 93358dd920270ef6ebfbd41af818910c2e72f447 Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Thu, 10 Sep 2026 06:23:38 +0300 Subject: [PATCH] ui: the window belongs to the miner page, not just the standings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Choosing a window on a miner's page navigated back to the standings. Not a mis-wired link: there was no URL for "this miner, over a day", so the only thing the control could do was go somewhere that had one. So the miner route carries it: `/quantus/miner/0x…/day`. That is the right place for it regardless — a miner's rank and hashrate are *of* a window, and a link sent without one shows the recipient something other than what the sender was looking at. In the path rather than a query string, so it reads the way the standings' own window already does. The bare `/quantus/miner/0x…` still resolves, at the default window, so existing links keep working. And the control now appears only where it changes something: the standings, and a miner's page, which is one row of those same standings. A block happened once, a balance has no span, a runtime is not a rate, and the three indexes are not ranked — on all of those it was a live control that did nothing, which teaches a reader to distrust it on the two pages where it works. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Jp6a8EDar9ueEhAxzep4V5 --- readme.md | 9 +++++++++ web/src/App.tsx | 43 ++++++++++++++++++++++++++++--------------- web/src/lib/routes.ts | 20 +++++++++++++++++--- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/readme.md b/readme.md index 02a1aed..45498c6 100644 --- a/readme.md +++ b/readme.md @@ -305,6 +305,15 @@ Route segments are named — `/quantus/block/13160` rather than two kinds of thing can look alike, and an SS58 address is arbitrary base58 with no rule keeping it clear of a window name forever. +A miner's page carries the window too: `/quantus/miner/0x…/day`. Its rank and +hashrate are *of* a window, so a link without one means something different from +what the sender was looking at — and until it was in the path, changing the +window on a miner's page could only navigate back to the standings, because +there was no URL for "this miner, over a day". The window control appears on +exactly the two pages where it changes something, the standings and a miner; +a block happened once and a balance has no span, and a control that does nothing +where it sits teaches a reader to distrust it everywhere else. + Everything navigable is an ``, including the chain chips and the window control. That is what "real URLs" has to mean in practice: middle-click opens the 24-hour view of another chain in a tab, right-click copies a link to a diff --git a/web/src/App.tsx b/web/src/App.tsx index 38953d3..470295e 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -53,6 +53,11 @@ export default function App() { } }, [route.chain, route.window, fallbackChain, navigate]) + // The standings and a miner's page, which is one row of them. Everything + // else is about a thing that happened once. + const windowApplies = + route.index === null && route.block === null && route.account === null && route.runtime === null + // Normalise a path that resolves to the standings but is not spelled like // them: `/quantus`, `/quantus/miner` (the standings *are* the miner index), // and anything unrecognised, which `parse` degrades to the front page rather @@ -109,21 +114,29 @@ export default function App() {
- {/* Links, not buttons: a window is part of the address, so "the - last 24 hours on Quantus" is a thing to send someone rather than - a control state that a shared link would drop. */} - + {/* Only where a window means something. It sets the span the ranks + are computed over, so it belongs on the standings and on a + miner's page — which is one row of those same standings. A block, + an account, a runtime and the three indexes have no window: a + block happened once, a balance has no span, and a control that + does nothing where it sits teaches a reader to distrust it. */} + {windowApplies && ( + + )}
diff --git a/web/src/lib/routes.ts b/web/src/lib/routes.ts index 864ded7..9273fc3 100644 --- a/web/src/lib/routes.ts +++ b/web/src/lib/routes.ts @@ -117,7 +117,7 @@ export function parse(pathname: string): Route { // same account on every Quantus chain, so the only thing it lacks is which // chain's figures to show, and the default is the right guess. if (parts[0] === 'miner' && parts[1]) { - return { ...EMPTY, miner: parts[1].toLowerCase() } + return { ...EMPTY, miner: parts[1].toLowerCase(), window: asWindow(parts[2]) ?? DEFAULT_WINDOW } } // Likewise a bare runtime: a spec_version is only meaningful against a chain, // and the default chain is the one a link without one means. @@ -132,7 +132,18 @@ export function parse(pathname: string): Route { const value = parts[2] if (kind && value) { if (kind === 'block') return { ...EMPTY, chain, block: value.toLowerCase() } - if (kind === 'miner') return { ...EMPTY, chain, miner: value.toLowerCase() } + // A miner carries a window, because its rank and hashrate are *of* a + // window and a link to one without it means something different from what + // the sender was looking at. In the path rather than a query string, so it + // reads the same way the standings' own window does. + if (kind === 'miner') { + return { + ...EMPTY, + chain, + miner: value.toLowerCase(), + window: asWindow(parts[3]) ?? DEFAULT_WINDOW, + } + } if (kind === 'account') return { ...EMPTY, chain, account: value } if (kind === 'runtime') return { ...EMPTY, chain, runtime: spec(value) } } @@ -155,7 +166,10 @@ export function parse(pathname: string): Route { export function href(route: Partial): string { const chain = route.chain ?? '' if (route.block) return `/${chain}/block/${route.block}` - if (route.miner) return chain ? `/${chain}/miner/${route.miner}` : `/miner/${route.miner}` + if (route.miner) { + const window = route.window ?? DEFAULT_WINDOW + return chain ? `/${chain}/miner/${route.miner}/${window}` : `/miner/${route.miner}/${window}` + } if (route.index) return `/${chain}/${route.index}` if (route.account) return `/${chain}/account/${route.account}` if (route.runtime !== null && route.runtime !== undefined) {