fix(channels): re-hydrate per-account manager state once the account is pinned

The provider tree mounts above <App>, which pins this tab's account only after /api/me
resolves — so on a genuinely fresh tab (empty sessionStorage) the manager's sort, filters,
view and layout initialised from an unknown account and stayed at defaults until a second
reload. Make the active account reactive (a subscribable store + useActiveAccountId via
useSyncExternalStore; setActiveAccount/clearActiveAccount notify) and re-read the stored
value when the account transitions null→id, via a shared useAccountRehydrate hook. A
markDirty guard (called by each owning setter) prevents clobbering a value the user changed
in the brief pre-pin window; the effect is keyed on the account id only, so the user's own
writes can't loop it. useSortState, useFilterState and ChannelsProvider (view/layout) all
adopt it. Affects sort identically — this was a long-standing caveat, not new to R13.
This commit is contained in:
2026-08-14 02:37:28 +02:00
parent ca9dd54923
commit b57e4fb9bc
9 changed files with 160 additions and 27 deletions
+4 -1
View File
@@ -5,7 +5,7 @@
import { notify, remove } from "../notifications";
import i18n from "../../i18n";
import { reportError } from "../errorDialog";
import { activeAccountId, ACTIVE_ACCOUNT_KEY } from "../storage";
import { activeAccountId, ACTIVE_ACCOUNT_KEY, notifyActiveAccountChange } from "../storage";
export class HttpError extends Error {
status: number;
@@ -136,6 +136,8 @@ export function setActiveAccount(id: number): void {
} catch {
/* ignore */
}
// Let per-account UI state re-hydrate now the account is known (see notifyActiveAccountChange).
notifyActiveAccountChange();
}
export function clearActiveAccount(): void {
try {
@@ -143,6 +145,7 @@ export function clearActiveAccount(): void {
} catch {
/* ignore */
}
notifyActiveAccountChange();
}
export async function req<T = unknown>(