fix(threads): review-wave fixes — decryption re-render, receipt dedupe, chip perf
Two-reviewer audit of the thread stack; confirmed findings fixed: - ThreadTimeline: wrap encrypted events in EncryptedContent so a live-arriving E2EE reply re-renders when its key decrypts (decryption emits neither RoomEvent.Timeline nor ThreadEvent.Update — previously stuck at "Unable to decrypt"). - ThreadPanel: mark-read deduped on the latest event id (RoomEvent.Timeline re-emits per backfilled event/edit/reaction; previously up to N receipt POSTs per panel open) + rejection handled with retry. - RoomTimeline: ThreadSummary chips now mount only for events carrying thread data (each chip holds a room-level listener; one per rendered message would blow the SDK's 100-listener emitter cap) with a single room-level ThreadEvent.New tick for new-thread liveness. - useThreadPendingEvents: keep a sent reply visible through the /send-response→ /sync window (was flashing out of the pending strip before landing). - ThreadTimeline: reseed the window on RoomEvent.TimelineReset (gappy sync left a detached timeline). Documented-acceptable (reviewer-noted): thread typing shows as room typing (no per-thread typing in the spec; Element matches), thread panel + members drawer can be open together, scheduled-send is thread-unaware but unreachable there. Gates: tsc clean, eslint 0 errors, build OK, 616/617 tests (1 IDB skip). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
EventStatus,
|
||||
EventTimeline,
|
||||
MatrixClient,
|
||||
MatrixEvent,
|
||||
@@ -121,7 +122,15 @@ export const useThreadPendingEvents = (
|
||||
|
||||
const alreadyInThread =
|
||||
eventId !== undefined && thread?.findEventById(eventId) !== undefined;
|
||||
const stillPending = isPendingThreadReply(event, threadRootId) && !alreadyInThread;
|
||||
// Keep a tracked event through the SENT window too: the /send response
|
||||
// flips status to SENT before /sync delivers the event into the thread
|
||||
// timeline — dropping it there would make the message flash out of view.
|
||||
// It falls out on the next LocalEchoUpdated once findEventById sees it.
|
||||
const trackedAndAwaitingSync =
|
||||
event.status === EventStatus.SENT &&
|
||||
prev.some((e) => e === event || (eventId !== undefined && e.getId() === eventId));
|
||||
const stillPending =
|
||||
!alreadyInThread && (isPendingThreadReply(event, threadRootId) || trackedAndAwaitingSync);
|
||||
|
||||
if (stillPending) return [...without, event];
|
||||
return without.length === prev.length ? prev : without;
|
||||
|
||||
Reference in New Issue
Block a user