1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-05 00:42:10 +03:00

Aggregate relations regardless of whether event fits into the timeline (#3496)

This commit is contained in:
Michael Telatynski
2023-06-26 09:39:25 +01:00
committed by GitHub
parent f884c78579
commit f16a6bc654
3 changed files with 39 additions and 29 deletions

View File

@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { mocked } from "jest-mock";
import * as utils from "../test-utils/test-utils"; import * as utils from "../test-utils/test-utils";
import { import {
DuplicateStrategy, DuplicateStrategy,
@@ -160,6 +162,33 @@ describe("EventTimelineSet", () => {
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, true, false); eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, true, false);
}).not.toThrow(); }).not.toThrow();
}); });
it("should aggregate relations which belong to unknown timeline without adding them to any timeline", () => {
// If threads are disabled all events go into the main timeline
mocked(client.supportsThreads).mockReturnValue(true);
const reactionEvent = utils.mkReaction(messageEvent, client, client.getSafeUserId(), roomId);
const liveTimeline = eventTimelineSet.getLiveTimeline();
expect(liveTimeline.getEvents().length).toStrictEqual(0);
eventTimelineSet.addEventToTimeline(reactionEvent, liveTimeline, {
toStartOfTimeline: true,
});
expect(liveTimeline.getEvents().length).toStrictEqual(0);
eventTimelineSet.addEventToTimeline(messageEvent, liveTimeline, {
toStartOfTimeline: true,
});
expect(liveTimeline.getEvents()).toHaveLength(1);
const [event] = liveTimeline.getEvents();
const reactions = eventTimelineSet.relations!.getChildEventsForEvent(
event.getId()!,
"m.annotation",
"m.reaction",
)!;
const relations = reactions.getRelations();
expect(relations).toHaveLength(1);
expect(relations[0].getId()).toBe(reactionEvent.getId());
});
}); });
describe("addEventToTimeline (thread timeline)", () => { describe("addEventToTimeline (thread timeline)", () => {

View File

@@ -721,13 +721,17 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
"in timelineSet(threadId=${this.thread?.id})`); "in timelineSet(threadId=${this.thread?.id})`);
} }
const eventId = event.getId()!;
this.relations.aggregateParentEvent(event);
this.relations.aggregateChildEvent(event, this);
// Make sure events don't get mixed in timelines they shouldn't be in (e.g. a // Make sure events don't get mixed in timelines they shouldn't be in (e.g. a
// threaded message should not be in the main timeline). // threaded message should not be in the main timeline).
// //
// We can only run this check for timelines with a `room` because `canContain` // We can only run this check for timelines with a `room` because `canContain`
// requires it // requires it
if (this.room && !this.canContain(event)) { if (this.room && !this.canContain(event)) {
let eventDebugString = `event=${event.getId()}`; let eventDebugString = `event=${eventId}`;
if (event.threadRootId) { if (event.threadRootId) {
eventDebugString += `(belongs to thread=${event.threadRootId})`; eventDebugString += `(belongs to thread=${event.threadRootId})`;
} }
@@ -738,7 +742,6 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
return; return;
} }
const eventId = event.getId()!;
timeline.addEvent(event, { timeline.addEvent(event, {
toStartOfTimeline, toStartOfTimeline,
roomState, roomState,
@@ -746,9 +749,6 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
}); });
this._eventIdToTimeline.set(eventId, timeline); this._eventIdToTimeline.set(eventId, timeline);
this.relations.aggregateParentEvent(event);
this.relations.aggregateChildEvent(event, this);
const data: IRoomTimelineData = { const data: IRoomTimelineData = {
timeline: timeline, timeline: timeline,
liveEvent: !toStartOfTimeline && timeline == this.liveTimeline && !fromCache, liveEvent: !toStartOfTimeline && timeline == this.liveTimeline && !fromCache,
@@ -782,13 +782,17 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
"in timelineSet(threadId=${this.thread?.id})`); "in timelineSet(threadId=${this.thread?.id})`);
} }
const eventId = event.getId()!;
this.relations.aggregateParentEvent(event);
this.relations.aggregateChildEvent(event, this);
// Make sure events don't get mixed in timelines they shouldn't be in (e.g. a // Make sure events don't get mixed in timelines they shouldn't be in (e.g. a
// threaded message should not be in the main timeline). // threaded message should not be in the main timeline).
// //
// We can only run this check for timelines with a `room` because `canContain` // We can only run this check for timelines with a `room` because `canContain`
// requires it // requires it
if (this.room && !this.canContain(event)) { if (this.room && !this.canContain(event)) {
let eventDebugString = `event=${event.getId()}`; let eventDebugString = `event=${eventId}`;
if (event.threadRootId) { if (event.threadRootId) {
eventDebugString += `(belongs to thread=${event.threadRootId})`; eventDebugString += `(belongs to thread=${event.threadRootId})`;
} }
@@ -830,13 +834,9 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
// If we got to the end of the loop, insertIndex points at the end of // If we got to the end of the loop, insertIndex points at the end of
// the list. // the list.
const eventId = event.getId()!;
timeline.insertEvent(event, insertIndex, roomState); timeline.insertEvent(event, insertIndex, roomState);
this._eventIdToTimeline.set(eventId, timeline); this._eventIdToTimeline.set(eventId, timeline);
this.relations.aggregateParentEvent(event);
this.relations.aggregateChildEvent(event, this);
const data: IRoomTimelineData = { const data: IRoomTimelineData = {
timeline: timeline, timeline: timeline,
liveEvent: timeline == this.liveTimeline, liveEvent: timeline == this.liveTimeline,

View File

@@ -2103,7 +2103,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
threadId?: string; threadId?: string;
} { } {
if (!this.client?.supportsThreads()) { if (!this.client?.supportsThreads()) {
logger.debug(`Room::eventShouldLiveIn: eventId=${event.getId()} client does not support threads`);
return { return {
shouldLiveInRoom: true, shouldLiveInRoom: true,
shouldLiveInThread: false, shouldLiveInThread: false,
@@ -2112,11 +2111,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
// A thread root is always shown in both timelines // A thread root is always shown in both timelines
if (event.isThreadRoot || roots?.has(event.getId()!)) { if (event.isThreadRoot || roots?.has(event.getId()!)) {
if (event.isThreadRoot) {
logger.debug(`Room::eventShouldLiveIn: eventId=${event.getId()} isThreadRoot is true`);
} else {
logger.debug(`Room::eventShouldLiveIn: eventId=${event.getId()} is a known thread root`);
}
return { return {
shouldLiveInRoom: true, shouldLiveInRoom: true,
shouldLiveInThread: true, shouldLiveInThread: true,
@@ -2127,9 +2121,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
// A thread relation (1st and 2nd order) is always only shown in a thread // A thread relation (1st and 2nd order) is always only shown in a thread
const threadRootId = event.threadRootId; const threadRootId = event.threadRootId;
if (threadRootId != undefined) { if (threadRootId != undefined) {
logger.debug(
`Room::eventShouldLiveIn: eventId=${event.getId()} threadRootId=${threadRootId} is part of a thread`,
);
return { return {
shouldLiveInRoom: false, shouldLiveInRoom: false,
shouldLiveInThread: true, shouldLiveInThread: true,
@@ -2141,9 +2132,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
let parentEvent: MatrixEvent | undefined; let parentEvent: MatrixEvent | undefined;
if (parentEventId) { if (parentEventId) {
parentEvent = this.findEventById(parentEventId) ?? events?.find((e) => e.getId() === parentEventId); parentEvent = this.findEventById(parentEventId) ?? events?.find((e) => e.getId() === parentEventId);
logger.debug(
`Room::eventShouldLiveIn: eventId=${event.getId()} parentEventId=${parentEventId} found=${!!parentEvent}`,
);
} }
// Treat relations and redactions as extensions of their parents so evaluate parentEvent instead // Treat relations and redactions as extensions of their parents so evaluate parentEvent instead
@@ -2152,7 +2140,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
} }
if (!event.isRelation()) { if (!event.isRelation()) {
logger.debug(`Room::eventShouldLiveIn: eventId=${event.getId()} not a relation`);
return { return {
shouldLiveInRoom: true, shouldLiveInRoom: true,
shouldLiveInThread: false, shouldLiveInThread: false,
@@ -2161,11 +2148,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
// Edge case where we know the event is a relation but don't have the parentEvent // Edge case where we know the event is a relation but don't have the parentEvent
if (roots?.has(event.relationEventId!)) { if (roots?.has(event.relationEventId!)) {
logger.debug(
`Room::eventShouldLiveIn: eventId=${event.getId()} relationEventId=${
event.relationEventId
} is a known root`,
);
return { return {
shouldLiveInRoom: true, shouldLiveInRoom: true,
shouldLiveInThread: true, shouldLiveInThread: true,
@@ -2176,7 +2158,6 @@ export class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
// We've exhausted all scenarios, // We've exhausted all scenarios,
// we cannot assume that it lives in the main timeline as this may be a relation for an unknown thread // we cannot assume that it lives in the main timeline as this may be a relation for an unknown thread
// adding the event in the wrong timeline causes stuck notifications and can break ability to send read receipts // adding the event in the wrong timeline causes stuck notifications and can break ability to send read receipts
logger.debug(`Room::eventShouldLiveIn: eventId=${event.getId()} belongs to an unknown timeline`);
return { return {
shouldLiveInRoom: false, shouldLiveInRoom: false,
shouldLiveInThread: false, shouldLiveInThread: false,