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, room: roomId,
user: userId, 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({ 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 // start the client, and wait for it to initialise
function startClient(httpBackend, client) { function startClient(httpBackend, client) {
httpBackend.when("GET", "/versions").respond(200, {}); 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(); 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; client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(true); Thread.setServerSideSupport(true);
client.stopClient(); // we don't need the client to be syncing at this time 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 thread = room.createThread(THREAD_ROOT.event_id, threadRoot, [threadRoot], false);
const timelineSet = thread.timelineSet; 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)) httpBackend.when("GET", "/rooms/!foo%3Abar/context/" + encodeURIComponent(EVENTS[0].event_id))
.respond(200, function() { .respond(200, function() {
return { return {
@ -566,14 +607,13 @@ describe("MatrixClient event timelines", function() {
}; };
}); });
const timelinePromise = client.getEventTimeline(timelineSet, EVENTS[0].event_id); return Promise.all([
await httpBackend.flushAllExpected(); expect(client.getEventTimeline(timelineSet, EVENTS[0].event_id)).resolves.toBeUndefined(),
httpBackend.flushAllExpected(),
const timeline = await timelinePromise; ]);
expect(timeline).toBeUndefined();
}); });
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; client.clientOpts.experimentalThreadSupport = true;
Thread.setServerSideSupport(true); Thread.setServerSideSupport(true);
client.stopClient(); // we don't need the client to be syncing at this time 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); return Promise.all([
await httpBackend.flushAllExpected(); expect(client.getEventTimeline(timelineSet, THREAD_REPLY.event_id)).resolves.toBeUndefined(),
httpBackend.flushAllExpected(),
const timeline = await timelinePromise; ]);
expect(timeline).toBeUndefined();
}); });
it("should should add lazy loading filter when requested", async () => { it("should should add lazy loading filter when requested", async () => {

View File

@ -74,7 +74,6 @@ interface IEventOpts {
sender?: string; sender?: string;
skey?: string; skey?: string;
content: IContent; content: IContent;
event?: boolean;
user?: string; user?: string;
unsigned?: IUnsigned; unsigned?: IUnsigned;
redacts?: string; 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. * @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. * @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) { if (!opts.type || !opts.content) {
throw new Error("Missing .type or .content =>" + JSON.stringify(opts)); throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
} }
@ -143,7 +144,9 @@ interface IPresenceOpts {
* @param {Object} opts Values for the presence. * @param {Object} opts Values for the presence.
* @return {Object|MatrixEvent} The event * @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 = { const event = {
event_id: "$" + Math.random() + "-" + Math.random(), event_id: "$" + Math.random() + "-" + Math.random(),
type: "m.presence", type: "m.presence",
@ -182,7 +185,9 @@ interface IMembershipOpts {
* @param {boolean} opts.event True to make a MatrixEvent. * @param {boolean} opts.event True to make a MatrixEvent.
* @return {Object|MatrixEvent} The event * @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 = { const eventOpts: IEventOpts = {
...opts, ...opts,
type: EventType.RoomMember, 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. * @param {MatrixClient} client If passed along with opts.event=true will be used to set up re-emitters.
* @return {Object|MatrixEvent} The event * @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 = { const eventOpts: IEventOpts = {
...opts, ...opts,
type: EventType.RoomMessage, 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. * @param {MatrixClient} client If passed along with opts.event=true will be used to set up re-emitters.
* @return {Object|MatrixEvent} The event * @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 = { const eventOpts: IEventOpts = {
...opts, ...opts,
type: EventType.RoomMessage, type: EventType.RoomMessage,

View File

@ -25,6 +25,8 @@ import {
Room, Room,
DuplicateStrategy, DuplicateStrategy,
} from '../../src'; } from '../../src';
import { Thread } from "../../src/models/thread";
import { ReEmitter } from "../../src/ReEmitter";
describe('EventTimelineSet', () => { describe('EventTimelineSet', () => {
const roomId = '!foo:bar'; const roomId = '!foo:bar';
@ -54,6 +56,7 @@ describe('EventTimelineSet', () => {
beforeEach(() => { beforeEach(() => {
client = utils.mock(MatrixClient, 'MatrixClient'); client = utils.mock(MatrixClient, 'MatrixClient');
client.reEmitter = utils.mock(ReEmitter, 'ReEmitter');
room = new Room(roomId, client, userA); room = new Room(roomId, client, userA);
eventTimelineSet = new EventTimelineSet(room); eventTimelineSet = new EventTimelineSet(room);
eventTimeline = new EventTimeline(eventTimelineSet); eventTimeline = new EventTimeline(eventTimelineSet);
@ -62,14 +65,14 @@ describe('EventTimelineSet', () => {
user: userA, user: userA,
msg: 'Hi!', msg: 'Hi!',
event: true, event: true,
}) as MatrixEvent; });
replyEvent = utils.mkReplyMessage({ replyEvent = utils.mkReplyMessage({
room: roomId, room: roomId,
user: userA, user: userA,
msg: 'Hoo!', msg: 'Hoo!',
event: true, event: true,
replyToMessage: messageEvent, replyToMessage: messageEvent,
}) as MatrixEvent; });
}); });
describe('addLiveEvent', () => { describe('addLiveEvent', () => {
@ -91,7 +94,7 @@ describe('EventTimelineSet', () => {
// make a duplicate // make a duplicate
const duplicateMessageEvent = utils.mkMessage({ const duplicateMessageEvent = utils.mkMessage({
room: roomId, user: userA, msg: "dupe", event: true, room: roomId, user: userA, msg: "dupe", event: true,
}) as MatrixEvent; });
duplicateMessageEvent.event.event_id = messageEvent.getId(); duplicateMessageEvent.event.event_id = messageEvent.getId();
// Adding the duplicate event should replace the `messageEvent` // 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 { import { RelationType } from "../../src";
MatrixEvent,
RelationType,
} from "../../src";
import { FilterComponent } from "../../src/filter-component"; import { FilterComponent } from "../../src/filter-component";
import { mkEvent } from '../test-utils/test-utils'; import { mkEvent } from '../test-utils/test-utils';
@ -14,7 +11,7 @@ describe("Filter Component", function() {
content: { }, content: { },
room: 'roomId', room: 'roomId',
event: true, event: true,
}) as MatrixEvent; });
const checkResult = filter.check(event); const checkResult = filter.check(event);
@ -28,7 +25,7 @@ describe("Filter Component", function() {
content: { }, content: { },
room: 'roomId', room: 'roomId',
event: true, event: true,
}) as MatrixEvent; });
const checkResult = filter.check(event); const checkResult = filter.check(event);
@ -55,7 +52,7 @@ describe("Filter Component", function() {
}, },
}, },
}, },
}) as MatrixEvent; });
expect(filter.check(threadRootNotParticipated)).toBe(false); expect(filter.check(threadRootNotParticipated)).toBe(false);
}); });
@ -80,7 +77,7 @@ describe("Filter Component", function() {
user: '@someone-else:server.org', user: '@someone-else:server.org',
room: 'roomId', room: 'roomId',
event: true, event: true,
}) as MatrixEvent; });
expect(filter.check(threadRootParticipated)).toBe(true); expect(filter.check(threadRootParticipated)).toBe(true);
}); });
@ -100,7 +97,7 @@ describe("Filter Component", function() {
[RelationType.Reference]: {}, [RelationType.Reference]: {},
}, },
}, },
}) as MatrixEvent; });
expect(filter.check(referenceRelationEvent)).toBe(false); expect(filter.check(referenceRelationEvent)).toBe(false);
}); });
@ -123,7 +120,7 @@ describe("Filter Component", function() {
}, },
room: 'roomId', room: 'roomId',
event: true, event: true,
}) as MatrixEvent; });
const eventWithMultipleRelations = mkEvent({ const eventWithMultipleRelations = mkEvent({
"type": "m.room.message", "type": "m.room.message",
@ -148,7 +145,7 @@ describe("Filter Component", function() {
}, },
"room": 'roomId', "room": 'roomId',
"event": true, "event": true,
}) as MatrixEvent; });
const noMatchEvent = mkEvent({ const noMatchEvent = mkEvent({
"type": "m.room.message", "type": "m.room.message",
@ -160,7 +157,7 @@ describe("Filter Component", function() {
}, },
"room": 'roomId', "room": 'roomId',
"event": true, "event": true,
}) as MatrixEvent; });
expect(filter.check(threadRootEvent)).toBe(true); expect(filter.check(threadRootEvent)).toBe(true);
expect(filter.check(eventWithMultipleRelations)).toBe(true); expect(filter.check(eventWithMultipleRelations)).toBe(true);

View File

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

View File

@ -5283,15 +5283,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
]; ];
if (this.supportsExperimentalThreads()) { if (this.supportsExperimentalThreads()) {
const { threadId, shouldLiveInRoom } = timelineSet.room.eventShouldLiveIn(event); if (!timelineSet.canContain(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
return undefined; return undefined;
} }

View File

@ -842,6 +842,31 @@ export class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTime
// the timelines are not contiguous. // the timelines are not contiguous.
return null; 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) { if (toStartOfTimeline) {
insertIndex = 0; insertIndex = 0;