import { type AccountSummary } from "../../store/slices/accountsSlice"; /** * A stored account as a switcher needs to render it: the profile summary plus * the two markers that say whether its stored credential is still worth trying. * * A superset of `AccountSummary`, so existing code that reads `id`/`name`/ * `email`/`avatar`/`username` off these is unaffected. */ export interface StoredAccount extends AccountSummary { /** * When this account's stored refresh token expires, as an epoch in * milliseconds, read from the token's own `exp` claim. * * **`0` means "unknown"** — the token carried no readable numeric `exp`, so * `useAccountSync` recorded 0 rather than guessing. It reads as * already-expired on purpose: treating a credential we cannot understand as * fresh is the failure mode worth avoiding. * * Proactive but not sufficient. It only knows what the JWT says, and a token * family can be killed while its `exp` is still far in the future — see * `needsReauth`. */ tokenExpiresAt: number; /** * `true` when a switch into this account has actually been refused by the * server: revoked, reuse-detected, or invalidated by a password change, a * remote sign-out-all or an admin revocation. All of those leave `exp` * untouched, so this is the only way to see them. * * Reactive but not sufficient either — it is only ever set by *trying*. * Render a switcher off both: `needsReauth || tokenExpiresAt <= Date.now()`. * * Clears the moment the account is successfully activated, whether by a * switch or by signing into it again. */ needsReauth: boolean; /** * `true` when this account's notifications are paused because this device's * push token changed while the account was in the background. * * Its stored credential is fine and its data is fine — only the server-side * notification binding points at a token this device no longer holds. * Switching into the account re-creates the binding and clears this, so the * useful thing to render is an invitation to open it: *"notifications paused * — open to resume"*. * * **Not the same as `needsReauth`, and saying so matters in both * directions.** `needsReauth` means the credential is dead and the user must * sign in again; telling them that when their notifications are merely stale * asks for a password they do not need. This one means the account still * works and is just quiet; treating it as "everything is fine" hides the one * thing they could fix by tapping it. * * Only ever set for accounts that explicitly enabled push. An account that * never asked for notifications has none to pause. */ needsPushRebind: boolean; } export interface UseAccountsReturn { accounts: StoredAccount[]; activeAccount: StoredAccount | null; accountCount: number; /** * `true` when no account is active *because the user deliberately signed * out* (or a stored session turned out to be dead), as opposed to "nothing * has ever been selected". Both look like `activeAccount === null`; this is * what tells them apart, and it survives a relaunch. */ signedOut: boolean; /** * `true` when an account was refused admission because the map was already * full. Clears on the next successful admission and on any removal. See * `useAddAccount` for how it differs from `canAddAccount`. */ accountLimitReached: boolean; } export default function useAccounts(): UseAccountsReturn;