Properly translate errors in AddThreepid.ts (#10432)

* Properly translate errors in AddThreepid.ts

Part of https://github.com/vector-im/element-web/issues/9597

* Use translated message

* Avoid returning undefined ever

* More usage

* Introduce UserFriendlyError

* Use UserFriendlyError

* Add more usage instead of normal error

* Use types and translatedMessage

* Fix lints

* Update i18n although it's wrong

* Use unknown for easier creation from try/catch

* Use types

* Use error types

* Use types

* Update i18n strings

* Remove generic re-label of HTTPError

See https://github.com/matrix-org/matrix-react-sdk/pull/10432#discussion_r1156468143

The HTTPError already has a good label and it isn't even translated if we re-label it here in this way generically

Probably best to just remove in favor of thinking about a translations in general from the `matrix-js-sdk`, see https://github.com/matrix-org/matrix-js-sdk/issues/1309

* Make error message extraction generic

* Update i18n strings

* Add tests for email addresses

* More consistent error logging to actually see error in logs

* Consistent error handling

* Any is okay because we have a fallback

* Check error type

* Use dedicated mockResolvedValue function

See https://github.com/matrix-org/matrix-react-sdk/pull/10432#discussion_r1163344034
This commit is contained in:
Eric Eastwood 2023-04-14 09:40:19 -05:00 committed by GitHub
parent 8f8b74b32c
commit c1e7905ddc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 300 additions and 132 deletions

View file

@ -27,9 +27,26 @@ limitations under the License.
import React from "react";
import { _t } from "../../../languageHandler";
import { _t, UserFriendlyError } from "../../../languageHandler";
import BaseDialog from "./BaseDialog";
/**
* Get a user friendly error message string from a given error. Useful for the
* `description` prop of the `ErrorDialog`
* @param err Error object in question to extract a useful message from. To make it easy
* to use with try/catch, this is typed as `any` because try/catch will type
* the error as `unknown`. And in any case we can use the fallback message.
* @param translatedFallbackMessage The fallback message to be used if the error doesn't have any message
* @returns a user friendly error message string from a given error
*/
export function extractErrorMessageFromError(err: any, translatedFallbackMessage: string): string {
return (
(err instanceof UserFriendlyError && err.translatedMessage) ||
(err instanceof Error && err.message) ||
translatedFallbackMessage
);
}
interface IProps {
onFinished: (success?: boolean) => void;
title?: string;

View file

@ -17,13 +17,14 @@ limitations under the License.
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 } from "../../../languageHandler";
import { _t, UserFriendlyError } from "../../../languageHandler";
import Modal from "../../../Modal";
import Spinner from "../elements/Spinner";
import ErrorDialog from "./ErrorDialog";
import ErrorDialog, { extractErrorMessageFromError } from "./ErrorDialog";
import QuestionDialog from "./QuestionDialog";
import BaseDialog from "./BaseDialog";
import EditableText from "../elements/EditableText";
@ -88,7 +89,7 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
logger.error("Unable to add email address " + emailAddress + " " + err);
Modal.createDialog(ErrorDialog, {
title: _t("Unable to add email address"),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
},
);
@ -114,7 +115,13 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
},
(err) => {
this.setState({ emailBusy: false });
if (err.errcode == "M_THREEPID_AUTH_FAILED") {
let underlyingError = err;
if (err instanceof UserFriendlyError) {
underlyingError = err.cause;
}
if (underlyingError instanceof MatrixError && underlyingError.errcode === "M_THREEPID_AUTH_FAILED") {
const message =
_t("Unable to verify email address.") +
" " +
@ -131,7 +138,7 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
logger.error("Unable to verify email address: " + err);
Modal.createDialog(ErrorDialog, {
title: _t("Unable to verify email address."),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
},

View file

@ -18,15 +18,16 @@ limitations under the License.
import React from "react";
import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../languageHandler";
import { _t, UserFriendlyError } from "../../../../languageHandler";
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import Field from "../../elements/Field";
import AccessibleButton from "../../elements/AccessibleButton";
import * as Email from "../../../../email";
import AddThreepid from "../../../../AddThreepid";
import Modal from "../../../../Modal";
import ErrorDialog from "../../dialogs/ErrorDialog";
import ErrorDialog, { extractErrorMessageFromError } from "../../dialogs/ErrorDialog";
/*
TODO: Improve the UX for everything in here.
@ -190,7 +191,7 @@ export default class EmailAddresses extends React.Component<IProps, IState> {
this.setState({ verifying: false, continueDisabled: false, addTask: null });
Modal.createDialog(ErrorDialog, {
title: _t("Unable to add email address"),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
});
};
@ -218,8 +219,16 @@ export default class EmailAddresses extends React.Component<IProps, IState> {
});
})
.catch((err) => {
logger.error("Unable to verify email address: ", err);
this.setState({ continueDisabled: false });
if (err.errcode === "M_THREEPID_AUTH_FAILED") {
let underlyingError = err;
if (err instanceof UserFriendlyError) {
underlyingError = err.cause;
}
if (underlyingError instanceof MatrixError && underlyingError.errcode === "M_THREEPID_AUTH_FAILED") {
Modal.createDialog(ErrorDialog, {
title: _t("Your email address hasn't been verified yet"),
description: _t(
@ -227,10 +236,9 @@ export default class EmailAddresses extends React.Component<IProps, IState> {
),
});
} else {
logger.error("Unable to verify email address: ", err);
Modal.createDialog(ErrorDialog, {
title: _t("Unable to verify email address."),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
});

View file

@ -19,14 +19,14 @@ import React from "react";
import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids";
import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../../../languageHandler";
import { _t, UserFriendlyError } from "../../../../languageHandler";
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import Field from "../../elements/Field";
import AccessibleButton from "../../elements/AccessibleButton";
import AddThreepid from "../../../../AddThreepid";
import CountryDropdown from "../../auth/CountryDropdown";
import Modal from "../../../../Modal";
import ErrorDialog from "../../dialogs/ErrorDialog";
import ErrorDialog, { extractErrorMessageFromError } from "../../dialogs/ErrorDialog";
import { PhoneNumberCountryDefinition } from "../../../../phonenumber";
/*
@ -81,7 +81,7 @@ export class ExistingPhoneNumber extends React.Component<IExistingPhoneNumberPro
logger.error("Unable to remove contact information: " + err);
Modal.createDialog(ErrorDialog, {
title: _t("Unable to remove contact information"),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
});
};
@ -192,7 +192,7 @@ export default class PhoneNumbers extends React.Component<IProps, IState> {
this.setState({ verifying: false, continueDisabled: false, addTask: null });
Modal.createDialog(ErrorDialog, {
title: _t("Error"),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
});
};
@ -224,12 +224,18 @@ export default class PhoneNumbers extends React.Component<IProps, IState> {
});
})
.catch((err) => {
logger.error("Unable to verify phone number: " + err);
this.setState({ continueDisabled: false });
if (err.errcode !== "M_THREEPID_AUTH_FAILED") {
logger.error("Unable to verify phone number: " + err);
let underlyingError = err;
if (err instanceof UserFriendlyError) {
underlyingError = err.cause;
}
if (underlyingError.errcode !== "M_THREEPID_AUTH_FAILED") {
Modal.createDialog(ErrorDialog, {
title: _t("Unable to verify phone number."),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
} else {
this.setState({ verifyError: _t("Incorrect verification code") });

View file

@ -18,12 +18,13 @@ limitations under the License.
import React from "react";
import { IThreepid } from "matrix-js-sdk/src/@types/threepids";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../languageHandler";
import { _t, UserFriendlyError } from "../../../../languageHandler";
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import Modal from "../../../../Modal";
import AddThreepid, { Binding } from "../../../../AddThreepid";
import ErrorDialog from "../../dialogs/ErrorDialog";
import ErrorDialog, { extractErrorMessageFromError } from "../../dialogs/ErrorDialog";
import AccessibleButton from "../../elements/AccessibleButton";
/*
@ -98,7 +99,7 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
}
this.setState({ bound: bind });
} catch (err) {
logger.error(`Unable to ${label} email address ${address} ${err}`);
logger.error(`changeBinding: Unable to ${label} email address ${address}`, err);
this.setState({
verifying: false,
continueDisabled: false,
@ -106,7 +107,7 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
});
Modal.createDialog(ErrorDialog, {
title: errorTitle,
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
}
@ -133,7 +134,7 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
bound: bind,
});
} catch (err) {
logger.error(`Unable to ${label} email address ${address} ${err}`);
logger.error(`changeBindingTangledAddBind: Unable to ${label} email address ${address}`, err);
this.setState({
verifying: false,
continueDisabled: false,
@ -141,7 +142,7 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
});
Modal.createDialog(ErrorDialog, {
title: errorTitle,
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
}
@ -170,17 +171,23 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
e.stopPropagation();
e.preventDefault();
// Prevent the continue button from being pressed multiple times while we're working
this.setState({ continueDisabled: true });
try {
await this.state.addTask?.checkEmailLinkClicked();
this.setState({
addTask: null,
continueDisabled: false,
verifying: false,
});
} catch (err) {
this.setState({ continueDisabled: false });
if (err.errcode === "M_THREEPID_AUTH_FAILED") {
logger.error(`Unable to verify email address:`, err);
let underlyingError = err;
if (err instanceof UserFriendlyError) {
underlyingError = err.cause;
}
if (underlyingError instanceof MatrixError && underlyingError.errcode === "M_THREEPID_AUTH_FAILED") {
Modal.createDialog(ErrorDialog, {
title: _t("Your email address hasn't been verified yet"),
description: _t(
@ -191,9 +198,12 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
logger.error("Unable to verify email address: " + err);
Modal.createDialog(ErrorDialog, {
title: _t("Unable to verify email address."),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
} finally {
// Re-enable the continue button so the user can retry
this.setState({ continueDisabled: false });
}
};

View file

@ -18,12 +18,13 @@ limitations under the License.
import React from "react";
import { IThreepid } from "matrix-js-sdk/src/@types/threepids";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../../languageHandler";
import { _t, UserFriendlyError } from "../../../../languageHandler";
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import Modal from "../../../../Modal";
import AddThreepid, { Binding } from "../../../../AddThreepid";
import ErrorDialog from "../../dialogs/ErrorDialog";
import ErrorDialog, { extractErrorMessageFromError } from "../../dialogs/ErrorDialog";
import Field from "../../elements/Field";
import AccessibleButton from "../../elements/AccessibleButton";
@ -99,7 +100,7 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
}
this.setState({ bound: bind });
} catch (err) {
logger.error(`Unable to ${label} phone number ${address} ${err}`);
logger.error(`changeBinding: Unable to ${label} phone number ${address}`, err);
this.setState({
verifying: false,
continueDisabled: false,
@ -107,7 +108,7 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
});
Modal.createDialog(ErrorDialog, {
title: errorTitle,
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
}
@ -140,7 +141,7 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
bound: bind,
});
} catch (err) {
logger.error(`Unable to ${label} phone number ${address} ${err}`);
logger.error(`changeBindingTangledAddBind: Unable to ${label} phone number ${address}`, err);
this.setState({
verifying: false,
continueDisabled: false,
@ -148,7 +149,7 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
});
Modal.createDialog(ErrorDialog, {
title: errorTitle,
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
}
@ -195,12 +196,18 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
verificationCode: "",
});
} catch (err) {
logger.error("Unable to verify phone number:", err);
let underlyingError = err;
if (err instanceof UserFriendlyError) {
underlyingError = err.cause;
}
this.setState({ continueDisabled: false });
if (err.errcode !== "M_THREEPID_AUTH_FAILED") {
logger.error("Unable to verify phone number: " + err);
if (underlyingError instanceof MatrixError && underlyingError.errcode !== "M_THREEPID_AUTH_FAILED") {
Modal.createDialog(ErrorDialog, {
title: _t("Unable to verify phone number."),
description: err && err.message ? err.message : _t("Operation failed"),
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
} else {
this.setState({ verifyError: _t("Incorrect verification code") });