Files
element-web/src/components/views/dialogs/SetEmailDialog.tsx
T
Richard van der Hoff f25fbdebc7 Modal: remove support for onFinished callback (#29852)
* Fix up type for `finished` result of Modal

The `finished` promise can be called with an empty array, for example if the
dialog is closed by a background click. This was not correctly represented in
the typing. Fix that, and add some documentation while we're at it.

* Type fixes to onFinished callbacks from Modal

These can all be called with zero arguments, despite what the type annotations
may say, so mark them accordingly.

* Remove uses of Modal `onFinished` property

... because it is confusing.

Instead, use the `finished` promise returned by `createDialog`.

* Modal: remove support for now-unused `onFinished` prop

* StopGapWidgetDriver: use `await` instead of promise chaining

* Fix up unit tests
2025-04-30 16:56:21 +01:00

173 lines
6.0 KiB
TypeScript

/*
Copyright 2018-2024 New Vector Ltd.
Copyright 2017 Vector Creations 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 { logger } from "matrix-js-sdk/src/logger";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import * as Email from "../../../email";
import AddThreepid from "../../../AddThreepid";
import { _t, UserFriendlyError } from "../../../languageHandler";
import Modal from "../../../Modal";
import Spinner from "../elements/Spinner";
import ErrorDialog, { extractErrorMessageFromError } from "./ErrorDialog";
import QuestionDialog from "./QuestionDialog";
import BaseDialog from "./BaseDialog";
import EditableText from "../elements/EditableText";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
interface IProps {
title: string;
onFinished(ok?: boolean): void;
}
interface IState {
emailAddress: string;
emailBusy: boolean;
}
/*
* Prompt the user to set an email address.
*
* On success, `onFinished(true)` is called.
*/
export default class SetEmailDialog extends React.Component<IProps, IState> {
private addThreepid?: AddThreepid;
public constructor(props: IProps) {
super(props);
this.state = {
emailAddress: "",
emailBusy: false,
};
}
private onEmailAddressChanged = (value: string): void => {
this.setState({
emailAddress: value,
});
};
private onSubmit = (): void => {
const emailAddress = this.state.emailAddress;
if (!Email.looksValid(emailAddress)) {
Modal.createDialog(ErrorDialog, {
title: _t("settings|general|error_invalid_email"),
description: _t("settings|general|error_invalid_email_detail"),
});
return;
}
this.addThreepid = new AddThreepid(MatrixClientPeg.safeGet());
this.addThreepid.addEmailAddress(emailAddress).then(
() => {
const { finished } = Modal.createDialog(QuestionDialog, {
title: _t("auth|set_email|verification_pending_title"),
description: _t("auth|set_email|verification_pending_description"),
button: _t("action|continue"),
});
finished.then(([ok]) => this.onEmailDialogFinished(ok));
},
(err) => {
this.setState({ emailBusy: false });
logger.error("Unable to add email address " + emailAddress + " " + err);
Modal.createDialog(ErrorDialog, {
title: _t("settings|general|error_add_email"),
description: extractErrorMessageFromError(err, _t("invite|failed_generic")),
});
},
);
this.setState({ emailBusy: true });
};
private onCancelled = (): void => {
this.props.onFinished(false);
};
private onEmailDialogFinished = (ok?: boolean): void => {
if (ok) {
this.verifyEmailAddress();
} else {
this.setState({ emailBusy: false });
}
};
private verifyEmailAddress(): void {
this.addThreepid?.checkEmailLinkClicked().then(
() => {
this.props.onFinished(true);
},
(err) => {
this.setState({ emailBusy: false });
let underlyingError = err;
if (err instanceof UserFriendlyError) {
underlyingError = err.cause;
}
if (underlyingError instanceof MatrixError && underlyingError.errcode === "M_THREEPID_AUTH_FAILED") {
const message =
_t("settings|general|error_email_verification") +
" " +
_t("auth|set_email|verification_pending_description");
const { finished } = Modal.createDialog(QuestionDialog, {
title: _t("auth|set_email|verification_pending_title"),
description: message,
button: _t("action|continue"),
});
finished.then(([ok]) => this.onEmailDialogFinished(ok));
} else {
logger.error("Unable to verify email address: " + err);
Modal.createDialog(ErrorDialog, {
title: _t("settings|general|error_email_verification"),
description: extractErrorMessageFromError(err, _t("invite|failed_generic")),
});
}
},
);
}
public render(): React.ReactNode {
const emailInput = this.state.emailBusy ? (
<Spinner />
) : (
<EditableText
initialValue={this.state.emailAddress}
className="mx_SetEmailDialog_email_input"
placeholder={_t("common|email_address")}
placeholderClassName="mx_SetEmailDialog_email_input_placeholder"
blurToCancel={false}
onValueChanged={this.onEmailAddressChanged}
/>
);
return (
<BaseDialog
className="mx_SetEmailDialog"
onFinished={this.onCancelled}
title={this.props.title}
contentId="mx_Dialog_content"
>
<div className="mx_Dialog_content">
<p id="mx_Dialog_content">{_t("auth|set_email|description")}</p>
{emailInput}
</div>
<div className="mx_Dialog_buttons">
<input
className="mx_Dialog_primary"
type="submit"
value={_t("action|continue")}
onClick={this.onSubmit}
/>
<input type="submit" value={_t("action|skip")} onClick={this.onCancelled} />
</div>
</BaseDialog>
);
}
}