Complete the compounding
This commit is contained in:
@@ -52,8 +52,8 @@ const DEFAULT_PROPS = {
|
|||||||
labelAllowedButUnsafe: _td("auth|password_field_weak_label"),
|
labelAllowedButUnsafe: _td("auth|password_field_weak_label"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const NewPassphraseField: React.FC<IProps> = (props) => {
|
const PassphraseField: React.FC<IProps> = (props) => {
|
||||||
const { labelEnterPassword, userInputs, minScore, label, labelStrongPassword, labelAllowedButUnsafe, className, id, fieldRef, autoFocus} = {...DEFAULT_PROPS, ...props};
|
const { labelEnterPassword, userInputs, minScore, label, labelStrongPassword, labelAllowedButUnsafe, className, id, fieldRef, autoFocus, onChange, onValidate} = {...DEFAULT_PROPS, ...props};
|
||||||
const validateFn = useMemo(() => withValidation<{}, ZxcvbnResult | null>({
|
const validateFn = useMemo(() => withValidation<{}, ZxcvbnResult | null>({
|
||||||
description: function (complexity) {
|
description: function (complexity) {
|
||||||
const score = complexity ? complexity.score : 0;
|
const score = complexity ? complexity.score : 0;
|
||||||
@@ -103,12 +103,14 @@ const NewPassphraseField: React.FC<IProps> = (props) => {
|
|||||||
const [feedback, setFeedback]= useState<string|JSX.Element>();
|
const [feedback, setFeedback]= useState<string|JSX.Element>();
|
||||||
|
|
||||||
const onInputChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((ev) => {
|
const onInputChange = useCallback<React.ChangeEventHandler<HTMLInputElement>>((ev) => {
|
||||||
|
onChange(ev);
|
||||||
validateFn({
|
validateFn({
|
||||||
value: ev.target.value,
|
value: ev.target.value,
|
||||||
focused: true,
|
focused: true,
|
||||||
}).then((v) => {
|
}).then((v) => {
|
||||||
setFeedback(v.feedback);
|
setFeedback(v.feedback);
|
||||||
})
|
onValidate?.(v);
|
||||||
|
});
|
||||||
}, [validateFn]);
|
}, [validateFn]);
|
||||||
|
|
||||||
|
|
||||||
@@ -119,4 +121,4 @@ const NewPassphraseField: React.FC<IProps> = (props) => {
|
|||||||
</Field>
|
</Field>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NewPassphraseField;
|
export default PassphraseField;
|
||||||
|
|||||||
@@ -10,11 +10,10 @@ import React from "react";
|
|||||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import AccessibleButton, { type AccessibleButtonKind } from "../elements/AccessibleButton";
|
|
||||||
import withValidation, { type IFieldState, type IValidationResult } from "../elements/Validation";
|
import withValidation, { type IFieldState, type IValidationResult } from "../elements/Validation";
|
||||||
import { UserFriendlyError, _t, _td } from "../../../languageHandler";
|
import { UserFriendlyError, _t, _td } from "../../../languageHandler";
|
||||||
import { PASSWORD_MIN_SCORE } from "../auth/RegistrationForm";
|
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";
|
import PassphraseField from "../auth/PassphraseField";
|
||||||
|
|
||||||
const FIELD_OLD_PASSWORD = "field_old_password";
|
const FIELD_OLD_PASSWORD = "field_old_password";
|
||||||
@@ -31,8 +30,6 @@ enum Phase {
|
|||||||
interface IProps {
|
interface IProps {
|
||||||
onFinished: (outcome: { didSetEmail?: boolean }) => void;
|
onFinished: (outcome: { didSetEmail?: boolean }) => void;
|
||||||
onError: (error: Error) => void;
|
onError: (error: Error) => void;
|
||||||
buttonClassName?: string;
|
|
||||||
buttonKind?: AccessibleButtonKind;
|
|
||||||
buttonLabel?: string;
|
buttonLabel?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,26 +247,25 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
|||||||
activeElement.blur();
|
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[] = [
|
const fieldIDsInDisplayOrder: FieldType[] = [
|
||||||
FIELD_OLD_PASSWORD,
|
FIELD_OLD_PASSWORD,
|
||||||
FIELD_NEW_PASSWORD,
|
FIELD_NEW_PASSWORD,
|
||||||
FIELD_NEW_PASSWORD_CONFIRM,
|
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
|
// 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.
|
// first. Queue a `setState` callback and wait for it to resolve.
|
||||||
await new Promise<void>((resolve) => this.setState({}, 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
|
// Focus the first invalid field and show feedback in the stricter mode
|
||||||
// that no longer allows empty values for required fields.
|
// that no longer allows empty values for required fields.
|
||||||
invalidField.focus();
|
invalidField.focus();
|
||||||
invalidField.validate({ allowEmpty: false, focused: true });
|
// TODO: HMM
|
||||||
|
// invalidField.validate({ allowEmpty: false, focused: true });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,8 +302,6 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const buttonClassName = this.props.buttonClassName;
|
|
||||||
|
|
||||||
const { fieldValid, phase } = this.state;
|
const { fieldValid, phase } = this.state;
|
||||||
|
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
@@ -317,7 +312,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
|||||||
<Label>
|
<Label>
|
||||||
{_t("auth|change_password_current_label")}
|
{_t("auth|change_password_current_label")}
|
||||||
</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>
|
||||||
{fieldValid[FIELD_OLD_PASSWORD]?.feedback}
|
{fieldValid[FIELD_OLD_PASSWORD]?.feedback}
|
||||||
</HelpMessage>}
|
</HelpMessage>}
|
||||||
@@ -337,18 +332,20 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
|||||||
<Label>
|
<Label>
|
||||||
{_t("auth|change_password_confirm_label")}
|
{_t("auth|change_password_confirm_label")}
|
||||||
</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>
|
||||||
{fieldValid[FIELD_NEW_PASSWORD_CONFIRM]?.feedback}
|
{fieldValid[FIELD_NEW_PASSWORD_CONFIRM]?.feedback}
|
||||||
</HelpMessage>}
|
</HelpMessage>}
|
||||||
</CpdField>
|
</CpdField>
|
||||||
<AccessibleButton
|
<Button
|
||||||
className={buttonClassName}
|
disabled={!this.allFieldsValid()}
|
||||||
kind={this.props.buttonKind}
|
style={{width: "fit-content"}}
|
||||||
onClick={this.onClickChange}
|
onClick={this.onClickChange}
|
||||||
|
kind="primary"
|
||||||
|
size="sm"
|
||||||
>
|
>
|
||||||
{this.props.buttonLabel || _t("auth|change_password_action")}
|
{this.props.buttonLabel || _t("auth|change_password_action")}
|
||||||
</AccessibleButton>
|
</Button>
|
||||||
</Root>
|
</Root>
|
||||||
);
|
);
|
||||||
case Phase.Uploading:
|
case Phase.Uploading:
|
||||||
|
|||||||
Reference in New Issue
Block a user