You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-03 00:33:22 +03:00 
			
		
		
		
	Add retry mechanism and progress bar to add existing to space dialog
This commit is contained in:
		@@ -101,7 +101,7 @@ limitations under the License.
 | 
			
		||||
 | 
			
		||||
        .mx_BaseAvatar {
 | 
			
		||||
            display: inline-flex;
 | 
			
		||||
            margin: 5px 16px 5px 5px;
 | 
			
		||||
            margin: auto 16px auto 5px;
 | 
			
		||||
            vertical-align: middle;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -160,31 +160,32 @@ limitations under the License.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .mx_AddExistingToSpaceDialog_errorText {
 | 
			
		||||
        font-weight: $font-semi-bold;
 | 
			
		||||
        font-size: $font-12px;
 | 
			
		||||
        line-height: $font-15px;
 | 
			
		||||
        color: $notice-primary-color;
 | 
			
		||||
        margin-bottom: 28px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .mx_AddExistingToSpace {
 | 
			
		||||
        display: contents;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .mx_AddExistingToSpaceDialog_footer {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        margin-top: 32px;
 | 
			
		||||
        margin-top: 20px;
 | 
			
		||||
 | 
			
		||||
        > span {
 | 
			
		||||
            flex-grow: 1;
 | 
			
		||||
            font-size: $font-14px;
 | 
			
		||||
            font-size: $font-12px;
 | 
			
		||||
            line-height: $font-15px;
 | 
			
		||||
            font-weight: $font-semi-bold;
 | 
			
		||||
            color: $secondary-fg-color;
 | 
			
		||||
 | 
			
		||||
            .mx_AccessibleButton {
 | 
			
		||||
                font-size: inherit;
 | 
			
		||||
                display: inline-block;
 | 
			
		||||
            .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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            > * {
 | 
			
		||||
@@ -192,8 +193,54 @@ limitations under the License.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,12 +29,13 @@ import RoomAvatar from "../avatars/RoomAvatar";
 | 
			
		||||
import {getDisplayAliasForRoom} from "../../../Rooms";
 | 
			
		||||
import AccessibleButton from "../elements/AccessibleButton";
 | 
			
		||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
 | 
			
		||||
import {allSettled} from "../../../utils/promise";
 | 
			
		||||
import {sleep} from "../../../utils/promise";
 | 
			
		||||
import DMRoomMap from "../../../utils/DMRoomMap";
 | 
			
		||||
import {calculateRoomVia} from "../../../utils/permalinks/Permalinks";
 | 
			
		||||
import StyledCheckbox from "../elements/StyledCheckbox";
 | 
			
		||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
 | 
			
		||||
import {sortRooms} from "../../../stores/room-list/algorithms/tag-sorting/RecentAlgorithm";
 | 
			
		||||
import ProgressBar from "../elements/ProgressBar";
 | 
			
		||||
 | 
			
		||||
interface IProps extends IDialogProps {
 | 
			
		||||
    matrixClient: MatrixClient;
 | 
			
		||||
@@ -46,7 +47,11 @@ const Entry = ({ room, checked, onChange }) => {
 | 
			
		||||
    return <label className="mx_AddExistingToSpace_entry">
 | 
			
		||||
        <RoomAvatar room={room} height={32} width={32} />
 | 
			
		||||
        <span className="mx_AddExistingToSpace_entry_name">{ room.name }</span>
 | 
			
		||||
        <StyledCheckbox onChange={(e) => onChange(e.target.checked)} checked={checked} />
 | 
			
		||||
        <StyledCheckbox
 | 
			
		||||
            onChange={onChange ? (e) => onChange(e.target.checked) : null}
 | 
			
		||||
            checked={checked}
 | 
			
		||||
            disabled={!onChange}
 | 
			
		||||
        />
 | 
			
		||||
    </label>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -104,9 +109,9 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space,
 | 
			
		||||
                            key={room.roomId}
 | 
			
		||||
                            room={room}
 | 
			
		||||
                            checked={selected.has(room)}
 | 
			
		||||
                            onChange={(checked) => {
 | 
			
		||||
                            onChange={onChange ? (checked) => {
 | 
			
		||||
                                onChange(checked, room);
 | 
			
		||||
                            }}
 | 
			
		||||
                            } : null}
 | 
			
		||||
                        />;
 | 
			
		||||
                    }) }
 | 
			
		||||
                </div>
 | 
			
		||||
@@ -120,9 +125,9 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space,
 | 
			
		||||
                            key={space.roomId}
 | 
			
		||||
                            room={space}
 | 
			
		||||
                            checked={selected.has(space)}
 | 
			
		||||
                            onChange={(checked) => {
 | 
			
		||||
                            onChange={onChange ? (checked) => {
 | 
			
		||||
                                onChange(checked, space);
 | 
			
		||||
                            }}
 | 
			
		||||
                            } : null}
 | 
			
		||||
                        />;
 | 
			
		||||
                    }) }
 | 
			
		||||
                </div>
 | 
			
		||||
@@ -136,9 +141,9 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({ space,
 | 
			
		||||
                            key={room.roomId}
 | 
			
		||||
                            room={room}
 | 
			
		||||
                            checked={selected.has(room)}
 | 
			
		||||
                            onChange={(checked) => {
 | 
			
		||||
                            onChange={onChange ? (checked) => {
 | 
			
		||||
                                onChange(checked, room);
 | 
			
		||||
                            }}
 | 
			
		||||
                            } : null}
 | 
			
		||||
                        />;
 | 
			
		||||
                    }) }
 | 
			
		||||
                </div>
 | 
			
		||||
@@ -156,8 +161,8 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
 | 
			
		||||
    const existingSubspaces = SpaceStore.instance.getChildSpaces(space.roomId);
 | 
			
		||||
    const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
 | 
			
		||||
 | 
			
		||||
    const [busy, setBusy] = useState(false);
 | 
			
		||||
    const [error, setError] = useState("");
 | 
			
		||||
    const [progress, setProgress] = useState<number>(null);
 | 
			
		||||
    const [error, setError] = useState<Error>(null);
 | 
			
		||||
 | 
			
		||||
    let spaceOptionSection;
 | 
			
		||||
    if (existingSubspaces.length > 0) {
 | 
			
		||||
@@ -197,6 +202,82 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
 | 
			
		||||
        </div>
 | 
			
		||||
    </React.Fragment>;
 | 
			
		||||
 | 
			
		||||
    const addRooms = async () => {
 | 
			
		||||
        setError(null);
 | 
			
		||||
        setProgress(0);
 | 
			
		||||
 | 
			
		||||
        let error;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
                });
 | 
			
		||||
                setProgress(i => i + 1);
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                console.error("Failed to add rooms to space", e);
 | 
			
		||||
                setError(error = e);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!error) {
 | 
			
		||||
            onFinished(true);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const busy = progress !== null;
 | 
			
		||||
 | 
			
		||||
    let footer;
 | 
			
		||||
    if (error) {
 | 
			
		||||
        footer = <>
 | 
			
		||||
            <img
 | 
			
		||||
                src={require("../../../../res/img/element-icons/warning-badge.svg")}
 | 
			
		||||
                height="24"
 | 
			
		||||
                width="24"
 | 
			
		||||
                alt=""
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            <span className="mx_AddExistingToSpaceDialog_error">
 | 
			
		||||
                <div className="mx_AddExistingToSpaceDialog_errorHeading">{ _t("Not all selected were added") }</div>
 | 
			
		||||
                <div className="mx_AddExistingToSpaceDialog_errorCaption">{ _t("Try again") }</div>
 | 
			
		||||
            </span>
 | 
			
		||||
 | 
			
		||||
            <AccessibleButton className="mx_AddExistingToSpaceDialog_retryButton" onClick={addRooms}>
 | 
			
		||||
                { _t("Retry") }
 | 
			
		||||
            </AccessibleButton>
 | 
			
		||||
        </>;
 | 
			
		||||
    } else if (busy) {
 | 
			
		||||
        footer = <span>
 | 
			
		||||
            <ProgressBar value={progress} max={selectedToAdd.size} />
 | 
			
		||||
            <div className="mx_AddExistingToSpaceDialog_progressText">
 | 
			
		||||
                { _t("Adding rooms... (%(progress)s out of %(count)s)", {
 | 
			
		||||
                    count: selectedToAdd.size,
 | 
			
		||||
                    progress,
 | 
			
		||||
                }) }
 | 
			
		||||
            </div>
 | 
			
		||||
        </span>;
 | 
			
		||||
    } else {
 | 
			
		||||
        footer = <>
 | 
			
		||||
            <span>
 | 
			
		||||
                <div>{ _t("Want to add a new room instead?") }</div>
 | 
			
		||||
                <AccessibleButton onClick={() => onCreateRoomClick(cli, space)} kind="link">
 | 
			
		||||
                    { _t("Create a new room") }
 | 
			
		||||
                </AccessibleButton>
 | 
			
		||||
            </span>
 | 
			
		||||
 | 
			
		||||
            <AccessibleButton kind="primary" disabled={selectedToAdd.size < 1} onClick={addRooms}>
 | 
			
		||||
                { _t("Add") }
 | 
			
		||||
            </AccessibleButton>
 | 
			
		||||
        </>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return <BaseDialog
 | 
			
		||||
        title={title}
 | 
			
		||||
        className="mx_AddExistingToSpaceDialog"
 | 
			
		||||
@@ -204,50 +285,23 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ matrixClient: cli, space,
 | 
			
		||||
        onFinished={onFinished}
 | 
			
		||||
        fixedWidth={false}
 | 
			
		||||
    >
 | 
			
		||||
        { error && <div className="mx_AddExistingToSpaceDialog_errorText">{ error }</div> }
 | 
			
		||||
 | 
			
		||||
        <MatrixClientContext.Provider value={cli}>
 | 
			
		||||
            <AddExistingToSpace
 | 
			
		||||
                space={space}
 | 
			
		||||
                selected={selectedToAdd}
 | 
			
		||||
                onChange={(checked, room) => {
 | 
			
		||||
                onChange={!busy && !error ? (checked, room) => {
 | 
			
		||||
                    if (checked) {
 | 
			
		||||
                        selectedToAdd.add(room);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        selectedToAdd.delete(room);
 | 
			
		||||
                    }
 | 
			
		||||
                    setSelectedToAdd(new Set(selectedToAdd));
 | 
			
		||||
                }}
 | 
			
		||||
                } : null}
 | 
			
		||||
            />
 | 
			
		||||
        </MatrixClientContext.Provider>
 | 
			
		||||
 | 
			
		||||
        <div className="mx_AddExistingToSpaceDialog_footer">
 | 
			
		||||
            <span>
 | 
			
		||||
                <div>{ _t("Don't want to add an existing room?") }</div>
 | 
			
		||||
                <AccessibleButton onClick={() => onCreateRoomClick(cli, space)} kind="link">
 | 
			
		||||
                    { _t("Create a new room") }
 | 
			
		||||
                </AccessibleButton>
 | 
			
		||||
            </span>
 | 
			
		||||
 | 
			
		||||
            <AccessibleButton
 | 
			
		||||
                kind="primary"
 | 
			
		||||
                disabled={busy || selectedToAdd.size < 1}
 | 
			
		||||
                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);
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                { busy ? _t("Adding...") : _t("Add") }
 | 
			
		||||
            </AccessibleButton>
 | 
			
		||||
            { footer }
 | 
			
		||||
        </div>
 | 
			
		||||
    </BaseDialog>;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -2033,10 +2033,11 @@
 | 
			
		||||
    "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?",
 | 
			
		||||
    "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",
 | 
			
		||||
    "Failed to add rooms to space": "Failed to add rooms to space",
 | 
			
		||||
    "Adding...": "Adding...",
 | 
			
		||||
    "Matrix ID": "Matrix ID",
 | 
			
		||||
    "Matrix Room ID": "Matrix Room ID",
 | 
			
		||||
    "email address": "email address",
 | 
			
		||||
@@ -2675,6 +2676,8 @@
 | 
			
		||||
    "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",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user