aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/demobank-ui/src/declaration.d.ts9
-rw-r--r--packages/demobank-ui/src/hooks/backend.ts34
-rw-r--r--packages/demobank-ui/src/pages/home/index.tsx156
-rw-r--r--packages/demobank-ui/src/utils.ts49
-rw-r--r--packages/demobank-ui/tsconfig.json18
5 files changed, 123 insertions, 143 deletions
diff --git a/packages/demobank-ui/src/declaration.d.ts b/packages/demobank-ui/src/declaration.d.ts
index 084686bd2..70272cc54 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -23,15 +23,6 @@ declare module "jed" {
* Type definitions for states and API calls. *
*********************************************/
-/**
- * Has the information to reach and
- * authenticate at the bank's backend.
- */
-interface BackendStateType {
- url?: string;
- username?: string;
- password?: string;
-}
/**
* Request body of POST /transactions.
diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts
new file mode 100644
index 000000000..fa4211f13
--- /dev/null
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -0,0 +1,34 @@
+import { hooks } from "@gnu-taler/web-util/lib/index.browser";
+import { StateUpdater } from "preact/hooks";
+
+
+/**
+ * Has the information to reach and
+ * authenticate at the bank's backend.
+ */
+export interface BackendStateType {
+ url?: string;
+ username?: string;
+ password?: string;
+}
+
+/**
+ * Return getters and setters for
+ * login credentials and backend's
+ * base URL.
+ */
+type BackendStateTypeOpt = BackendStateType | undefined;
+export function useBackendState(
+ state?: BackendStateType,
+): [BackendStateTypeOpt, StateUpdater<BackendStateTypeOpt>] {
+ const ret = hooks.useLocalStorage("backend-state", JSON.stringify(state));
+ const retObj: BackendStateTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0];
+ const retSetter: StateUpdater<BackendStateTypeOpt> = function (val) {
+ const newVal =
+ val instanceof Function
+ ? JSON.stringify(val(retObj))
+ : JSON.stringify(val);
+ ret[1](newVal);
+ };
+ return [retObj, retSetter];
+}
diff --git a/packages/demobank-ui/src/pages/home/index.tsx b/packages/demobank-ui/src/pages/home/index.tsx
index a64c4abe3..49fe9e929 100644
--- a/packages/demobank-ui/src/pages/home/index.tsx
+++ b/packages/demobank-ui/src/pages/home/index.tsx
@@ -15,26 +15,26 @@
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
-import { createContext, Fragment, h, VNode } from "preact";
+import { h, Fragment, VNode } from "preact";
import useSWR, { SWRConfig, useSWRConfig } from "swr";
-import {
- StateUpdater,
- useContext,
- useEffect,
- useRef,
- useState,
-} from "preact/hooks";
-import talerLogo from "../../assets/logo-white.svg";
-import { LangSelectorLikePy as LangSelector } from "../../components/menu/LangSelector.js";
-import { useTranslationContext } from "../../context/translation.js";
import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util";
+import { hooks } from "@gnu-taler/web-util/lib/index.browser";
import { createHashHistory } from "history";
import Router, { Route, route } from "preact-router";
-import { QrCodeSection } from "./QrCodeSection.js";
-import { hooks } from "@gnu-taler/web-util/lib/index.browser";
-import { bankUiSettings } from "../../settings.js";
+import { StateUpdater, useEffect, useRef, useState } from "preact/hooks";
+import talerLogo from "../../assets/logo-white.svg";
+import { LangSelectorLikePy as LangSelector } from "../../components/menu/LangSelector.js";
import { PageStateType, usePageContext } from "../../context/pageState.js";
+import { useTranslationContext } from "../../context/translation.js";
+import { BackendStateType, useBackendState } from "../../hooks/backend.js";
+import { bankUiSettings } from "../../settings.js";
+import { QrCodeSection } from "./QrCodeSection.js";
+import {
+ getBankBackendBaseUrl,
+ getIbanFromPayto,
+ validateAmount,
+} from "../../utils.js";
/**
* FIXME:
@@ -53,22 +53,6 @@ import { PageStateType, usePageContext } from "../../context/pageState.js";
* - Many strings need to be i18n-wrapped.
*/
-/***********
- * Globals *
- **********/
-
-/************
- * Contexts *
- ***********/
-
-/**
- * Bank account specific information.
- */
-// interface AccountStateType {
-// balance: string;
-// /* FIXME: Need history here. */
-// }
-
/************
* Helpers. *
***********/
@@ -92,56 +76,10 @@ function goPublicAccounts(pageStateSetter: StateUpdater<PageStateType>) {
}
/**
- * Validate (the number part of) an amount. If needed,
- * replace comma with a dot. Returns 'false' whenever
- * the input is invalid, the valid amount otherwise.
- */
-function validateAmount(maybeAmount: string): any {
- const amountRegex = "^[0-9]+(.[0-9]+)?$";
- if (!maybeAmount) {
- console.log(`Entered amount (${maybeAmount}) mismatched <input> pattern.`);
- return;
- }
- if (typeof maybeAmount !== "undefined" || maybeAmount !== "") {
- console.log(`Maybe valid amount: ${maybeAmount}`);
- // tolerating comma instead of point.
- const re = RegExp(amountRegex);
- if (!re.test(maybeAmount)) {
- console.log(`Not using invalid amount '${maybeAmount}'.`);
- return false;
- }
- }
- return maybeAmount;
-}
-
-/**
- * Extract IBAN from a Payto URI.
- */
-function getIbanFromPayto(url: string): string {
- const pathSplit = new URL(url).pathname.split("/");
- let lastIndex = pathSplit.length - 1;
- // Happens if the path ends with "/".
- if (pathSplit[lastIndex] === "") lastIndex--;
- const iban = pathSplit[lastIndex];
- return iban;
-}
-
-/**
- * Extract value and currency from a $currency:x.y amount.
- */
-// function parseAmount(val: string): Amount {
-// Amounts.parse(val)
-// const format = /^[A-Z]+:[0-9]+(\.[0-9]+)?$/;
-// if (!format.test(val)) throw Error(`Backend gave invalid amount: ${val}.`);
-// const amountSplit = val.split(":");
-// return { value: amountSplit[1], currency: amountSplit[0] };
-// }
-
-/**
* Get username from the backend state, and throw
* exception if not found.
*/
-function getUsername(backendState: BackendStateTypeOpt): string {
+function getUsername(backendState: BackendStateType | undefined): string {
if (typeof backendState === "undefined")
throw Error("Username can't be found in a undefined backend state.");
@@ -158,7 +96,7 @@ function getUsername(backendState: BackendStateTypeOpt): string {
*/
async function postToBackend(
uri: string,
- backendState: BackendStateTypeOpt,
+ backendState: BackendStateType | undefined,
body: string,
): Promise<any> {
if (typeof backendState === "undefined")
@@ -203,20 +141,6 @@ function prepareHeaders(username?: string, password?: string): Headers {
return headers;
}
-const getBankBackendBaseUrl = (): string => {
- const overrideUrl = localStorage.getItem("bank-base-url");
- if (overrideUrl) {
- console.log(
- `using bank base URL ${overrideUrl} (override via bank-base-url localStorage)`,
- );
- return overrideUrl;
- }
- const maybeRootPath = "https://bank.demo.taler.net/demobanks/default/";
- if (!maybeRootPath.endsWith("/")) return `${maybeRootPath}/`;
- console.log(`using bank base URL (${maybeRootPath})`);
- return maybeRootPath;
-};
-
/*******************
* State managers. *
******************/
@@ -316,27 +240,6 @@ function useCredentialsRequestType(
}
/**
- * Return getters and setters for
- * login credentials and backend's
- * base URL.
- */
-type BackendStateTypeOpt = BackendStateType | undefined;
-function useBackendState(
- state?: BackendStateType,
-): [BackendStateTypeOpt, StateUpdater<BackendStateTypeOpt>] {
- const ret = hooks.useLocalStorage("backend-state", JSON.stringify(state));
- const retObj: BackendStateTypeOpt = ret[0] ? JSON.parse(ret[0]) : ret[0];
- const retSetter: StateUpdater<BackendStateTypeOpt> = function (val) {
- const newVal =
- val instanceof Function
- ? JSON.stringify(val(retObj))
- : JSON.stringify(val);
- ret[1](newVal);
- };
- return [retObj, retSetter];
-}
-
-/**
* Request preparators.
*
* These functions aim at sanitizing the input received
@@ -356,7 +259,7 @@ function useBackendState(
* Abort a withdrawal operation via the Access API's /abort.
*/
async function abortWithdrawalCall(
- backendState: BackendStateTypeOpt,
+ backendState: BackendStateType | undefined,
withdrawalId: string | undefined,
pageStateSetter: StateUpdater<PageStateType>,
): Promise<void> {
@@ -455,7 +358,7 @@ async function abortWithdrawalCall(
* 'page state' and let the related components refresh.
*/
async function confirmWithdrawalCall(
- backendState: BackendStateTypeOpt,
+ backendState: BackendStateType | undefined,
withdrawalId: string | undefined,
pageStateSetter: StateUpdater<PageStateType>,
): Promise<void> {
@@ -554,7 +457,7 @@ async function confirmWithdrawalCall(
*/
async function createTransactionCall(
req: TransactionRequestType,
- backendState: BackendStateTypeOpt,
+ backendState: BackendStateType | undefined,
pageStateSetter: StateUpdater<PageStateType>,
/**
* Optional since the raw payto form doesn't have
@@ -623,9 +526,9 @@ async function createTransactionCall(
* the user about the operation's outcome. (2) use POST helper. */
async function createWithdrawalCall(
amount: string,
- backendState: BackendStateTypeOpt,
+ backendState: BackendStateType | undefined,
pageStateSetter: StateUpdater<PageStateType>,
-) {
+): Promise<void> {
if (typeof backendState === "undefined") {
console.log("Page has a problem: no credentials found in the state.");
pageStateSetter((prevState) => ({
@@ -699,9 +602,9 @@ async function loginCall(
* FIXME: figure out if the two following
* functions can be retrieved from the state.
*/
- backendStateSetter: StateUpdater<BackendStateTypeOpt>,
+ backendStateSetter: StateUpdater<BackendStateType | undefined>,
pageStateSetter: StateUpdater<PageStateType>,
-) {
+): Promise<void> {
/**
* Optimistically setting the state as 'logged in', and
* let the Account component request the balance to check
@@ -732,9 +635,9 @@ async function registrationCall(
* functions can be retrieved somewhat from
* the state.
*/
- backendStateSetter: StateUpdater<BackendStateTypeOpt>,
+ backendStateSetter: StateUpdater<BackendStateType | undefined>,
pageStateSetter: StateUpdater<PageStateType>,
-) {
+): Promise<void> {
let baseUrl = getBankBackendBaseUrl();
/**
* If the base URL doesn't end with slash and the path
@@ -1718,8 +1621,10 @@ function LoginForm(Props: any): VNode {
/**
* Collect and submit registration data.
*/
-function RegistrationForm(Props: any): VNode {
+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 { i18n } = useTranslationContext();
@@ -1823,7 +1728,7 @@ function RegistrationForm(Props: any): VNode {
if (!submitData) return;
registrationCall(
{ ...submitData },
- Props.backendStateSetter, // will store BE URL, if OK.
+ backendStateSetter, // will store BE URL, if OK.
pageStateSetter,
);
console.log("Clearing the input data");
@@ -2278,7 +2183,6 @@ function PublicHistoriesPage(): VNode {
}
function RegistrationPage(): VNode {
- const [backendState, backendStateSetter] = useBackendState();
const { i18n } = useTranslationContext();
if (!bankUiSettings.allowRegistrations) {
return (
@@ -2289,7 +2193,7 @@ function RegistrationPage(): VNode {
}
return (
<BankFrame>
- <RegistrationForm backendStateSetter={backendStateSetter} />
+ <RegistrationForm />
</BankFrame>
);
}
diff --git a/packages/demobank-ui/src/utils.ts b/packages/demobank-ui/src/utils.ts
new file mode 100644
index 000000000..d812f2ec9
--- /dev/null
+++ b/packages/demobank-ui/src/utils.ts
@@ -0,0 +1,49 @@
+
+/**
+ * Validate (the number part of) an amount. If needed,
+ * replace comma with a dot. Returns 'false' whenever
+ * the input is invalid, the valid amount otherwise.
+ */
+export function validateAmount(maybeAmount: string): any {
+ const amountRegex = "^[0-9]+(.[0-9]+)?$";
+ if (!maybeAmount) {
+ console.log(`Entered amount (${maybeAmount}) mismatched <input> pattern.`);
+ return;
+ }
+ if (typeof maybeAmount !== "undefined" || maybeAmount !== "") {
+ console.log(`Maybe valid amount: ${maybeAmount}`);
+ // tolerating comma instead of point.
+ const re = RegExp(amountRegex);
+ if (!re.test(maybeAmount)) {
+ console.log(`Not using invalid amount '${maybeAmount}'.`);
+ return false;
+ }
+ }
+ return maybeAmount;
+}
+
+/**
+ * Extract IBAN from a Payto URI.
+ */
+export function getIbanFromPayto(url: string): string {
+ const pathSplit = new URL(url).pathname.split("/");
+ let lastIndex = pathSplit.length - 1;
+ // Happens if the path ends with "/".
+ if (pathSplit[lastIndex] === "") lastIndex--;
+ const iban = pathSplit[lastIndex];
+ return iban;
+}
+
+export function getBankBackendBaseUrl(): string {
+ const overrideUrl = localStorage.getItem("bank-base-url");
+ if (overrideUrl) {
+ console.log(
+ `using bank base URL ${overrideUrl} (override via bank-base-url localStorage)`,
+ );
+ return overrideUrl;
+ }
+ const maybeRootPath = "https://bank.demo.taler.net/demobanks/default/";
+ if (!maybeRootPath.endsWith("/")) return `${maybeRootPath}/`;
+ console.log(`using bank base URL (${maybeRootPath})`);
+ return maybeRootPath;
+}
diff --git a/packages/demobank-ui/tsconfig.json b/packages/demobank-ui/tsconfig.json
index d9d56ad4f..61be44bf1 100644
--- a/packages/demobank-ui/tsconfig.json
+++ b/packages/demobank-ui/tsconfig.json
@@ -3,16 +3,20 @@
/* Basic Options */
"target": "ES5",
"module": "ES6",
- "lib": ["DOM", "ES2016"],
+ "lib": [
+ "DOM",
+ "ES2016"
+ ],
"allowJs": true /* Allow javascript files to be compiled. */,
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react-jsx" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
"jsxImportSource": "preact",
+ "jsxFactory": "h",
+ "jsxFragmentFactory": "Fragment",
"noEmit": true /* Do not emit outputs. */,
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
-
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
@@ -21,7 +25,6 @@
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
-
/* Module Resolution Options */
"moduleResolution": "Node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
"esModuleInterop": true /* */,
@@ -32,19 +35,18 @@
// "types": [], /* Type declaration files to be included in compilation. */
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
-
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
-
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
-
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */
},
- "include": ["src/**/*"]
-}
+ "include": [
+ "src/**/*"
+ ]
+} \ No newline at end of file