diff --git a/src/app/utils/notifications.ts b/src/app/utils/notifications.ts index c0ff68596..cbf6d7158 100644 --- a/src/app/utils/notifications.ts +++ b/src/app/utils/notifications.ts @@ -21,8 +21,13 @@ export async function markAsRead(mx: MatrixClient, roomId: string, privateReceip const latestEvent = getLatestValidEvent(); if (latestEvent === null) return; + // Unthreaded receipt: with client threadSupport enabled the SDK would + // otherwise scope this to the main timeline (thread_id: "main"), leaving + // per-thread notification counts permanently unread. Unthreaded preserves + // the pre-threads wire behavior — one receipt clears everything. await mx.sendReadReceipt( latestEvent, privateReceipt || privateReadReceipts ? ReceiptType.ReadPrivate : ReceiptType.Read, + true, ); } diff --git a/src/client/initMatrix.ts b/src/client/initMatrix.ts index fe5258bee..02d8fcd9e 100644 --- a/src/client/initMatrix.ts +++ b/src/client/initMatrix.ts @@ -64,6 +64,11 @@ export const initClient = async (session: Session): Promise => { export const startClient = async (mx: MatrixClient) => { await mx.startClient({ lazyLoadMembers: true, + // P3-8: partition m.thread relations into Thread objects/timelines. Thread + // replies leave the main timeline (roots stay + get a summary chip); the + // thread panel renders them. markAsRead sends UNTHREADED receipts + // (utils/notifications.ts) so room badges keep clearing. + threadSupport: true, }); };