1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Clarify code that chooses a thread ID to include in a receipt (#3797)

* Extract threadIdForReceipt function from sendReceipt

* Tests for threadIdForReceipt

* Correct test of threadIdForReceipt to expect main for redaction of threaded

* Expand and comment implementation of threadIdForReceipt
This commit is contained in:
Andy Balaam
2023-10-16 11:35:36 +01:00
committed by GitHub
parent 5d233f3863
commit 5595e8497f
2 changed files with 265 additions and 18 deletions

View File

@@ -5157,24 +5157,12 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
$eventId: event.getId()!,
});
if (!unthreaded && this.supportsThreads()) {
// XXX: the spec currently says a threaded read receipt can be sent for the root of a thread,
// but in practice this isn't possible and the spec needs updating.
const isThread =
!!event.threadRootId &&
// A thread cannot be just a thread root and a thread root can only be read in the main timeline
!event.isThreadRoot &&
// Similarly non-thread relations upon the thread root (reactions, edits) should also be for the main timeline.
event.isRelation() &&
(event.isRelation(THREAD_RELATION_TYPE.name) || event.relationEventId !== event.threadRootId);
body = {
...body,
// Only thread replies should define a specific thread. Thread roots can only be read in the main timeline.
thread_id: isThread ? event.threadRootId : MAIN_ROOM_TIMELINE,
};
}
// Unless we're explicitly making an unthreaded receipt or we don't
// support threads, include the `thread_id` property in the body.
const shouldAddThreadId = !unthreaded && this.supportsThreads();
const fullBody = shouldAddThreadId ? { ...body, thread_id: threadIdForReceipt(event) } : body;
const promise = this.http.authedRequest<{}>(Method.Post, path, undefined, body || {});
const promise = this.http.authedRequest<{}>(Method.Post, path, undefined, fullBody || {});
const room = this.getRoom(event.getRoomId());
if (room && this.credentials.userId) {
@@ -9925,3 +9913,66 @@ export function fixNotificationCountOnDecryption(cli: MatrixClient, event: Matri
}
}
}
/**
* Given an event, figure out the thread ID we should use for it in a receipt.
*
* This will either be "main", or event.threadRootId. For the thread root, or
* e.g. reactions to the thread root, this will be main. For events inside the
* thread, or e.g. reactions to them, this will be event.threadRootId.
*
* (Exported for test.)
*/
export function threadIdForReceipt(event: MatrixEvent): string {
return inMainTimelineForReceipt(event) ? MAIN_ROOM_TIMELINE : event.threadRootId!;
}
/**
* a) True for non-threaded messages, thread roots and non-thread relations to thread roots.
* b) False for messages with thread relations to the thread root.
* c) False for messages with any kind of relation to a message from case b.
*
* Note: true for redactions of messages that are in threads. Redacted messages
* are not really in threads (because their relations are gone), so if they look
* like they are in threads, that is a sign of a bug elsewhere. (At time of
* writing, this bug definitely exists - messages are not moved to another
* thread when they are redacted.)
*
* @returns true if this event is considered to be in the main timeline as far
* as receipts are concerned.
*/
function inMainTimelineForReceipt(event: MatrixEvent): boolean {
if (!event.threadRootId) {
// Not in a thread: then it is in the main timeline
return true;
}
if (event.isThreadRoot) {
// Thread roots are in the main timeline. Note: the spec is ambiguous (or
// wrong) on this - see
// https://github.com/matrix-org/matrix-spec-proposals/pull/4037
return true;
}
if (!event.isRelation()) {
// If it's not related to anything, it can't be related via a chain of
// relations to a thread root.
//
// Note: this is a bug, because how does it have a threadRootId if it is
// neither a thread root, nor related to one?
logger.warn(`Event is not a relation or a thread root, but still has a threadRootId! id=${event.getId()}`);
return true;
}
if (event.isRelation(THREAD_RELATION_TYPE.name)) {
// It's a message in a thread - definitely not in the main timeline.
return false;
}
const isRelatedToRoot = event.relationEventId === event.threadRootId;
// If it's related to the thread root (and we already know it's not a thread
// relation) then it's in the main timeline. If it's related to something
// else, then it's in the thread (because it has a thread ID).
return isRelatedToRoot;
}