Avoid showing two chat timelines side by side after a call (#32484)

* Avoid showing two chat timelines side by side after a call

In certain situations you could still end up with the chat timeline visible in the right panel in addition to the main split. For instance if you are in a call, open the chat panel, then leave the call while looking at another room, the chat panel would remain open upon navigating back to the original room.

* Avoid using flushPromises in tests
This commit is contained in:
Robin
2026-02-12 17:28:07 +01:00
committed by GitHub
parent 2ee6933cfd
commit 5094613180
4 changed files with 90 additions and 6 deletions

View File

@@ -647,13 +647,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
};
if (
this.state.mainSplitContentType !== MainSplitContentType.Timeline &&
newState.mainSplitContentType === MainSplitContentType.Timeline &&
this.context.rightPanelStore.isOpen &&
this.context.rightPanelStore.currentCard.phase === RightPanelPhases.Timeline &&
this.context.rightPanelStore.roomPhaseHistory.some((card) => card.phase === RightPanelPhases.Timeline)
) {
// We're returning to the main timeline, so hide the right panel timeline
// The main split shows the main timeline, so hide the right panel timeline
this.context.rightPanelStore.setCard({ phase: RightPanelPhases.RoomSummary });
this.context.rightPanelStore.togglePanel(this.state.roomId ?? null);
newState.showRightPanel = false;

View File

@@ -186,6 +186,7 @@ export function createTestClient(): MatrixClient {
peekInRoom: jest.fn().mockResolvedValue(mkStubRoom(undefined, undefined, undefined)),
stopPeeking: jest.fn(),
getEventTimeline: jest.fn().mockResolvedValue([]),
paginateEventTimeline: jest.fn().mockResolvedValue(undefined),
sendReadReceipt: jest.fn().mockResolvedValue(undefined),
getRoomIdForAlias: jest.fn().mockResolvedValue(undefined),

View File

@@ -24,7 +24,7 @@ import {
} from "matrix-js-sdk/src/matrix";
import { type CryptoApi, CryptoEvent, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { act, cleanup, fireEvent, render, type RenderResult, screen, waitFor } from "jest-matrix-react";
import { act, cleanup, fireEvent, render, type RenderResult, screen, waitFor, findByRole } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import {
@@ -643,6 +643,90 @@ describe("RoomView", () => {
});
});
describe("group calls", () => {
beforeEach(async () => {
await setupAsyncStoreWithClient(CallStore.instance, MatrixClientPeg.safeGet());
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
});
it("hides the right panel chat when closing a call", async () => {
await mountRoomView();
// Open the call
await act(() =>
stores.roomViewStore.viewRoom({
action: Action.ViewRoom,
room_id: stores.roomViewStore.getRoomId()!,
event_id: "$eventId",
metricsTrigger: undefined,
view_call: true,
}),
);
// Open the chat in the right panel
act(() => stores.rightPanelStore.setCard({ phase: RightPanelPhases.Timeline }));
// Chat should be visible in the right panel
await findByRole(await screen.findByRole("complementary"), "heading", { name: "Chat" });
// Close the call
await act(() =>
stores.roomViewStore.viewRoom({
action: Action.ViewRoom,
room_id: stores.roomViewStore.getRoomId()!,
event_id: "$eventId",
metricsTrigger: undefined,
view_call: false,
}),
);
// Right panel should be gone
expect(screen.queryByRole("complementary")).toBe(null);
// Opening the right panel again should just show the room summary
act(() => stores.rightPanelStore.show(room.roomId));
await findByRole(await screen.findByRole("complementary"), "heading", { name: room.roomId });
});
it("hides the right panel chat when returning to a room that previously showed a call", async () => {
const room2 = new Room(`!roomswitchtest:example.org`, cli, "@alice:example.org");
rooms.set(room2.roomId, room2);
await mountRoomView();
// Open the call
await act(() =>
stores.roomViewStore.viewRoom({
action: Action.ViewRoom,
room_id: room.roomId,
event_id: "$eventId",
metricsTrigger: undefined,
view_call: true,
}),
);
// Open the chat in the right panel
act(() => stores.rightPanelStore.setCard({ phase: RightPanelPhases.Timeline }));
// Chat should be visible in the right panel
await findByRole(await screen.findByRole("complementary"), "heading", { name: "Chat" });
// Navigate away to another room
await act(() =>
stores.roomViewStore.viewRoom({
action: Action.ViewRoom,
room_id: room2.roomId,
event_id: "$eventId",
metricsTrigger: undefined,
}),
);
// Navigate back to the original room
await act(() =>
stores.roomViewStore.viewRoom({
action: Action.ViewRoom,
room_id: room.roomId,
event_id: "$eventId",
metricsTrigger: undefined,
}),
);
// Right panel should be gone
expect(screen.queryByRole("complementary")).toBe(null);
});
});
describe("for a local room", () => {
let localRoom: LocalRoom;

View File

@@ -212,7 +212,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
</div>
</div>
<div
aria-labelledby="_r_ti_"
aria-labelledby="_r_1c3_"
class="_banner_17clp_8"
data-type="critical"
role="status"
@@ -238,7 +238,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`]
>
<p
class="_typography_6v6n8_153 _font-body-md-semibold_6v6n8_55 _container_mqidv_1"
id="_r_ti_"
id="_r_1c3_"
>
Could not start a chat with this user
</p>
@@ -417,7 +417,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] =
>
<svg
aria-label="Messages in this room are not end-to-end encrypted"
aria-labelledby="_r_qm_"
aria-labelledby="_r_197_"
class="mx_E2EIcon mx_MessageComposer_e2eIcon"
color="var(--cpd-color-icon-info-primary)"
fill="currentColor"