# State management — handoff ## Summary Client UI state uses **Jotai** atoms: room lists, unread counts, settings, modals, drafts, navigation memory, and sidebar collapse. Many atoms sync to localStorage via `atomWithLocalStorage`. ## User-facing behavior - Room list order, collapsed categories, sidebar folders persist across sessions - Message drafts and upload queues per room persist locally - Settings (theme, emoji style, zoom) persist locally - Unread badges drive favicon and sidebar indicators ## Architecture ``` JotaiProvider (App.tsx) → atoms in state/ → hooks in state/hooks/ wrap atoms for components ClientBindAtoms.tsx binds Matrix events → atom updates ``` ## Key files | Path | Role | |------|------| | `cinny/src/app/state/settings.ts` | User settings atom | | `cinny/src/app/state/sessions.ts` | Auth sessions | | `cinny/src/app/state/room-list/roomList.ts` | `allRoomsAtom`, ordering | | `cinny/src/app/state/room/roomToUnread.ts` | Unread counts | | `cinny/src/app/state/room/roomInputDrafts.ts` | Composer drafts + uploads | | `cinny/src/app/state/room/roomToParents.ts` | Space parent mapping | | `cinny/src/app/state/mDirectList.ts` | Direct message index | | `cinny/src/app/state/navToActivePath.ts` | Last active path per context | | `cinny/src/app/state/closedNavCategories.ts` | Collapsed nav sections | | `cinny/src/app/state/closedLobbyCategories.ts` | Collapsed lobby categories | | `cinny/src/app/state/createRoomModal.ts` | Create room modal state | | `cinny/src/app/state/createSpaceModal.ts` | Create space modal state | | `cinny/src/app/state/searchModal.ts` | Global search modal | | `cinny/src/app/state/utils/atomWithLocalStorage.ts` | Persistence helper | | `cinny/src/app/pages/client/ClientBindAtoms.ts` | Matrix → atom bindings | ## Data model Most state is **local only** (not Matrix). Exceptions: settings that also write account data (notification prefs via separate hooks). | Atom area | Persists? | |-----------|-----------| | settings | localStorage | | room list order | localStorage | | drafts | localStorage | | unread | memory (derived from sync) | | modals | memory | ## Dependencies - `jotai` - `@tanstack/react-query` (separate from Jotai, used in App.tsx) ## Integration points - **matrix-client**: sync updates room/unread atoms via ClientBindAtoms - **home-and-sidebar**: room list atoms - **room-timeline**: draft atoms - **routing-and-navigation**: navToActivePath ## Testing ### Manual 1. Type draft, reload — draft should restore. 2. Collapse category, reload — stays collapsed. 3. Reorder rooms; verify order persists. 4. Logout — verify which atoms clear vs persist. ### Automated - None ## Known issues & gotchas - Logout clears **all** localStorage in session-logout handler — wipes settings too - Atom families (`roomIdToMsgDraftAtomFamily`) — memory growth in many rooms - `ClientBindAtoms` must mount early or UI shows stale unreads ## Future work - Selective localStorage clear on logout (keep non-sensitive prefs) - Document atom dependency graph ## Related docs - [matrix-client HANDOFF](../matrix-client/HANDOFF.md) - [home-and-sidebar HANDOFF](../home-and-sidebar/HANDOFF.md) ## Add your extra things here - `useSetting` hook in `state/hooks/settings.ts` — primary settings accessor - `joiningProgress.ts` — room join UX state