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>
This commit is contained in:
@@ -25,11 +25,8 @@ import { NOTIFICATION_SOUND_MAP } from '../../utils/notificationSounds';
|
||||
import { useSetting } from '../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../state/settings';
|
||||
import { allInvitesAtom } from '../../state/room-list/inviteList';
|
||||
|
||||
// Grace period after the initial sync settles before invite notifications arm, so
|
||||
// the async invite-atom population lands first and isn't mistaken for new invites.
|
||||
const INVITE_NOTIFY_ARM_DELAY_MS = 3000;
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { useHydrateMsgDrafts } from '../../hooks/useHydrateMsgDrafts';
|
||||
import { getDirectRoomPath, getHomeRoomPath, getInboxInvitesPath } from '../pathUtils';
|
||||
import { mDirectAtom } from '../../state/mDirectList';
|
||||
import {
|
||||
@@ -67,6 +64,10 @@ import {
|
||||
THREAD_NOTIFICATIONS_FALLBACK_BEHAVIOR,
|
||||
} from '../../utils/threadNotifications';
|
||||
|
||||
// Grace period after the initial sync settles before invite notifications arm, so
|
||||
// the async invite-atom population lands first and isn't mistaken for new invites.
|
||||
const INVITE_NOTIFY_ARM_DELAY_MS = 3000;
|
||||
|
||||
function isInQuietHours(start: string, end: string): boolean {
|
||||
const now = new Date();
|
||||
const [sh, sm] = start.split(':').map(Number);
|
||||
@@ -865,9 +866,15 @@ function KeyboardShortcutsFeature() {
|
||||
return <KeyboardShortcutsDialog />;
|
||||
}
|
||||
|
||||
function MsgDraftHydrator(): null {
|
||||
useHydrateMsgDrafts();
|
||||
return null;
|
||||
}
|
||||
|
||||
export function ClientNonUIFeatures({ children }: ClientNonUIFeaturesProps) {
|
||||
return (
|
||||
<>
|
||||
<MsgDraftHydrator />
|
||||
<SystemEmojiFeature />
|
||||
<PageZoomFeature />
|
||||
<FaviconUpdater />
|
||||
|
||||
Reference in New Issue
Block a user