Files
cinny/src/app/features/room/thread/threadSummaryData.ts
T
jaredandClaude Opus 4.8 992d2b83b3
CI / Build & Quality Checks (push) Failing after 5m22s
CI / Trigger Desktop Build (push) Has been skipped
fix(build): rename threadSummary.ts — case-collision broke the Windows release
threadSummary.ts (pure helpers) and ThreadSummary.tsx (chip component) lived in
the same directory differing only by case. On the case-insensitive Windows
release runner, RoomTimeline's extensionless import of ./thread/ThreadSummary
resolved .ts BEFORE .tsx and matched the helper module → rolldown
MISSING_EXPORT "ThreadSummary" — invisible on every Linux/macOS build (and the
cause of the earlier masked pdf.worker failure). Helper module renamed to
threadSummaryData.ts (+ test), 3 importers updated.

Prevention: new caseCollision.test.ts walks src/ and fails on any same-directory
names differing only by case (extensionless compare, so Foo.tsx vs foo.ts is
caught) — verified it fails on the pre-rename tree. Runs in the hard CI gate.

Gates: tsc clean, eslint/prettier clean, build OK, 658/659 tests (1 IDB skip).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 23:43:20 -04:00

56 lines
1.8 KiB
TypeScript

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<IThreadBundledRelationship>(
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;
};