several minor fixes worked out
This commit is contained in:
parent
4e4f526fa5
commit
3d595ddae2
@ -1,45 +1,49 @@
|
|||||||
Urgent TODOs:
|
Urgent TODOs:
|
||||||
|
|
||||||
- General:
|
- General:
|
||||||
* not only Nora dark-theme, but default light! (CSS)
|
|
||||||
* auto-focus on input fields is not working well
|
- not only Nora dark-theme, but default light! (CSS)
|
||||||
* buttons should be visibly insensitive
|
- DONE: auto-focus on input fields is not working well
|
||||||
|
- DONE: buttons should be visibly insensitive
|
||||||
as long as required input fields are not
|
as long as required input fields are not
|
||||||
working
|
working
|
||||||
* next required invalid/missing input field is
|
- DONE: next required invalid/missing input field is
|
||||||
not properly highlighted in red
|
not properly highlighted in red
|
||||||
* Logout button needs more padding to the right (CSS)
|
- Logout button needs more padding to the right (CSS)
|
||||||
|
|
||||||
- Error bar:
|
- Error bar:
|
||||||
* shows JSON, should only show good error message
|
- DONE: hows JSON, should only show good error message
|
||||||
and numeric code, not JSON syntax
|
and numeric code, not JSON syntax
|
||||||
* should auto-hide after next action, no need for
|
- should auto-hide after next action, no need for
|
||||||
"clear"!
|
"clear"!
|
||||||
* need variant "status bar" in green (or blue)
|
- need variant "status bar" in green (or blue)
|
||||||
which shows status of last operation
|
which shows status of last operation
|
||||||
|
|
||||||
* H1-Titles:
|
* H1-Titles:
|
||||||
* Center more (currently way on the left) (CSS)
|
- Center more (currently way on the left) (CSS)
|
||||||
|
|
||||||
- Assets:
|
- Assets:
|
||||||
* Numeric amount needs to be shown MUCH bigger (CSS)
|
|
||||||
* Center more? (CSS)
|
- Numeric amount needs to be shown MUCH bigger (CSS)
|
||||||
|
- Center more? (CSS)
|
||||||
|
|
||||||
- Payments:
|
- Payments:
|
||||||
* Amount to withdraw currently shown in white-on-white (CSS)
|
|
||||||
* Big frame drawn around notebook-tabs is not nice (CSS)
|
- Amount to withdraw currently shown in white-on-white (CSS)
|
||||||
* Center more? (CSS)
|
- Big frame drawn around notebook-tabs is not nice (CSS)
|
||||||
* "Wire to bank account"
|
- Center more? (CSS)
|
||||||
|
- "Wire to bank account"
|
||||||
- maybe split two types (payto and IBAN) into
|
- maybe split two types (payto and IBAN) into
|
||||||
two tabs?
|
two tabs?
|
||||||
- currently cannot switch back from payto to IBAN
|
- currently cannot switch back from payto to IBAN
|
||||||
|
|
||||||
- Withdraw:
|
- Withdraw:
|
||||||
* Should use new 'status' bar at the end, instead
|
|
||||||
|
- Should use new 'status' bar at the end, instead
|
||||||
of extra dialog with "close" button
|
of extra dialog with "close" button
|
||||||
* ditto for bank-wire-transfer final stage
|
- ditto for bank-wire-transfer final stage
|
||||||
|
|
||||||
- Footer:
|
- Footer:
|
||||||
* overlaps with transaction history or other
|
- overlaps with transaction history or other
|
||||||
content, needs to consistently show at the
|
content, needs to consistently show at the
|
||||||
end! => change rendering logic!? (CSS?)
|
end! => change rendering logic!? (CSS?)
|
||||||
|
@ -31,7 +31,7 @@ import { QR } from "../../components/QR.js";
|
|||||||
import { useLocalStorage, useNotNullLocalStorage } from "../../hooks/index.js";
|
import { useLocalStorage, useNotNullLocalStorage } from "../../hooks/index.js";
|
||||||
import { Translate, useTranslator } from "../../i18n/index.js";
|
import { Translate, useTranslator } from "../../i18n/index.js";
|
||||||
import "../../scss/main.scss";
|
import "../../scss/main.scss";
|
||||||
import { parsePaytoUri } from "@gnu-taler/taler-util";
|
import { Amounts, parsePaytoUri } from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the first argument does not look like a placeholder, return it.
|
* If the first argument does not look like a placeholder, return it.
|
||||||
@ -80,7 +80,22 @@ const UI_BANK_NAME = replacementOrDefault(
|
|||||||
* Contexts *
|
* Contexts *
|
||||||
***********/
|
***********/
|
||||||
const CurrencyContext = createContext<any>(null);
|
const CurrencyContext = createContext<any>(null);
|
||||||
const PageContext = createContext<any>(null);
|
type PageContextType = [PageStateType, StateUpdater<PageStateType>];
|
||||||
|
const PageContextDefault: PageContextType = [
|
||||||
|
{
|
||||||
|
hasError: false,
|
||||||
|
hasInfo: false,
|
||||||
|
isLoggedIn: false,
|
||||||
|
isRawPayto: false,
|
||||||
|
showPublicHistories: false,
|
||||||
|
tryRegister: false,
|
||||||
|
withdrawalInProgress: false,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
null;
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const PageContext = createContext<PageContextType>(PageContextDefault);
|
||||||
|
|
||||||
/**********************************************
|
/**********************************************
|
||||||
* Type definitions for states and API calls. *
|
* Type definitions for states and API calls. *
|
||||||
@ -91,9 +106,9 @@ const PageContext = createContext<any>(null);
|
|||||||
* authenticate at the bank's backend.
|
* authenticate at the bank's backend.
|
||||||
*/
|
*/
|
||||||
interface BackendStateType {
|
interface BackendStateType {
|
||||||
url: string;
|
url?: string;
|
||||||
username: string;
|
username?: string;
|
||||||
password: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,9 +127,9 @@ interface TransactionRequestType {
|
|||||||
* Request body of /register.
|
* Request body of /register.
|
||||||
*/
|
*/
|
||||||
interface CredentialsRequestType {
|
interface CredentialsRequestType {
|
||||||
username: string;
|
username?: string;
|
||||||
password: string;
|
password?: string;
|
||||||
repeatPassword: string;
|
repeatPassword?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,14 +141,9 @@ interface CredentialsRequestType {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
interface WireTransferRequestType {
|
interface WireTransferRequestType {
|
||||||
iban: string;
|
iban?: string;
|
||||||
subject: string;
|
subject?: string;
|
||||||
amount: string;
|
amount?: string;
|
||||||
}
|
|
||||||
|
|
||||||
interface Amount {
|
|
||||||
value: string;
|
|
||||||
currency: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,7 +157,12 @@ interface PageStateType {
|
|||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
hasInfo: boolean;
|
hasInfo: boolean;
|
||||||
withdrawalInProgress: boolean;
|
withdrawalInProgress: boolean;
|
||||||
error?: string;
|
error?: {
|
||||||
|
description?: string;
|
||||||
|
title: string;
|
||||||
|
debug?: string;
|
||||||
|
};
|
||||||
|
|
||||||
info?: string;
|
info?: string;
|
||||||
talerWithdrawUri?: string;
|
talerWithdrawUri?: string;
|
||||||
/**
|
/**
|
||||||
@ -225,12 +240,13 @@ function getIbanFromPayto(url: string): string {
|
|||||||
/**
|
/**
|
||||||
* Extract value and currency from a $currency:x.y amount.
|
* Extract value and currency from a $currency:x.y amount.
|
||||||
*/
|
*/
|
||||||
function parseAmount(val: string): Amount {
|
// function parseAmount(val: string): Amount {
|
||||||
const format = /^[A-Z]+:[0-9]+(\.[0-9]+)?$/;
|
// Amounts.parse(val)
|
||||||
if (!format.test(val)) throw Error(`Backend gave invalid amount: ${val}.`);
|
// const format = /^[A-Z]+:[0-9]+(\.[0-9]+)?$/;
|
||||||
const amountSplit = val.split(":");
|
// if (!format.test(val)) throw Error(`Backend gave invalid amount: ${val}.`);
|
||||||
return { value: amountSplit[1], currency: amountSplit[0] };
|
// const amountSplit = val.split(":");
|
||||||
}
|
// return { value: amountSplit[1], currency: amountSplit[0] };
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get username from the backend state, and throw
|
* Get username from the backend state, and throw
|
||||||
@ -240,6 +256,9 @@ function getUsername(backendState: BackendStateTypeOpt): string {
|
|||||||
if (typeof backendState === "undefined")
|
if (typeof backendState === "undefined")
|
||||||
throw Error("Username can't be found in a undefined backend state.");
|
throw Error("Username can't be found in a undefined backend state.");
|
||||||
|
|
||||||
|
if (!backendState.username) {
|
||||||
|
throw Error("No username, must login first.");
|
||||||
|
}
|
||||||
return backendState.username;
|
return backendState.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,12 +302,14 @@ function useTransactionPageNumber(): [number, StateUpdater<number>] {
|
|||||||
/**
|
/**
|
||||||
* Craft headers with Authorization and Content-Type.
|
* Craft headers with Authorization and Content-Type.
|
||||||
*/
|
*/
|
||||||
function prepareHeaders(username: string, password: string): Headers {
|
function prepareHeaders(username?: string, password?: string): Headers {
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.append(
|
if (username && password) {
|
||||||
"Authorization",
|
headers.append(
|
||||||
`Basic ${window.btoa(`${username}:${password}`)}`,
|
"Authorization",
|
||||||
);
|
`Basic ${window.btoa(`${username}:${password}`)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
headers.append("Content-Type", "application/json");
|
headers.append("Content-Type", "application/json");
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
@ -461,13 +482,13 @@ function usePageState(
|
|||||||
): [PageStateType, StateUpdater<PageStateType>] {
|
): [PageStateType, StateUpdater<PageStateType>] {
|
||||||
const ret = useNotNullLocalStorage("page-state", JSON.stringify(state));
|
const ret = useNotNullLocalStorage("page-state", JSON.stringify(state));
|
||||||
const retObj: PageStateType = JSON.parse(ret[0]);
|
const retObj: PageStateType = JSON.parse(ret[0]);
|
||||||
console.log("Current page state", retObj);
|
|
||||||
const retSetter: StateUpdater<PageStateType> = function (val) {
|
const retSetter: StateUpdater<PageStateType> = function (val) {
|
||||||
const newVal =
|
const newVal =
|
||||||
val instanceof Function
|
val instanceof Function
|
||||||
? JSON.stringify(val(retObj))
|
? JSON.stringify(val(retObj))
|
||||||
: JSON.stringify(val);
|
: JSON.stringify(val);
|
||||||
console.log("Setting new page state", newVal);
|
|
||||||
ret[1](newVal);
|
ret[1](newVal);
|
||||||
};
|
};
|
||||||
return [retObj, retSetter];
|
return [retObj, retSetter];
|
||||||
@ -502,7 +523,9 @@ async function abortWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: "No credentials found.",
|
error: {
|
||||||
|
title: `No credentials found.`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -511,7 +534,9 @@ async function abortWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: "No withdrawal ID found.",
|
error: {
|
||||||
|
title: `No withdrawal ID found.`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -541,11 +566,16 @@ async function abortWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: `Could not abort the withdrawal: ${error}`,
|
error: {
|
||||||
|
title: `Could not abort the withdrawal.`,
|
||||||
|
description: (error as any).error.description,
|
||||||
|
debug: JSON.stringify(error),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
const response = await res.json();
|
||||||
console.log(
|
console.log(
|
||||||
`Withdrawal abort gave response error (${res.status})`,
|
`Withdrawal abort gave response error (${res.status})`,
|
||||||
res.statusText,
|
res.statusText,
|
||||||
@ -553,7 +583,11 @@ async function abortWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: `Withdrawal abortion gave response error (${res.status})`,
|
error: {
|
||||||
|
title: `Withdrawal abortion failed.`,
|
||||||
|
description: response.error.description,
|
||||||
|
debug: JSON.stringify(res.status),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -587,7 +621,9 @@ async function confirmWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: "No credentials found.",
|
error: {
|
||||||
|
title: "No credentials found.",
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -596,7 +632,9 @@ async function confirmWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: "No withdrawal ID found.",
|
error: {
|
||||||
|
title: "No withdrawal ID found.",
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -629,11 +667,15 @@ async function confirmWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: `Could not confirm the withdrawal: ${error}`,
|
error: {
|
||||||
|
title: `Could not confirm the withdrawal`,
|
||||||
|
description: (error as any).error.description,
|
||||||
|
debug: JSON.stringify(error),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (res ? !res.ok : true) {
|
if (!res || !res.ok) {
|
||||||
// assume not ok if res is null
|
// assume not ok if res is null
|
||||||
console.log(
|
console.log(
|
||||||
`Withdrawal confirmation gave response error (${res.status})`,
|
`Withdrawal confirmation gave response error (${res.status})`,
|
||||||
@ -642,7 +684,10 @@ async function confirmWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: `Withdrawal confirmation gave response error (${res.status})`,
|
error: {
|
||||||
|
title: `Withdrawal confirmation gave response error`,
|
||||||
|
debug: JSON.stringify(res.status),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -684,20 +729,28 @@ async function createTransactionCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: `Could not create the wire transfer: ${error}`,
|
error: {
|
||||||
|
title: `Could not create the wire transfer`,
|
||||||
|
description: (error as any).error.description,
|
||||||
|
debug: JSON.stringify(error),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// POST happened, status not sure yet.
|
// POST happened, status not sure yet.
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const responseText = JSON.stringify(await res.json());
|
const response = await res.json();
|
||||||
console.log(
|
console.log(
|
||||||
`Transfer creation gave response error: ${responseText} (${res.status})`,
|
`Transfer creation gave response error: ${response} (${res.status})`,
|
||||||
);
|
);
|
||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: `Transfer creation gave response error: ${responseText} (${res.status})`,
|
error: {
|
||||||
|
title: `Transfer creation gave response error`,
|
||||||
|
description: response.error.description,
|
||||||
|
debug: JSON.stringify(response),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -733,7 +786,9 @@ async function createWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: "No credentials given.",
|
error: {
|
||||||
|
title: "No credentials given.",
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -758,19 +813,27 @@ async function createWithdrawalCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: `Could not create withdrawal operation: ${error}`,
|
error: {
|
||||||
|
title: `Could not create withdrawal operation`,
|
||||||
|
description: (error as any).error.description,
|
||||||
|
debug: JSON.stringify(error),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const responseText = await res.text();
|
const response = await res.text();
|
||||||
console.log(
|
console.log(
|
||||||
`Withdrawal creation gave response error: ${responseText} (${res.status})`,
|
`Withdrawal creation gave response error: ${response} (${res.status})`,
|
||||||
);
|
);
|
||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: `Withdrawal creation gave response error: ${responseText} (${res.status})`,
|
error: {
|
||||||
|
title: `Withdrawal creation gave response error`,
|
||||||
|
description: response.error.description,
|
||||||
|
debug: `${response} (${res.status})`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -853,7 +916,10 @@ async function registrationCall(
|
|||||||
pageStateSetter((prevState) => ({
|
pageStateSetter((prevState) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: "Registration failed, please report.",
|
error: {
|
||||||
|
title: `Registration failed, please report`,
|
||||||
|
debug: JSON.stringify(error),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -893,7 +959,24 @@ function ErrorBanner(Props: any): VNode | null {
|
|||||||
if (!pageState.hasError) return null;
|
if (!pageState.hasError) return null;
|
||||||
|
|
||||||
const rval = (
|
const rval = (
|
||||||
<p class="informational informational-fail">{pageState.error}</p>
|
<div class="informational informational-fail" style={{ marginTop: 8 }}>
|
||||||
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
|
<p>
|
||||||
|
<b>{pageState.error.title}</b>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="button"
|
||||||
|
class="pure-button"
|
||||||
|
value="Clear"
|
||||||
|
onClick={async () => {
|
||||||
|
pageStateSetter((prev) => ({ ...prev, error: undefined }));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>{pageState.error.description}</p>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
delete pageState.error;
|
delete pageState.error;
|
||||||
pageState.hasError = false;
|
pageState.hasError = false;
|
||||||
@ -1024,7 +1107,11 @@ function ShowInputErrorLabel({
|
|||||||
isDirty: boolean;
|
isDirty: boolean;
|
||||||
}): VNode {
|
}): VNode {
|
||||||
if (message && isDirty)
|
if (message && isDirty)
|
||||||
return <p class="informational informational-fail">{message}</p>;
|
return (
|
||||||
|
<div class="informational informational-fail" style={{ marginTop: 8 }}>
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
return <Fragment />;
|
return <Fragment />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1046,6 +1133,33 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
if (focus) ref.current?.focus();
|
if (focus) ref.current?.focus();
|
||||||
}, [focus, pageState.isRawPayto]);
|
}, [focus, pageState.isRawPayto]);
|
||||||
|
|
||||||
|
// typeof submitData === "undefined" ||
|
||||||
|
// typeof submitData.iban === "undefined" ||
|
||||||
|
// submitData.iban === "" ||
|
||||||
|
// typeof submitData.subject === "undefined" ||
|
||||||
|
// submitData.subject === "" ||
|
||||||
|
// typeof submitData.amount === "undefined" ||
|
||||||
|
// submitData.amount === ""
|
||||||
|
let parsedAmount = undefined;
|
||||||
|
|
||||||
|
const errorsWire = !submitData
|
||||||
|
? undefined
|
||||||
|
: undefinedIfEmpty({
|
||||||
|
iban: !submitData.iban
|
||||||
|
? i18n`Missing IBAN`
|
||||||
|
: !/^[A-Z0-9]*$/.test(submitData.iban)
|
||||||
|
? i18n`IBAN should have just uppercased letters and numbers`
|
||||||
|
: undefined,
|
||||||
|
subject: !submitData.subject ? i18n`Missing subject` : undefined,
|
||||||
|
amount: !submitData.amount
|
||||||
|
? i18n`Missing amount`
|
||||||
|
: !(parsedAmount = Amounts.parse(`${currency}:${submitData.amount}`))
|
||||||
|
? i18n`Amount is not valid`
|
||||||
|
: Amounts.isZero(parsedAmount)
|
||||||
|
? i18n`Should be greater than 0`
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
if (!pageState.isRawPayto)
|
if (!pageState.isRawPayto)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -1069,6 +1183,10 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
|
<ShowInputErrorLabel
|
||||||
|
message={errorsWire?.iban}
|
||||||
|
isDirty={submitData?.iban !== undefined}
|
||||||
|
/>
|
||||||
<br />
|
<br />
|
||||||
<label for="subject">{i18n`Transfer subject:`}</label>
|
<label for="subject">{i18n`Transfer subject:`}</label>
|
||||||
<input
|
<input
|
||||||
@ -1086,6 +1204,10 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
|
<ShowInputErrorLabel
|
||||||
|
message={errorsWire?.subject}
|
||||||
|
isDirty={submitData?.subject !== undefined}
|
||||||
|
/>
|
||||||
<br />
|
<br />
|
||||||
<label for="amount">{i18n`Amount:`}</label>
|
<label for="amount">{i18n`Amount:`}</label>
|
||||||
<input
|
<input
|
||||||
@ -1099,7 +1221,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
onInput={(e): void => {
|
onInput={(e): void => {
|
||||||
submitDataSetter((submitData: any) => ({
|
submitDataSetter((submitData: any) => ({
|
||||||
...submitData,
|
...submitData,
|
||||||
amount: e.currentTarget.value.replace(",", "."),
|
amount: e.currentTarget.value,
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -1113,11 +1235,17 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
value={currency}
|
value={currency}
|
||||||
/>
|
/>
|
||||||
|
<ShowInputErrorLabel
|
||||||
|
message={errorsWire?.amount}
|
||||||
|
isDirty={submitData?.amount !== undefined}
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
|
<p style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
class="pure-button pure-button-primary"
|
class="pure-button pure-button-primary"
|
||||||
|
disabled={!!errorsWire}
|
||||||
value="Send"
|
value="Send"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
if (
|
if (
|
||||||
@ -1133,7 +1261,9 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
pageStateSetter((prevState: PageStateType) => ({
|
pageStateSetter((prevState: PageStateType) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: i18n`Field(s) missing.`,
|
error: {
|
||||||
|
title: i18n`Field(s) missing.`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1147,15 +1277,18 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
transactionData,
|
transactionData,
|
||||||
backendState,
|
backendState,
|
||||||
pageStateSetter,
|
pageStateSetter,
|
||||||
() =>
|
() => submitDataSetter((p) => ({})),
|
||||||
submitDataSetter((p) => ({
|
|
||||||
amount: "",
|
|
||||||
iban: "",
|
|
||||||
subject: "",
|
|
||||||
})),
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
type="button"
|
||||||
|
class="pure-button"
|
||||||
|
value="Clear"
|
||||||
|
onClick={async () => {
|
||||||
|
submitDataSetter((p) => ({}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
@ -1175,7 +1308,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const errors = undefinedIfEmpty({
|
const errorsPayto = undefinedIfEmpty({
|
||||||
rawPaytoInput: !rawPaytoInput
|
rawPaytoInput: !rawPaytoInput
|
||||||
? i18n`Missing payto address`
|
? i18n`Missing payto address`
|
||||||
: !parsePaytoUri(rawPaytoInput)
|
: !parsePaytoUri(rawPaytoInput)
|
||||||
@ -1204,7 +1337,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ShowInputErrorLabel
|
<ShowInputErrorLabel
|
||||||
message={errors?.rawPaytoInput}
|
message={errorsPayto?.rawPaytoInput}
|
||||||
isDirty={rawPaytoInputDirty}
|
isDirty={rawPaytoInputDirty}
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
@ -1220,7 +1353,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
|||||||
<input
|
<input
|
||||||
class="pure-button pure-button-primary"
|
class="pure-button pure-button-primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!!errors}
|
disabled={!!errorsPayto}
|
||||||
value={i18n`Send`}
|
value={i18n`Send`}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
// empty string evaluates to false.
|
// empty string evaluates to false.
|
||||||
@ -1323,7 +1456,9 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
|||||||
pageStateSetter((prevState: PageStateType) => ({
|
pageStateSetter((prevState: PageStateType) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: i18n`Answer is wrong.`,
|
error: {
|
||||||
|
title: i18n`Answer is wrong.`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -1366,7 +1501,7 @@ function QrCodeSection({
|
|||||||
}: {
|
}: {
|
||||||
talerWithdrawUri: string;
|
talerWithdrawUri: string;
|
||||||
abortButton: h.JSX.Element;
|
abortButton: h.JSX.Element;
|
||||||
}) {
|
}): VNode {
|
||||||
const i18n = useTranslator();
|
const i18n = useTranslator();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//Taler Wallet WebExtension is listening to headers response and tab updates.
|
//Taler Wallet WebExtension is listening to headers response and tab updates.
|
||||||
@ -1405,8 +1540,12 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
|||||||
class="pure-button"
|
class="pure-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
pageStateSetter((prevState: PageStateType) => {
|
pageStateSetter((prevState: PageStateType) => {
|
||||||
const { withdrawalId, talerWithdrawUri, ...rest } = prevState;
|
return {
|
||||||
return { ...rest, withdrawalInProgress: false };
|
...prevState,
|
||||||
|
withdrawalId: undefined,
|
||||||
|
talerWithdrawUri: undefined,
|
||||||
|
withdrawalInProgress: false,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>{i18n`Abort`}</a>
|
>{i18n`Abort`}</a>
|
||||||
@ -1415,8 +1554,9 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
|||||||
console.log(`Showing withdraw URI: ${talerWithdrawUri}`);
|
console.log(`Showing withdraw URI: ${talerWithdrawUri}`);
|
||||||
// waiting for the wallet:
|
// waiting for the wallet:
|
||||||
|
|
||||||
const { data, error, mutate } = useSWR(
|
const { data, error } = useSWR(
|
||||||
`integration-api/withdrawal-operation/${withdrawalId}`,
|
`integration-api/withdrawal-operation/${withdrawalId}`,
|
||||||
|
{ refreshInterval: 1000 },
|
||||||
);
|
);
|
||||||
|
|
||||||
if (typeof error !== "undefined") {
|
if (typeof error !== "undefined") {
|
||||||
@ -1427,7 +1567,9 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
|||||||
pageStateSetter((prevState: PageStateType) => ({
|
pageStateSetter((prevState: PageStateType) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: i18n`withdrawal (${withdrawalId}) was never (correctly) created at the bank...`,
|
error: {
|
||||||
|
title: i18n`withdrawal (${withdrawalId}) was never (correctly) created at the bank...`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -1453,12 +1595,13 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
|||||||
...rest,
|
...rest,
|
||||||
withdrawalInProgress: false,
|
withdrawalInProgress: false,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
error: i18n`This withdrawal was aborted!`,
|
error: {
|
||||||
|
title: i18n`This withdrawal was aborted!`,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!data.selection_done) {
|
if (!data.selection_done) {
|
||||||
setTimeout(() => mutate(), 1000); // check again after 1 second.
|
|
||||||
return (
|
return (
|
||||||
<QrCodeSection
|
<QrCodeSection
|
||||||
talerWithdrawUri={talerWithdrawUri}
|
talerWithdrawUri={talerWithdrawUri}
|
||||||
@ -1699,10 +1842,7 @@ function LoginForm(Props: any): VNode {
|
|||||||
console.log("login data is undefined", submitData);
|
console.log("login data is undefined", submitData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (!submitData.password || !submitData.username) {
|
||||||
submitData.password.length == 0 ||
|
|
||||||
submitData.username.length == 0
|
|
||||||
) {
|
|
||||||
console.log(
|
console.log(
|
||||||
"username or password is the empty string",
|
"username or password is the empty string",
|
||||||
submitData,
|
submitData,
|
||||||
@ -1716,7 +1856,7 @@ function LoginForm(Props: any): VNode {
|
|||||||
backendStateSetter,
|
backendStateSetter,
|
||||||
pageStateSetter,
|
pageStateSetter,
|
||||||
);
|
);
|
||||||
submitDataSetter(undefined);
|
submitDataSetter({});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18n`Login`}
|
{i18n`Login`}
|
||||||
@ -1963,7 +2103,13 @@ function Account(Props: any): VNode {
|
|||||||
const { accountLabel, backendState } = Props;
|
const { accountLabel, backendState } = Props;
|
||||||
// Getting the bank account balance:
|
// Getting the bank account balance:
|
||||||
const endpoint = `access-api/accounts/${accountLabel}`;
|
const endpoint = `access-api/accounts/${accountLabel}`;
|
||||||
const { data, error } = useSWR(endpoint);
|
const { data, error } = useSWR(endpoint, {
|
||||||
|
// refreshInterval: 0,
|
||||||
|
// revalidateIfStale: false,
|
||||||
|
// revalidateOnMount: false,
|
||||||
|
// revalidateOnFocus: false,
|
||||||
|
// revalidateOnReconnect: false,
|
||||||
|
});
|
||||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||||
const { withdrawalInProgress, withdrawalId, isLoggedIn, talerWithdrawUri } =
|
const { withdrawalInProgress, withdrawalId, isLoggedIn, talerWithdrawUri } =
|
||||||
pageState;
|
pageState;
|
||||||
@ -1978,7 +2124,7 @@ function Account(Props: any): VNode {
|
|||||||
txsPages.push(<Transactions accountLabel={accountLabel} pageNumber={i} />);
|
txsPages.push(<Transactions accountLabel={accountLabel} pageNumber={i} />);
|
||||||
|
|
||||||
if (typeof error !== "undefined") {
|
if (typeof error !== "undefined") {
|
||||||
console.log("account error", error);
|
console.log("account error", error, endpoint);
|
||||||
/**
|
/**
|
||||||
* FIXME: to minimize the code, try only one invocation
|
* FIXME: to minimize the code, try only one invocation
|
||||||
* of pageStateSetter, after having decided the error
|
* of pageStateSetter, after having decided the error
|
||||||
@ -1990,7 +2136,9 @@ function Account(Props: any): VNode {
|
|||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
error: i18n`Username or account label '${accountLabel}' not found. Won't login.`,
|
error: {
|
||||||
|
title: i18n`Username or account label '${accountLabel}' not found. Won't login.`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2014,7 +2162,9 @@ function Account(Props: any): VNode {
|
|||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
error: i18n`Wrong credentials given.`,
|
error: {
|
||||||
|
title: i18n`Wrong credentials given.`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return <p>Wrong credentials...</p>;
|
return <p>Wrong credentials...</p>;
|
||||||
}
|
}
|
||||||
@ -2022,8 +2172,11 @@ function Account(Props: any): VNode {
|
|||||||
pageStateSetter((prevState: PageStateType) => ({
|
pageStateSetter((prevState: PageStateType) => ({
|
||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
isLoggedIn: false,
|
// isLoggedIn: false,
|
||||||
error: i18n`Account information could not be retrieved.`,
|
error: {
|
||||||
|
title: i18n`Account information could not be retrieved.`,
|
||||||
|
debug: JSON.stringify(error),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return <p>Unknown problem...</p>;
|
return <p>Unknown problem...</p>;
|
||||||
}
|
}
|
||||||
@ -2057,7 +2210,8 @@ function Account(Props: any): VNode {
|
|||||||
</BankFrame>
|
</BankFrame>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const balance = parseAmount(data.balance.amount);
|
const balance = Amounts.parseOrThrow(data.balance.amount);
|
||||||
|
const balanceValue = Amounts.stringifyValue(balance);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BankFrame>
|
<BankFrame>
|
||||||
@ -2073,7 +2227,7 @@ function Account(Props: any): VNode {
|
|||||||
<h2>{i18n`Bank account balance`}</h2>
|
<h2>{i18n`Bank account balance`}</h2>
|
||||||
{data.balance.credit_debit_indicator == "debit" ? <b>-</b> : null}
|
{data.balance.credit_debit_indicator == "debit" ? <b>-</b> : null}
|
||||||
<div class="large-amount amount">
|
<div class="large-amount amount">
|
||||||
<span class="value">{`${balance.value}`}</span>
|
<span class="value">{`${balanceValue}`}</span>
|
||||||
<span class="currency">{`${balance.currency}`}</span>
|
<span class="currency">{`${balance.currency}`}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -2292,7 +2446,9 @@ export function BankHome(): VNode {
|
|||||||
...prevState,
|
...prevState,
|
||||||
hasError: true,
|
hasError: true,
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
error: i18n`Page has a problem: logged in but backend state is lost.`,
|
error: {
|
||||||
|
title: i18n`Page has a problem: logged in but backend state is lost.`,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return <p>Error: waiting for details...</p>;
|
return <p>Error: waiting for details...</p>;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user