diff --git a/src/components/views/messages/RoomCreate.tsx b/src/components/views/messages/RoomCreate.tsx index 25cfb90a48..ccad5ea11e 100644 --- a/src/components/views/messages/RoomCreate.tsx +++ b/src/components/views/messages/RoomCreate.tsx @@ -1,6 +1,6 @@ /* Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2023 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useCallback } from "react"; +import React, { useCallback, useContext } from "react"; +import { logger } from "matrix-js-sdk/src/logger"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import dis from "../../../dispatcher/dispatcher"; @@ -25,6 +26,8 @@ import { _t } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import EventTileBubble from "./EventTileBubble"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; +import RoomContext from "../../../contexts/RoomContext"; +import { useRoomState } from "../../../hooks/useRoomState"; interface IProps { /** The m.room.create MatrixEvent that this tile represents */ @@ -37,31 +40,57 @@ interface IProps { * room. */ export const RoomCreate: React.FC = ({ mxEvent, timestamp }) => { + // Note: we ask the room for its predecessor here, instead of directly using + // the information inside mxEvent. This allows us the flexibility later to + // use a different predecessor (e.g. through MSC3946) and still display it + // in the timeline location of the create event. + const roomContext = useContext(RoomContext); + const predecessor = useRoomState( + roomContext.room, + useCallback((state) => state.findPredecessor(), []), + ); + const onLinkClicked = useCallback( (e: React.MouseEvent): void => { e.preventDefault(); - const predecessor = mxEvent.getContent()["predecessor"]; - dis.dispatch({ action: Action.ViewRoom, - event_id: predecessor["event_id"], + event_id: predecessor.eventId, highlighted: true, - room_id: predecessor["room_id"], + room_id: predecessor.roomId, metricsTrigger: "Predecessor", metricsViaKeyboard: e.type !== "click", }); }, - [mxEvent], + [predecessor?.eventId, predecessor?.roomId], ); - const predecessor = mxEvent.getContent()["predecessor"]; - if (predecessor === undefined) { - return
; // We should never have been instantiated in this case + + if (!roomContext.room || roomContext.room.roomId !== mxEvent.getRoomId()) { + logger.warn( + "RoomCreate unexpectedly used outside of the context of the room containing this m.room.create event.", + ); + return <>; } - const prevRoom = MatrixClientPeg.get().getRoom(predecessor["room_id"]); - const permalinkCreator = new RoomPermalinkCreator(prevRoom, predecessor["room_id"]); + + if (!predecessor) { + logger.warn("RoomCreate unexpectedly used in a room with no predecessor."); + return
; + } + + const prevRoom = MatrixClientPeg.get().getRoom(predecessor.roomId); + if (!prevRoom) { + logger.warn(`Failed to find predecessor room with id ${predecessor.roomId}`); + return <>; + } + const permalinkCreator = new RoomPermalinkCreator(prevRoom, predecessor.roomId); permalinkCreator.load(); - const predecessorPermalink = permalinkCreator.forEvent(predecessor["event_id"]); + let predecessorPermalink: string; + if (predecessor.eventId) { + predecessorPermalink = permalinkCreator.forEvent(predecessor.eventId); + } else { + predecessorPermalink = permalinkCreator.forRoom(); + } const link = ( {_t("Click here to see older messages.")} diff --git a/test/components/views/messages/RoomCreate-test.tsx b/test/components/views/messages/RoomCreate-test.tsx index 09f17e2ae4..c3fcf51c61 100644 --- a/test/components/views/messages/RoomCreate-test.tsx +++ b/test/components/views/messages/RoomCreate-test.tsx @@ -18,13 +18,16 @@ import React from "react"; import { act, render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { mocked } from "jest-mock"; -import { EventType, MatrixEvent } from "matrix-js-sdk/src/matrix"; +import { EventType, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; import dis from "../../../../src/dispatcher/dispatcher"; import SettingsStore from "../../../../src/settings/SettingsStore"; import { RoomCreate } from "../../../../src/components/views/messages/RoomCreate"; -import { stubClient } from "../../../test-utils/test-utils"; +import { stubClient, upsertRoomStateEvents } from "../../../test-utils/test-utils"; import { Action } from "../../../../src/dispatcher/actions"; +import RoomContext from "../../../../src/contexts/RoomContext"; +import { getRoomContext } from "../../../test-utils"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; jest.mock("../../../../src/dispatcher/dispatcher"); @@ -33,6 +36,7 @@ describe("", () => { const roomId = "!room:server.org"; const createEvent = new MatrixEvent({ type: EventType.RoomCreate, + state_key: "", sender: userId, room_id: roomId, content: { @@ -40,6 +44,20 @@ describe("", () => { }, event_id: "$create", }); + const createEventWithoutPredecessor = new MatrixEvent({ + type: EventType.RoomCreate, + state_key: "", + sender: userId, + room_id: roomId, + content: {}, + event_id: "$create", + }); + stubClient(); + const client = mocked(MatrixClientPeg.get()); + const room = new Room(roomId, client, userId); + upsertRoomStateEvents(room, [createEvent]); + const roomNoPredecessors = new Room(roomId, client, userId); + upsertRoomStateEvents(roomNoPredecessors, [createEventWithoutPredecessor]); beforeEach(() => { jest.clearAllMocks(); @@ -54,21 +72,34 @@ describe("", () => { jest.spyOn(SettingsStore, "setValue").mockRestore(); }); + function renderRoomCreate(room: Room) { + return render( + + + , + ); + } + it("Renders as expected", () => { - const roomCreate = render(); + const roomCreate = renderRoomCreate(room); expect(roomCreate.asFragment()).toMatchSnapshot(); }); it("Links to the old version of the room", () => { - render(); + renderRoomCreate(room); expect(screen.getByText("Click here to see older messages.")).toHaveAttribute( "href", "https://matrix.to/#/old_room_id/tombstone_event_id", ); }); + it("Shows an empty div if there is no predecessor", () => { + renderRoomCreate(roomNoPredecessors); + expect(screen.queryByText("Click here to see older messages.", { exact: false })).toBeNull(); + }); + it("Opens the old room on click", async () => { - render(); + renderRoomCreate(room); const link = screen.getByText("Click here to see older messages."); await act(() => userEvent.click(link));