1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-07-30 02:21:17 +03:00

Overlay virtual room call events into main timeline (#9626)

* super WIP POC for merging virtual room events into main timeline

* remove some debugs

* c

* add some todos

* remove hardcoded fake virtual user

* insert overlay events into main timeline without resorting main tl events

* remove more debugs

* add extra tick to roomview tests

* RoomView test case for virtual room

* test case for merged timeline

* make overlay event filter generic

* remove TODOs from LegacyCallEventGrouper

* tidy comments

* remove some newlines

* test timelinepanel room timeline event handling

* use newState.roomId

* fix strict errors in RoomView

* fix strict errors in TimelinePanel

* add type

* pr tweaks

* strict errors

* more strict fix

* strict error whackamole

* update ROomView tests to use rtl
This commit is contained in:
Kerry
2022-12-09 10:37:25 +13:00
committed by GitHub
parent 1b6d753cfe
commit 6150b86421
7 changed files with 1171 additions and 85 deletions

View File

@ -26,6 +26,8 @@ import {
MatrixEvent,
PendingEventOrdering,
Room,
RoomEvent,
TimelineWindow,
} from 'matrix-js-sdk/src/matrix';
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import {
@ -41,7 +43,8 @@ import TimelinePanel from '../../../src/components/structures/TimelinePanel';
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
import { MatrixClientPeg } from '../../../src/MatrixClientPeg';
import SettingsStore from "../../../src/settings/SettingsStore";
import { mkRoom, stubClient } from "../../test-utils";
import { isCallEvent } from '../../../src/components/structures/LegacyCallEventGrouper';
import { flushPromises, mkRoom, stubClient } from "../../test-utils";
const newReceipt = (eventId: string, userId: string, readTs: number, fullyReadTs: number): MatrixEvent => {
const receiptContent = {
@ -80,7 +83,7 @@ const mockEvents = (room: Room, count = 2): MatrixEvent[] => {
for (let index = 0; index < count; index++) {
events.push(new MatrixEvent({
room_id: room.roomId,
event_id: `event_${index}`,
event_id: `${room.roomId}_event_${index}`,
type: EventType.RoomMessage,
user_id: "userId",
content: MessageEvent.from(`Event${index}`).serialize().content,
@ -90,6 +93,13 @@ const mockEvents = (room: Room, count = 2): MatrixEvent[] => {
return events;
};
const setupTestData = (): [MatrixClient, Room, MatrixEvent[]] => {
const client = MatrixClientPeg.get();
const room = mkRoom(client, "roomId");
const events = mockEvents(room);
return [client, room, events];
};
describe('TimelinePanel', () => {
beforeEach(() => {
stubClient();
@ -155,9 +165,7 @@ describe('TimelinePanel', () => {
});
it("sends public read receipt when enabled", () => {
const client = MatrixClientPeg.get();
const room = mkRoom(client, "roomId");
const events = mockEvents(room);
const [client, room, events] = setupTestData();
const getValueCopy = SettingsStore.getValue;
SettingsStore.getValue = jest.fn().mockImplementation((name: string) => {
@ -170,9 +178,7 @@ describe('TimelinePanel', () => {
});
it("does not send public read receipt when enabled", () => {
const client = MatrixClientPeg.get();
const room = mkRoom(client, "roomId");
const events = mockEvents(room);
const [client, room, events] = setupTestData();
const getValueCopy = SettingsStore.getValue;
SettingsStore.getValue = jest.fn().mockImplementation((name: string) => {
@ -202,6 +208,146 @@ describe('TimelinePanel', () => {
expect(props.onEventScrolledIntoView).toHaveBeenCalledWith(events[1].getId());
});
describe('onRoomTimeline', () => {
it('ignores events for other timelines', () => {
const [client, room, events] = setupTestData();
const otherTimelineSet = { room: room as Room } as EventTimelineSet;
const otherTimeline = new EventTimeline(otherTimelineSet);
const props = {
...getProps(room, events),
onEventScrolledIntoView: jest.fn(),
};
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
render(<TimelinePanel {...props} />);
const event = new MatrixEvent({ type: RoomEvent.Timeline });
const data = { timeline: otherTimeline, liveEvent: true };
client.emit(RoomEvent.Timeline, event, room, false, false, data);
expect(paginateSpy).not.toHaveBeenCalled();
});
it('ignores timeline updates without a live event', () => {
const [client, room, events] = setupTestData();
const props = getProps(room, events);
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
render(<TimelinePanel {...props} />);
const event = new MatrixEvent({ type: RoomEvent.Timeline });
const data = { timeline: props.timelineSet.getLiveTimeline(), liveEvent: false };
client.emit(RoomEvent.Timeline, event, room, false, false, data);
expect(paginateSpy).not.toHaveBeenCalled();
});
it('ignores timeline where toStartOfTimeline is true', () => {
const [client, room, events] = setupTestData();
const props = getProps(room, events);
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
render(<TimelinePanel {...props} />);
const event = new MatrixEvent({ type: RoomEvent.Timeline });
const data = { timeline: props.timelineSet.getLiveTimeline(), liveEvent: false };
const toStartOfTimeline = true;
client.emit(RoomEvent.Timeline, event, room, toStartOfTimeline, false, data);
expect(paginateSpy).not.toHaveBeenCalled();
});
it('advances the timeline window', () => {
const [client, room, events] = setupTestData();
const props = getProps(room, events);
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
render(<TimelinePanel {...props} />);
const event = new MatrixEvent({ type: RoomEvent.Timeline });
const data = { timeline: props.timelineSet.getLiveTimeline(), liveEvent: true };
client.emit(RoomEvent.Timeline, event, room, false, false, data);
expect(paginateSpy).toHaveBeenCalledWith(EventTimeline.FORWARDS, 1, false);
});
it('advances the overlay timeline window', async () => {
const [client, room, events] = setupTestData();
const virtualRoom = mkRoom(client, "virtualRoomId");
const virtualEvents = mockEvents(virtualRoom);
const { timelineSet: overlayTimelineSet } = getProps(virtualRoom, virtualEvents);
const props = {
...getProps(room, events),
overlayTimelineSet,
};
const paginateSpy = jest.spyOn(TimelineWindow.prototype, 'paginate').mockClear();
render(<TimelinePanel {...props} />);
const event = new MatrixEvent({ type: RoomEvent.Timeline });
const data = { timeline: props.timelineSet.getLiveTimeline(), liveEvent: true };
client.emit(RoomEvent.Timeline, event, room, false, false, data);
await flushPromises();
expect(paginateSpy).toHaveBeenCalledTimes(2);
});
});
describe('with overlayTimeline', () => {
it('renders merged timeline', () => {
const [client, room, events] = setupTestData();
const virtualRoom = mkRoom(client, "virtualRoomId");
const virtualCallInvite = new MatrixEvent({
type: 'm.call.invite',
room_id: virtualRoom.roomId,
event_id: `virtualCallEvent1`,
});
const virtualCallMetaEvent = new MatrixEvent({
type: 'org.matrix.call.sdp_stream_metadata_changed',
room_id: virtualRoom.roomId,
event_id: `virtualCallEvent2`,
});
const virtualEvents = [
virtualCallInvite,
...mockEvents(virtualRoom),
virtualCallMetaEvent,
];
const { timelineSet: overlayTimelineSet } = getProps(virtualRoom, virtualEvents);
const props = {
...getProps(room, events),
overlayTimelineSet,
overlayTimelineSetFilter: isCallEvent,
};
const { container } = render(<TimelinePanel {...props} />);
const eventTiles = container.querySelectorAll('.mx_EventTile');
const eventTileIds = [...eventTiles].map(tileElement => tileElement.getAttribute('data-event-id'));
expect(eventTileIds).toEqual([
// main timeline events are included
events[1].getId(),
events[0].getId(),
// virtual timeline call event is included
virtualCallInvite.getId(),
// virtual call event has no tile renderer => not rendered
]);
});
});
describe("when a thread updates", () => {
let client: MatrixClient;
let room: Room;