- { spaces.length > 0 ? (
-
-
{ _t("Spaces") }
- { spaces.map(space => {
- return {
- if (checked) {
- selectedToAdd.add(space);
- } else {
- selectedToAdd.delete(space);
- }
- setSelectedToAdd(new Set(selectedToAdd));
- }}
- />;
- }) }
-
- ) : null }
-
{ rooms.length > 0 ? (
{ _t("Rooms") }
@@ -172,6 +151,27 @@ const AddExistingToSpaceDialog: React.FC = ({ matrixClient: cli, space,
) : undefined }
+ { spaces.length > 0 ? (
+
+
{ _t("Spaces") }
+ { spaces.map(space => {
+ return {
+ if (checked) {
+ selectedToAdd.add(space);
+ } else {
+ selectedToAdd.delete(space);
+ }
+ setSelectedToAdd(new Set(selectedToAdd));
+ }}
+ />;
+ }) }
+
+ ) : null }
+
{ spaces.length + rooms.length < 1 ?
{ _t("No results") }
: undefined }
@@ -185,8 +185,8 @@ const AddExistingToSpaceDialog: React.FC = ({ matrixClient: cli, space,
- {
setBusy(true);
@@ -200,7 +200,9 @@ const AddExistingToSpaceDialog: React.FC = ({ matrixClient: cli, space,
}
setBusy(false);
}}
- />
+ >
+ { busy ? _t("Adding...") : _t("Add") }
+
;
};
diff --git a/src/components/views/dialogs/SpaceSettingsDialog.tsx b/src/components/views/dialogs/SpaceSettingsDialog.tsx
index f6bf5b87e6..b016e320eb 100644
--- a/src/components/views/dialogs/SpaceSettingsDialog.tsx
+++ b/src/components/views/dialogs/SpaceSettingsDialog.tsx
@@ -28,7 +28,6 @@ import {getTopic} from "../elements/RoomTopic";
import {avatarUrlForRoom} from "../../../Avatar";
import ToggleSwitch from "../elements/ToggleSwitch";
import AccessibleButton from "../elements/AccessibleButton";
-import FormButton from "../elements/FormButton";
import Modal from "../../../Modal";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import {allSettled} from "../../../utils/promise";
@@ -134,16 +133,17 @@ const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFin
/>
- {
defaultDispatcher.dispatch({
action: "leave_room",
room_id: space.roomId,
});
}}
- />
+ >
+ { _t("Leave Space") }
+
Modal.createDialog(DevtoolsDialog, {roomId: space.roomId})}>
@@ -152,7 +152,9 @@ const SpaceSettingsDialog: React.FC = ({ matrixClient: cli, space, onFin
{ _t("Cancel") }
-
+
+ { busy ? _t("Saving...") : _t("Save Changes") }
+
;
diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx
index ce426a64ed..c85b9d7868 100644
--- a/src/components/views/rooms/NewRoomIntro.tsx
+++ b/src/components/views/rooms/NewRoomIntro.tsx
@@ -28,6 +28,7 @@ import defaultDispatcher from "../../../dispatcher/dispatcher";
import {ViewUserPayload} from "../../../dispatcher/payloads/ViewUserPayload";
import {Action} from "../../../dispatcher/actions";
import dis from "../../../dispatcher/dispatcher";
+import SpaceStore from "../../../stores/SpaceStore";
const NewRoomIntro = () => {
const cli = useContext(MatrixClientContext);
@@ -100,17 +101,48 @@ const NewRoomIntro = () => {
});
}
- let buttons;
- if (room.canInvite(cli.getUserId())) {
- const onInviteClick = () => {
- dis.dispatch({ action: "view_invite", roomId });
- };
+ let parentSpace;
+ if (
+ SpaceStore.instance.activeSpace?.canInvite(cli.getUserId()) &&
+ SpaceStore.instance.getSpaceFilteredRoomIds(SpaceStore.instance.activeSpace).has(room.roomId)
+ ) {
+ parentSpace = SpaceStore.instance.activeSpace;
+ }
+ let buttons;
+ if (parentSpace) {
buttons =
-
+ {
+ dis.dispatch({ action: "view_invite", roomId });
+ }}
+ >
+ {_t("Invite to %(spaceName)s", { spaceName: parentSpace.name })}
+
+ { room.canInvite(cli.getUserId()) && {
+ dis.dispatch({ action: "view_invite", roomId });
+ }}
+ >
+ {_t("Invite to just this room")}
+ }
+ ;
+ } else if (room.canInvite(cli.getUserId())) {
+ buttons =
+
{
+ dis.dispatch({ action: "view_invite", roomId });
+ }}
+ >
{_t("Invite to this room")}
-
+ ;
}
const avatarUrl = room.currentState.getStateEvents(EventType.RoomAvatar, "")?.getContent()?.url;
diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx
index 4378154d8f..e83b07f71b 100644
--- a/src/components/views/rooms/RoomList.tsx
+++ b/src/components/views/rooms/RoomList.tsx
@@ -20,6 +20,7 @@ import React, { ReactComponentElement } from "react";
import { Dispatcher } from "flux";
import { Room } from "matrix-js-sdk/src/models/room";
import * as fbEmitter from "fbemitter";
+import { EventType } from "matrix-js-sdk/src/@types/event";
import { _t, _td } from "../../../languageHandler";
import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
@@ -48,12 +49,15 @@ import { IconizedContextMenuOption, IconizedContextMenuOptionList } from "../con
import AccessibleButton from "../elements/AccessibleButton";
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
import CallHandler from "../../../CallHandler";
-import SpaceStore, { SUGGESTED_ROOMS } from "../../../stores/SpaceStore";
+import SpaceStore, {SUGGESTED_ROOMS} from "../../../stores/SpaceStore";
import { showAddExistingRooms, showCreateNewRoom } from "../../../utils/space";
-import { EventType } from "matrix-js-sdk/src/@types/event";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import RoomAvatar from "../avatars/RoomAvatar";
import { ISpaceSummaryRoom } from "../../structures/SpaceRoomDirectory";
+import { showRoomInviteDialog } from "../../../RoomInvite";
+import Modal from "../../../Modal";
+import SpacePublicShare from "../spaces/SpacePublicShare";
+import InfoDialog from "../dialogs/InfoDialog";
interface IProps {
onKeyDown: (ev: React.KeyboardEvent) => void;
@@ -62,6 +66,7 @@ interface IProps {
onResize: () => void;
resizeNotifier: ResizeNotifier;
isMinimized: boolean;
+ activeSpace: Room;
}
interface IState {
@@ -194,8 +199,8 @@ const TAG_AESTHETICS: ITagAestheticsMap = {
: _t("You do not have permissions to add rooms to this space")}
/>
{
e.preventDefault();
e.stopPropagation();
@@ -424,6 +429,25 @@ export default class RoomList extends React.PureComponent {
dis.dispatch({ action: Action.ViewRoomDirectory, initialText });
};
+ private onSpaceInviteClick = () => {
+ const initialText = RoomListStore.instance.getFirstNameFilterCondition()?.search;
+ if (this.props.activeSpace.getJoinRule() === "public") {
+ const modal = Modal.createTrackedDialog("Space Invite", "User Menu", InfoDialog, {
+ title: _t("Invite to %(spaceName)s", { spaceName: this.props.activeSpace.name }),
+ description:
+ { _t("Share your public space") }
+ modal.close()} />
+ ,
+ fixedWidth: false,
+ button: false,
+ className: "mx_SpacePanel_sharePublicSpace",
+ hasCloseButton: true,
+ });
+ } else {
+ showRoomInviteDialog(this.props.activeSpace.roomId, initialText);
+ }
+ };
+
private renderSuggestedRooms(): ReactComponentElement[] {
return this.state.suggestedRooms.map(room => {
const name = room.name || room.canonical_alias || room.aliases.pop() || _t("Empty room");
@@ -569,7 +593,23 @@ export default class RoomList extends React.PureComponent {
kind="link"
onClick={this.onExplore}
>
- {_t("Explore all public rooms")}
+ { this.props.activeSpace ? _t("Explore rooms") : _t("Explore all public rooms") }
+
+ ;
+ } else if (this.props.activeSpace) {
+ explorePrompt =
+
{ _t("Quick actions") }
+ { this.props.activeSpace.canInvite(MatrixClientPeg.get().getUserId()) &&
+ {_t("Invite people")}
+ }
+
+ {_t("Explore rooms")}
;
} else if (Object.values(this.state.sublists).some(list => list.length > 0)) {
diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx
index 879cf929e0..9ee6edc489 100644
--- a/src/components/views/spaces/SpaceCreateMenu.tsx
+++ b/src/components/views/spaces/SpaceCreateMenu.tsx
@@ -21,7 +21,6 @@ import {EventType, RoomType, RoomCreateTypeField} from "matrix-js-sdk/src/@types
import {_t} from "../../../languageHandler";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import {ChevronFace, ContextMenu} from "../../structures/ContextMenu";
-import FormButton from "../elements/FormButton";
import createRoom, {IStateEvent, Preset} from "../../../createRoom";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import SpaceBasicSettings from "./SpaceBasicSettings";
@@ -89,6 +88,7 @@ const SpaceCreateMenu = ({ onFinished }) => {
power_level_content_override: {
// Only allow Admins to write to the timeline to prevent hidden sync spam
events_default: 100,
+ ...Visibility.Public ? { invite: 0 } : {},
},
},
spinner: false,
@@ -148,11 +148,9 @@ const SpaceCreateMenu = ({ onFinished }) => {
-
+
+ { busy ? _t("Creating...") : _t("Create") }
+
;
}
diff --git a/src/components/views/spaces/SpacePublicShare.tsx b/src/components/views/spaces/SpacePublicShare.tsx
index b2d3b7ce29..fa81b75525 100644
--- a/src/components/views/spaces/SpacePublicShare.tsx
+++ b/src/components/views/spaces/SpacePublicShare.tsx
@@ -26,7 +26,7 @@ import {showRoomInviteDialog} from "../../../RoomInvite";
interface IProps {
space: Room;
- onFinished(): void;
+ onFinished?(): void;
}
const SpacePublicShare = ({ space, onFinished }: IProps) => {
@@ -54,7 +54,7 @@ const SpacePublicShare = ({ space, onFinished }: IProps) => {
className="mx_SpacePublicShare_inviteButton"
onClick={() => {
showRoomInviteDialog(space.roomId);
- onFinished();
+ if (onFinished) onFinished();
}}
>
{ _t("Invite people") }
diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx
index 83bc2296e7..ca6f90fa91 100644
--- a/src/components/views/spaces/SpaceTreeLevel.tsx
+++ b/src/components/views/spaces/SpaceTreeLevel.tsx
@@ -30,9 +30,14 @@ import IconizedContextMenu, {
import {_t} from "../../../languageHandler";
import {ContextMenuTooltipButton} from "../../../accessibility/context_menu/ContextMenuTooltipButton";
import {toRightOf} from "../../structures/ContextMenu";
-import {shouldShowSpaceSettings, showCreateNewRoom, showSpaceSettings} from "../../../utils/space";
+import {
+ shouldShowSpaceSettings,
+ showAddExistingRooms,
+ showCreateNewRoom,
+ showSpaceSettings,
+} from "../../../utils/space";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
-import {ButtonEvent} from "../elements/AccessibleButton";
+import AccessibleButton, {ButtonEvent} from "../elements/AccessibleButton";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import Modal from "../../../Modal";
import SpacePublicShare from "./SpacePublicShare";
@@ -127,7 +132,7 @@ export class SpaceItem extends React.PureComponent {
if (this.props.space.getJoinRule() === "public") {
const modal = Modal.createTrackedDialog("Space Invite", "User Menu", InfoDialog, {
- title: _t("Invite members"),
+ title: _t("Invite to %(spaceName)s", { spaceName: this.props.space.name }),
description:
{ _t("Share your public space") }
modal.close()} />
@@ -170,6 +175,14 @@ export class SpaceItem extends React.PureComponent {
this.setState({contextMenuPosition: null}); // also close the menu
};
+ private onAddExistingRoomClick = (ev: ButtonEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ showAddExistingRooms(this.context, this.props.space);
+ this.setState({contextMenuPosition: null}); // also close the menu
+ };
+
private onMembersClick = (ev: ButtonEvent) => {
ev.preventDefault();
ev.stopPropagation();
@@ -236,15 +249,20 @@ export class SpaceItem extends React.PureComponent {
;
}
- let newRoomOption;
+ let newRoomSection;
if (this.props.space.currentState.maySendStateEvent(EventType.SpaceChild, userId)) {
- newRoomOption = (
+ newRoomSection =
- );
+
+ ;
}
contextMenu = {
label={_t("Explore rooms")}
onClick={this.onExploreRoomsClick}
/>
- { newRoomOption }
+ { newRoomSection }
{ leaveSection }
;
}
@@ -335,7 +353,7 @@ export class SpaceItem extends React.PureComponent {
const avatarSize = isNested ? 24 : 32;
const toggleCollapseButton = childSpaces && childSpaces.length ?
-