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>
This commit is contained in:
2026-07-10 11:48:35 -04:00
co-authored by Claude Opus 4.8
parent c44ef8d795
commit eaf6853910
5 changed files with 25 additions and 10 deletions
+12 -1
View File
@@ -602,6 +602,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
resetEditor(editor);
resetEditorHistory(editor);
setCharCount(0);
setMsgDraft([]);
localStorage.removeItem(`draft-msg-${draftKey}`);
setReplyDraft(undefined);
sendTypingStatus(false);
@@ -614,6 +615,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
replyDraft,
sendTypingStatus,
setReplyDraft,
setMsgDraft,
isMarkdown,
commands,
setToast,
@@ -687,11 +689,20 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
});
resetEditor(editor);
resetEditorHistory(editor);
setMsgDraft([]);
localStorage.removeItem(`draft-msg-${draftKey}`);
setReplyDraft(undefined);
sendTypingStatus(false);
},
[setScheduledMessages, roomId, draftKey, editor, setReplyDraft, sendTypingStatus],
[
setScheduledMessages,
roomId,
draftKey,
editor,
setReplyDraft,
setMsgDraft,
sendTypingStatus,
],
);
const handleKeyDown: KeyboardEventHandler = useCallback(