Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Gitter sunsetting: Use findPredecessor in EventTileFactory #10075

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/e2e/timeline/timeline.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ describe("Timeline", () => {
cy.contains(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]", "MessageEdit").should("exist");

// Click top left of the event toggle, which should not be covered by MessageActionBar's safe area
cy.get(".mx_EventTile .mx_ViewSourceEvent")
cy.get(".mx_EventTile:not(:first-child) .mx_ViewSourceEvent")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment here to explain this might be helpful - I assume it's because of "show hidden events" as we discussed?

.should("exist")
.realHover()
.within(() => {
Expand Down
19 changes: 16 additions & 3 deletions src/events/EventTileFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { M_POLL_START } from "matrix-js-sdk/src/@types/polls";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { GroupCallIntent } from "matrix-js-sdk/src/webrtc/groupCall";

import SettingsStore from "../settings/SettingsStore";
import EditorStateTransfer from "../utils/EditorStateTransfer";
import { RoomPermalinkCreator } from "../utils/permalinks/Permalinks";
import LegacyCallEventGrouper from "../components/structures/LegacyCallEventGrouper";
Expand Down Expand Up @@ -91,6 +92,7 @@ const HiddenEventFactory: Factory = (ref, props) => <HiddenBody ref={ref} {...pr
// These factories are exported for reference comparison against pickFactory()
export const JitsiEventFactory: Factory = (ref, props) => <MJitsiWidgetEvent ref={ref} {...props} />;
export const JSONEventFactory: Factory = (ref, props) => <ViewSourceEvent ref={ref} {...props} />;
export const RoomCreateEventFactory: Factory = (ref, props) => <RoomCreate {...props} />;

const EVENT_TILE_TYPES = new Map<string, Factory>([
[EventType.RoomMessage, MessageEventFactory], // note that verification requests are handled in pickFactory()
Expand All @@ -105,7 +107,7 @@ const EVENT_TILE_TYPES = new Map<string, Factory>([
const STATE_EVENT_TILE_TYPES = new Map<string, Factory>([
[EventType.RoomEncryption, (ref, props) => <EncryptionEvent ref={ref} {...props} />],
[EventType.RoomCanonicalAlias, TextualEventFactory],
[EventType.RoomCreate, (_ref, props) => <RoomCreate {...props} />],
[EventType.RoomCreate, RoomCreateEventFactory],
[EventType.RoomMember, TextualEventFactory],
[EventType.RoomName, TextualEventFactory],
[EventType.RoomAvatar, (ref, props) => <RoomAvatarEvent ref={ref} {...props} />],
Expand Down Expand Up @@ -213,6 +215,14 @@ export function pickFactory(
}
}

if (evType === EventType.RoomCreate) {
const dynamicPredecessorsEnabled = SettingsStore.getValue("feature_dynamic_room_predecessors");
const predecessor = cli.getRoom(mxEvent.getRoomId())?.findPredecessor(dynamicPredecessorsEnabled);
if (!predecessor) {
return noEventFactoryFactory();
}
}

// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
if (evType === "im.vector.modular.widgets") {
let type = mxEvent.getContent()["type"];
Expand Down Expand Up @@ -415,12 +425,15 @@ export function haveRendererForEvent(mxEvent: MatrixEvent, showHiddenEvents: boo
// No tile for replacement events since they update the original tile
if (mxEvent.isRelation(RelationType.Replace)) return false;

const handler = pickFactory(mxEvent, MatrixClientPeg.get(), showHiddenEvents);
const cli = MatrixClientPeg.get();
const handler = pickFactory(mxEvent, cli, showHiddenEvents);
if (!handler) return false;
if (handler === TextualEventFactory) {
return hasText(mxEvent, showHiddenEvents);
} else if (handler === STATE_EVENT_TILE_TYPES.get(EventType.RoomCreate)) {
return Boolean(mxEvent.getContent()["predecessor"]);
const dynamicPredecessorsEnabled = SettingsStore.getValue("feature_dynamic_room_predecessors");
const predecessor = cli.getRoom(mxEvent.getRoomId())?.findPredecessor(dynamicPredecessorsEnabled);
return Boolean(predecessor);
} else if (
ElementCall.CALL_EVENT_TYPE.names.some((eventType) => handler === STATE_EVENT_TILE_TYPES.get(eventType))
) {
Expand Down
11 changes: 8 additions & 3 deletions test/components/structures/MessagePanel-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
} from "../../test-utils";
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
import { IRoomState } from "../../../src/components/structures/RoomView";
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";

jest.mock("../../../src/utils/beacon", () => ({
useBeacon: jest.fn(),
Expand All @@ -58,6 +59,7 @@ describe("MessagePanel", function () {
getRoom: jest.fn(),
getClientWellKnown: jest.fn().mockReturnValue({}),
});
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(client);

const room = new Room(roomId, client, userId);

Expand Down Expand Up @@ -464,11 +466,12 @@ describe("MessagePanel", function () {

it("should collapse creation events", function () {
const events = mkCreationEvents();
TestUtilsMatrix.upsertRoomStateEvents(room, events);
const { container } = render(getComponent({ events }));

const createEvent = events.find((event) => event.getType() === "m.room.create");
const encryptionEvent = events.find((event) => event.getType() === "m.room.encryption");
client.getRoom.mockImplementation((id) => (id === createEvent!.getRoomId() ? room : null));
TestUtilsMatrix.upsertRoomStateEvents(room, events);

const { container } = render(getComponent({ events }));

// we expect that
// - the room creation event, the room encryption event, and Alice inviting Bob,
Expand Down Expand Up @@ -508,6 +511,8 @@ describe("MessagePanel", function () {

it("should hide read-marker at the end of creation event summary", function () {
const events = mkCreationEvents();
const createEvent = events.find((event) => event.getType() === "m.room.create");
client.getRoom.mockImplementation((id) => (id === createEvent!.getRoomId() ? room : null));
TestUtilsMatrix.upsertRoomStateEvents(room, events);

const { container } = render(
Expand Down
130 changes: 128 additions & 2 deletions test/events/EventTileFactory-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,75 @@ import {
JSONEventFactory,
MessageEventFactory,
pickFactory,
RoomCreateEventFactory,
TextualEventFactory,
} from "../../src/events/EventTileFactory";
import SettingsStore from "../../src/settings/SettingsStore";
import { VoiceBroadcastChunkEventType, VoiceBroadcastInfoState } from "../../src/voice-broadcast";
import { createTestClient, mkEvent } from "../test-utils";
import { mkVoiceBroadcastInfoStateEvent } from "../voice-broadcast/utils/test-utils";

const roomId = "!room:example.com";

describe("pickFactory", () => {
let client: MatrixClient;
let room: Room;

let createEventWithPredecessor: MatrixEvent;
let createEventWithoutPredecessor: MatrixEvent;
let dynamicPredecessorEvent: MatrixEvent;

let voiceBroadcastStartedEvent: MatrixEvent;
let voiceBroadcastStoppedEvent: MatrixEvent;
let voiceBroadcastChunkEvent: MatrixEvent;
let utdEvent: MatrixEvent;
let utdBroadcastChunkEvent: MatrixEvent;
let audioMessageEvent: MatrixEvent;
let client: MatrixClient;

beforeAll(() => {
client = createTestClient();

const room = new Room(roomId, client, client.getSafeUserId());
room = new Room(roomId, client, client.getSafeUserId());
mocked(client.getRoom).mockImplementation((getRoomId: string): Room | null => {
if (getRoomId === room.roomId) return room;
return null;
});

createEventWithoutPredecessor = mkEvent({
event: true,
type: EventType.RoomCreate,
user: client.getUserId()!,
room: roomId,
content: {
creator: client.getUserId()!,
room_version: "9",
},
});
createEventWithPredecessor = mkEvent({
event: true,
type: EventType.RoomCreate,
user: client.getUserId()!,
room: roomId,
content: {
creator: client.getUserId()!,
room_version: "9",
predecessor: {
room_id: "roomid1",
event_id: null,
},
},
});
dynamicPredecessorEvent = mkEvent({
event: true,
type: EventType.RoomPredecessor,
user: client.getUserId()!,
room: roomId,
skey: "",
content: {
predecessor_room_id: "roomid2",
last_known_event_id: null,
},
});
voiceBroadcastStartedEvent = mkVoiceBroadcastInfoStateEvent(
roomId,
VoiceBroadcastInfoState.Started,
Expand Down Expand Up @@ -117,6 +160,15 @@ describe("pickFactory", () => {
expect(pickFactory(voiceBroadcastChunkEvent, client, true)).toBe(JSONEventFactory);
});

it("should return a JSONEventFactory for a room create event without predecessor", () => {
room.currentState.events.set(
EventType.RoomCreate,
new Map([[createEventWithoutPredecessor.getStateKey()!, createEventWithoutPredecessor]]),
);
room.currentState.events.set(EventType.RoomPredecessor, new Map());
expect(pickFactory(createEventWithoutPredecessor, client, true)).toBe(JSONEventFactory);
});

it("should return a TextualEventFactory for a voice broadcast stopped event", () => {
expect(pickFactory(voiceBroadcastStoppedEvent, client, true)).toBe(TextualEventFactory);
});
Expand All @@ -131,6 +183,80 @@ describe("pickFactory", () => {
});

describe("when not showing hidden events", () => {
describe("without dynamic predecessor support", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockReset();
});

it("should return undefined for a room without predecessor", () => {
room.currentState.events.set(
EventType.RoomCreate,
new Map([[createEventWithoutPredecessor.getStateKey()!, createEventWithoutPredecessor]]),
);
room.currentState.events.set(EventType.RoomPredecessor, new Map());
expect(pickFactory(createEventWithoutPredecessor, client, false)).toBeUndefined();
});

it("should return a RoomCreateFactory for a room with fixed predecessor", () => {
room.currentState.events.set(
EventType.RoomCreate,
new Map([[createEventWithPredecessor.getStateKey()!, createEventWithPredecessor]]),
);
room.currentState.events.set(EventType.RoomPredecessor, new Map());
expect(pickFactory(createEventWithPredecessor, client, false)).toBe(RoomCreateEventFactory);
});

it("should return undefined for a room with dynamic predecessor", () => {
room.currentState.events.set(
EventType.RoomCreate,
new Map([[createEventWithoutPredecessor.getStateKey()!, createEventWithoutPredecessor]]),
);
room.currentState.events.set(
EventType.RoomPredecessor,
new Map([[dynamicPredecessorEvent.getStateKey()!, dynamicPredecessorEvent]]),
);
expect(pickFactory(createEventWithoutPredecessor, client, false)).toBeUndefined();
});
});

describe("with dynamic predecessor support", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue")
.mockReset()
.mockImplementation((settingName) => settingName === "feature_dynamic_room_predecessors");
});

it("should return undefined for a room without predecessor", () => {
room.currentState.events.set(
EventType.RoomCreate,
new Map([[createEventWithoutPredecessor.getStateKey()!, createEventWithoutPredecessor]]),
);
room.currentState.events.set(EventType.RoomPredecessor, new Map());
expect(pickFactory(createEventWithoutPredecessor, client, false)).toBeUndefined();
});

it("should return a RoomCreateFactory for a room with fixed predecessor", () => {
room.currentState.events.set(
EventType.RoomCreate,
new Map([[createEventWithPredecessor.getStateKey()!, createEventWithPredecessor]]),
);
room.currentState.events.set(EventType.RoomPredecessor, new Map());
expect(pickFactory(createEventWithPredecessor, client, false)).toBe(RoomCreateEventFactory);
});

it("should return a RoomCreateFactory for a room with dynamic predecessor", () => {
room.currentState.events.set(
EventType.RoomCreate,
new Map([[createEventWithoutPredecessor.getStateKey()!, createEventWithoutPredecessor]]),
);
room.currentState.events.set(
EventType.RoomPredecessor,
new Map([[dynamicPredecessorEvent.getStateKey()!, dynamicPredecessorEvent]]),
);
expect(pickFactory(createEventWithoutPredecessor, client, false)).toBe(RoomCreateEventFactory);
});
});

it("should return undefined for a voice broadcast event", () => {
expect(pickFactory(voiceBroadcastChunkEvent, client, false)).toBeUndefined();
});
Expand Down