You've already forked matrix-js-sdk
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:
committed by
GitHub
parent
f884c78579
commit
f16a6bc654
@@ -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)", () => {
|
||||||
|
@@ -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,
|
||||||
|
@@ -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,
|
||||||
|
Reference in New Issue
Block a user