From 5f92dad2734aabbf055567698687ed4b3feaae9c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 19 Dec 2023 16:01:17 +0000 Subject: [PATCH] Fix editing event from search room view (#11992) * Fix editing event from search room view Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Handle different room for all rooms search case Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Increase coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.tsx | 34 ++++- test/components/structures/RoomView-test.tsx | 127 ++++++++++++++++++- 2 files changed, 154 insertions(+), 7 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 7e36a5aa06b..ac90dd9c81b 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -186,6 +186,9 @@ export interface IRoomState { initialEventScrollIntoView?: boolean; replyToEvent?: MatrixEvent; numUnreadMessages: number; + /** + * The state of an ongoing search if there is one. + */ search?: ISearchInfo; callState?: CallState; activeCall: Call | null; @@ -1208,12 +1211,33 @@ export class RoomView extends React.Component { case Action.EditEvent: { // Quit early if we're trying to edit events in wrong rendering context if (payload.timelineRenderingType !== this.state.timelineRenderingType) return; + if (payload.event && payload.event.getRoomId() !== this.state.roomId) { + // If the event is in a different room (e.g. because the event to be edited is being displayed + // in the results of an all-rooms search), we need to view that room first. + dis.dispatch({ + action: Action.ViewRoom, + room_id: payload.event.getRoomId(), + metricsTrigger: undefined, + deferred_action: payload, + }); + return; + } + const editState = payload.event ? new EditorStateTransfer(payload.event) : undefined; - this.setState({ editState }, () => { - if (payload.event) { - this.messagePanel?.scrollToEventIfNeeded(payload.event.getId()); - } - }); + this.setState( + { + editState, + // If a search is active (implying that the "edit" button has been pressed on one of the + // events in the search result), we need to close that search, because RoomSearchView + // doesn't handle editing and won't render the composer. + search: undefined, + }, + () => { + if (payload.event) { + this.messagePanel?.scrollToEventIfNeeded(payload.event.getId()); + } + }, + ); break; } diff --git a/test/components/structures/RoomView-test.tsx b/test/components/structures/RoomView-test.tsx index 8f6034d2e15..6d4dadbc8f7 100644 --- a/test/components/structures/RoomView-test.tsx +++ b/test/components/structures/RoomView-test.tsx @@ -26,9 +26,12 @@ import { MatrixError, RoomStateEvent, MatrixEvent, + SearchResult, + IEvent, } from "matrix-js-sdk/src/matrix"; import { MEGOLM_ALGORITHM } from "matrix-js-sdk/src/crypto/olmlib"; -import { fireEvent, render, screen, RenderResult } from "@testing-library/react"; +import { fireEvent, render, screen, RenderResult, waitForElementToBeRemoved, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { stubClient, @@ -60,12 +63,13 @@ import { LocalRoom, LocalRoomState } from "../../../src/models/LocalRoom"; import { DirectoryMember } from "../../../src/utils/direct-messages"; import { createDmLocalRoom } from "../../../src/utils/dm/createDmLocalRoom"; import { UPDATE_EVENT } from "../../../src/stores/AsyncStore"; -import { SdkContextClass, SDKContext } from "../../../src/contexts/SDKContext"; +import { SDKContext, SdkContextClass } from "../../../src/contexts/SDKContext"; import VoipUserMapper from "../../../src/VoipUserMapper"; import WidgetUtils from "../../../src/utils/WidgetUtils"; import { WidgetType } from "../../../src/widgets/WidgetType"; import WidgetStore from "../../../src/stores/WidgetStore"; import { ViewRoomErrorPayload } from "../../../src/dispatcher/payloads/ViewRoomErrorPayload"; +import { SearchScope } from "../../../src/components/views/rooms/SearchBar"; const RoomView = wrapInMatrixClientContext(_RoomView); @@ -585,4 +589,123 @@ describe("RoomView", () => { expect(dis.dispatch).toHaveBeenCalledWith({ action: "cancel_ask_to_join", roomId: room.roomId }); }); }); + + it("should close search results when edit is clicked", async () => { + room.getMyMembership = jest.fn().mockReturnValue("join"); + + const eventMapper = (obj: Partial) => new MatrixEvent(obj); + + const roomViewRef = createRef<_RoomView>(); + const { container, getByText, findByLabelText } = await mountRoomView(roomViewRef); + // @ts-ignore - triggering a search organically is a lot of work + roomViewRef.current!.setState({ + search: { + searchId: 1, + roomId: room.roomId, + term: "search term", + scope: SearchScope.Room, + promise: Promise.resolve({ + results: [ + SearchResult.fromJson( + { + rank: 1, + result: { + content: { + body: "search term", + msgtype: "m.text", + }, + type: "m.room.message", + event_id: "$eventId", + sender: cli.getSafeUserId(), + origin_server_ts: 123456789, + room_id: room.roomId, + }, + context: { + events_before: [], + events_after: [], + profile_info: {}, + }, + }, + eventMapper, + ), + ], + highlights: [], + count: 1, + }), + inProgress: false, + count: 1, + }, + }); + + await waitFor(() => { + expect(container.querySelector(".mx_RoomView_searchResultsPanel")).toBeVisible(); + }); + const prom = waitForElementToBeRemoved(() => container.querySelector(".mx_RoomView_searchResultsPanel")); + + await userEvent.hover(getByText("search term")); + await userEvent.click(await findByLabelText("Edit")); + + await prom; + }); + + it("should switch rooms when edit is clicked on a search result for a different room", async () => { + const room2 = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org"); + rooms.set(room2.roomId, room2); + + room.getMyMembership = jest.fn().mockReturnValue("join"); + + const eventMapper = (obj: Partial) => new MatrixEvent(obj); + + const roomViewRef = createRef<_RoomView>(); + const { container, getByText, findByLabelText } = await mountRoomView(roomViewRef); + // @ts-ignore - triggering a search organically is a lot of work + roomViewRef.current!.setState({ + search: { + searchId: 1, + roomId: room.roomId, + term: "search term", + scope: SearchScope.All, + promise: Promise.resolve({ + results: [ + SearchResult.fromJson( + { + rank: 1, + result: { + content: { + body: "search term", + msgtype: "m.text", + }, + type: "m.room.message", + event_id: "$eventId", + sender: cli.getSafeUserId(), + origin_server_ts: 123456789, + room_id: room2.roomId, + }, + context: { + events_before: [], + events_after: [], + profile_info: {}, + }, + }, + eventMapper, + ), + ], + highlights: [], + count: 1, + }), + inProgress: false, + count: 1, + }, + }); + + await waitFor(() => { + expect(container.querySelector(".mx_RoomView_searchResultsPanel")).toBeVisible(); + }); + const prom = untilDispatch(Action.ViewRoom, dis); + + await userEvent.hover(getByText("search term")); + await userEvent.click(await findByLabelText("Edit")); + + await expect(prom).resolves.toEqual(expect.objectContaining({ room_id: room2.roomId })); + }); });