Show a "progress" dialog while invites are being sent (#30561)
* InviteDialog: show some words and a spinner while invites are being sent * MultiInviter-test: avoid building unhandled rejected promises If we don't handle rejected promises, jest gets confused by them. Instead, let's create them on-demand. * Open a "progress" dialog while invites are being sent * Inhibit invite progress dialog when RoomUpgradeWarning dialog is kept open ... otherwise the `RoomUpgradeWarning` dialog disappears during the invites, and the tests that assert that it is showing the correct thing fail. enter the commit message for your changes. Lines starting * Switch to compound CSS variables instead of old pcss vars * update playwright screenshots * Revert "update playwright screenshots" This reverts commit b0a15d97f35a088fe5b67009085eab46be1316fd. * Another go at updating screenshots * Address review comments * remove redundant Props
This commit is contained in:
@@ -40,7 +40,6 @@ import Field from "../elements/Field";
|
||||
import TabbedView, { Tab, TabLocation } from "../../structures/TabbedView";
|
||||
import Dialpad from "../voip/DialPad";
|
||||
import QuestionDialog from "./QuestionDialog";
|
||||
import Spinner from "../elements/Spinner";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialPadBackspaceButton from "../elements/DialPadBackspaceButton";
|
||||
import LegacyCallHandler from "../../../LegacyCallHandler";
|
||||
@@ -65,6 +64,7 @@ import { UNKNOWN_PROFILE_ERRORS } from "../../../utils/MultiInviter";
|
||||
import AskInviteAnywayDialog, { type UnknownProfiles } from "./AskInviteAnywayDialog";
|
||||
import { SdkContextClass } from "../../../contexts/SDKContext";
|
||||
import { type UserProfilesStore } from "../../../stores/UserProfilesStore";
|
||||
import InviteProgressBody from "./InviteProgressBody.tsx";
|
||||
|
||||
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
|
||||
/* eslint-disable camelcase */
|
||||
@@ -329,8 +329,14 @@ interface IInviteDialogState {
|
||||
dialPadValue: string;
|
||||
currentTabId: TabId;
|
||||
|
||||
// These two flags are used for the 'Go' button to communicate what is going on.
|
||||
/**
|
||||
* True if we are sending the invites.
|
||||
*
|
||||
* We will grey out the action button, hide the suggestions, and display a spinner.
|
||||
*/
|
||||
busy: boolean;
|
||||
|
||||
/** Error from the last attempt to send invites. */
|
||||
errorText?: string;
|
||||
}
|
||||
|
||||
@@ -617,7 +623,10 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await inviteMultipleToRoom(cli, this.props.roomId, targetIds);
|
||||
const result = await inviteMultipleToRoom(cli, this.props.roomId, targetIds, {
|
||||
// We show our own progress body, so don't pop up a separate dialog.
|
||||
inhibitProgressDialog: true,
|
||||
});
|
||||
if (!this.shouldAbortAfterInviteError(result, room)) {
|
||||
// handles setting error message too
|
||||
this.props.onFinished(true);
|
||||
@@ -1328,11 +1337,6 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
||||
* "CallTransfer" one.
|
||||
*/
|
||||
private renderMainTab(): JSX.Element {
|
||||
let spinner: JSX.Element | undefined;
|
||||
if (this.state.busy) {
|
||||
spinner = <Spinner w={20} h={20} />;
|
||||
}
|
||||
|
||||
let helpText;
|
||||
let buttonText;
|
||||
let goButtonFn: (() => Promise<void>) | null = null;
|
||||
@@ -1437,12 +1441,9 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
||||
<p className="mx_InviteDialog_helpText">{helpText}</p>
|
||||
<div className="mx_InviteDialog_addressBar">
|
||||
{this.renderEditor()}
|
||||
<div className="mx_InviteDialog_buttonAndSpinner">
|
||||
{goButton}
|
||||
{spinner}
|
||||
</div>
|
||||
{goButton}
|
||||
</div>
|
||||
{this.renderSuggestions()}
|
||||
{this.state.busy ? <InviteProgressBody /> : this.renderSuggestions()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
import InlineSpinner from "../elements/InlineSpinner";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
/** The common body of components that show the progress of sending room invites. */
|
||||
const InviteProgressBody: React.FC = () => {
|
||||
return (
|
||||
<div className="mx_InviteProgressBody">
|
||||
<InlineSpinner w={32} h={32} />
|
||||
<h1>{_t("invite|progress|preparing")}</h1>
|
||||
{_t("invite|progress|dont_close")}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InviteProgressBody;
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2025 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
import Modal from "../../../Modal.tsx";
|
||||
import InviteProgressBody from "./InviteProgressBody.tsx";
|
||||
|
||||
/** A Modal dialog that pops up while room invites are being sent. */
|
||||
const InviteProgressDialog: React.FC = (_) => {
|
||||
return <InviteProgressBody />;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the invite progress dialog.
|
||||
*
|
||||
* Returns a callback which will close the dialog again.
|
||||
*/
|
||||
export function openInviteProgressDialog(): () => void {
|
||||
const onBeforeClose = async (reason?: string): Promise<boolean> => {
|
||||
// Inhibit closing via background click
|
||||
return reason != "backgroundClick";
|
||||
};
|
||||
|
||||
const { close } = Modal.createDialog(
|
||||
InviteProgressDialog,
|
||||
/* props */ {},
|
||||
/* className */ undefined,
|
||||
/* isPriorityModal */ false,
|
||||
/* isStaticModal */ false,
|
||||
{ onBeforeClose },
|
||||
);
|
||||
return close;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import Modal from "../../../Modal";
|
||||
import ManageRestrictedJoinRuleDialog from "../dialogs/ManageRestrictedJoinRuleDialog";
|
||||
import RoomUpgradeWarningDialog, { type IFinishedOpts } from "../dialogs/RoomUpgradeWarningDialog";
|
||||
import { upgradeRoom } from "../../../utils/RoomUpgrade";
|
||||
import { type RoomUpgradeProgress, upgradeRoom } from "../../../utils/RoomUpgrade";
|
||||
import { arrayHasDiff } from "../../../utils/arrays";
|
||||
import { useLocalEcho } from "../../../hooks/useLocalEcho";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
@@ -120,7 +120,7 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
|
||||
opts: IFinishedOpts,
|
||||
fn: (progressText: string, progress: number, total: number) => void,
|
||||
): Promise<void> => {
|
||||
const roomId = await upgradeRoom(room, targetVersion, opts.invite, true, true, true, (progress) => {
|
||||
const progressCallback = (progress: RoomUpgradeProgress): void => {
|
||||
const total = 2 + progress.updateSpacesTotal + progress.inviteUsersTotal;
|
||||
if (!progress.roomUpgraded) {
|
||||
fn(_t("room_settings|security|join_rule_upgrade_upgrading_room"), 0, total);
|
||||
@@ -151,7 +151,20 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
|
||||
total,
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
const roomId = await upgradeRoom(
|
||||
room,
|
||||
targetVersion,
|
||||
opts.invite,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
progressCallback,
|
||||
|
||||
// We want to keep the RoomUpgradeDialog open during the upgrade, so don't replace it with the
|
||||
// invite progress dialog.
|
||||
/* inhibitInviteProgressDialog: */ true,
|
||||
);
|
||||
|
||||
closeSettingsFn?.();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user