1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-07-31 15:24:23 +03:00

Fix issue with getEventTimeline returning undefined for thread roots in main timeline (#2454)

* Fix test message utils using overload

* Tweak existing tests

* Add test around `MatrixClient::getEventTimeline`

* Fix test to actually exercise the faulty behaviour

* Extract timelineSet thread belongs logic and test it

* tweak method name
This commit is contained in:
Michael Telatynski
2022-06-15 15:46:08 +01:00
committed by GitHub
parent b43b4aa9f9
commit ab588f0e51
8 changed files with 273 additions and 137 deletions

View File

@ -70,10 +70,23 @@ const EVENTS = [
}),
];
const THREAD_ROOT = utils.mkMessage({
const THREAD_ROOT = utils.mkEvent({
room: roomId,
user: userId,
msg: "thread root",
type: "m.room.message",
content: {
"body": "thread root",
"msgtype": "m.text",
},
unsigned: {
"m.relations": {
"io.element.thread": {
"latest_event": undefined,
"count": 1,
"current_user_participated": true,
},
},
},
});
const THREAD_REPLY = utils.mkEvent({
@ -91,6 +104,8 @@ const THREAD_REPLY = utils.mkEvent({
},
});
THREAD_ROOT.unsigned["m.relations"]["io.element.thread"].latest_event = THREAD_REPLY;
// start the client, and wait for it to initialise
function startClient(httpBackend, client) {
httpBackend.when("GET", "/versions").respond(200, {});
@ -540,7 +555,38 @@ describe("MatrixClient event timelines", function() {
expect(timeline.getEvents().find(e => e.getId() === THREAD_REPLY.event_id)).toBeTruthy();
});
it("should return undefined when event is not within a thread but timelineSet is", async () => {
it("should return relevant timeline from non-thread timelineSet when asking for the thread root", async () => {
client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(true);
client.stopClient(); // we don't need the client to be syncing at this time
const room = client.getRoom(roomId);
const threadRoot = new MatrixEvent(THREAD_ROOT);
const thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false);
const timelineSet = room.getTimelineSets()[0];
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(THREAD_ROOT.event_id))
.respond(200, function() {
return {
start: "start_token0",
events_before: [],
event: THREAD_ROOT,
events_after: [],
end: "end_token0",
state: [],
};
});
const [timeline] = await Promise.all([
client.getEventTimeline(timelineSet, THREAD_ROOT.event_id),
httpBackend.flushAllExpected(),
]);
expect(timeline).not.toBe(thread.liveTimeline);
expect(timelineSet.getTimelines()).toContain(timeline);
expect(timeline.getEvents().find(e => e.getId() === THREAD_ROOT.event_id)).toBeTruthy();
});
it("should return undefined when event is not in the thread that the given timelineSet is representing", () => {
client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(true);
client.stopClient(); // we don't need the client to be syncing at this time
@ -549,11 +595,6 @@ describe("MatrixClient event timelines", function() {
const thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false);
const timelineSet = thread.timelineSet;
httpBackend.when("GET", "/rooms/!foo%3Abar/event/" + encodeURIComponent(THREAD_ROOT.event_id))
.respond(200, function() {
return THREAD_ROOT;
});
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(EVENTS[0].event_id))
.respond(200, function() {
return {
@ -566,14 +607,13 @@ describe("MatrixClient event timelines", function() {
};
});
const timelinePromise = client.getEventTimeline(timelineSet, EVENTS[0].event_id);
await httpBackend.flushAllExpected();
const timeline = await timelinePromise;
expect(timeline).toBeUndefined();
return Promise.all([
expect(client.getEventTimeline(timelineSet, EVENTS[0].event_id)).resolves.toBeUndefined(),
httpBackend.flushAllExpected(),
]);
});
it("should return undefined when event is within a thread but timelineSet is not", async () => {
it("should return undefined when event is within a thread but timelineSet is not", () => {
client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(true);
client.stopClient(); // we don't need the client to be syncing at this time
@ -592,11 +632,10 @@ describe("MatrixClient event timelines", function() {
};
});
const timelinePromise = client.getEventTimeline(timelineSet, THREAD_REPLY.event_id);
await httpBackend.flushAllExpected();
const timeline = await timelinePromise;
expect(timeline).toBeUndefined();
return Promise.all([
expect(client.getEventTimeline(timelineSet, THREAD_REPLY.event_id)).resolves.toBeUndefined(),
httpBackend.flushAllExpected(),
]);
});
it("should should add lazy loading filter when requested", async () => {

View File

@ -74,7 +74,6 @@ interface IEventOpts {
sender?: string;
skey?: string;
content: IContent;
event?: boolean;
user?: string;
unsigned?: IUnsigned;
redacts?: string;
@ -93,7 +92,9 @@ let testEventIndex = 1; // counter for events, easier for comparison of randomly
* @param {MatrixClient} client If passed along with opts.event=true will be used to set up re-emitters.
* @return {Object} a JSON object representing this event.
*/
export function mkEvent(opts: IEventOpts, client?: MatrixClient): object | MatrixEvent {
export function mkEvent(opts: IEventOpts & { event: true }, client?: MatrixClient): MatrixEvent;
export function mkEvent(opts: IEventOpts & { event?: false }, client?: MatrixClient): object;
export function mkEvent(opts: IEventOpts & { event?: boolean }, client?: MatrixClient): object | MatrixEvent {
if (!opts.type || !opts.content) {
throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
}
@ -143,7 +144,9 @@ interface IPresenceOpts {
* @param {Object} opts Values for the presence.
* @return {Object|MatrixEvent} The event
*/
export function mkPresence(opts: IPresenceOpts): object | MatrixEvent {
export function mkPresence(opts: IPresenceOpts & { event: true }): MatrixEvent;
export function mkPresence(opts: IPresenceOpts & { event?: false }): object;
export function mkPresence(opts: IPresenceOpts & { event?: boolean }): object | MatrixEvent {
const event = {
event_id: "$" + Math.random() + "-" + Math.random(),
type: "m.presence",
@ -182,7 +185,9 @@ interface IMembershipOpts {
* @param {boolean} opts.event True to make a MatrixEvent.
* @return {Object|MatrixEvent} The event
*/
export function mkMembership(opts: IMembershipOpts): object | MatrixEvent {
export function mkMembership(opts: IMembershipOpts & { event: true }): MatrixEvent;
export function mkMembership(opts: IMembershipOpts & { event?: false }): object;
export function mkMembership(opts: IMembershipOpts & { event?: boolean }): object | MatrixEvent {
const eventOpts: IEventOpts = {
...opts,
type: EventType.RoomMember,
@ -220,7 +225,9 @@ interface IMessageOpts {
* @param {MatrixClient} client If passed along with opts.event=true will be used to set up re-emitters.
* @return {Object|MatrixEvent} The event
*/
export function mkMessage(opts: IMessageOpts, client?: MatrixClient): object | MatrixEvent {
export function mkMessage(opts: IMessageOpts & { event: true }, client?: MatrixClient): MatrixEvent;
export function mkMessage(opts: IMessageOpts & { event?: false }, client?: MatrixClient): object;
export function mkMessage(opts: IMessageOpts & { event?: boolean }, client?: MatrixClient): object | MatrixEvent {
const eventOpts: IEventOpts = {
...opts,
type: EventType.RoomMessage,
@ -252,7 +259,12 @@ interface IReplyMessageOpts extends IMessageOpts {
* @param {MatrixClient} client If passed along with opts.event=true will be used to set up re-emitters.
* @return {Object|MatrixEvent} The event
*/
export function mkReplyMessage(opts: IReplyMessageOpts, client?: MatrixClient): object | MatrixEvent {
export function mkReplyMessage(opts: IReplyMessageOpts & { event: true }, client?: MatrixClient): MatrixEvent;
export function mkReplyMessage(opts: IReplyMessageOpts & { event?: false }, client?: MatrixClient): object;
export function mkReplyMessage(
opts: IReplyMessageOpts & { event?: boolean },
client?: MatrixClient,
): object | MatrixEvent {
const eventOpts: IEventOpts = {
...opts,
type: EventType.RoomMessage,

View File

@ -25,6 +25,8 @@ import {
Room,
DuplicateStrategy,
} from '../../src';
import { Thread } from "../../src/models/thread";
import { ReEmitter } from "../../src/ReEmitter";
describe('EventTimelineSet', () => {
const roomId = '!foo:bar';
@ -54,6 +56,7 @@ describe('EventTimelineSet', () => {
beforeEach(() => {
client = utils.mock(MatrixClient, 'MatrixClient');
client.reEmitter = utils.mock(ReEmitter, 'ReEmitter');
room = new Room(roomId, client, userA);
eventTimelineSet = new EventTimelineSet(room);
eventTimeline = new EventTimeline(eventTimelineSet);
@ -62,14 +65,14 @@ describe('EventTimelineSet', () => {
user: userA,
msg: 'Hi!',
event: true,
}) as MatrixEvent;
});
replyEvent = utils.mkReplyMessage({
room: roomId,
user: userA,
msg: 'Hoo!',
event: true,
replyToMessage: messageEvent,
}) as MatrixEvent;
});
});
describe('addLiveEvent', () => {
@ -91,7 +94,7 @@ describe('EventTimelineSet', () => {
// make a duplicate
const duplicateMessageEvent = utils.mkMessage({
room: roomId, user: userA, msg: "dupe", event: true,
}) as MatrixEvent;
});
duplicateMessageEvent.event.event_id = messageEvent.getId();
// Adding the duplicate event should replace the `messageEvent`
@ -220,4 +223,72 @@ describe('EventTimelineSet', () => {
});
});
});
describe("canContain", () => {
const mkThreadResponse = (root: MatrixEvent) => utils.mkEvent({
event: true,
type: EventType.RoomMessage,
user: userA,
room: roomId,
content: {
"body": "Thread response :: " + Math.random(),
"m.relates_to": {
"event_id": root.getId(),
"m.in_reply_to": {
"event_id": root.getId(),
},
"rel_type": "m.thread",
},
},
}, room.client);
let thread: Thread;
beforeEach(() => {
(client.supportsExperimentalThreads as jest.Mock).mockReturnValue(true);
thread = new Thread("!thread_id:server", messageEvent, { room, client });
});
it("should throw if timeline set has no room", () => {
const eventTimelineSet = new EventTimelineSet(undefined, {}, client);
expect(() => eventTimelineSet.canContain(messageEvent)).toThrowError();
});
it("should return false if timeline set is for thread but event is not threaded", () => {
const eventTimelineSet = new EventTimelineSet(room, {}, client, thread);
expect(eventTimelineSet.canContain(replyEvent)).toBeFalsy();
});
it("should return false if timeline set it for thread but event it for a different thread", () => {
const eventTimelineSet = new EventTimelineSet(room, {}, client, thread);
const event = mkThreadResponse(replyEvent);
expect(eventTimelineSet.canContain(event)).toBeFalsy();
});
it("should return false if timeline set is not for a thread but event is a thread response", () => {
const eventTimelineSet = new EventTimelineSet(room, {}, client);
const event = mkThreadResponse(replyEvent);
expect(eventTimelineSet.canContain(event)).toBeFalsy();
});
it("should return true if the timeline set is not for a thread and the event is a thread root", () => {
const eventTimelineSet = new EventTimelineSet(room, {}, client);
expect(eventTimelineSet.canContain(messageEvent)).toBeTruthy();
});
it("should return true if the timeline set is for a thread and the event is its thread root", () => {
const thread = new Thread(messageEvent.getId(), messageEvent, { room, client });
const eventTimelineSet = new EventTimelineSet(room, {}, client, thread);
messageEvent.setThread(thread);
expect(eventTimelineSet.canContain(messageEvent)).toBeTruthy();
});
it("should return true if the timeline set is for a thread and the event is a response to it", () => {
const thread = new Thread(messageEvent.getId(), messageEvent, { room, client });
const eventTimelineSet = new EventTimelineSet(room, {}, client, thread);
messageEvent.setThread(thread);
const event = mkThreadResponse(messageEvent);
expect(eventTimelineSet.canContain(event)).toBeTruthy();
});
});
});

View File

@ -1,7 +1,4 @@
import {
MatrixEvent,
RelationType,
} from "../../src";
import { RelationType } from "../../src";
import { FilterComponent } from "../../src/filter-component";
import { mkEvent } from '../test-utils/test-utils';
@ -14,7 +11,7 @@ describe("Filter Component", function() {
content: { },
room: 'roomId',
event: true,
}) as MatrixEvent;
});
const checkResult = filter.check(event);
@ -28,7 +25,7 @@ describe("Filter Component", function() {
content: { },
room: 'roomId',
event: true,
}) as MatrixEvent;
});
const checkResult = filter.check(event);
@ -55,7 +52,7 @@ describe("Filter Component", function() {
},
},
},
}) as MatrixEvent;
});
expect(filter.check(threadRootNotParticipated)).toBe(false);
});
@ -80,7 +77,7 @@ describe("Filter Component", function() {
user: '@someone-else:server.org',
room: 'roomId',
event: true,
}) as MatrixEvent;
});
expect(filter.check(threadRootParticipated)).toBe(true);
});
@ -100,7 +97,7 @@ describe("Filter Component", function() {
[RelationType.Reference]: {},
},
},
}) as MatrixEvent;
});
expect(filter.check(referenceRelationEvent)).toBe(false);
});
@ -123,7 +120,7 @@ describe("Filter Component", function() {
},
room: 'roomId',
event: true,
}) as MatrixEvent;
});
const eventWithMultipleRelations = mkEvent({
"type": "m.room.message",
@ -148,7 +145,7 @@ describe("Filter Component", function() {
},
"room": 'roomId',
"event": true,
}) as MatrixEvent;
});
const noMatchEvent = mkEvent({
"type": "m.room.message",
@ -160,7 +157,7 @@ describe("Filter Component", function() {
},
"room": 'roomId',
"event": true,
}) as MatrixEvent;
});
expect(filter.check(threadRootEvent)).toBe(true);
expect(filter.check(eventWithMultipleRelations)).toBe(true);

View File

@ -52,7 +52,7 @@ describe("Room", function() {
event: true,
user: userA,
room: roomId,
}, room.client) as MatrixEvent;
}, room.client);
const mkReply = (target: MatrixEvent) => utils.mkEvent({
event: true,
@ -67,7 +67,7 @@ describe("Room", function() {
},
},
},
}, room.client) as MatrixEvent;
}, room.client);
const mkEdit = (target: MatrixEvent, salt = Math.random()) => utils.mkEvent({
event: true,
@ -84,7 +84,7 @@ describe("Room", function() {
event_id: target.getId(),
},
},
}, room.client) as MatrixEvent;
}, room.client);
const mkThreadResponse = (root: MatrixEvent) => utils.mkEvent({
event: true,
@ -101,7 +101,7 @@ describe("Room", function() {
"rel_type": "m.thread",
},
},
}, room.client) as MatrixEvent;
}, room.client);
const mkReaction = (target: MatrixEvent) => utils.mkEvent({
event: true,
@ -115,7 +115,7 @@ describe("Room", function() {
"key": Math.random().toString(),
},
},
}, room.client) as MatrixEvent;
}, room.client);
const mkRedaction = (target: MatrixEvent) => utils.mkEvent({
event: true,
@ -124,7 +124,7 @@ describe("Room", function() {
room: roomId,
redacts: target.getId(),
content: {},
}, room.client) as MatrixEvent;
}, room.client);
beforeEach(function() {
room = new Room(roomId, new TestClient(userA, "device").client, userA);
@ -210,11 +210,11 @@ describe("Room", function() {
const events: MatrixEvent[] = [
utils.mkMessage({
room: roomId, user: userA, msg: "changing room name", event: true,
}) as MatrixEvent,
}),
utils.mkEvent({
type: EventType.RoomName, room: roomId, user: userA, event: true,
content: { name: "New Room Name" },
}) as MatrixEvent,
}),
];
it("Make sure legacy overload passing options directly as parameters still works", () => {
@ -235,7 +235,7 @@ describe("Room", function() {
// make a duplicate
const dupe = utils.mkMessage({
room: roomId, user: userA, msg: "dupe", event: true,
}) as MatrixEvent;
});
dupe.event.event_id = events[0].getId();
room.addLiveEvents(events);
expect(room.timeline[0]).toEqual(events[0]);
@ -249,7 +249,7 @@ describe("Room", function() {
// make a duplicate
const dupe = utils.mkMessage({
room: roomId, user: userA, msg: "dupe", event: true,
}) as MatrixEvent;
});
dupe.event.event_id = events[0].getId();
room.addLiveEvents(events);
expect(room.timeline[0]).toEqual(events[0]);
@ -277,13 +277,13 @@ describe("Room", function() {
const events: MatrixEvent[] = [
utils.mkMembership({
room: roomId, mship: "invite", user: userB, skey: userA, event: true,
}) as MatrixEvent,
}),
utils.mkEvent({
type: EventType.RoomName, room: roomId, user: userB, event: true,
content: {
name: "New room",
},
}) as MatrixEvent,
}),
];
room.addLiveEvents(events);
expect(room.currentState.setStateEvents).toHaveBeenCalledWith(
@ -318,13 +318,13 @@ describe("Room", function() {
it("should emit Room.localEchoUpdated when a local echo is updated", function() {
const localEvent = utils.mkMessage({
room: roomId, user: userA, event: true,
}) as MatrixEvent;
});
localEvent.status = EventStatus.SENDING;
const localEventId = localEvent.getId();
const remoteEvent = utils.mkMessage({
room: roomId, user: userA, event: true,
}) as MatrixEvent;
});
remoteEvent.event.unsigned = { transaction_id: "TXN_ID" };
const remoteEventId = remoteEvent.getId();
@ -445,11 +445,11 @@ describe("Room", function() {
const newEv = utils.mkEvent({
type: EventType.RoomName, room: roomId, user: userA, event: true,
content: { name: "New Room Name" },
}) as MatrixEvent;
});
const oldEv = utils.mkEvent({
type: EventType.RoomName, room: roomId, user: userA, event: true,
content: { name: "Old Room Name" },
}) as MatrixEvent;
});
room.addLiveEvents([newEv]);
expect(newEv.sender).toEqual(sentinel);
room.addEventsToTimeline([oldEv], true, room.getLiveTimeline());
@ -482,10 +482,10 @@ describe("Room", function() {
const newEv = utils.mkMembership({
room: roomId, mship: "invite", user: userB, skey: userA, event: true,
}) as MatrixEvent;
});
const oldEv = utils.mkMembership({
room: roomId, mship: "ban", user: userB, skey: userA, event: true,
}) as MatrixEvent;
});
room.addLiveEvents([newEv]);
expect(newEv.target).toEqual(sentinel);
room.addEventsToTimeline([oldEv], true, room.getLiveTimeline());
@ -497,13 +497,13 @@ describe("Room", function() {
const events: MatrixEvent[] = [
utils.mkMembership({
room: roomId, mship: "invite", user: userB, skey: userA, event: true,
}) as MatrixEvent,
}),
utils.mkEvent({
type: EventType.RoomName, room: roomId, user: userB, event: true,
content: {
name: "New room",
},
}) as MatrixEvent,
}),
];
room.addEventsToTimeline(events, true, room.getLiveTimeline());
@ -631,13 +631,13 @@ describe("Room", function() {
const events: MatrixEvent[] = [
utils.mkMessage({
room: roomId, user: userA, msg: "1111", event: true,
}) as MatrixEvent,
}),
utils.mkMessage({
room: roomId, user: userA, msg: "2222", event: true,
}) as MatrixEvent,
}),
utils.mkMessage({
room: roomId, user: userA, msg: "3333", event: true,
}) as MatrixEvent,
}),
];
it("should handle events in the same timeline", function() {
@ -778,26 +778,26 @@ describe("Room", function() {
type: EventType.RoomJoinRules, room: roomId, user: userA, content: {
join_rule: rule,
}, event: true,
}) as MatrixEvent]);
})]);
};
const setAltAliases = function(aliases: string[]) {
room.addLiveEvents([utils.mkEvent({
type: EventType.RoomCanonicalAlias, room: roomId, skey: "", content: {
alt_aliases: aliases,
}, event: true,
}) as MatrixEvent]);
})]);
};
const setAlias = function(alias: string) {
room.addLiveEvents([utils.mkEvent({
type: EventType.RoomCanonicalAlias, room: roomId, skey: "", content: { alias }, event: true,
}) as MatrixEvent]);
})]);
};
const setRoomName = function(name: string) {
room.addLiveEvents([utils.mkEvent({
type: EventType.RoomName, room: roomId, user: userA, content: {
name: name,
}, event: true,
}) as MatrixEvent]);
})]);
};
const addMember = function(userId: string, state = "join", opts: any = {}) {
opts.room = roomId;
@ -805,7 +805,7 @@ describe("Room", function() {
opts.user = opts.user || userId;
opts.skey = userId;
opts.event = true;
const event = utils.mkMembership(opts) as MatrixEvent;
const event = utils.mkMembership(opts);
room.addLiveEvents([event]);
return event;
};
@ -1113,7 +1113,7 @@ describe("Room", function() {
const eventToAck = utils.mkMessage({
room: roomId, user: userA, msg: "PLEASE ACKNOWLEDGE MY EXISTENCE",
event: true,
}) as MatrixEvent;
});
function mkReceipt(roomId: string, records) {
const content = {};
@ -1179,7 +1179,7 @@ describe("Room", function() {
const nextEventToAck = utils.mkMessage({
room: roomId, user: userA, msg: "I AM HERE YOU KNOW",
event: true,
}) as MatrixEvent;
});
const ts = 13787898424;
room.addReceipt(mkReceipt(roomId, [
mkRecord(eventToAck.getId(), "m.read", userB, ts),
@ -1214,11 +1214,11 @@ describe("Room", function() {
const eventTwo = utils.mkMessage({
room: roomId, user: userA, msg: "2222",
event: true,
}) as MatrixEvent;
});
const eventThree = utils.mkMessage({
room: roomId, user: userA, msg: "3333",
event: true,
}) as MatrixEvent;
});
const ts = 13787898424;
room.addReceipt(mkReceipt(roomId, [
mkRecord(eventToAck.getId(), "m.read", userB, ts),
@ -1266,15 +1266,15 @@ describe("Room", function() {
utils.mkMessage({
room: roomId, user: userA, msg: "1111",
event: true,
}) as MatrixEvent,
}),
utils.mkMessage({
room: roomId, user: userA, msg: "2222",
event: true,
}) as MatrixEvent,
}),
utils.mkMessage({
room: roomId, user: userA, msg: "3333",
event: true,
}) as MatrixEvent,
}),
];
room.addLiveEvents(events);
@ -1304,15 +1304,15 @@ describe("Room", function() {
utils.mkMessage({
room: roomId, user: userA, msg: "1111",
event: true,
}) as MatrixEvent,
}),
utils.mkMessage({
room: roomId, user: userA, msg: "2222",
event: true,
}) as MatrixEvent,
}),
utils.mkMessage({
room: roomId, user: userA, msg: "3333",
event: true,
}) as MatrixEvent,
}),
];
room.addLiveEvents(events);
@ -1404,14 +1404,14 @@ describe("Room", function() {
});
const eventA = utils.mkMessage({
room: roomId, user: userA, msg: "remote 1", event: true,
}) as MatrixEvent;
});
const eventB = utils.mkMessage({
room: roomId, user: userA, msg: "local 1", event: true,
}) as MatrixEvent;
});
eventB.status = EventStatus.SENDING;
const eventC = utils.mkMessage({
room: roomId, user: userA, msg: "remote 2", event: true,
}) as MatrixEvent;
});
room.addLiveEvents([eventA]);
room.addPendingEvent(eventB, "TXN1");
room.addLiveEvents([eventC]);
@ -1430,14 +1430,14 @@ describe("Room", function() {
});
const eventA = utils.mkMessage({
room: roomId, user: userA, msg: "remote 1", event: true,
}) as MatrixEvent;
});
const eventB = utils.mkMessage({
room: roomId, user: userA, msg: "local 1", event: true,
}) as MatrixEvent;
});
eventB.status = EventStatus.SENDING;
const eventC = utils.mkMessage({
room: roomId, user: userA, msg: "remote 2", event: true,
}) as MatrixEvent;
});
room.addLiveEvents([eventA]);
room.addPendingEvent(eventB, "TXN1");
room.addLiveEvents([eventC]);
@ -1457,7 +1457,7 @@ describe("Room", function() {
});
const eventA = utils.mkMessage({
room: roomId, user: userA, event: true,
}) as MatrixEvent;
});
eventA.status = EventStatus.SENDING;
const eventId = eventA.getId();
@ -1490,7 +1490,7 @@ describe("Room", function() {
const room = new Room(roomId, null, userA);
const eventA = utils.mkMessage({
room: roomId, user: userA, event: true,
}) as MatrixEvent;
});
eventA.status = EventStatus.SENDING;
const eventId = eventA.getId();
@ -1567,7 +1567,7 @@ describe("Room", function() {
room: roomId,
event: true,
name: "User A",
}) as MatrixEvent;
});
it("should load members from server on first call", async function() {
const client = createClientMock([memberEvent]);
@ -1587,7 +1587,7 @@ describe("Room", function() {
room: roomId,
event: true,
name: "Ms A",
}) as MatrixEvent;
});
const client = createClientMock([memberEvent2], [memberEvent]);
const room = new Room(roomId, client as any, null, { lazyLoadMembers: true });
@ -1658,7 +1658,7 @@ describe("Room", function() {
mship: "join",
room: roomId,
event: true,
}) as MatrixEvent]);
})]);
expect(room.guessDMUserId()).toEqual(userB);
});
it("should return self if only member present", function() {
@ -1691,11 +1691,11 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B");
});
@ -1706,11 +1706,11 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "ban",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("Empty room (was User B)");
});
@ -1721,11 +1721,11 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "invite",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B");
});
@ -1736,11 +1736,11 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "leave",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("Empty room (was User B)");
});
@ -1751,15 +1751,15 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userC, mship: "join",
room: roomId, event: true, name: "User C",
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B and User C");
});
@ -1770,19 +1770,19 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userC, mship: "join",
room: roomId, event: true, name: "User C",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userD, mship: "join",
room: roomId, event: true, name: "User D",
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B and 2 others");
});
@ -1795,18 +1795,18 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkEvent({
type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "",
room: roomId, event: true,
content: {
service_members: [],
},
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B");
});
@ -1817,11 +1817,11 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkEvent({
type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name,
skey: "",
@ -1830,7 +1830,7 @@ describe("Room", function() {
content: {
service_members: 1,
},
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B");
});
@ -1841,18 +1841,18 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkEvent({
type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "",
room: roomId, event: true,
content: {
service_members: userB,
},
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B");
});
@ -1863,18 +1863,18 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkEvent({
type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "",
room: roomId, event: true,
content: {
service_members: [userB],
},
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("Empty room");
});
@ -1885,22 +1885,22 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userC, mship: "join",
room: roomId, event: true, name: "User C",
}) as MatrixEvent,
}),
utils.mkEvent({
type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "",
room: roomId, event: true, user: userA,
content: {
service_members: [userC],
},
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B");
});
@ -1911,22 +1911,22 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userC, mship: "join",
room: roomId, event: true, name: "User C",
}) as MatrixEvent,
}),
utils.mkEvent({
type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "",
room: roomId, event: true, user: userA,
content: {
service_members: [userB, userC],
},
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("Empty room");
});
@ -1937,18 +1937,18 @@ describe("Room", function() {
utils.mkMembership({
user: userA, mship: "join",
room: roomId, event: true, name: "User A",
}) as MatrixEvent,
}),
utils.mkMembership({
user: userB, mship: "join",
room: roomId, event: true, name: "User B",
}) as MatrixEvent,
}),
utils.mkEvent({
type: UNSTABLE_ELEMENT_FUNCTIONAL_USERS.name, skey: "",
room: roomId, event: true, user: userA,
content: {
service_members: [userC],
},
}) as MatrixEvent,
}),
]);
expect(room.getDefaultRoomName(userA)).toEqual("User B");
});

View File

@ -5283,15 +5283,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
];
if (this.supportsExperimentalThreads()) {
const { threadId, shouldLiveInRoom } = timelineSet.room.eventShouldLiveIn(event);
if (!timelineSet.thread && !shouldLiveInRoom) {
// Thread response does not belong in this timelineSet
return undefined;
}
if (timelineSet.thread?.id !== threadId) {
// Event does not belong in this timelineSet
if (!timelineSet.canContain(event)) {
return undefined;
}

View File

@ -842,6 +842,31 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
// the timelines are not contiguous.
return null;
}
/**
* Determine whether a given event can sanely be added to this event timeline set,
* for timeline sets relating to a thread, only return true for events in the same
* thread timeline, for timeline sets not relating to a thread only return true
* for events which should be shown in the main room timeline.
* Requires the `room` property to have been set at EventTimelineSet construction time.
*
* @param event {MatrixEvent} the event to check whether it belongs to this timeline set.
* @throws {Error} if `room` was not set when constructing this timeline set.
* @return {boolean} whether the event belongs to this timeline set.
*/
public canContain(event: MatrixEvent): boolean {
if (!this.room) {
throw new Error("Cannot call `EventTimelineSet::canContain without a `room` set. " +
"Set the room when creating the EventTimelineSet to call this method.");
}
const { threadId, shouldLiveInRoom } = this.room.eventShouldLiveIn(event);
if (this.thread) {
return this.thread.id === threadId;
}
return shouldLiveInRoom;
}
}
/**

View File

@ -437,7 +437,7 @@ export class EventTimeline {
}
}
let insertIndex;
let insertIndex: number;
if (toStartOfTimeline) {
insertIndex = 0;