diff --git a/res/css/views/dialogs/_AddExistingToSpaceDialog.scss b/res/css/views/dialogs/_AddExistingToSpaceDialog.scss index 91947be76a..575faf4a97 100644 --- a/res/css/views/dialogs/_AddExistingToSpaceDialog.scss +++ b/res/css/views/dialogs/_AddExistingToSpaceDialog.scss @@ -105,6 +105,90 @@ limitations under the License. mask-position: center; } } + + .mx_AddExistingToSpace_footer { + display: flex; + margin-top: 20px; + + > span { + flex-grow: 1; + font-size: $font-12px; + line-height: $font-15px; + color: $secondary-fg-color; + + .mx_ProgressBar { + height: 8px; + width: 100%; + + @mixin ProgressBarBorderRadius 8px; + } + + .mx_AddExistingToSpace_progressText { + margin-top: 8px; + font-size: $font-15px; + line-height: $font-24px; + color: $primary-fg-color; + } + + > * { + vertical-align: middle; + } + } + + .mx_AddExistingToSpace_error { + padding-left: 12px; + + > img { + align-self: center; + } + + .mx_AddExistingToSpace_errorHeading { + font-weight: $font-semi-bold; + font-size: $font-15px; + line-height: $font-18px; + color: $notice-primary-color; + } + + .mx_AddExistingToSpace_errorCaption { + margin-top: 4px; + font-size: $font-12px; + line-height: $font-15px; + color: $primary-fg-color; + } + } + + .mx_AccessibleButton { + display: inline-block; + align-self: center; + } + + .mx_AccessibleButton_kind_primary { + padding: 8px 36px; + } + + .mx_AddExistingToSpace_retryButton { + margin-left: 12px; + padding-left: 24px; + position: relative; + + &::before { + content: ''; + position: absolute; + background-color: $primary-fg-color; + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + mask-image: url('$(res)/img/element-icons/retry.svg'); + width: 18px; + height: 18px; + left: 0; + } + } + + .mx_AccessibleButton_kind_link { + padding: 0; + } + } } .mx_AddExistingToSpaceDialog { @@ -189,88 +273,4 @@ limitations under the License. .mx_AddExistingToSpace { display: contents; } - - .mx_AddExistingToSpaceDialog_footer { - display: flex; - margin-top: 20px; - - > span { - flex-grow: 1; - font-size: $font-12px; - line-height: $font-15px; - color: $secondary-fg-color; - - .mx_ProgressBar { - height: 8px; - width: 100%; - - @mixin ProgressBarBorderRadius 8px; - } - - .mx_AddExistingToSpaceDialog_progressText { - margin-top: 8px; - font-size: $font-15px; - line-height: $font-24px; - color: $primary-fg-color; - } - - > * { - vertical-align: middle; - } - } - - .mx_AddExistingToSpaceDialog_error { - padding-left: 12px; - - > img { - align-self: center; - } - - .mx_AddExistingToSpaceDialog_errorHeading { - font-weight: $font-semi-bold; - font-size: $font-15px; - line-height: $font-18px; - color: $notice-primary-color; - } - - .mx_AddExistingToSpaceDialog_errorCaption { - margin-top: 4px; - font-size: $font-12px; - line-height: $font-15px; - color: $primary-fg-color; - } - } - - .mx_AccessibleButton { - display: inline-block; - align-self: center; - } - - .mx_AccessibleButton_kind_primary { - padding: 8px 36px; - } - - .mx_AddExistingToSpaceDialog_retryButton { - margin-left: 12px; - padding-left: 24px; - position: relative; - - &::before { - content: ''; - position: absolute; - background-color: $primary-fg-color; - mask-repeat: no-repeat; - mask-position: center; - mask-size: contain; - mask-image: url('$(res)/img/element-icons/retry.svg'); - width: 18px; - height: 18px; - left: 0; - } - } - - .mx_AccessibleButton_kind_link { - padding: 0; - } - } } diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 583caf4b1e..56ee9dd62a 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -52,8 +52,6 @@ import {useStateToggle} from "../../hooks/useStateToggle"; import SpaceStore from "../../stores/SpaceStore"; import FacePile from "../views/elements/FacePile"; import {AddExistingToSpace} from "../views/dialogs/AddExistingToSpaceDialog"; -import {sleep} from "../../utils/promise"; -import {calculateRoomVia} from "../../utils/permalinks/Permalinks"; import {ChevronFace, ContextMenuButton, useContextMenu} from "./ContextMenu"; import IconizedContextMenu, { IconizedContextMenuOption, @@ -519,39 +517,6 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { }; const SpaceAddExistingRooms = ({ space, onFinished }) => { - const [selectedToAdd, setSelectedToAdd] = useState(new Set()); - - const [busy, setBusy] = useState(false); - const [error, setError] = useState(""); - - let onClick = onFinished; - let buttonLabel = _t("Skip for now"); - if (selectedToAdd.size > 0) { - onClick = async () => { - setBusy(true); - - for (const room of selectedToAdd) { - const via = calculateRoomVia(room); - try { - await SpaceStore.instance.addRoomToSpace(space, room.roomId, via).catch(async e => { - if (e.errcode === "M_LIMIT_EXCEEDED") { - await sleep(e.data.retry_after_ms); - return SpaceStore.instance.addRoomToSpace(space, room.roomId, via); // retry - } - - throw e; - }); - } catch (e) { - console.error("Failed to add rooms to space", e); - setError(_t("Failed to add rooms to space")); - break; - } - } - setBusy(false); - }; - buttonLabel = busy ? _t("Adding...") : _t("Add"); - } - return

{ _t("What do you want to organise?") }

@@ -559,29 +524,18 @@ const SpaceAddExistingRooms = ({ space, onFinished }) => { "no one will be informed. You can add more later.") }
- { error &&
{ error }
} - { - if (checked) { - selectedToAdd.add(room); - } else { - selectedToAdd.delete(room); - } - setSelectedToAdd(new Set(selectedToAdd)); - }} + emptySelectionButton={ + + { _t("Skip for now") } + + } + onFinished={onFinished} />
- - { buttonLabel } - +
; diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index 8ac68e99b7..c952614c9f 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {useContext, useMemo, useState} from "react"; +import React, {ReactNode, useContext, useMemo, useState} from "react"; import classNames from "classnames"; import {Room} from "matrix-js-sdk/src/models/room"; import {MatrixClient} from "matrix-js-sdk/src/client"; @@ -58,14 +58,23 @@ const Entry = ({ room, checked, onChange }) => { interface IAddExistingToSpaceProps { space: Room; - selected: Set; - onChange(checked: boolean, room: Room): void; + footerPrompt?: ReactNode; + emptySelectionButton?: ReactNode; + onFinished(added: boolean): void; } -export const AddExistingToSpace: React.FC = ({ space, selected, onChange }) => { +export const AddExistingToSpace: React.FC = ({ + space, + footerPrompt, + emptySelectionButton, + onFinished, +}) => { const cli = useContext(MatrixClientContext); const visibleRooms = useMemo(() => sortRooms(cli.getVisibleRooms()), [cli]); + const [selectedToAdd, setSelectedToAdd] = useState(new Set()); + const [progress, setProgress] = useState(null); + const [error, setError] = useState(null); const [query, setQuery] = useState(""); const lcQuery = query.toLowerCase(); @@ -93,120 +102,6 @@ export const AddExistingToSpace: React.FC = ({ space, return arr; }, [[], [], []]); - return
- - - { rooms.length > 0 ? ( -
-

{ _t("Rooms") }

- { rooms.map(room => { - return { - onChange(checked, room); - } : null} - />; - }) } -
- ) : undefined } - - { spaces.length > 0 ? ( -
-

{ _t("Spaces") }

-
-
{ _t("Feeling experimental?") }
-
{ _t("You can add existing spaces to a space.") }
-
- { spaces.map(space => { - return { - onChange(checked, space); - } : null} - />; - }) } -
- ) : null } - - { dms.length > 0 ? ( -
-

{ _t("Direct Messages") }

- { dms.map(room => { - return { - onChange(checked, room); - } : null} - />; - }) } -
- ) : null } - - { spaces.length + rooms.length + dms.length < 1 ? - { _t("No results") } - : undefined } -
-
; -}; - -const AddExistingToSpaceDialog: React.FC = ({ matrixClient: cli, space, onCreateRoomClick, onFinished }) => { - const [selectedSpace, setSelectedSpace] = useState(space); - const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId); - const [selectedToAdd, setSelectedToAdd] = useState(new Set()); - - const [progress, setProgress] = useState(null); - const [error, setError] = useState(null); - - let spaceOptionSection; - if (existingSubspaces.length > 0) { - const options = [space, ...existingSubspaces].map((space) => { - const classes = classNames("mx_AddExistingToSpaceDialog_dropdownOption", { - mx_AddExistingToSpaceDialog_dropdownOptionActive: space === selectedSpace, - }); - return
- - { space.name || getDisplayAliasForRoom(space) || space.roomId } -
; - }); - - spaceOptionSection = ( - { - setSelectedSpace(existingSubspaces.find(space => space.roomId === key) || space); - }} - value={selectedSpace.roomId} - label={_t("Space selection")} - > - { options } - - ); - } else { - spaceOptionSection =
- { space.name || getDisplayAliasForRoom(space) || space.roomId } -
; - } - - const title = - -
-

{ _t("Add existing rooms") }

- { spaceOptionSection } -
-
; - const addRooms = async () => { setError(null); setProgress(0); @@ -269,20 +164,145 @@ const AddExistingToSpaceDialog: React.FC = ({ matrixClient: cli, space, ; } else { + let button = emptySelectionButton; + if (!button || selectedToAdd.size > 0) { + button = + { _t("Add") } + ; + } + footer = <> -
{ _t("Want to add a new room instead?") }
- onCreateRoomClick(cli, space)} kind="link"> - { _t("Create a new room") } - + { footerPrompt }
- - { _t("Add") } - + { button } ; } + const onChange = !busy && !error ? (checked, room) => { + if (checked) { + selectedToAdd.add(room); + } else { + selectedToAdd.delete(room); + } + setSelectedToAdd(new Set(selectedToAdd)); + } : null; + + return
+ + + { rooms.length > 0 ? ( +
+

{ _t("Rooms") }

+ { rooms.map(room => { + return { + onChange(checked, room); + } : null} + />; + }) } +
+ ) : undefined } + + { spaces.length > 0 ? ( +
+

{ _t("Spaces") }

+
+
{ _t("Feeling experimental?") }
+
{ _t("You can add existing spaces to a space.") }
+
+ { spaces.map(space => { + return { + onChange(checked, space); + } : null} + />; + }) } +
+ ) : null } + + { dms.length > 0 ? ( +
+

{ _t("Direct Messages") }

+ { dms.map(room => { + return { + onChange(checked, room); + } : null} + />; + }) } +
+ ) : null } + + { spaces.length + rooms.length + dms.length < 1 ? + { _t("No results") } + : undefined } +
+ +
+ { footer } +
+
; +}; + +const AddExistingToSpaceDialog: React.FC = ({ matrixClient: cli, space, onCreateRoomClick, onFinished }) => { + const [selectedSpace, setSelectedSpace] = useState(space); + const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId); + + let spaceOptionSection; + if (existingSubspaces.length > 0) { + const options = [space, ...existingSubspaces].map((space) => { + const classes = classNames("mx_AddExistingToSpaceDialog_dropdownOption", { + mx_AddExistingToSpaceDialog_dropdownOptionActive: space === selectedSpace, + }); + return
+ + { space.name || getDisplayAliasForRoom(space) || space.roomId } +
; + }); + + spaceOptionSection = ( + { + setSelectedSpace(existingSubspaces.find(space => space.roomId === key) || space); + }} + value={selectedSpace.roomId} + label={_t("Space selection")} + > + { options } + + ); + } else { + spaceOptionSection =
+ { space.name || getDisplayAliasForRoom(space) || space.roomId } +
; + } + + const title = + +
+

{ _t("Add existing rooms") }

+ { spaceOptionSection } +
+
; + return = ({ matrixClient: cli, space, { - if (checked) { - selectedToAdd.add(room); - } else { - selectedToAdd.delete(room); - } - setSelectedToAdd(new Set(selectedToAdd)); - } : null} + onFinished={onFinished} + footerPrompt={<> +
{ _t("Want to add a new room instead?") }
+ onCreateRoomClick(cli, space)} kind="link"> + { _t("Create a new room") } + + } />
-
- { footer } -
onFinished(false)} />
; }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c2176d79a8..7e54c69128 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2033,15 +2033,15 @@ "Add a new server...": "Add a new server...", "%(networkName)s rooms": "%(networkName)s rooms", "Matrix rooms": "Matrix rooms", + "Not all selected were added": "Not all selected were added", + "Adding rooms... (%(progress)s out of %(count)s)|other": "Adding rooms... (%(progress)s out of %(count)s)", + "Adding rooms... (%(progress)s out of %(count)s)|one": "Adding room...", "Filter your rooms and spaces": "Filter your rooms and spaces", "Feeling experimental?": "Feeling experimental?", "You can add existing spaces to a space.": "You can add existing spaces to a space.", "Direct Messages": "Direct Messages", "Space selection": "Space selection", "Add existing rooms": "Add existing rooms", - "Not all selected were added": "Not all selected were added", - "Adding rooms... (%(progress)s out of %(count)s)|other": "Adding rooms... (%(progress)s out of %(count)s)", - "Adding rooms... (%(progress)s out of %(count)s)|one": "Adding room...", "Want to add a new room instead?": "Want to add a new room instead?", "Create a new room": "Create a new room", "Matrix ID": "Matrix ID", @@ -2704,8 +2704,6 @@ "Failed to create initial space rooms": "Failed to create initial space rooms", "Skip for now": "Skip for now", "Creating rooms...": "Creating rooms...", - "Failed to add rooms to space": "Failed to add rooms to space", - "Adding...": "Adding...", "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",