Merge pull request #5923 from matrix-org/t3chguy/fix/16628
Space creation prompt user to add existing rooms for "Just Me" spaces
This commit is contained in:
commit
d203e8f129
5 changed files with 245 additions and 146 deletions
|
@ -81,6 +81,16 @@ $SpaceRoomViewInnerWidth: 428px;
|
||||||
color: $secondary-fg-color;
|
color: $secondary-fg-color;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
max-width: $SpaceRoomViewInnerWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace {
|
||||||
|
max-width: $SpaceRoomViewInnerWidth;
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_content {
|
||||||
|
height: calc(100vh - 360px);
|
||||||
|
max-height: 400px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpaceRoomView_buttons {
|
.mx_SpaceRoomView_buttons {
|
||||||
|
|
|
@ -21,6 +21,66 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace {
|
||||||
|
.mx_SearchBox {
|
||||||
|
// To match the space around the title
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_content {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_noResults {
|
||||||
|
display: block;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_section {
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
font-size: $font-12px;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
line-height: $font-15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_entry {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
.mx_BaseAvatar {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_entry_name {
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: 30px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Checkbox {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_section_spaces {
|
||||||
|
.mx_BaseAvatar_image {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog {
|
.mx_AddExistingToSpaceDialog {
|
||||||
width: 480px;
|
width: 480px;
|
||||||
color: $primary-fg-color;
|
color: $primary-fg-color;
|
||||||
|
@ -100,12 +160,6 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SearchBox {
|
|
||||||
// To match the space around the title
|
|
||||||
margin: 0 0 15px 0;
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_errorText {
|
.mx_AddExistingToSpaceDialog_errorText {
|
||||||
font-weight: $font-semi-bold;
|
font-weight: $font-semi-bold;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
|
@ -114,56 +168,8 @@ limitations under the License.
|
||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_content {
|
.mx_AddExistingToSpace {
|
||||||
flex-grow: 1;
|
display: contents;
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_noResults {
|
|
||||||
display: block;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_section {
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> h3 {
|
|
||||||
margin: 0;
|
|
||||||
color: $secondary-fg-color;
|
|
||||||
font-size: $font-12px;
|
|
||||||
font-weight: $font-semi-bold;
|
|
||||||
line-height: $font-15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_entry {
|
|
||||||
display: flex;
|
|
||||||
margin-top: 12px;
|
|
||||||
|
|
||||||
.mx_BaseAvatar {
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_entry_name {
|
|
||||||
font-size: $font-15px;
|
|
||||||
line-height: 30px;
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Checkbox {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_section_spaces {
|
|
||||||
.mx_BaseAvatar_image {
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_footer {
|
.mx_AddExistingToSpaceDialog_footer {
|
||||||
|
|
|
@ -51,6 +51,9 @@ import MemberAvatar from "../views/avatars/MemberAvatar";
|
||||||
import {useStateToggle} from "../../hooks/useStateToggle";
|
import {useStateToggle} from "../../hooks/useStateToggle";
|
||||||
import SpaceStore from "../../stores/SpaceStore";
|
import SpaceStore from "../../stores/SpaceStore";
|
||||||
import FacePile from "../views/elements/FacePile";
|
import FacePile from "../views/elements/FacePile";
|
||||||
|
import {AddExistingToSpace} from "../views/dialogs/AddExistingToSpaceDialog";
|
||||||
|
import {allSettled} from "../../utils/promise";
|
||||||
|
import {calculateRoomVia} from "../../utils/permalinks/Permalinks";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
space: Room;
|
space: Room;
|
||||||
|
@ -354,7 +357,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
||||||
let buttonLabel = _t("Skip for now");
|
let buttonLabel = _t("Skip for now");
|
||||||
if (roomNames.some(name => name.trim())) {
|
if (roomNames.some(name => name.trim())) {
|
||||||
onClick = onNextClick;
|
onClick = onNextClick;
|
||||||
buttonLabel = busy ? _t("Creating rooms...") : _t("Continue")
|
buttonLabel = busy ? _t("Creating rooms...") : _t("Continue");
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
|
@ -376,6 +379,65 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SpaceAddExistingRooms = ({ space, onFinished }) => {
|
||||||
|
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
|
||||||
|
|
||||||
|
const [busy, setBusy] = useState(false);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
|
let onClick = onFinished;
|
||||||
|
let buttonLabel = _t("Skip for now");
|
||||||
|
if (selectedToAdd.size > 0) {
|
||||||
|
onClick = async () => {
|
||||||
|
// TODO rate limiting
|
||||||
|
setBusy(true);
|
||||||
|
try {
|
||||||
|
await allSettled(Array.from(selectedToAdd).map((room) =>
|
||||||
|
SpaceStore.instance.addRoomToSpace(space, room.roomId, calculateRoomVia(room))));
|
||||||
|
onFinished(true);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to add rooms to space", e);
|
||||||
|
setError(_t("Failed to add rooms to space"));
|
||||||
|
}
|
||||||
|
setBusy(false);
|
||||||
|
};
|
||||||
|
buttonLabel = busy ? _t("Adding...") : _t("Add");
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<h1>{ _t("What do you want to organise?") }</h1>
|
||||||
|
<div className="mx_SpaceRoomView_description">
|
||||||
|
{ _t("Pick rooms or conversations to add. This is just a space for you, " +
|
||||||
|
"no one will be informed. You can add more later.") }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{ error && <div className="mx_SpaceRoomView_errorText">{ error }</div> }
|
||||||
|
|
||||||
|
<AddExistingToSpace
|
||||||
|
space={space}
|
||||||
|
selected={selectedToAdd}
|
||||||
|
onChange={(checked, room) => {
|
||||||
|
if (checked) {
|
||||||
|
selectedToAdd.add(room);
|
||||||
|
} else {
|
||||||
|
selectedToAdd.delete(room);
|
||||||
|
}
|
||||||
|
setSelectedToAdd(new Set(selectedToAdd));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="mx_SpaceRoomView_buttons">
|
||||||
|
<AccessibleButton
|
||||||
|
kind="primary"
|
||||||
|
disabled={busy}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{ buttonLabel }
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
|
||||||
const SpaceSetupPublicShare = ({ space, onFinished }) => {
|
const SpaceSetupPublicShare = ({ space, onFinished }) => {
|
||||||
return <div className="mx_SpaceRoomView_publicShare">
|
return <div className="mx_SpaceRoomView_publicShare">
|
||||||
<h1>{ _t("Share %(name)s", { name: space.name }) }</h1>
|
<h1>{ _t("Share %(name)s", { name: space.name }) }</h1>
|
||||||
|
@ -659,7 +721,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||||
return <SpaceSetupPrivateScope
|
return <SpaceSetupPrivateScope
|
||||||
space={this.props.space}
|
space={this.props.space}
|
||||||
onFinished={(invite: boolean) => {
|
onFinished={(invite: boolean) => {
|
||||||
this.setState({ phase: invite ? Phase.PrivateInvite : Phase.PrivateCreateRooms });
|
this.setState({ phase: invite ? Phase.PrivateInvite : Phase.PrivateExistingRooms });
|
||||||
}}
|
}}
|
||||||
/>;
|
/>;
|
||||||
case Phase.PrivateInvite:
|
case Phase.PrivateInvite:
|
||||||
|
@ -675,6 +737,11 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||||
"You can add more later too, including already existing ones.")}
|
"You can add more later too, including already existing ones.")}
|
||||||
onFinished={() => this.setState({ phase: Phase.Landing })}
|
onFinished={() => this.setState({ phase: Phase.Landing })}
|
||||||
/>;
|
/>;
|
||||||
|
case Phase.PrivateExistingRooms:
|
||||||
|
return <SpaceAddExistingRooms
|
||||||
|
space={this.props.space}
|
||||||
|
onFinished={() => this.setState({ phase: Phase.Landing })}
|
||||||
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {useState} from "react";
|
import React, {useContext, useState} from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import {Room} from "matrix-js-sdk/src/models/room";
|
import {Room} from "matrix-js-sdk/src/models/room";
|
||||||
import {MatrixClient} from "matrix-js-sdk/src/client";
|
import {MatrixClient} from "matrix-js-sdk/src/client";
|
||||||
|
@ -33,6 +33,7 @@ import {allSettled} from "../../../utils/promise";
|
||||||
import DMRoomMap from "../../../utils/DMRoomMap";
|
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||||
import {calculateRoomVia} from "../../../utils/permalinks/Permalinks";
|
import {calculateRoomVia} from "../../../utils/permalinks/Permalinks";
|
||||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||||
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
interface IProps extends IDialogProps {
|
interface IProps extends IDialogProps {
|
||||||
matrixClient: MatrixClient;
|
matrixClient: MatrixClient;
|
||||||
|
@ -41,31 +42,35 @@ interface IProps extends IDialogProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Entry = ({ room, checked, onChange }) => {
|
const Entry = ({ room, checked, onChange }) => {
|
||||||
return <label className="mx_AddExistingToSpaceDialog_entry">
|
return <label className="mx_AddExistingToSpace_entry">
|
||||||
<RoomAvatar room={room} height={32} width={32} />
|
<RoomAvatar room={room} height={32} width={32} />
|
||||||
<span className="mx_AddExistingToSpaceDialog_entry_name">{ room.name }</span>
|
<span className="mx_AddExistingToSpace_entry_name">{ room.name }</span>
|
||||||
<StyledCheckbox onChange={(e) => onChange(e.target.checked)} checked={checked} />
|
<StyledCheckbox onChange={(e) => onChange(e.target.checked)} checked={checked} />
|
||||||
</label>;
|
</label>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space, onCreateRoomClick, onFinished }) => {
|
interface IAddExistingToSpaceProps {
|
||||||
|
space: Room;
|
||||||
|
selected: Set<Room>;
|
||||||
|
onChange(checked: boolean, room: Room): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space, selected, onChange }) => {
|
||||||
|
const cli = useContext(MatrixClientContext);
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const lcQuery = query.toLowerCase();
|
const lcQuery = query.toLowerCase();
|
||||||
|
|
||||||
const [selectedSpace, setSelectedSpace] = useState(space);
|
|
||||||
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
|
|
||||||
|
|
||||||
const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId);
|
const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId);
|
||||||
const existingSubspacesSet = new Set(existingSubspaces);
|
const existingSubspacesSet = new Set(existingSubspaces);
|
||||||
const existingRoomsSet = new Set(SpaceStore.instance.getChildRooms(space.roomId));
|
const existingRoomsSet = new Set(SpaceStore.instance.getChildRooms(space.roomId));
|
||||||
|
|
||||||
const joinRule = selectedSpace.getJoinRule();
|
const joinRule = space.getJoinRule();
|
||||||
const [spaces, rooms, dms] = cli.getVisibleRooms().reduce((arr, room) => {
|
const [spaces, rooms, dms] = cli.getVisibleRooms().reduce((arr, room) => {
|
||||||
if (room.getMyMembership() !== "join") return arr;
|
if (room.getMyMembership() !== "join") return arr;
|
||||||
if (!room.name.toLowerCase().includes(lcQuery)) return arr;
|
if (!room.name.toLowerCase().includes(lcQuery)) return arr;
|
||||||
|
|
||||||
if (room.isSpaceRoom()) {
|
if (room.isSpaceRoom()) {
|
||||||
if (room !== space && room !== selectedSpace && !existingSubspacesSet.has(room)) {
|
if (room !== space && !existingSubspacesSet.has(room)) {
|
||||||
arr[0].push(room);
|
arr[0].push(room);
|
||||||
}
|
}
|
||||||
} else if (!existingRoomsSet.has(room)) {
|
} else if (!existingRoomsSet.has(room)) {
|
||||||
|
@ -79,11 +84,80 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
||||||
return arr;
|
return arr;
|
||||||
}, [[], [], []]);
|
}, [[], [], []]);
|
||||||
|
|
||||||
|
return <div className="mx_AddExistingToSpace">
|
||||||
|
<SearchBox
|
||||||
|
className="mx_textinput_icon mx_textinput_search"
|
||||||
|
placeholder={ _t("Filter your rooms and spaces") }
|
||||||
|
onSearch={setQuery}
|
||||||
|
autoComplete={true}
|
||||||
|
autoFocus={true}
|
||||||
|
/>
|
||||||
|
<AutoHideScrollbar className="mx_AddExistingToSpace_content" id="mx_AddExistingToSpace">
|
||||||
|
{ rooms.length > 0 ? (
|
||||||
|
<div className="mx_AddExistingToSpace_section">
|
||||||
|
<h3>{ _t("Rooms") }</h3>
|
||||||
|
{ rooms.map(room => {
|
||||||
|
return <Entry
|
||||||
|
key={room.roomId}
|
||||||
|
room={room}
|
||||||
|
checked={selected.has(room)}
|
||||||
|
onChange={(checked) => {
|
||||||
|
onChange(checked, room);
|
||||||
|
}}
|
||||||
|
/>;
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
) : undefined }
|
||||||
|
|
||||||
|
{ spaces.length > 0 ? (
|
||||||
|
<div className="mx_AddExistingToSpace_section mx_AddExistingToSpace_section_spaces">
|
||||||
|
<h3>{ _t("Spaces") }</h3>
|
||||||
|
{ spaces.map(space => {
|
||||||
|
return <Entry
|
||||||
|
key={space.roomId}
|
||||||
|
room={space}
|
||||||
|
checked={selected.has(space)}
|
||||||
|
onChange={(checked) => {
|
||||||
|
onChange(checked, space);
|
||||||
|
}}
|
||||||
|
/>;
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
) : null }
|
||||||
|
|
||||||
|
{ dms.length > 0 ? (
|
||||||
|
<div className="mx_AddExistingToSpace_section">
|
||||||
|
<h3>{ _t("Direct Messages") }</h3>
|
||||||
|
{ dms.map(room => {
|
||||||
|
return <Entry
|
||||||
|
key={room.roomId}
|
||||||
|
room={room}
|
||||||
|
checked={selected.has(room)}
|
||||||
|
onChange={(checked) => {
|
||||||
|
onChange(checked, room);
|
||||||
|
}}
|
||||||
|
/>;
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
) : null }
|
||||||
|
|
||||||
|
{ spaces.length + rooms.length + dms.length < 1 ? <span className="mx_AddExistingToSpace_noResults">
|
||||||
|
{ _t("No results") }
|
||||||
|
</span> : undefined }
|
||||||
|
</AutoHideScrollbar>
|
||||||
|
</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space, onCreateRoomClick, onFinished }) => {
|
||||||
|
const [selectedSpace, setSelectedSpace] = useState(space);
|
||||||
|
const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId);
|
||||||
|
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
|
||||||
|
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
let spaceOptionSection;
|
let spaceOptionSection;
|
||||||
if (existingSubspacesSet.size > 0) {
|
if (existingSubspaces.length > 0) {
|
||||||
const options = [space, ...existingSubspaces].map((space) => {
|
const options = [space, ...existingSubspaces].map((space) => {
|
||||||
const classes = classNames("mx_AddExistingToSpaceDialog_dropdownOption", {
|
const classes = classNames("mx_AddExistingToSpaceDialog_dropdownOption", {
|
||||||
mx_AddExistingToSpaceDialog_dropdownOptionActive: space === selectedSpace,
|
mx_AddExistingToSpaceDialog_dropdownOptionActive: space === selectedSpace,
|
||||||
|
@ -123,87 +197,26 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
||||||
return <BaseDialog
|
return <BaseDialog
|
||||||
title={title}
|
title={title}
|
||||||
className="mx_AddExistingToSpaceDialog"
|
className="mx_AddExistingToSpaceDialog"
|
||||||
contentId="mx_AddExistingToSpaceDialog"
|
contentId="mx_AddExistingToSpace"
|
||||||
onFinished={onFinished}
|
onFinished={onFinished}
|
||||||
fixedWidth={false}
|
fixedWidth={false}
|
||||||
>
|
>
|
||||||
{ error && <div className="mx_AddExistingToSpaceDialog_errorText">{ error }</div> }
|
{ error && <div className="mx_AddExistingToSpaceDialog_errorText">{ error }</div> }
|
||||||
|
|
||||||
<SearchBox
|
<MatrixClientContext.Provider value={cli}>
|
||||||
className="mx_textinput_icon mx_textinput_search"
|
<AddExistingToSpace
|
||||||
placeholder={ _t("Filter your rooms and spaces") }
|
space={space}
|
||||||
onSearch={setQuery}
|
selected={selectedToAdd}
|
||||||
autoComplete={true}
|
onChange={(checked, room) => {
|
||||||
autoFocus={true}
|
if (checked) {
|
||||||
/>
|
selectedToAdd.add(room);
|
||||||
<AutoHideScrollbar className="mx_AddExistingToSpaceDialog_content" id="mx_AddExistingToSpaceDialog">
|
} else {
|
||||||
{ rooms.length > 0 ? (
|
selectedToAdd.delete(room);
|
||||||
<div className="mx_AddExistingToSpaceDialog_section">
|
}
|
||||||
<h3>{ _t("Rooms") }</h3>
|
setSelectedToAdd(new Set(selectedToAdd));
|
||||||
{ rooms.map(room => {
|
}}
|
||||||
return <Entry
|
/>
|
||||||
key={room.roomId}
|
</MatrixClientContext.Provider>
|
||||||
room={room}
|
|
||||||
checked={selectedToAdd.has(room)}
|
|
||||||
onChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
selectedToAdd.add(room);
|
|
||||||
} else {
|
|
||||||
selectedToAdd.delete(room);
|
|
||||||
}
|
|
||||||
setSelectedToAdd(new Set(selectedToAdd));
|
|
||||||
}}
|
|
||||||
/>;
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
) : undefined }
|
|
||||||
|
|
||||||
{ spaces.length > 0 ? (
|
|
||||||
<div className="mx_AddExistingToSpaceDialog_section mx_AddExistingToSpaceDialog_section_spaces">
|
|
||||||
<h3>{ _t("Spaces") }</h3>
|
|
||||||
{ spaces.map(space => {
|
|
||||||
return <Entry
|
|
||||||
key={space.roomId}
|
|
||||||
room={space}
|
|
||||||
checked={selectedToAdd.has(space)}
|
|
||||||
onChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
selectedToAdd.add(space);
|
|
||||||
} else {
|
|
||||||
selectedToAdd.delete(space);
|
|
||||||
}
|
|
||||||
setSelectedToAdd(new Set(selectedToAdd));
|
|
||||||
}}
|
|
||||||
/>;
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
) : null }
|
|
||||||
|
|
||||||
{ dms.length > 0 ? (
|
|
||||||
<div className="mx_AddExistingToSpaceDialog_section">
|
|
||||||
<h3>{ _t("Direct Messages") }</h3>
|
|
||||||
{ dms.map(space => {
|
|
||||||
return <Entry
|
|
||||||
key={space.roomId}
|
|
||||||
room={space}
|
|
||||||
checked={selectedToAdd.has(space)}
|
|
||||||
onChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
selectedToAdd.add(space);
|
|
||||||
} else {
|
|
||||||
selectedToAdd.delete(space);
|
|
||||||
}
|
|
||||||
setSelectedToAdd(new Set(selectedToAdd));
|
|
||||||
}}
|
|
||||||
/>;
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
) : null }
|
|
||||||
|
|
||||||
{ spaces.length + rooms.length + dms.length < 1 ? <span className="mx_AddExistingToSpaceDialog_noResults">
|
|
||||||
{ _t("No results") }
|
|
||||||
</span> : undefined }
|
|
||||||
</AutoHideScrollbar>
|
|
||||||
|
|
||||||
<div className="mx_AddExistingToSpaceDialog_footer">
|
<div className="mx_AddExistingToSpaceDialog_footer">
|
||||||
<span>
|
<span>
|
||||||
|
@ -217,6 +230,7 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
|
||||||
kind="primary"
|
kind="primary"
|
||||||
disabled={busy || selectedToAdd.size < 1}
|
disabled={busy || selectedToAdd.size < 1}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
|
// TODO rate limiting
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
try {
|
try {
|
||||||
await allSettled(Array.from(selectedToAdd).map((room) =>
|
await allSettled(Array.from(selectedToAdd).map((room) =>
|
||||||
|
|
|
@ -2021,11 +2021,11 @@
|
||||||
"Add a new server...": "Add a new server...",
|
"Add a new server...": "Add a new server...",
|
||||||
"%(networkName)s rooms": "%(networkName)s rooms",
|
"%(networkName)s rooms": "%(networkName)s rooms",
|
||||||
"Matrix rooms": "Matrix rooms",
|
"Matrix rooms": "Matrix rooms",
|
||||||
"Space selection": "Space selection",
|
|
||||||
"Add existing rooms": "Add existing rooms",
|
|
||||||
"Filter your rooms and spaces": "Filter your rooms and spaces",
|
"Filter your rooms and spaces": "Filter your rooms and spaces",
|
||||||
"Spaces": "Spaces",
|
"Spaces": "Spaces",
|
||||||
"Direct Messages": "Direct Messages",
|
"Direct Messages": "Direct Messages",
|
||||||
|
"Space selection": "Space selection",
|
||||||
|
"Add existing rooms": "Add existing rooms",
|
||||||
"Don't want to add an existing room?": "Don't want to add an existing room?",
|
"Don't want to add an existing room?": "Don't want to add an existing room?",
|
||||||
"Create a new room": "Create a new room",
|
"Create a new room": "Create a new room",
|
||||||
"Failed to add rooms to space": "Failed to add rooms to space",
|
"Failed to add rooms to space": "Failed to add rooms to space",
|
||||||
|
@ -2667,6 +2667,8 @@
|
||||||
"Failed to create initial space rooms": "Failed to create initial space rooms",
|
"Failed to create initial space rooms": "Failed to create initial space rooms",
|
||||||
"Skip for now": "Skip for now",
|
"Skip for now": "Skip for now",
|
||||||
"Creating rooms...": "Creating rooms...",
|
"Creating rooms...": "Creating rooms...",
|
||||||
|
"What do you want to organise?": "What do you want to organise?",
|
||||||
|
"Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.": "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.",
|
||||||
"Share %(name)s": "Share %(name)s",
|
"Share %(name)s": "Share %(name)s",
|
||||||
"It's just you at the moment, it will be even better with others.": "It's just you at the moment, it will be even better with others.",
|
"It's just you at the moment, it will be even better with others.": "It's just you at the moment, it will be even better with others.",
|
||||||
"Go to my first room": "Go to my first room",
|
"Go to my first room": "Go to my first room",
|
||||||
|
|
Loading…
Reference in a new issue