Files
observer/web/src/components/SectionNav.tsx
T
grenadeandClaude Opus 5 6ed9a6c35a
deploy / build (push) Successful in 8m29s
deploy / deploy-web (push) Successful in 5s
deploy / deploy-api (push) Successful in 15s
feat: an about route, and somewhere to say thanks
Adds `/:chain/about`, linked from the header just before the connection light
and the theme toggle — it is about the site rather than the chain, so it sits
there instead of in the section nav, and `header: true` on its `SECTIONS` entry
keeps it out while still letting the URL parse.

The page says what the observer is, that it and the CUDA-optimised miner are
built and freely sourced by a member of the Quantus user community rather than
by the project, and takes donations in QTC.

**The QR is generated in the browser from the address printed beside it, never
shipped as an image.** A supplied PNG next to a typed address is two
independent claims about where money goes, and nothing on the page could tell a
reader if they ever stopped agreeing — a mismatch sends funds somewhere
unrecoverable.

Verified rather than trusted. The address was put through this crate's own SS58
decoder: checksum and network prefix both hold, and it round-trips to the same
string. The QR matrix was then decoded from first principles — function-pattern
map, mask 3 unapplied, codewords de-interleaved across version 4-M's two blocks
— and reads back the address exactly. A note for whoever checks this again: an
independent encoder produces a different matrix for the same string at every
mask, because mode segmentation differs, so comparing matrices proves nothing
and only a decode does.

`the_donation_address_on_the_site_is_a_valid_quantus_address` reads the literal
out of the frontend source and validates it, so a typo fails CI rather than
costing somebody their money. Confirmed to fail on one changed character.

Also on the vesting page, from verifying it live: a "claimable now" figure and
a per-grant claimable column. Two grants began vesting at launch while the other
forty-six wait for 2027, so 68,871 QTC is already claimable and none of it has
been taken — a page that only said "first claimable" would have missed it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 16:48:21 +03:00

77 lines
2.5 KiB
TypeScript

/**
* The site's sections.
*
* Every route this site has was reachable only by already knowing an address —
* a block hash, a preimage, an SS58 string, a spec_version. That is fine for a
* link somebody sent you and useless for finding out the pages exist at all.
* This is the answer to "what else is here".
*
* The standings are the first entry because they are the front page, and there
* is no "Miners" beside them for the same reason: the standings already are the
* index of miners, and a second page listing the same rows under a different
* address would be two answers to one question.
*/
import { Link } from 'react-router-dom'
import type { Window as WindowName } from '../api/generated/Window'
import { href, isStandings, type Route, SECTIONS } from '../lib/routes'
export function SectionNav({
chain,
route,
window: windowName,
}: {
chain: string | null
route: Route
/** Carried into the standings link so leaving a section and coming back does
* not silently reset the span someone chose. */
window: WindowName
}) {
if (!chain) return null
// The standings are current when nothing else is: no panel open, no index.
const onStandings = isStandings(route)
return (
<nav className="sections" aria-label="Section">
<Link
to={href({ chain, window: windowName })}
className="section-link"
aria-current={onStandings ? 'page' : undefined}
>
Standings
</Link>
{/* Straight to block zero. Endowments happen there and are invisible to
anyone who has not read the chainspec, so somebody who would never
think to look is given an unmissable way to. Not a `SECTIONS` entry
because it is not an index — it is one particular block. */}
<Link
to={href({ chain, block: '0' })}
className="section-link"
aria-current={route.block === '0' ? 'page' : undefined}
>
Genesis
</Link>
{SECTIONS.filter((s) => !s.header).map((s) => (
<Link
key={s.id}
to={href({ chain, index: s.id })}
className="section-link"
// A call's own page belongs to the Calls section, so the nav marks
// it there rather than nowhere.
aria-current={
route.index === s.id ||
(s.id === 'call' && route.call !== null) ||
(s.id === 'event' && route.event !== null) ||
(s.id === 'node' && route.node !== null)
? 'page'
: undefined
}
>
{s.label}
</Link>
))}
</nav>
)
}