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
29 lines
1.7 KiB
TypeScript
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;
|
|
};
|