import { EventStatus, IThreadBundledRelationship, MatrixEvent, RelationType } from 'matrix-js-sdk'; export type ThreadSummaryData = { count: number; latestTs: number | undefined; }; /** * Summary data for a thread root's "N replies" chip. * * Prefers the live {@link Thread} object when it exists (it reflects local * echo + pagination), otherwise falls back to the server-aggregated bundle * (`unsigned['m.relations']['m.thread']`) so the chip renders before any * Thread object has been created. Returns `undefined` when the root has no * thread at all. */ export const getThreadSummary = (rootEvent: MatrixEvent): ThreadSummaryData | undefined => { const thread = rootEvent.getThread(); if (thread) { const lastReply = thread.lastReply(); return { count: thread.length, latestTs: lastReply?.getTs(), }; } const bundle = rootEvent.getServerAggregatedRelation( RelationType.Thread, ); if (bundle) { return { count: bundle.count, latestTs: bundle.latest_event?.origin_server_ts, }; } return undefined; }; /** * True when `event` is a still-in-flight (local echo) reply belonging to the * given thread root. Used to render the pending strip, since pending thread * sends never enter the thread's timelineSet. */ export const isPendingThreadReply = (event: MatrixEvent, threadRootId: string): boolean => { const { status } = event; if (status !== EventStatus.SENDING && status !== EventStatus.NOT_SENT) return false; // Prefer the SDK's resolved thread root id; fall back to the raw relation // content for events the SDK hasn't associated with a thread yet. if (event.threadRootId === threadRootId) return true; const relation = event.getRelation(); return relation?.rel_type === RelationType.Thread && relation.event_id === threadRootId; };