Space creation prompt user to add existing rooms instead of creating new ones

This commit is contained in:
Michael Telatynski 2021-04-26 12:41:04 +01:00
parent dda18c9384
commit 8656212eb9
5 changed files with 244 additions and 145 deletions

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {useState} from "react";
import React, {useContext, useState} from "react";
import classNames from "classnames";
import {Room} from "matrix-js-sdk/src/models/room";
import {MatrixClient} from "matrix-js-sdk/src/client";
@ -33,6 +33,7 @@ import {allSettled} from "../../../utils/promise";
import DMRoomMap from "../../../utils/DMRoomMap";
import {calculateRoomVia} from "../../../utils/permalinks/Permalinks";
import StyledCheckbox from "../elements/StyledCheckbox";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
interface IProps extends IDialogProps {
matrixClient: MatrixClient;
@ -41,31 +42,35 @@ interface IProps extends IDialogProps {
}
const Entry = ({ room, checked, onChange }) => {
return <div className="mx_AddExistingToSpaceDialog_entry">
return <div className="mx_AddExistingToSpace_entry">
<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} />
</div>;
};
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 lcQuery = query.toLowerCase();
const [selectedSpace, setSelectedSpace] = useState(space);
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId);
const existingSubspacesSet = new Set(existingSubspaces);
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) => {
if (room.getMyMembership() !== "join") return arr;
if (!room.name.toLowerCase().includes(lcQuery)) return arr;
if (room.isSpaceRoom()) {
if (room !== space && room !== selectedSpace && !existingSubspacesSet.has(room)) {
if (room !== space && !existingSubspacesSet.has(room)) {
arr[0].push(room);
}
} else if (!existingRoomsSet.has(room) && joinRule !== "public") {
@ -75,11 +80,79 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
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}
/>
<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 [error, setError] = useState("");
let spaceOptionSection;
if (existingSubspacesSet.size > 0) {
if (existingSubspaces.length > 0) {
const options = [space, ...existingSubspaces].map((space) => {
const classes = classNames("mx_AddExistingToSpaceDialog_dropdownOption", {
mx_AddExistingToSpaceDialog_dropdownOptionActive: space === selectedSpace,
@ -119,86 +192,26 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
return <BaseDialog
title={title}
className="mx_AddExistingToSpaceDialog"
contentId="mx_AddExistingToSpaceDialog"
contentId="mx_AddExistingToSpace"
onFinished={onFinished}
fixedWidth={false}
>
{ error && <div className="mx_AddExistingToSpaceDialog_errorText">{ error }</div> }
<SearchBox
className="mx_textinput_icon mx_textinput_search"
placeholder={ _t("Filter your rooms and spaces") }
onSearch={setQuery}
autoComplete={true}
/>
<AutoHideScrollbar className="mx_AddExistingToSpaceDialog_content" id="mx_AddExistingToSpaceDialog">
{ rooms.length > 0 ? (
<div className="mx_AddExistingToSpaceDialog_section">
<h3>{ _t("Rooms") }</h3>
{ rooms.map(room => {
return <Entry
key={room.roomId}
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>
<MatrixClientContext.Provider value={cli}>
<AddExistingToSpace
space={space}
selected={selectedToAdd}
onChange={(checked, room) => {
if (checked) {
selectedToAdd.add(room);
} else {
selectedToAdd.delete(room);
}
setSelectedToAdd(new Set(selectedToAdd));
}}
/>
</MatrixClientContext.Provider>
<div className="mx_AddExistingToSpaceDialog_footer">
<span>
@ -212,6 +225,7 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
kind="primary"
disabled={busy || selectedToAdd.size < 1}
onClick={async () => {
// TODO rate limiting
setBusy(true);
try {
await allSettled(Array.from(selectedToAdd).map((room) =>