aa62df9c75
Right-side thread drawer (MembersDrawer pattern; mobile fullscreen): - ThreadPanel: header + close/Escape, ThreadTimeline, its own RoomInput (threadRootId prop; drafts/replies/uploads isolated per roomId::threadId; schedule + slash-commands off in threads v1) and threaded mark-as-read. - ThreadTimeline: lean reimplementation over thread.liveTimeline — copied useTimelinePagination pattern (/relations back-pagination + decryption), virtualized, root event emphasized + "N replies" divider, reactions/edits/ redactions, and a pending strip (chronological local echo never enters the thread timelineSet — rendered from LocalEchoUpdated instead). - ThreadSummary chips on root messages (server-aggregated bundle or live Thread; unread badge via getThreadUnreadNotificationCount) keep threads discoverable now that replies leave the main timeline. - Reply-in-Thread menu + thread indicators open the panel; deep links to thread events redirect into it. - State: roomIdToActiveThreadIdAtomFamily + getThreadDraftKey (+18 tests). Gates: tsc clean, eslint 0 errors, build OK, 616/617 tests (1 IDB skip). Awaiting live QA; release note: threaded replies no longer render inline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
23 lines
853 B
TypeScript
23 lines
853 B
TypeScript
import { atom, PrimitiveAtom } from 'jotai';
|
|
import { atomFamily } from 'jotai/utils';
|
|
|
|
const createActiveThreadIdAtom = () => atom<string | null>(null);
|
|
export type TActiveThreadIdAtom = PrimitiveAtom<string | null>;
|
|
|
|
/**
|
|
* Per-room "which thread is open in the panel" state. Mirrors
|
|
* `roomIdToReplyDraftAtomFamily` in `roomInputDrafts.ts` — the same atom
|
|
* instance is returned for the same roomId, so a room's panel state survives
|
|
* remounts.
|
|
*/
|
|
export const roomIdToActiveThreadIdAtomFamily = atomFamily<string, TActiveThreadIdAtom>(() =>
|
|
createActiveThreadIdAtom(),
|
|
);
|
|
|
|
/**
|
|
* Key used to scope a thread's composer drafts (message/reply/upload) away from
|
|
* the main room composer, e.g. `"!room:server::$rootEventId"`.
|
|
*/
|
|
export const getThreadDraftKey = (roomId: string, threadRootId: string): string =>
|
|
`${roomId}::${threadRootId}`;
|