29 lines
1.3 KiB
TypeScript
29 lines
1.3 KiB
TypeScript
import { MatrixClient, ReceiptType, Thread } from 'matrix-js-sdk';
|
|||
|
|
|
||
|
|
/**
|
||
|
|
* Send a threaded read receipt for a thread, clearing its per-thread unread
|
||
|
|
* count.
|
||
|
|
*
|
||
|
|
* CRITICAL: never receipt the thread ROOT. A thread's liveTimeline is
|
||
|
|
* `[root, reply1, …]`, so the latest event IS the root when replies aren't
|
||
|
|
* loaded yet (common — the thread panel fires this on mount before replies
|
||
|
|
* fetch). The root is "in the main timeline", so a receipt for it is written by
|
||
|
|
* the SDK with `thread_id:"main"` at the old root, dragging the room's MAIN read
|
||
|
|
* marker backwards (`getEventReadUpTo` → an old/unloaded event) and re-lighting
|
||
|
|
* the whole room. We only receipt a genuine loaded reply (`thread.lastReply()`);
|
||
|
|
* if none is loaded we bail (the per-thread count clears when the reply loads
|
||
|
|
* and this runs again). Mirrors the root guard in `utils/notifications.ts`.
|
||
|
|
*
|
||
|
|
* Pure (no React/CSS) so it can be unit-tested — see `threadReceipt.test.ts`.
|
||
|
|
*/
|
||
|
|
export const markThreadAsRead = async (
|
||
|
|
mx: MatrixClient,
|
||
|
|
thread: Thread,
|
||
|
|
privateReceipt: boolean,
|
||
|
|
): Promise<void> => {
|
||
|
|
const lastReply = thread.lastReply();
|
||
|
|
if (!lastReply || lastReply.isSending() || lastReply.getId() === thread.id) return;
|
||
|
|
|
||
|
|
await mx.sendReadReceipt(lastReply, privateReceipt ? ReceiptType.ReadPrivate : ReceiptType.Read);
|
||
|
|
};
|