-
-
Notifications
You must be signed in to change notification settings - Fork 829
Properly translate errors in ChangePassword.tsx
so they show up translated to the user but not in our logs
#10615
Changes from all commits
32ece81
07a0a74
c62c6c9
7786dd1
a7c2fe4
772c15d
329b868
93f238b
e0fbd73
1f8bd91
fc70a22
798bb19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,7 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg"; | |
import AccessibleButton from "../elements/AccessibleButton"; | ||
import Spinner from "../elements/Spinner"; | ||
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation"; | ||
import { _t, _td } from "../../../languageHandler"; | ||
import { UserFriendlyError, _t, _td } from "../../../languageHandler"; | ||
import Modal from "../../../Modal"; | ||
import PassphraseField from "../auth/PassphraseField"; | ||
import { PASSWORD_MIN_SCORE } from "../auth/RegistrationForm"; | ||
|
@@ -48,7 +48,7 @@ interface IProps { | |
/** Was one or more other devices logged out whilst changing the password */ | ||
didLogoutOutOtherDevices: boolean; | ||
}) => void; | ||
onError: (error: { error: string }) => void; | ||
onError: (error: Error) => void; | ||
rowClassName?: string; | ||
buttonClassName?: string; | ||
buttonKind?: string; | ||
|
@@ -183,7 +183,16 @@ export default class ChangePassword extends React.Component<IProps, IState> { | |
} | ||
}, | ||
(err) => { | ||
this.props.onError(err); | ||
if (err instanceof Error) { | ||
this.props.onError(err); | ||
} else { | ||
this.props.onError( | ||
new UserFriendlyError("Error while changing password: %(error)s", { | ||
error: String(err), | ||
cause: undefined, | ||
}), | ||
); | ||
} | ||
}, | ||
) | ||
.finally(() => { | ||
|
@@ -196,15 +205,19 @@ export default class ChangePassword extends React.Component<IProps, IState> { | |
}); | ||
} | ||
|
||
private checkPassword(oldPass: string, newPass: string, confirmPass: string): { error: string } | undefined { | ||
/** | ||
* Checks the `newPass` and throws an error if it is unacceptable. | ||
* @param oldPass The old password | ||
* @param newPass The new password that the user is trying to be set | ||
* @param confirmPass The confirmation password where the user types the `newPass` | ||
* again for confirmation and should match the `newPass` before we accept their new | ||
* password. | ||
*/ | ||
private checkPassword(oldPass: string, newPass: string, confirmPass: string): void { | ||
if (newPass !== confirmPass) { | ||
return { | ||
error: _t("New passwords don't match"), | ||
}; | ||
throw new UserFriendlyError("New passwords don't match"); | ||
} else if (!newPass || newPass.length === 0) { | ||
return { | ||
error: _t("Passwords can't be empty"), | ||
}; | ||
throw new UserFriendlyError("Passwords can't be empty"); | ||
} | ||
} | ||
|
||
|
@@ -307,11 +320,24 @@ export default class ChangePassword extends React.Component<IProps, IState> { | |
const oldPassword = this.state.oldPassword; | ||
const newPassword = this.state.newPassword; | ||
const confirmPassword = this.state.newPasswordConfirm; | ||
const err = this.checkPassword(oldPassword, newPassword, confirmPassword); | ||
MadLittleMods marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (err) { | ||
this.props.onError(err); | ||
} else { | ||
try { | ||
// TODO: We can remove this check (but should add some Cypress tests to | ||
// sanity check this flow). This logic is redundant with the input field | ||
// validation we do and `verifyFieldsBeforeSubmit()` above. See | ||
// https://github.com/matrix-org/matrix-react-sdk/pull/10615#discussion_r1167364214 | ||
this.checkPassword(oldPassword, newPassword, confirmPassword); | ||
MadLittleMods marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return this.onChangePassword(oldPassword, newPassword); | ||
} catch (err) { | ||
if (err instanceof Error) { | ||
this.props.onError(err); | ||
} else { | ||
this.props.onError( | ||
new UserFriendlyError("Error while changing password: %(error)s", { | ||
error: String(err), | ||
cause: undefined, | ||
}), | ||
); | ||
} | ||
Comment on lines
+330
to
+340
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't quite see this yet. I think the problem here arises more because I'm having a hard time imagining how we can handle this generically vs actually wanting our user friendly error message for a given case. Or maybe I'm just blind to what you're thinking and need an actual example 🙏. It's definitely possible there are better patterns left on the table here though and this just hasn't matured enough. (feel free to continue conversation after merge) |
||
} | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,9 +21,9 @@ import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types"; | |
import { IThreepid } from "matrix-js-sdk/src/@types/threepids"; | ||
import { logger } from "matrix-js-sdk/src/logger"; | ||
import { IDelegatedAuthConfig, M_AUTHENTICATION } from "matrix-js-sdk/src/matrix"; | ||
import { MatrixError } from "matrix-js-sdk/src/matrix"; | ||
import { HTTPError } from "matrix-js-sdk/src/matrix"; | ||
|
||
import { _t } from "../../../../../languageHandler"; | ||
import { UserFriendlyError, _t } from "../../../../../languageHandler"; | ||
import ProfileSettings from "../../ProfileSettings"; | ||
import * as languageHandler from "../../../../../languageHandler"; | ||
import SettingsStore from "../../../../../settings/SettingsStore"; | ||
|
@@ -43,7 +43,7 @@ import Spinner from "../../../elements/Spinner"; | |
import { SettingLevel } from "../../../../../settings/SettingLevel"; | ||
import { UIFeature } from "../../../../../settings/UIFeature"; | ||
import { ActionPayload } from "../../../../../dispatcher/payloads"; | ||
import ErrorDialog from "../../../dialogs/ErrorDialog"; | ||
import ErrorDialog, { extractErrorMessageFromError } from "../../../dialogs/ErrorDialog"; | ||
import AccountPhoneNumbers from "../../account/PhoneNumbers"; | ||
import AccountEmailAddresses from "../../account/EmailAddresses"; | ||
import DiscoveryEmailAddresses from "../../discovery/EmailAddresses"; | ||
|
@@ -253,18 +253,35 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta | |
PlatformPeg.get()?.setSpellCheckEnabled(spellCheckEnabled); | ||
}; | ||
|
||
private onPasswordChangeError = (err: { error: string } & MatrixError): void => { | ||
// TODO: Figure out a design that doesn't involve replacing the current dialog | ||
let errMsg = err.error || err.message || ""; | ||
if (err.httpStatus === 403) { | ||
errMsg = _t("Failed to change password. Is your password correct?"); | ||
} else if (!errMsg) { | ||
errMsg += ` (HTTP status ${err.httpStatus})`; | ||
private onPasswordChangeError = (err: Error): void => { | ||
logger.error("Failed to change password: " + err); | ||
|
||
let underlyingError = err; | ||
if (err instanceof UserFriendlyError && err.cause instanceof Error) { | ||
underlyingError = err.cause; | ||
} | ||
logger.error("Failed to change password: " + errMsg); | ||
|
||
const errorMessage = extractErrorMessageFromError( | ||
err, | ||
_t("Unknown password change error (%(stringifiedError)s)", { | ||
stringifiedError: String(err), | ||
}), | ||
); | ||
|
||
let errorMessageToDisplay = errorMessage; | ||
if (underlyingError instanceof HTTPError && underlyingError.httpStatus === 403) { | ||
errorMessageToDisplay = _t("Failed to change password. Is your password correct?"); | ||
} else if (underlyingError instanceof HTTPError) { | ||
errorMessageToDisplay = _t("%(errorMessage)s (HTTP status %(httpStatus)s)", { | ||
errorMessage, | ||
httpStatus: underlyingError.httpStatus, | ||
}); | ||
} | ||
|
||
// TODO: Figure out a design that doesn't involve replacing the current dialog | ||
Modal.createDialog(ErrorDialog, { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remaining strict Typescript failures are unlrelated to this PR and the problem is generally tracked by element-hq/element-web#24899 |
||
title: _t("Error"), | ||
description: errMsg, | ||
title: _t("Error changing password"), | ||
description: errorMessageToDisplay, | ||
}); | ||
}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SonarCloud coverage not happy with these files not tested at all but it's not something I'm going to invest in now.
Will need an exception here