fix: show error message on login and registration form, prevent saving password on localstorage

This commit is contained in:
Sebastian 2022-12-07 10:53:06 -03:00
parent 2c04459a58
commit ac2f680f68
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1

View File

@ -213,32 +213,6 @@ function useWireTransferRequestType(
return [retObj, retSetter];
}
/**
* Stores in the state a object containing a 'username'
* and 'password' field, in order to avoid losing the
* handle of the data entered by the user in <input> fields.
*/
type CredentialsRequestTypeOpt = CredentialsRequestType | undefined;
function useCredentialsRequestType(
state?: CredentialsRequestType,
): [CredentialsRequestTypeOpt, StateUpdater<CredentialsRequestTypeOpt>] {
const ret = hooks.useLocalStorage(
"credentials-request-state",
JSON.stringify(state),
);
const retObj: CredentialsRequestTypeOpt = ret[0]
? JSON.parse(ret[0])
: ret[0];
const retSetter: StateUpdater<CredentialsRequestTypeOpt> = function (val) {
const newVal =
val instanceof Function
? JSON.stringify(val(retObj))
: JSON.stringify(val);
ret[1](newVal);
};
return [retObj, retSetter];
}
/**
* Request preparators.
*
@ -597,7 +571,7 @@ async function createWithdrawalCall(
}
async function loginCall(
req: CredentialsRequestType,
req: { username: string; password: string },
/**
* FIXME: figure out if the two following
* functions can be retrieved from the state.
@ -629,7 +603,7 @@ async function loginCall(
* the page's (to indicate a successful login or a problem).
*/
async function registrationCall(
req: CredentialsRequestType,
req: { username: string; password: string },
/**
* FIXME: figure out if the two following
* functions can be retrieved somewhat from
@ -882,11 +856,7 @@ function ShowInputErrorLabel({
isDirty: boolean;
}): VNode {
if (message && isDirty)
return (
<div class="informational informational-fail" style={{ marginTop: 8 }}>
{message}
</div>
);
return <div style={{ marginTop: 8, color: "red" }}>{message}</div>;
return <Fragment />;
}
@ -1488,24 +1458,6 @@ function PaymentOptions({ currency }: { currency?: string }): VNode {
);
}
function RegistrationButton(Props: any): VNode {
const { backendStateSetter, pageStateSetter } = Props;
const { i18n } = useTranslationContext();
if (bankUiSettings.allowRegistrations)
return (
<button
class="pure-button pure-button-secondary btn-cancel"
onClick={() => {
route("/register");
}}
>
{i18n.str`Register`}
</button>
);
return <span />;
}
function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
? obj
@ -1514,20 +1466,20 @@ function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
/**
* Collect and submit login data.
*/
function LoginForm(Props: any): VNode {
const { backendStateSetter, pageStateSetter } = Props;
const [submitData, submitDataSetter] = useCredentialsRequestType();
function LoginForm(): VNode {
const [backendState, backendStateSetter] = useBackendState();
const { pageState, pageStateSetter } = usePageContext();
const [username, setUsername] = useState<string | undefined>();
const [password, setPassword] = useState<string | undefined>();
const { i18n } = useTranslationContext();
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
ref.current?.focus();
}, []);
const errors = !submitData
? undefined
: undefinedIfEmpty({
username: !submitData.username ? i18n.str`Missing username` : undefined,
password: !submitData.password ? i18n.str`Missing password` : undefined,
const errors = undefinedIfEmpty({
username: !username ? i18n.str`Missing username` : undefined,
password: !password ? i18n.str`Missing password` : undefined,
});
return (
@ -1544,16 +1496,17 @@ function LoginForm(Props: any): VNode {
type="text"
name="username"
id="username"
value={submitData && submitData.username}
value={username ?? ""}
placeholder="Username"
required
onInput={(e): void => {
submitDataSetter((submitData: any) => ({
...submitData,
username: e.currentTarget.value,
}));
setUsername(e.currentTarget.value);
}}
/>
<ShowInputErrorLabel
message={errors?.username}
isDirty={username !== undefined}
/>
<p class="passFieldLabel loginFieldLabel formFieldLabel">
<label for="password">{i18n.str`Password:`}</label>
</p>
@ -1561,50 +1514,48 @@ function LoginForm(Props: any): VNode {
type="password"
name="password"
id="password"
value={submitData && submitData.password}
value={password ?? ""}
placeholder="Password"
required
onInput={(e): void => {
submitDataSetter((submitData: any) => ({
...submitData,
password: e.currentTarget.value,
}));
setPassword(e.currentTarget.value);
}}
/>
<ShowInputErrorLabel
message={errors?.password}
isDirty={password !== undefined}
/>
<br />
<button
type="submit"
class="pure-button pure-button-primary"
disabled={!!errors}
onClick={() => {
if (typeof submitData === "undefined") {
console.log("login data is undefined", submitData);
return;
}
if (!submitData.password || !submitData.username) {
console.log(
"username or password is the empty string",
submitData,
);
return;
}
if (!username || !password) return;
loginCall(
// Deep copy, to avoid the cleanup
// below make data disappear.
{ ...submitData },
{ username, password },
backendStateSetter,
pageStateSetter,
);
submitDataSetter({
password: "",
repeatPassword: "",
username: "",
});
setUsername(undefined);
setPassword(undefined);
}}
>
{i18n.str`Login`}
</button>
{RegistrationButton(Props)}
{bankUiSettings.allowRegistrations ? (
<button
class="pure-button pure-button-secondary btn-cancel"
onClick={() => {
route("/register");
}}
>
{i18n.str`Register`}
</button>
) : (
<div />
)}
</div>
</form>
</div>
@ -1615,21 +1566,20 @@ function LoginForm(Props: any): VNode {
* Collect and submit registration data.
*/
function RegistrationForm(): VNode {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [backendState, backendStateSetter] = useBackendState();
const { pageState, pageStateSetter } = usePageContext();
const [submitData, submitDataSetter] = useCredentialsRequestType();
const [username, setUsername] = useState<string | undefined>();
const [password, setPassword] = useState<string | undefined>();
const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
const { i18n } = useTranslationContext();
const errors = !submitData
? undefined
: undefinedIfEmpty({
username: !submitData.username ? i18n.str`Missing username` : undefined,
password: !submitData.password ? i18n.str`Missing password` : undefined,
repeatPassword: !submitData.repeatPassword
const errors = undefinedIfEmpty({
username: !username ? i18n.str`Missing username` : undefined,
password: !password ? i18n.str`Missing password` : undefined,
repeatPassword: !repeatPassword
? i18n.str`Missing password`
: submitData.repeatPassword !== submitData.password
: repeatPassword !== password
? i18n.str`Password don't match`
: undefined,
});
@ -1650,16 +1600,15 @@ function RegistrationForm(): VNode {
name="register-un"
type="text"
placeholder="Username"
value={submitData && submitData.username}
required
value={username ?? ""}
onInput={(e): void => {
submitDataSetter((submitData: any) => ({
...submitData,
username: e.currentTarget.value,
}));
setUsername(e.currentTarget.value);
}}
/>
<br />
<ShowInputErrorLabel
message={errors?.username}
isDirty={username !== undefined}
/>
<p class="unameFieldLabel registerFieldLabel formFieldLabel">
<label for="register-pw">{i18n.str`Password:`}</label>
</p>
@ -1668,15 +1617,16 @@ function RegistrationForm(): VNode {
name="register-pw"
id="register-pw"
placeholder="Password"
value={submitData && submitData.password}
value={password ?? ""}
required
onInput={(e): void => {
submitDataSetter((submitData: any) => ({
...submitData,
password: e.currentTarget.value,
}));
setPassword(e.currentTarget.value);
}}
/>
<ShowInputErrorLabel
message={errors?.password}
isDirty={username !== undefined}
/>
<p class="unameFieldLabel registerFieldLabel formFieldLabel">
<label for="register-repeat">{i18n.str`Repeat Password:`}</label>
</p>
@ -1686,57 +1636,31 @@ function RegistrationForm(): VNode {
name="register-repeat"
id="register-repeat"
placeholder="Same password"
value={submitData && submitData.repeatPassword}
value={repeatPassword ?? ""}
required
onInput={(e): void => {
submitDataSetter((submitData: any) => ({
...submitData,
repeatPassword: e.currentTarget.value,
}));
setRepeatPassword(e.currentTarget.value);
}}
/>
<ShowInputErrorLabel
message={errors?.repeatPassword}
isDirty={username !== undefined}
/>
<br />
{/*
<label for="phone">{i18n.str`Phone number:`}</label>
// FIXME: add input validation (must start with +, otherwise only numbers)
<input
name="phone"
id="phone"
type="phone"
placeholder="+CC-123456789"
value={submitData && submitData.phone}
required
onInput={(e): void => {
submitDataSetter((submitData: any) => ({
...submitData,
phone: e.currentTarget.value,
}))}} />
<br />
*/}
<button
class="pure-button pure-button-primary btn-register"
disabled={!!errors}
onClick={() => {
console.log("maybe submitting the registration..");
if (!submitData) return;
if (!username || !password) return;
registrationCall(
{ ...submitData },
{ username, password },
backendStateSetter, // will store BE URL, if OK.
pageStateSetter,
);
console.log("Clearing the input data");
/**
* FIXME: clearing the data should be done by setting
* it to undefined, instead of the empty strings, just
* like done in the login function. Now set to the empty
* strings due to a non lively update of the <input> fields
* after setting to undefined.
*/
submitDataSetter({
username: "",
password: "",
repeatPassword: "",
});
setUsername(undefined);
setPassword(undefined);
setRepeatPassword(undefined);
}}
>
{i18n.str`Register`}
@ -1745,11 +1669,9 @@ function RegistrationForm(): VNode {
<button
class="pure-button pure-button-secondary btn-cancel"
onClick={() => {
submitDataSetter({
username: "",
password: "",
repeatPassword: "",
});
setUsername(undefined);
setPassword(undefined);
setRepeatPassword(undefined);
route("/account");
}}
>