Update the live timeline when the JS SDK resets it (#8806)

pull/28788/head^2
Robin 2022-06-10 08:59:07 -04:00 committed by GitHub
parent 1b7e9d95da
commit 53340db5e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 50 deletions

View File

@ -30,6 +30,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import { EventTimeline } from 'matrix-js-sdk/src/models/event-timeline';
import { EventType } from 'matrix-js-sdk/src/@types/event';
import { RoomState, RoomStateEvent } from 'matrix-js-sdk/src/models/room-state';
import { EventTimelineSet } from "matrix-js-sdk/src/models/event-timeline-set";
import { CallState, CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import { throttle } from "lodash";
import { MatrixError } from 'matrix-js-sdk/src/http-api';
@ -282,6 +283,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
this.dispatcherRef = dis.register(this.onAction);
context.on(ClientEvent.Room, this.onRoom);
context.on(RoomEvent.Timeline, this.onRoomTimeline);
context.on(RoomEvent.TimelineReset, this.onRoomTimelineReset);
context.on(RoomEvent.Name, this.onRoomName);
context.on(RoomStateEvent.Events, this.onRoomStateEvents);
context.on(RoomStateEvent.Update, this.onRoomStateUpdate);
@ -1022,6 +1024,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
});
};
private onRoomTimelineReset = (room: Room, timelineSet: EventTimelineSet) => {
if (!room || room.roomId !== this.state.room?.roomId) return;
logger.log(`Live timeline of ${room.roomId} was reset`);
this.setState({ liveTimeline: timelineSet.getLiveTimeline() });
};
private getRoomTombstone(room = this.state.room) {
return room?.currentState.getStateEvents(EventType.RoomTombstone, "");
}

View File

@ -15,64 +15,82 @@ limitations under the License.
*/
import React from "react";
import TestRenderer from "react-test-renderer";
import { mount, ReactWrapper } from "enzyme";
import { mocked, MockedObject } from "jest-mock";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { stubClient, wrapInMatrixClientContext } from "../../test-utils";
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
import { stubClient } from "../../test-utils";
import { Action } from "../../../src/dispatcher/actions";
import dis from "../../../src/dispatcher/dispatcher";
import { ViewRoomPayload } from "../../../src/dispatcher/payloads/ViewRoomPayload";
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
import { RoomView } from "../../../src/components/structures/RoomView";
import { RoomView as _RoomView } from "../../../src/components/structures/RoomView";
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
import { RoomViewStore } from "../../../src/stores/RoomViewStore";
import DMRoomMap from "../../../src/utils/DMRoomMap";
describe("RoomView", () => {
it("updates url preview visibility on encryption state change", async () => {
stubClient();
const cli = MatrixClientPeg.get();
cli.hasLazyLoadMembersEnabled = () => false;
cli.isInitialSyncComplete = () => true;
cli.stopPeeking = () => undefined;
const RoomView = wrapInMatrixClientContext(_RoomView);
const r1 = new Room("r1", cli, "@name:example.com");
cli.getRoom = () => r1;
r1.getPendingEvents = () => [];
describe("RoomView", () => {
let cli: MockedObject<MatrixClient>;
let room: Room;
beforeEach(() => {
stubClient();
cli = mocked(MatrixClientPeg.get());
room = new Room("r1", cli, "@alice:example.com");
room.getPendingEvents = () => [];
cli.getRoom.mockReturnValue(room);
// Re-emit certain events on the mocked client
room.on(RoomEvent.Timeline, (...args) => cli.emit(RoomEvent.Timeline, ...args));
room.on(RoomEvent.TimelineReset, (...args) => cli.emit(RoomEvent.TimelineReset, ...args));
DMRoomMap.makeShared();
});
const switchRoomPromise = new Promise<void>(resolve => {
const subscription = RoomViewStore.instance.addListener(() => {
if (RoomViewStore.instance.getRoomId()) {
subscription.remove();
resolve();
}
const mountRoomView = async (): Promise<ReactWrapper> => {
if (RoomViewStore.instance.getRoomId() !== room.roomId) {
const switchRoomPromise = new Promise<void>(resolve => {
const subscription = RoomViewStore.instance.addListener(() => {
if (RoomViewStore.instance.getRoomId()) {
subscription.remove();
resolve();
}
});
});
});
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: r1.roomId,
metricsTrigger: null,
});
dis.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: room.roomId,
metricsTrigger: null,
});
await switchRoomPromise;
await switchRoomPromise;
}
const renderer = TestRenderer.create(<MatrixClientContext.Provider value={cli}>
<RoomView mxClient={cli}
return mount(
<RoomView
mxClient={cli}
threepidInvite={null}
oobData={null}
resizeNotifier={new ResizeNotifier()}
justCreatedOpts={null}
forceTimeline={false}
onRegistered={null}
/>
</MatrixClientContext.Provider>);
/>,
);
};
const getRoomViewInstance = async (): Promise<_RoomView> =>
(await mountRoomView()).find(_RoomView).instance() as _RoomView;
const roomViewInstance = renderer.root.findByType(RoomView).instance;
it("updates url preview visibility on encryption state change", async () => {
// we should be starting unencrypted
expect(cli.isCryptoEnabled()).toEqual(false);
expect(cli.isRoomEncrypted(room.roomId)).toEqual(false);
const roomViewInstance = await getRoomViewInstance();
// in a default (non-encrypted room, it should start out with url previews enabled)
// This is a white-box test in that we're asserting things about the state, which
@ -84,32 +102,28 @@ describe("RoomView", () => {
// 2) SettingsStore is a static class and so very hard to mock out.
expect(roomViewInstance.state.showUrlPreview).toBe(true);
// now enable encryption (by mocking out the tests for whether a room is encrypted)
cli.isCryptoEnabled = () => true;
cli.isRoomEncrypted = () => true;
// now enable encryption
cli.isCryptoEnabled.mockReturnValue(true);
cli.isRoomEncrypted.mockReturnValue(true);
// and fake an encryption event into the room to prompt it to re-check
// wait until the event has been added
const eventAddedPromise = new Promise<void>(resolve => {
r1.once(RoomEvent.Timeline, (...args) => {
// we're also using mock client that doesn't re-emit, so
// we emit the event to client manually
cli.emit(RoomEvent.Timeline, ...args);
resolve();
});
});
r1.addLiveEvents([new MatrixEvent({
room.addLiveEvents([new MatrixEvent({
type: "m.room.encryption",
sender: cli.getUserId(),
content: {},
event_id: "someid",
room_id: r1.roomId,
room_id: room.roomId,
})]);
await eventAddedPromise;
// URL previews should now be disabled
expect(roomViewInstance.state.showUrlPreview).toBe(false);
});
it("updates live timeline when a timeline reset happens", async () => {
const roomViewInstance = await getRoomViewInstance();
const oldTimeline = roomViewInstance.state.liveTimeline;
room.getUnfilteredTimelineSet().resetLiveTimeline();
expect(roomViewInstance.state.liveTimeline).not.toEqual(oldTimeline);
});
});

View File

@ -93,6 +93,7 @@ export function createTestClient(): MatrixClient {
emit: eventEmitter.emit.bind(eventEmitter),
isRoomEncrypted: jest.fn().mockReturnValue(false),
peekInRoom: jest.fn().mockResolvedValue(mkStubRoom(undefined, undefined, undefined)),
stopPeeking: jest.fn(),
paginateEventTimeline: jest.fn().mockResolvedValue(undefined),
sendReadReceipt: jest.fn().mockResolvedValue(undefined),
@ -155,6 +156,8 @@ export function createTestClient(): MatrixClient {
setPushRuleActions: jest.fn().mockResolvedValue(undefined),
relations: jest.fn().mockRejectedValue(undefined),
isCryptoEnabled: jest.fn().mockReturnValue(false),
hasLazyLoadMembersEnabled: jest.fn().mockReturnValue(false),
isInitialSyncComplete: jest.fn().mockReturnValue(true),
downloadKeys: jest.fn(),
fetchRoomEvent: jest.fn(),
} as unknown as MatrixClient;
@ -358,7 +361,7 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl
getJoinedMemberCount: jest.fn().mockReturnValue(1),
getMembers: jest.fn().mockReturnValue([]),
getPendingEvents: () => [],
getLiveTimeline: () => stubTimeline,
getLiveTimeline: jest.fn().mockReturnValue(stubTimeline),
getUnfilteredTimelineSet: () => null,
findEventById: () => null,
getAccountData: () => null,