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

Commit 06f69ab

Browse files
authored
Don't switch to the home page needlessly after leaving a room (#9477)
Currently, if you leave a room that you're not currently viewing, you'll get sent back to the home page for no reason. This creates needless friction when trying to leave multiple rooms belonging to a space from the room list. In addition to that fix, this improves the behavior when leaving a subspace, by making it take you to the parent space rather than all the way back home.
1 parent f6347d2 commit 06f69ab

File tree

2 files changed

+150
-11
lines changed

2 files changed

+150
-11
lines changed

src/utils/leave-behaviour.ts

+26-11
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,32 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
128128
return;
129129
}
130130

131-
if (!isMetaSpace(SpaceStore.instance.activeSpace) &&
132-
SpaceStore.instance.activeSpace !== roomId &&
133-
SdkContextClass.instance.roomViewStore.getRoomId() === roomId
134-
) {
135-
dis.dispatch<ViewRoomPayload>({
136-
action: Action.ViewRoom,
137-
room_id: SpaceStore.instance.activeSpace,
138-
metricsTrigger: undefined, // other
139-
});
140-
} else {
141-
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage });
131+
if (SdkContextClass.instance.roomViewStore.getRoomId() === roomId) {
132+
// We were viewing the room that was just left. In order to avoid
133+
// accidentally viewing the next room in the list and clearing its
134+
// notifications, switch to a neutral ground such as the home page or
135+
// space landing page.
136+
if (isMetaSpace(SpaceStore.instance.activeSpace)) {
137+
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage });
138+
} else if (SpaceStore.instance.activeSpace === roomId) {
139+
// View the parent space, if there is one
140+
const parent = SpaceStore.instance.getCanonicalParent(roomId);
141+
if (parent !== null) {
142+
dis.dispatch<ViewRoomPayload>({
143+
action: Action.ViewRoom,
144+
room_id: parent.roomId,
145+
metricsTrigger: undefined, // other
146+
});
147+
} else {
148+
dis.dispatch<ViewHomePagePayload>({ action: Action.ViewHomePage });
149+
}
150+
} else {
151+
dis.dispatch<ViewRoomPayload>({
152+
action: Action.ViewRoom,
153+
room_id: SpaceStore.instance.activeSpace,
154+
metricsTrigger: undefined, // other
155+
});
156+
}
142157
}
143158
}
144159

test/utils/leave-behaviour-test.ts

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { mocked, Mocked } from "jest-mock";
18+
import { MatrixClient } from "matrix-js-sdk/src/client";
19+
import { Room } from "matrix-js-sdk/src/models/room";
20+
21+
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
22+
import { mkRoom, resetAsyncStoreWithClient, setupAsyncStoreWithClient, stubClient } from "../test-utils";
23+
import defaultDispatcher from "../../src/dispatcher/dispatcher";
24+
import { ViewRoomPayload } from "../../src/dispatcher/payloads/ViewRoomPayload";
25+
import { Action } from "../../src/dispatcher/actions";
26+
import { leaveRoomBehaviour } from "../../src/utils/leave-behaviour";
27+
import { SdkContextClass } from "../../src/contexts/SDKContext";
28+
import DMRoomMap from "../../src/utils/DMRoomMap";
29+
import SpaceStore from "../../src/stores/spaces/SpaceStore";
30+
import { MetaSpace } from "../../src/stores/spaces";
31+
import { ActionPayload } from "../../src/dispatcher/payloads";
32+
33+
describe("leaveRoomBehaviour", () => {
34+
SdkContextClass.instance.constructEagerStores(); // Initialize RoomViewStore
35+
36+
let client: Mocked<MatrixClient>;
37+
let room: Mocked<Room>;
38+
let space: Mocked<Room>;
39+
40+
beforeEach(async () => {
41+
stubClient();
42+
client = mocked(MatrixClientPeg.get());
43+
DMRoomMap.makeShared();
44+
45+
room = mkRoom(client, "!1:example.org");
46+
space = mkRoom(client, "!2:example.org");
47+
space.isSpaceRoom.mockReturnValue(true);
48+
client.getRoom.mockImplementation(roomId => {
49+
switch (roomId) {
50+
case room.roomId: return room;
51+
case space.roomId: return space;
52+
default: return null;
53+
}
54+
});
55+
56+
await setupAsyncStoreWithClient(SpaceStore.instance, client);
57+
});
58+
59+
afterEach(async () => {
60+
SpaceStore.instance.setActiveSpace(MetaSpace.Home);
61+
await resetAsyncStoreWithClient(SpaceStore.instance);
62+
jest.restoreAllMocks();
63+
});
64+
65+
const viewRoom = (room: Room) => defaultDispatcher.dispatch<ViewRoomPayload>({
66+
action: Action.ViewRoom,
67+
room_id: room.roomId,
68+
metricsTrigger: undefined,
69+
}, true);
70+
71+
const expectDispatch = async <T extends ActionPayload>(payload: T) => {
72+
const dispatcherSpy = jest.fn();
73+
const dispatcherRef = defaultDispatcher.register(dispatcherSpy);
74+
await new Promise<void>(resolve => setImmediate(resolve)); // Flush the dispatcher
75+
expect(dispatcherSpy).toHaveBeenCalledWith(payload);
76+
defaultDispatcher.unregister(dispatcherRef);
77+
};
78+
79+
it("returns to the home page after leaving a room outside of a space that was being viewed", async () => {
80+
viewRoom(room);
81+
82+
await leaveRoomBehaviour(room.roomId);
83+
await expectDispatch({ action: Action.ViewHomePage });
84+
});
85+
86+
it("returns to the parent space after leaving a room inside of a space that was being viewed", async () => {
87+
jest.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation(
88+
roomId => roomId === room.roomId ? space : null,
89+
);
90+
viewRoom(room);
91+
SpaceStore.instance.setActiveSpace(space.roomId, false);
92+
93+
await leaveRoomBehaviour(room.roomId);
94+
await expectDispatch({
95+
action: Action.ViewRoom,
96+
room_id: space.roomId,
97+
metricsTrigger: undefined,
98+
});
99+
});
100+
101+
it("returns to the home page after leaving a top-level space that was being viewed", async () => {
102+
viewRoom(space);
103+
SpaceStore.instance.setActiveSpace(space.roomId, false);
104+
105+
await leaveRoomBehaviour(space.roomId);
106+
await expectDispatch({ action: Action.ViewHomePage });
107+
});
108+
109+
it("returns to the parent space after leaving a subspace that was being viewed", async () => {
110+
room.isSpaceRoom.mockReturnValue(true);
111+
jest.spyOn(SpaceStore.instance, "getCanonicalParent").mockImplementation(
112+
roomId => roomId === room.roomId ? space : null,
113+
);
114+
viewRoom(room);
115+
SpaceStore.instance.setActiveSpace(room.roomId, false);
116+
117+
await leaveRoomBehaviour(room.roomId);
118+
await expectDispatch({
119+
action: Action.ViewRoom,
120+
room_id: space.roomId,
121+
metricsTrigger: undefined,
122+
});
123+
});
124+
});

0 commit comments

Comments
 (0)