Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions res/css/structures/_SpaceHierarchy.scss
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ limitations under the License.
visibility: visible;
}
}

&.mx_SpaceHierarchy_joining .mx_AccessibleButton {
visibility: visible;
padding: 4px 18px;
}
}

li.mx_SpaceHierarchy_roomTileWrapper {
Expand Down
88 changes: 57 additions & 31 deletions src/components/structures/SpaceHierarchy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,15 @@ import { getDisplayAliasForRoom } from "./RoomDirectory";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { useEventEmitterState } from "../../hooks/useEventEmitter";
import { IOOBData } from "../../stores/ThreepidInviteStore";
import { awaitRoomDownSync } from "../../utils/RoomUpgrade";
import RoomViewStore from "../../stores/RoomViewStore";

interface IProps {
space: Room;
initialText?: string;
additionalButtons?: ReactNode;
showRoom(
cli: MatrixClient,
hierarchy: RoomHierarchy,
roomId: string,
autoJoin?: boolean,
roomType?: RoomType,
): void;
showRoom(cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string, roomType?: RoomType): void;
joinRoom(cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string): void;
}

interface ITileProps {
Expand All @@ -80,7 +77,8 @@ interface ITileProps {
selected?: boolean;
numChildRooms?: number;
hasPermissions?: boolean;
onViewRoomClick(autoJoin: boolean, roomType: RoomType): void;
onViewRoomClick(): void;
onJoinRoomClick(): void;
onToggleClick?(): void;
}

Expand All @@ -91,31 +89,50 @@ const Tile: React.FC<ITileProps> = ({
hasPermissions,
onToggleClick,
onViewRoomClick,
onJoinRoomClick,
numChildRooms,
children,
}) => {
const cli = useContext(MatrixClientContext);
const joinedRoom = cli.getRoom(room.room_id)?.getMyMembership() === "join" ? cli.getRoom(room.room_id) : null;
const [joinedRoom, setJoinedRoom] = useState<Room>(() => {
const cliRoom = cli.getRoom(room.room_id);
return cliRoom?.getMyMembership() === "join" ? cliRoom : null;
});
const joinedRoomName = useEventEmitterState(joinedRoom, "Room.name", room => room?.name);
const name = joinedRoomName || room.name || room.canonical_alias || room.aliases?.[0]
|| (room.room_type === RoomType.Space ? _t("Unnamed Space") : _t("Unnamed Room"));

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

const onPreviewClick = (ev: ButtonEvent) => {
ev.preventDefault();
ev.stopPropagation();
onViewRoomClick(false, room.room_type as RoomType);
onViewRoomClick();
};
const onJoinClick = (ev: ButtonEvent) => {
const onJoinClick = async (ev: ButtonEvent) => {
setBusy(true);
ev.preventDefault();
ev.stopPropagation();
onViewRoomClick(true, room.room_type as RoomType);
onJoinRoomClick();
setJoinedRoom(await awaitRoomDownSync(cli, room.room_id));
setBusy(false);
};

let button;
if (joinedRoom) {
if (busy) {
button = <AccessibleTooltipButton
disabled={true}
onClick={onJoinClick}
kind="primary_outline"
onFocus={onFocus}
tabIndex={isActive ? 0 : -1}
title={_t("Joining")}
>
<Spinner w={24} h={24} />
</AccessibleTooltipButton>;
} else if (joinedRoom) {
button = <AccessibleButton
onClick={onPreviewClick}
kind="primary_outline"
Expand Down Expand Up @@ -282,6 +299,7 @@ const Tile: React.FC<ITileProps> = ({
<AccessibleButton
className={classNames("mx_SpaceHierarchy_roomTile", {
mx_SpaceHierarchy_subspace: room.room_type === RoomType.Space,
mx_SpaceHierarchy_joining: busy,
})}
onClick={(hasPermissions && onToggleClick) ? onToggleClick : onPreviewClick}
onKeyDown={onKeyDown}
Expand All @@ -296,13 +314,7 @@ const Tile: React.FC<ITileProps> = ({
</li>;
};

export const showRoom = (
cli: MatrixClient,
hierarchy: RoomHierarchy,
roomId: string,
autoJoin = false,
roomType?: RoomType,
) => {
export const showRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string, roomType?: RoomType): void => {
const room = hierarchy.roomMap.get(roomId);

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

export const joinRoom = (cli: MatrixClient, hierarchy: RoomHierarchy, roomId: string): void => {
// Don't let the user view a room they won't be able to either peek or join:
// fail earlier so they don't have to click back to the directory.
if (cli.isGuest()) {
dis.dispatch({ action: "require_registration" });
return;
}

cli.joinRoom(roomId, {
viaServers: Array.from(hierarchy.viaMap.get(roomId) || []),
}).catch(err => {
RoomViewStore.showJoinRoomError(err, roomId);
});
};

interface IHierarchyLevelProps {
root: IHierarchyRoom;
roomSet: Set<IHierarchyRoom>;
hierarchy: RoomHierarchy;
parents: Set<string>;
selectedMap?: Map<string, Set<string>>;
onViewRoomClick(roomId: string, autoJoin: boolean, roomType?: RoomType): void;
onViewRoomClick(roomId: string, roomType?: RoomType): void;
onJoinRoomClick(roomId: string): void;
onToggleClick?(parentId: string, childId: string): void;
}

Expand Down Expand Up @@ -373,6 +400,7 @@ export const HierarchyLevel = ({
parents,
selectedMap,
onViewRoomClick,
onJoinRoomClick,
onToggleClick,
}: IHierarchyLevelProps) => {
const cli = useContext(MatrixClientContext);
Expand Down Expand Up @@ -400,9 +428,8 @@ export const HierarchyLevel = ({
room={room}
suggested={hierarchy.isSuggested(root.room_id, room.room_id)}
selected={selectedMap?.get(root.room_id)?.has(room.room_id)}
onViewRoomClick={(autoJoin, roomType) => {
onViewRoomClick(room.room_id, autoJoin, roomType);
}}
onViewRoomClick={() => onViewRoomClick(room.room_id, room.room_type as RoomType)}
onJoinRoomClick={() => onJoinRoomClick(room.room_id)}
hasPermissions={hasPermissions}
onToggleClick={onToggleClick ? () => onToggleClick(root.room_id, room.room_id) : undefined}
/>
Expand All @@ -420,9 +447,8 @@ export const HierarchyLevel = ({
}).length}
suggested={hierarchy.isSuggested(root.room_id, space.room_id)}
selected={selectedMap?.get(root.room_id)?.has(space.room_id)}
onViewRoomClick={(autoJoin, roomType) => {
onViewRoomClick(space.room_id, autoJoin, roomType);
}}
onViewRoomClick={() => onViewRoomClick(space.room_id, RoomType.Space)}
onJoinRoomClick={() => onJoinRoomClick(space.room_id)}
hasPermissions={hasPermissions}
onToggleClick={onToggleClick ? () => onToggleClick(root.room_id, space.room_id) : undefined}
>
Expand All @@ -433,6 +459,7 @@ export const HierarchyLevel = ({
parents={newParents}
selectedMap={selectedMap}
onViewRoomClick={onViewRoomClick}
onJoinRoomClick={onJoinRoomClick}
onToggleClick={onToggleClick}
/>
</Tile>
Expand Down Expand Up @@ -696,9 +723,8 @@ const SpaceHierarchy = ({
parents={new Set()}
selectedMap={selected}
onToggleClick={hasPermissions ? onToggleClick : undefined}
onViewRoomClick={(roomId, autoJoin, roomType) => {
showRoom(cli, hierarchy, roomId, autoJoin, roomType);
}}
onViewRoomClick={(roomId, roomType) => showRoom(cli, hierarchy, roomId, roomType)}
onJoinRoomClick={(roomId) => joinRoom(cli, hierarchy, roomId)}
/>
</>;
} else if (!hierarchy.canLoadMore) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/structures/SpaceRoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import {
showSpaceInvite,
showSpaceSettings,
} from "../../utils/space";
import SpaceHierarchy, { showRoom } from "./SpaceHierarchy";
import SpaceHierarchy, { joinRoom, showRoom } from "./SpaceHierarchy";
import MemberAvatar from "../views/avatars/MemberAvatar";
import SpaceStore from "../../stores/SpaceStore";
import FacePile from "../views/elements/FacePile";
Expand Down Expand Up @@ -508,7 +508,7 @@ const SpaceLanding = ({ space }: { space: Room }) => {
) }
</RoomTopic>

<SpaceHierarchy space={space} showRoom={showRoom} additionalButtons={addRoomButton} />
<SpaceHierarchy space={space} showRoom={showRoom} joinRoom={joinRoom} additionalButtons={addRoomButton} />
</div>;
};

Expand Down
4 changes: 2 additions & 2 deletions src/components/views/avatars/RoomAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
import React, { ComponentProps } from 'react';
import { Room } from 'matrix-js-sdk/src/models/room';
import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import classNames from "classnames";

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

// TODO: type when js-sdk has types
private onRoomStateEvents = (ev: any) => {
private onRoomStateEvents = (ev: MatrixEvent) => {
if (!this.props.room ||
ev.getRoomId() !== this.props.room.roomId ||
ev.getType() !== 'm.room.avatar'
Expand Down
1 change: 1 addition & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2928,6 +2928,7 @@
"Drop file here to upload": "Drop file here to upload",
"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.",
"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.",
"Joining": "Joining",
"You don't have permission": "You don't have permission",
"Joined": "Joined",
"This room is suggested as a good one to join": "This room is suggested as a good one to join",
Expand Down
19 changes: 11 additions & 8 deletions src/stores/RoomViewStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ class RoomViewStore extends Store<ActionPayload> {
}
}

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

private joinRoomError(payload: ActionPayload) {
this.setState({
joining: false,
joinError: payload.err,
});
const err = payload.err;
public showJoinRoomError(err: Error | MatrixError, roomId: string) {
let msg = err.message ? err.message : JSON.stringify(err);
logger.log("Failed to join room:", msg);

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

private joinRoomError(payload: ActionPayload) {
this.setState({
joining: false,
joinError: payload.err,
});
this.showJoinRoomError(payload.err, this.state.roomId);
}

public reset() {
this.state = Object.assign({}, INITIAL_STATE);
}
Expand Down
37 changes: 19 additions & 18 deletions src/utils/RoomUpgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import SpaceStore from "../stores/SpaceStore";
import Spinner from "../components/views/elements/Spinner";

import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClient } from "matrix-js-sdk/src/client";

interface IProgress {
roomUpgraded: boolean;
Expand All @@ -35,6 +36,23 @@ interface IProgress {
updateSpacesTotal: number;
}

export async function awaitRoomDownSync(cli: MatrixClient, roomId: string): Promise<Room> {
const room = cli.getRoom(roomId);
if (room) return room; // already have the room

return new Promise<Room>(resolve => {
// We have to wait for the js-sdk to give us the room back so
// we can more effectively abuse the MultiInviter behaviour
// which heavily relies on the Room object being available.
const checkForRoomFn = (room: Room) => {
if (room.roomId !== roomId) return;
resolve(room);
cli.off("Room", checkForRoomFn);
};
cli.on("Room", checkForRoomFn);
});
}

export async function upgradeRoom(
room: Room,
targetVersion: string,
Expand Down Expand Up @@ -93,24 +111,7 @@ export async function upgradeRoom(
progressCallback?.(progress);

if (awaitRoom || inviteUsers) {
await new Promise<void>(resolve => {
// already have the room
if (room.client.getRoom(newRoomId)) {
resolve();
return;
}

// We have to wait for the js-sdk to give us the room back so
// we can more effectively abuse the MultiInviter behaviour
// which heavily relies on the Room object being available.
const checkForRoomFn = (newRoom: Room) => {
if (newRoom.roomId !== newRoomId) return;
resolve();
cli.off("Room", checkForRoomFn);
};
cli.on("Room", checkForRoomFn);
});

await awaitRoomDownSync(room.client, newRoomId);
progress.roomSynced = true;
progressCallback?.(progress);
}
Expand Down