# 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
