using util to parse uri, some more fixs
This commit is contained in:
parent
b4bad2deaf
commit
af29a02e5b
@ -14,6 +14,7 @@
|
||||
"jed": "1.1.1",
|
||||
"preact": "10.6.5",
|
||||
"preact-router": "3.2.1",
|
||||
"@gnu-taler/taler-util": "workspace:*",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"react": "npm:@preact/compat@^17.1.2",
|
||||
"swr": "1.3.0"
|
||||
|
@ -31,6 +31,7 @@ import { QR } from "../../components/QR.js";
|
||||
import { useLocalStorage, useNotNullLocalStorage } from "../../hooks/index.js";
|
||||
import { Translate, useTranslator } from "../../i18n/index.js";
|
||||
import "../../scss/main.scss";
|
||||
import { parsePaytoUri } from "@gnu-taler/taler-util";
|
||||
|
||||
/**
|
||||
* If the first argument does not look like a placeholder, return it.
|
||||
@ -113,6 +114,7 @@ interface TransactionRequestType {
|
||||
interface CredentialsRequestType {
|
||||
username: string;
|
||||
password: string;
|
||||
repeatPassword: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -336,14 +338,16 @@ type RawPaytoInputType = string;
|
||||
type RawPaytoInputTypeOpt = RawPaytoInputType | undefined;
|
||||
function useRawPaytoInputType(
|
||||
state?: RawPaytoInputType,
|
||||
): [RawPaytoInputTypeOpt, StateUpdater<RawPaytoInputTypeOpt>] {
|
||||
): [RawPaytoInputTypeOpt, StateUpdater<RawPaytoInputTypeOpt>, boolean] {
|
||||
const ret = useLocalStorage("raw-payto-input-state", state);
|
||||
const [dirty, setDirty] = useState(false);
|
||||
const retObj: RawPaytoInputTypeOpt = ret[0];
|
||||
const retSetter: StateUpdater<RawPaytoInputTypeOpt> = function (val) {
|
||||
const newVal = val instanceof Function ? val(retObj) : val;
|
||||
setDirty(true);
|
||||
ret[1](newVal);
|
||||
};
|
||||
return [retObj, retSetter];
|
||||
return [retObj, retSetter, dirty];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1012,12 +1016,24 @@ function BankFrame(Props: any): VNode {
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
function ShowInputErrorLabel({
|
||||
isDirty,
|
||||
message,
|
||||
}: {
|
||||
message: string | undefined;
|
||||
isDirty: boolean;
|
||||
}): VNode {
|
||||
if (message && isDirty)
|
||||
return <p class="informational informational-fail">{message}</p>;
|
||||
return <Fragment />;
|
||||
}
|
||||
|
||||
function PaytoWireTransfer(Props: any): VNode {
|
||||
const currency = useContext(CurrencyContext);
|
||||
const [pageState, pageStateSetter] = useContext(PageContext); // NOTE: used for go-back button?
|
||||
const [submitData, submitDataSetter] = useWireTransferRequestType();
|
||||
const [rawPaytoInput, rawPaytoInputSetter] = useRawPaytoInputType();
|
||||
const [rawPaytoInput, rawPaytoInputSetter, rawPaytoInputDirty] =
|
||||
useRawPaytoInputType();
|
||||
const i18n = useTranslator();
|
||||
const { focus, backendState } = Props;
|
||||
const amountRegex = "^[0-9]+(.[0-9]+)?$";
|
||||
@ -1159,6 +1175,14 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
</div>
|
||||
);
|
||||
|
||||
const errors = undefinedIfEmpty({
|
||||
rawPaytoInput: !rawPaytoInput
|
||||
? i18n`Missing payto address`
|
||||
: !parsePaytoUri(rawPaytoInput)
|
||||
? i18n`Payto does not follow the pattern`
|
||||
: undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{i18n`Transfer money to account identified by payto:// URI:`}</p>
|
||||
@ -1168,17 +1192,21 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
<input
|
||||
name="address"
|
||||
type="text"
|
||||
size={90}
|
||||
size={50}
|
||||
ref={ref}
|
||||
id="address"
|
||||
value={rawPaytoInput}
|
||||
required
|
||||
placeholder={i18n`payto address`}
|
||||
pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 ]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
|
||||
// pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 ]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
|
||||
onInput={(e): void => {
|
||||
rawPaytoInputSetter(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
<ShowInputErrorLabel
|
||||
message={errors?.rawPaytoInput}
|
||||
isDirty={rawPaytoInputDirty}
|
||||
/>
|
||||
<br />
|
||||
<div class="hint">
|
||||
Hint:
|
||||
@ -1192,6 +1220,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
<input
|
||||
class="pure-button pure-button-primary"
|
||||
type="submit"
|
||||
disabled={!!errors}
|
||||
value={i18n`Send`}
|
||||
onClick={async () => {
|
||||
// empty string evaluates to false.
|
||||
@ -1593,6 +1622,11 @@ function RegistrationButton(Props: any): VNode {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
|
||||
return Object.keys(obj).some((k) => (obj as any)[k] !== undefined)
|
||||
? obj
|
||||
: undefined;
|
||||
}
|
||||
/**
|
||||
* Collect and submit login data.
|
||||
*/
|
||||
@ -1604,6 +1638,16 @@ function LoginForm(Props: any): VNode {
|
||||
useEffect(() => {
|
||||
ref.current?.focus();
|
||||
}, []);
|
||||
|
||||
const errors = undefinedIfEmpty({
|
||||
username: !(submitData && submitData.username)
|
||||
? i18n`Missing username`
|
||||
: undefined,
|
||||
password: !(submitData && submitData.password)
|
||||
? i18n`Missing password`
|
||||
: undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="login-div">
|
||||
<form action="javascript:void(0);" class="login-form">
|
||||
@ -1649,6 +1693,7 @@ function LoginForm(Props: any): VNode {
|
||||
<button
|
||||
type="submit"
|
||||
class="pure-button pure-button-primary"
|
||||
disabled={!!errors}
|
||||
onClick={() => {
|
||||
if (typeof submitData === "undefined") {
|
||||
console.log("login data is undefined", submitData);
|
||||
@ -1691,7 +1736,19 @@ function RegistrationForm(Props: any): VNode {
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
const [submitData, submitDataSetter] = useCredentialsRequestType();
|
||||
const i18n = useTranslator();
|
||||
// https://stackoverflow.com/questions/36683770/how-to-get-the-value-of-an-input-field-using-reactjs
|
||||
|
||||
const errors = !submitData
|
||||
? undefined
|
||||
: undefinedIfEmpty({
|
||||
username: !submitData.username ? i18n`Missing username` : undefined,
|
||||
password: !submitData.password ? i18n`Missing password` : undefined,
|
||||
repeatPassword: !submitData.repeatPassword
|
||||
? i18n`Missing password`
|
||||
: submitData.repeatPassword !== submitData.password
|
||||
? i18n`Password don't match`
|
||||
: undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h1 class="nav">{i18n`Welcome to ${UI_BANK_NAME}!`}</h1>
|
||||
@ -1735,6 +1792,24 @@ function RegistrationForm(Props: any): VNode {
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<p class="unameFieldLabel registerFieldLabel formFieldLabel">
|
||||
<label for="register-repeat">{i18n`Repeat Password:`}</label>
|
||||
</p>
|
||||
<input
|
||||
type="password"
|
||||
style={{ marginBottom: 8 }}
|
||||
name="register-repeat"
|
||||
id="register-repeat"
|
||||
placeholder="Same password"
|
||||
value={submitData && submitData.repeatPassword}
|
||||
required
|
||||
onInput={(e): void => {
|
||||
submitDataSetter((submitData: any) => ({
|
||||
...submitData,
|
||||
repeatPassword: e.currentTarget.value,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<br />
|
||||
{/*
|
||||
<label for="phone">{i18n`Phone number:`}</label>
|
||||
@ -1755,28 +1830,10 @@ function RegistrationForm(Props: any): VNode {
|
||||
*/}
|
||||
<button
|
||||
class="pure-button pure-button-primary btn-register"
|
||||
disabled={!!errors}
|
||||
onClick={() => {
|
||||
console.log("maybe submitting the registration..");
|
||||
console.log(submitData);
|
||||
if (typeof submitData === "undefined") {
|
||||
console.log(`submit data ${submitData} is undefined`);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
typeof submitData.password === "undefined" ||
|
||||
typeof submitData.username === "undefined"
|
||||
) {
|
||||
console.log("username or password is undefined");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
submitData.password.length === 0 ||
|
||||
submitData.username.length === 0
|
||||
) {
|
||||
console.log("username or password are the empty string");
|
||||
return;
|
||||
}
|
||||
console.log("submitting the registration..");
|
||||
if (!submitData) return;
|
||||
registrationCall(
|
||||
{ ...submitData },
|
||||
Props.backendStateSetter, // will store BE URL, if OK.
|
||||
@ -1790,7 +1847,11 @@ function RegistrationForm(Props: any): VNode {
|
||||
* strings due to a non lively update of the <input> fields
|
||||
* after setting to undefined.
|
||||
*/
|
||||
submitDataSetter({ username: "", password: "" });
|
||||
submitDataSetter({
|
||||
username: "",
|
||||
password: "",
|
||||
repeatPassword: "",
|
||||
});
|
||||
}}
|
||||
>
|
||||
{i18n`Register`}
|
||||
@ -1799,6 +1860,11 @@ function RegistrationForm(Props: any): VNode {
|
||||
<button
|
||||
class="pure-button pure-button-secondary btn-cancel"
|
||||
onClick={() => {
|
||||
submitDataSetter({
|
||||
username: "",
|
||||
password: "",
|
||||
repeatPassword: "",
|
||||
});
|
||||
pageStateSetter((prevState: PageStateType) => ({
|
||||
...prevState,
|
||||
tryRegister: false,
|
||||
|
@ -120,7 +120,7 @@ input {
|
||||
|
||||
.large-amount {
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
font-size: xxx-large;
|
||||
}
|
||||
|
||||
.currency {
|
||||
@ -259,3 +259,6 @@ html {
|
||||
.hint {
|
||||
scale: 0.7;
|
||||
}
|
||||
h1.nav {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ importers:
|
||||
specifiers:
|
||||
'@creativebulma/bulma-tooltip': ^1.2.0
|
||||
'@gnu-taler/pogen': ^0.0.5
|
||||
'@gnu-taler/taler-util': workspace:*
|
||||
'@typescript-eslint/eslint-plugin': ^5.41.0
|
||||
'@typescript-eslint/parser': ^5.41.0
|
||||
bulma: ^0.9.4
|
||||
@ -116,6 +117,7 @@ importers:
|
||||
swr: 1.3.0
|
||||
typescript: ^4.4.4
|
||||
dependencies:
|
||||
'@gnu-taler/taler-util': link:../taler-util
|
||||
date-fns: 2.29.3
|
||||
jed: 1.1.1
|
||||
preact: 10.6.5
|
||||
|
Loading…
Reference in New Issue
Block a user