Complete the compounding

This commit is contained in:
Half-Shot
2025-03-13 14:40:00 +00:00
parent 856a35175f
commit e3f43e1060
2 changed files with 30 additions and 31 deletions

View File

@@ -52,8 +52,8 @@ const DEFAULT_PROPS = {
labelAllowedButUnsafe: _td("auth|password_field_weak_label"),
};
const NewPassphraseField: React.FC<IProps> = (props) => {
const { labelEnterPassword, userInputs, minScore, label, labelStrongPassword, labelAllowedButUnsafe, className, id, fieldRef, autoFocus} = {...DEFAULT_PROPS, ...props};
const PassphraseField: React.FC<IProps> = (props) => {
const { labelEnterPassword, userInputs, minScore, label, labelStrongPassword, labelAllowedButUnsafe, className, id, fieldRef, autoFocus, onChange, onValidate} = {...DEFAULT_PROPS, ...props};
const validateFn = useMemo(() => withValidation<{}, ZxcvbnResult | null>({
description: function (complexity) {
const score = complexity ? complexity.score : 0;
@@ -103,12 +103,14 @@ const NewPassphraseField: React.FC<IProps> = (props) => {
const [feedback, setFeedback]= useState<string|JSX.Element>();
const onInputChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((ev) => {
onChange(ev);
validateFn({
value: ev.target.value,
focused: true,
}).then((v) => {
setFeedback(v.feedback);
})
onValidate?.(v);
});
}, [validateFn]);
@@ -119,4 +121,4 @@ const NewPassphraseField: React.FC<IProps> = (props) => {
</Field>
}
export default NewPassphraseField;
export default PassphraseField;

View File

@@ -10,11 +10,10 @@ import React from "react";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import AccessibleButton, { type AccessibleButtonKind } from "../elements/AccessibleButton";
import withValidation, { type IFieldState, type IValidationResult } from "../elements/Validation";
import { UserFriendlyError, _t, _td } from "../../../languageHandler";
import { PASSWORD_MIN_SCORE } from "../auth/RegistrationForm";
import { Root, Field as CpdField, PasswordInput, Label, InlineSpinner, HelpMessage } from "@vector-im/compound-web";
import { Root, Field as CpdField, PasswordInput, Label, InlineSpinner, HelpMessage, Button } from "@vector-im/compound-web";
import PassphraseField from "../auth/PassphraseField";
const FIELD_OLD_PASSWORD = "field_old_password";
@@ -31,8 +30,6 @@ enum Phase {
interface IProps {
onFinished: (outcome: { didSetEmail?: boolean }) => void;
onError: (error: Error) => void;
buttonClassName?: string;
buttonKind?: AccessibleButtonKind;
buttonLabel?: string;
}
@@ -250,26 +247,25 @@ export default class ChangePassword extends React.Component<IProps, IState> {
activeElement.blur();
}
// Run all fields with stricter validation that no longer allows empty
// values for required fields.
await this.onOldPasswordValidate({
value: this[FIELD_OLD_PASSWORD]?.value ?? null,
allowEmpty: false,
focused: true,
});
await this.onNewPasswordConfirmValidate({
value: this[FIELD_NEW_PASSWORD_CONFIRM]?.value ?? null,
allowEmpty: false,
focused: true,
});
const fieldIDsInDisplayOrder: FieldType[] = [
FIELD_OLD_PASSWORD,
FIELD_NEW_PASSWORD,
FIELD_NEW_PASSWORD_CONFIRM,
];
// Run all fields with stricter validation that no longer allows empty
// values for required fields.
for (const fieldID of fieldIDsInDisplayOrder) {
const field = this[fieldID];
if (!field) {
continue;
}
// We must wait for these validations to finish before queueing
// up the setState below so our setState goes in the queue after
// all the setStates from these validate calls (that's how we
// know they've finished).
await field.validate({ allowEmpty: false });
}
// Validation and state updates are async, so we need to wait for them to complete
// first. Queue a `setState` callback and wait for it to resolve.
await new Promise<void>((resolve) => this.setState({}, resolve));
@@ -287,7 +283,8 @@ export default class ChangePassword extends React.Component<IProps, IState> {
// Focus the first invalid field and show feedback in the stricter mode
// that no longer allows empty values for required fields.
invalidField.focus();
invalidField.validate({ allowEmpty: false, focused: true });
// TODO: HMM
// invalidField.validate({ allowEmpty: false, focused: true });
return false;
}
@@ -305,8 +302,6 @@ export default class ChangePassword extends React.Component<IProps, IState> {
}
public render(): React.ReactNode {
const buttonClassName = this.props.buttonClassName;
const { fieldValid, phase } = this.state;
switch (phase) {
@@ -317,7 +312,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
<Label>
{_t("auth|change_password_current_label")}
</Label>
<PasswordInput ref={(field) => (this[FIELD_OLD_PASSWORD] = field)} data-invalid={fieldValid[FIELD_OLD_PASSWORD]?.valid} value={this.state.oldPassword} onChange={this.onChangeOldPassword} />
<PasswordInput ref={(field) => (this[FIELD_OLD_PASSWORD] = field)} data-invalid={fieldValid[FIELD_OLD_PASSWORD]?.valid === false ? true : undefined} value={this.state.oldPassword} onChange={this.onChangeOldPassword} />
{fieldValid[FIELD_OLD_PASSWORD]?.feedback && <HelpMessage>
{fieldValid[FIELD_OLD_PASSWORD]?.feedback}
</HelpMessage>}
@@ -337,18 +332,20 @@ export default class ChangePassword extends React.Component<IProps, IState> {
<Label>
{_t("auth|change_password_confirm_label")}
</Label>
<PasswordInput autoComplete="new-password" ref={(field) => (this[FIELD_NEW_PASSWORD_CONFIRM] = field)} data-invalid={fieldValid[FIELD_NEW_PASSWORD_CONFIRM]} value={this.state.newPasswordConfirm} onChange={this.onChangeNewPasswordConfirm} />
<PasswordInput autoComplete="new-password" ref={(field) => (this[FIELD_NEW_PASSWORD_CONFIRM] = field)} data-invalid={fieldValid[FIELD_NEW_PASSWORD_CONFIRM]?.valid === false ? true : undefined} value={this.state.newPasswordConfirm} onChange={this.onChangeNewPasswordConfirm} />
{fieldValid[FIELD_NEW_PASSWORD_CONFIRM]?.feedback && <HelpMessage>
{fieldValid[FIELD_NEW_PASSWORD_CONFIRM]?.feedback}
</HelpMessage>}
</CpdField>
<AccessibleButton
className={buttonClassName}
kind={this.props.buttonKind}
<Button
disabled={!this.allFieldsValid()}
style={{width: "fit-content"}}
onClick={this.onClickChange}
kind="primary"
size="sm"
>
{this.props.buttonLabel || _t("auth|change_password_action")}
</AccessibleButton>
</Button>
</Root>
);
case Phase.Uploading: