Commit Graph
3 Commits
Author SHA1 Message Date
jaredandClaude Opus 5 6e4c4bc795 fix(security): composer drafts no longer survive logout or cross accounts
draft-msg-<roomId> was unscoped and deliberately skipped on logout, then
hydrated into whoever logged in next. Wipe drafts in clearPlaintextCaches,
and only hydrate a draft whose stored userId matches the current user.
Drafts written before this change carry no userId and are dropped on
first load (a one-time loss of unsent drafts, accepted for the leak fix).

Fixes #41

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-12 14:48:34 -04:00
jaredandClaude Opus 4.8 eaf6853910 fix(nav): harden draft indicator after review
Address findings from 2 review agents on the room-nav draft indicator:

- Icon semantics (a11y): Icons.Message read as message activity and
  collided with two existing bubble uses in the same row (call-chat
  toggle, mark-unread), especially next to the unread badge. Replace it
  with the composer's shared DraftDot (a small color.Success.Main dot),
  so both draft surfaces share one visual language. Rendered as a
  role="img" span with aria-label "Unsent draft" (reliably announced,
  unlike a bare aria-labelled svg).

- Precise thread-key filter: hydration skipped any draftKey containing
  '::', which would also skip an IPv6-literal server name in a roomId.
  Match '::$' (thread root is an event id) so only real thread drafts are
  skipped.

- Defensive hasMsgDraft: guard toPlainText so a corrupted/foreign draft
  value can't throw during a nav render.

- Clear the draft atom on send: the send / scheduled-send handlers reset
  the editor and localStorage but left the jotai draft atom set, so the
  composer DraftIndicator could show a stale dot after sending a restored
  draft. Add setMsgDraft([]) to both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:48:35 -04:00
jaredandClaude Opus 4.8 c44ef8d795 feat(nav): draft indicator on room-nav items
Composer drafts persist per room, but nothing in the room list showed
which OTHER rooms had an unsent draft. Add a subtle chat-bubble icon on a
room's nav item when it has a message draft (and isn't the open room), so
half-written messages elsewhere are visible at a glance.

- Shared pure helper hasMsgDraft (utils/draft.ts, unit-tested) replaces
  the inline emptiness check; the composer DraftIndicator now reuses it.
- RoomNavItem reads a memoized selectAtom(draftAtom, hasMsgDraft) so a row
  re-renders only when its draft flag flips (the draft atom is written on
  room-leave, not per keystroke). Uses Icons.Message (pencil is reserved
  for the custom-name marker), muted, aria-label "Unsent draft".
- useHydrateMsgDrafts (mounted in ClientNonUIFeatures) pre-fills the
  per-room draft atoms from draft-msg-* localStorage on startup, so
  indicators are correct after a page reload, not only after revisiting a
  room. Thread drafts (key contains ::) are skipped; room-level only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:37:21 -04:00