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

Commit 27e1636

Browse files
authored
Make join button on space hierarchy action in the background (#7041)
1 parent 43cbb94 commit 27e1636

File tree

7 files changed

+97
-61
lines changed

7 files changed

+97
-61
lines changed

res/css/structures/_SpaceHierarchy.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,11 @@ limitations under the License.
288288
visibility: visible;
289289
}
290290
}
291+
292+
&.mx_SpaceHierarchy_joining .mx_AccessibleButton {
293+
visibility: visible;
294+
padding: 4px 18px;
295+
}
291296
}
292297

293298
li.mx_SpaceHierarchy_roomTileWrapper {

src/components/structures/SpaceHierarchy.tsx

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,15 @@ import { getDisplayAliasForRoom } from "./RoomDirectory";
6060
import MatrixClientContext from "../../contexts/MatrixClientContext";
6161
import { useEventEmitterState } from "../../hooks/useEventEmitter";
6262
import { IOOBData } from "../../stores/ThreepidInviteStore";
63+
import { awaitRoomDownSync } from "../../utils/RoomUpgrade";
64+
import RoomViewStore from "../../stores/RoomViewStore";
6365

6466
interface IProps {
6567
space: Room;
6668
initialText?: string;
6769
additionalButtons?: ReactNode;
68-
showRoom(
69-
cli: MatrixClient,
70-
hierarchy: RoomHierarchy,
71-
roomId: string,
72-
autoJoin?: boolean,
73-
roomType?: RoomType,
74-
): void;
70+
showRoom(cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string, roomType?: RoomType): void;
71+
joinRoom(cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string): void;
7572
}
7673

7774
interface ITileProps {
@@ -80,7 +77,8 @@ interface ITileProps {
8077
selected?: boolean;
8178
numChildRooms?: number;
8279
hasPermissions?: boolean;
83-
onViewRoomClick(autoJoin: boolean, roomType: RoomType): void;
80+
onViewRoomClick(): void;
81+
onJoinRoomClick(): void;
8482
onToggleClick?(): void;
8583
}
8684

@@ -91,31 +89,50 @@ const Tile: React.FC<ITileProps> = ({
9189
hasPermissions,
9290
onToggleClick,
9391
onViewRoomClick,
92+
onJoinRoomClick,
9493
numChildRooms,
9594
children,
9695
}) => {
9796
const cli = useContext(MatrixClientContext);
98-
const joinedRoom = cli.getRoom(room.room_id)?.getMyMembership() === "join" ? cli.getRoom(room.room_id) : null;
97+
const [joinedRoom, setJoinedRoom] = useState<Room>(() => {
98+
const cliRoom = cli.getRoom(room.room_id);
99+
return cliRoom?.getMyMembership() === "join" ? cliRoom : null;
100+
});
99101
const joinedRoomName = useEventEmitterState(joinedRoom, "Room.name", room => room?.name);
100102
const name = joinedRoomName || room.name || room.canonical_alias || room.aliases?.[0]
101103
|| (room.room_type === RoomType.Space ? _t("Unnamed Space") : _t("Unnamed Room"));
102104

103105
const [showChildren, toggleShowChildren] = useStateToggle(true);
104106
const [onFocus, isActive, ref] = useRovingTabIndex();
107+
const [busy, setBusy] = useState(false);
105108

106109
const onPreviewClick = (ev: ButtonEvent) => {
107110
ev.preventDefault();
108111
ev.stopPropagation();
109-
onViewRoomClick(false, room.room_type as RoomType);
112+
onViewRoomClick();
110113
};
111-
const onJoinClick = (ev: ButtonEvent) => {
114+
const onJoinClick = async (ev: ButtonEvent) => {
115+
setBusy(true);
112116
ev.preventDefault();
113117
ev.stopPropagation();
114-
onViewRoomClick(true, room.room_type as RoomType);
118+
onJoinRoomClick();
119+
setJoinedRoom(await awaitRoomDownSync(cli, room.room_id));
120+
setBusy(false);
115121
};
116122

117123
let button;
118-
if (joinedRoom) {
124+
if (busy) {
125+
button = <AccessibleTooltipButton
126+
disabled={true}
127+
onClick={onJoinClick}
128+
kind="primary_outline"
129+
onFocus={onFocus}
130+
tabIndex={isActive ? 0 : -1}
131+
title={_t("Joining")}
132+
>
133+
<Spinner w={24} h={24} />
134+
</AccessibleTooltipButton>;
135+
} else if (joinedRoom) {
119136
button = <AccessibleButton
120137
onClick={onPreviewClick}
121138
kind="primary_outline"
@@ -282,6 +299,7 @@ const Tile: React.FC<ITileProps> = ({
282299
<AccessibleButton
283300
className={classNames("mx_SpaceHierarchy_roomTile", {
284301
mx_SpaceHierarchy_subspace: room.room_type === RoomType.Space,
302+
mx_SpaceHierarchy_joining: busy,
285303
})}
286304
onClick={(hasPermissions && onToggleClick) ? onToggleClick : onPreviewClick}
287305
onKeyDown={onKeyDown}
@@ -296,13 +314,7 @@ const Tile: React.FC<ITileProps> = ({
296314
</li>;
297315
};
298316

299-
export const showRoom = (
300-
cli: MatrixClient,
301-
hierarchy: RoomHierarchy,
302-
roomId: string,
303-
autoJoin = false,
304-
roomType?: RoomType,
305-
) => {
317+
export const showRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string, roomType?: RoomType): void => {
306318
const room = hierarchy.roomMap.get(roomId);
307319

308320
// Don't let the user view a room they won't be able to either peek or join:
@@ -317,7 +329,6 @@ export const showRoom = (
317329
const roomAlias = getDisplayAliasForRoom(room) || undefined;
318330
dis.dispatch({
319331
action: "view_room",
320-
auto_join: autoJoin,
321332
should_peek: true,
322333
_type: "room_directory", // instrumentation
323334
room_alias: roomAlias,
@@ -332,13 +343,29 @@ export const showRoom = (
332343
});
333344
};
334345

346+
export const joinRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string): void => {
347+
// Don't let the user view a room they won't be able to either peek or join:
348+
// fail earlier so they don't have to click back to the directory.
349+
if (cli.isGuest()) {
350+
dis.dispatch({ action: "require_registration" });
351+
return;
352+
}
353+
354+
cli.joinRoom(roomId, {
355+
viaServers: Array.from(hierarchy.viaMap.get(roomId) || []),
356+
}).catch(err => {
357+
RoomViewStore.showJoinRoomError(err, roomId);
358+
});
359+
};
360+
335361
interface IHierarchyLevelProps {
336362
root: IHierarchyRoom;
337363
roomSet: Set<IHierarchyRoom>;
338364
hierarchy: RoomHierarchy;
339365
parents: Set<string>;
340366
selectedMap?: Map<string, Set<string>>;
341-
onViewRoomClick(roomId: string, autoJoin: boolean, roomType?: RoomType): void;
367+
onViewRoomClick(roomId: string, roomType?: RoomType): void;
368+
onJoinRoomClick(roomId: string): void;
342369
onToggleClick?(parentId: string, childId: string): void;
343370
}
344371

@@ -373,6 +400,7 @@ export const HierarchyLevel = ({
373400
parents,
374401
selectedMap,
375402
onViewRoomClick,
403+
onJoinRoomClick,
376404
onToggleClick,
377405
}: IHierarchyLevelProps) => {
378406
const cli = useContext(MatrixClientContext);
@@ -400,9 +428,8 @@ export const HierarchyLevel = ({
400428
room={room}
401429
suggested={hierarchy.isSuggested(root.room_id, room.room_id)}
402430
selected={selectedMap?.get(root.room_id)?.has(room.room_id)}
403-
onViewRoomClick={(autoJoin, roomType) => {
404-
onViewRoomClick(room.room_id, autoJoin, roomType);
405-
}}
431+
onViewRoomClick={() => onViewRoomClick(room.room_id, room.room_type as RoomType)}
432+
onJoinRoomClick={() => onJoinRoomClick(room.room_id)}
406433
hasPermissions={hasPermissions}
407434
onToggleClick={onToggleClick ? () => onToggleClick(root.room_id, room.room_id) : undefined}
408435
/>
@@ -420,9 +447,8 @@ export const HierarchyLevel = ({
420447
}).length}
421448
suggested={hierarchy.isSuggested(root.room_id, space.room_id)}
422449
selected={selectedMap?.get(root.room_id)?.has(space.room_id)}
423-
onViewRoomClick={(autoJoin, roomType) => {
424-
onViewRoomClick(space.room_id, autoJoin, roomType);
425-
}}
450+
onViewRoomClick={() => onViewRoomClick(space.room_id, RoomType.Space)}
451+
onJoinRoomClick={() => onJoinRoomClick(space.room_id)}
426452
hasPermissions={hasPermissions}
427453
onToggleClick={onToggleClick ? () => onToggleClick(root.room_id, space.room_id) : undefined}
428454
>
@@ -433,6 +459,7 @@ export const HierarchyLevel = ({
433459
parents={newParents}
434460
selectedMap={selectedMap}
435461
onViewRoomClick={onViewRoomClick}
462+
onJoinRoomClick={onJoinRoomClick}
436463
onToggleClick={onToggleClick}
437464
/>
438465
</Tile>
@@ -696,9 +723,8 @@ const SpaceHierarchy = ({
696723
parents={new Set()}
697724
selectedMap={selected}
698725
onToggleClick={hasPermissions ? onToggleClick : undefined}
699-
onViewRoomClick={(roomId, autoJoin, roomType) => {
700-
showRoom(cli, hierarchy, roomId, autoJoin, roomType);
701-
}}
726+
onViewRoomClick={(roomId, roomType) => showRoom(cli, hierarchy, roomId, roomType)}
727+
onJoinRoomClick={(roomId) => joinRoom(cli, hierarchy, roomId)}
702728
/>
703729
</>;
704730
} else if (!hierarchy.canLoadMore) {

src/components/structures/SpaceRoomView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import {
5555
showSpaceInvite,
5656
showSpaceSettings,
5757
} from "../../utils/space";
58-
import SpaceHierarchy, { showRoom } from "./SpaceHierarchy";
58+
import SpaceHierarchy, { joinRoom, showRoom } from "./SpaceHierarchy";
5959
import MemberAvatar from "../views/avatars/MemberAvatar";
6060
import SpaceStore from "../../stores/SpaceStore";
6161
import FacePile from "../views/elements/FacePile";
@@ -508,7 +508,7 @@ const SpaceLanding = ({ space }: { space: Room }) => {
508508
) }
509509
</RoomTopic>
510510

511-
<SpaceHierarchy space={space} showRoom={showRoom} additionalButtons={addRoomButton} />
511+
<SpaceHierarchy space={space} showRoom={showRoom} joinRoom={joinRoom} additionalButtons={addRoomButton} />
512512
</div>;
513513
};
514514

src/components/views/avatars/RoomAvatar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
import React, { ComponentProps } from 'react';
1818
import { Room } from 'matrix-js-sdk/src/models/room';
1919
import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials';
20+
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
2021
import classNames from "classnames";
2122

2223
import BaseAvatar from './BaseAvatar';
@@ -83,8 +84,7 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
8384
};
8485
}
8586

86-
// TODO: type when js-sdk has types
87-
private onRoomStateEvents = (ev: any) => {
87+
private onRoomStateEvents = (ev: MatrixEvent) => {
8888
if (!this.props.room ||
8989
ev.getRoomId() !== this.props.room.roomId ||
9090
ev.getType() !== 'm.room.avatar'

src/i18n/strings/en_EN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2930,6 +2930,7 @@
29302930
"Drop file here to upload": "Drop file here to upload",
29312931
"You have %(count)s unread notifications in a prior version of this room.|other": "You have %(count)s unread notifications in a prior version of this room.",
29322932
"You have %(count)s unread notifications in a prior version of this room.|one": "You have %(count)s unread notification in a prior version of this room.",
2933+
"Joining": "Joining",
29332934
"You don't have permission": "You don't have permission",
29342935
"Joined": "Joined",
29352936
"This room is suggested as a good one to join": "This room is suggested as a good one to join",

src/stores/RoomViewStore.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ class RoomViewStore extends Store<ActionPayload> {
308308
}
309309
}
310310

311-
private getInvitingUserId(roomId: string): string {
311+
private static getInvitingUserId(roomId: string): string {
312312
const cli = MatrixClientPeg.get();
313313
const room = cli.getRoom(roomId);
314314
if (room && room.getMyMembership() === "invite") {
@@ -318,12 +318,7 @@ class RoomViewStore extends Store<ActionPayload> {
318318
}
319319
}
320320

321-
private joinRoomError(payload: ActionPayload) {
322-
this.setState({
323-
joining: false,
324-
joinError: payload.err,
325-
});
326-
const err = payload.err;
321+
public showJoinRoomError(err: Error | MatrixError, roomId: string) {
327322
let msg = err.message ? err.message : JSON.stringify(err);
328323
logger.log("Failed to join room:", msg);
329324

@@ -335,7 +330,7 @@ class RoomViewStore extends Store<ActionPayload> {
335330
{ _t("Please contact your homeserver administrator.") }
336331
</div>;
337332
} else if (err.httpStatus === 404) {
338-
const invitingUserId = this.getInvitingUserId(this.state.roomId);
333+
const invitingUserId = RoomViewStore.getInvitingUserId(roomId);
339334
// only provide a better error message for invites
340335
if (invitingUserId) {
341336
// if the inviting user is on the same HS, there can only be one cause: they left.
@@ -355,6 +350,14 @@ class RoomViewStore extends Store<ActionPayload> {
355350
});
356351
}
357352

353+
private joinRoomError(payload: ActionPayload) {
354+
this.setState({
355+
joining: false,
356+
joinError: payload.err,
357+
});
358+
this.showJoinRoomError(payload.err, this.state.roomId);
359+
}
360+
358361
public reset() {
359362
this.state = Object.assign({}, INITIAL_STATE);
360363
}

src/utils/RoomUpgrade.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import SpaceStore from "../stores/SpaceStore";
2525
import Spinner from "../components/views/elements/Spinner";
2626

2727
import { logger } from "matrix-js-sdk/src/logger";
28+
import { MatrixClient } from "matrix-js-sdk/src/client";
2829

2930
interface IProgress {
3031
roomUpgraded: boolean;
@@ -35,6 +36,23 @@ interface IProgress {
3536
updateSpacesTotal: number;
3637
}
3738

39+
export async function awaitRoomDownSync(cli: MatrixClient, roomId: string): Promise<Room> {
40+
const room = cli.getRoom(roomId);
41+
if (room) return room; // already have the room
42+
43+
return new Promise<Room>(resolve => {
44+
// We have to wait for the js-sdk to give us the room back so
45+
// we can more effectively abuse the MultiInviter behaviour
46+
// which heavily relies on the Room object being available.
47+
const checkForRoomFn = (room: Room) => {
48+
if (room.roomId !== roomId) return;
49+
resolve(room);
50+
cli.off("Room", checkForRoomFn);
51+
};
52+
cli.on("Room", checkForRoomFn);
53+
});
54+
}
55+
3856
export async function upgradeRoom(
3957
room: Room,
4058
targetVersion: string,
@@ -93,24 +111,7 @@ export async function upgradeRoom(
93111
progressCallback?.(progress);
94112

95113
if (awaitRoom || inviteUsers) {
96-
await new Promise<void>(resolve => {
97-
// already have the room
98-
if (room.client.getRoom(newRoomId)) {
99-
resolve();
100-
return;
101-
}
102-
103-
// We have to wait for the js-sdk to give us the room back so
104-
// we can more effectively abuse the MultiInviter behaviour
105-
// which heavily relies on the Room object being available.
106-
const checkForRoomFn = (newRoom: Room) => {
107-
if (newRoom.roomId !== newRoomId) return;
108-
resolve();
109-
cli.off("Room", checkForRoomFn);
110-
};
111-
cli.on("Room", checkForRoomFn);
112-
});
113-
114+
await awaitRoomDownSync(room.client, newRoomId);
114115
progress.roomSynced = true;
115116
progressCallback?.(progress);
116117
}

0 commit comments

Comments
 (0)