Files
pastilhasandClaude Opus 5 778d7d3f12 the reader reads the param the platform actually passes
clicking a message did nothing. the reading pane stayed on "Select an email to
read" whatever you opened, and it had been that way since email became a plugin.

the reader asks the URL which message is open. it asked for `emailId`, which was
the param name back when email was part of the platform and declared its own
`/email/:emailId` route. a plugin declares no routes: the platform mounts every
one at `<route>` and `<route>/:section`, so the id arrives as `section`. asking
for `emailId` got undefined, `selectedId` was null on every render, and the pane
rendered its empty state.

nothing else was wrong, which is why this took so long to place — the list
showed, the link navigated, the URL changed, and both API routes return full
data (verified against the real 19k-message store: /messages carries every
snippet, /thread/:id returns 55KB of html). one undefined param, and the whole
right-hand side of the screen looked dead.

still accepts `emailId`, so this keeps working if email ever gets its own route
again. the underlying smell is the platform naming a plugin's param for it —
worth fixing there, not here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
2026-08-26 18:32:29 +00:00

29 lines
1.7 KiB
TypeScript

import { useParams } from 'react-router';
// Which email is open is `/email/:id`. The route pair already existed, but the URL was a mirror
// rather than the state: the selection lived in an `EMAIL_SELECTED` global and two effects copied it
// back and forth with the route. That round-trip is what kept a row a `<button>` — nothing to link to,
// no cmd-click, no middle-click — and it meant Back raced the effect that had just written the URL
// with `replace`. The param is the state now; there is nothing to keep in sync.
/** `search` carries the folder you are reading in (`?folder=sent`), which a bare pathname would drop. */
export const emailPath = (id: string, search = '') => `/email/${encodeURIComponent(id)}${search}`;
/**
* The open email, or null on the bare `/email` route — which is a real state, not one to redirect away.
*
* ── Why it reads `section` ──
*
* This asked for `emailId` while email was part of the platform and had its own `/email/:emailId` route.
* A plugin does not declare its routes: the platform mounts every one at `<route>` and `<route>/:section`,
* so the param arrives under the platform's name whatever the plugin means by it. Asking for `emailId`
* got `undefined` from the day email became a plugin — the reader then had no selection to show and sat on
* "Select an email to read" however many messages you clicked, with the list and the API both working.
*
* `emailId` is still accepted so the hook keeps working if this ever mounts under its own route again.
*/
export const useSelectedEmailId = (): string | null => {
const params = useParams<{ section?: string; emailId?: string }>();
return params.emailId ?? params.section ?? null;
};