aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/demobank-ui/package.json3
-rw-r--r--packages/demobank-ui/src/components/menu/index.tsx4
-rw-r--r--packages/demobank-ui/src/pages/home/index.tsx307
-rw-r--r--pnpm-lock.yaml2
4 files changed, 182 insertions, 134 deletions
diff --git a/packages/demobank-ui/package.json b/packages/demobank-ui/package.json
index 9de37b9cc..c774105e3 100644
--- a/packages/demobank-ui/package.json
+++ b/packages/demobank-ui/package.json
@@ -10,11 +10,12 @@
"pretty": "prettier --write src"
},
"dependencies": {
+ "@gnu-taler/taler-util": "workspace:*",
"date-fns": "2.29.3",
+ "history": "4.10.1",
"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"
diff --git a/packages/demobank-ui/src/components/menu/index.tsx b/packages/demobank-ui/src/components/menu/index.tsx
index 99d0f7646..6c8292a0c 100644
--- a/packages/demobank-ui/src/components/menu/index.tsx
+++ b/packages/demobank-ui/src/components/menu/index.tsx
@@ -17,8 +17,8 @@
import { ComponentChildren, Fragment, h, VNode } from "preact";
import Match from "preact-router/match";
import { useEffect, useState } from "preact/hooks";
-import { NavigationBar } from "./NavigationBar";
-import { Sidebar } from "./SideBar";
+import { NavigationBar } from "./NavigationBar.js";
+import { Sidebar } from "./SideBar.js";
interface MenuProps {
title: string;
diff --git a/packages/demobank-ui/src/pages/home/index.tsx b/packages/demobank-ui/src/pages/home/index.tsx
index be115412b..568f124b6 100644
--- a/packages/demobank-ui/src/pages/home/index.tsx
+++ b/packages/demobank-ui/src/pages/home/index.tsx
@@ -32,6 +32,8 @@ import { useLocalStorage, useNotNullLocalStorage } from "../../hooks/index.js";
import { Translate, useTranslator } from "../../i18n/index.js";
import "../../scss/main.scss";
import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util";
+import { createHashHistory } from "history";
+import Router, { Route, route } from "preact-router";
interface BankUiSettings {
allowRegistrations: boolean;
@@ -94,7 +96,7 @@ const PageContextDefault: PageContextType = [
isLoggedIn: false,
isRawPayto: false,
showPublicHistories: false,
- tryRegister: false,
+
withdrawalInProgress: false,
},
() => {
@@ -158,7 +160,6 @@ interface WireTransferRequestType {
interface PageStateType {
isLoggedIn: boolean;
isRawPayto: boolean;
- tryRegister: boolean;
showPublicHistories: boolean;
hasError: boolean;
hasInfo: boolean;
@@ -480,7 +481,6 @@ function usePageState(
state: PageStateType = {
isLoggedIn: false,
isRawPayto: false,
- tryRegister: false,
showPublicHistories: false,
hasError: false,
hasInfo: false,
@@ -502,17 +502,18 @@ function usePageState(
//when moving from one page to another
//clean up the info and error bar
function removeLatestInfo(val: any): ReturnType<typeof retSetter> {
- const updater = typeof val === 'function' ? val : (c:any) => val
- return retSetter((current:any) => {
- const cleanedCurrent: PageStateType = {...current,
- hasInfo: false,
- info: undefined,
- hasError: false,
- errors: undefined,
- timestamp: new Date().getTime()
- }
- return updater(cleanedCurrent)
- })
+ const updater = typeof val === "function" ? val : (c: any) => val;
+ return retSetter((current: any) => {
+ const cleanedCurrent: PageStateType = {
+ ...current,
+ hasInfo: false,
+ info: undefined,
+ hasError: false,
+ errors: undefined,
+ timestamp: new Date().getTime(),
+ };
+ return updater(cleanedCurrent);
+ });
}
return [retObj, removeLatestInfo];
@@ -926,7 +927,7 @@ async function registrationCall(
const headers = new Headers();
headers.append("Content-Type", "application/json");
const url = new URL("access-api/testing/register", baseUrl);
- let res: any;
+ let res: Response;
try {
res = await fetch(url.href, {
method: "POST",
@@ -953,19 +954,30 @@ async function registrationCall(
}
if (!res.ok) {
const response = await res.json();
- pageStateSetter((prevState) => ({
- ...prevState,
- hasError: true,
- error: {
- title: `New registration gave response error`,
- debug: JSON.stringify(response),
- },
- }));
+ if (res.status === 409) {
+ pageStateSetter((prevState) => ({
+ ...prevState,
+ hasError: true,
+ error: {
+ title: `That username is already taken`,
+ debug: JSON.stringify(response),
+ },
+ }));
+ } else {
+ pageStateSetter((prevState) => ({
+ ...prevState,
+ hasError: true,
+ error: {
+ title: `New registration gave response error`,
+ debug: JSON.stringify(response),
+ },
+ }));
+ }
} else {
+ // registration was ok
pageStateSetter((prevState) => ({
...prevState,
isLoggedIn: true,
- tryRegister: false,
}));
backendStateSetter((prevState) => ({
...prevState,
@@ -973,6 +985,7 @@ async function registrationCall(
username: req.username,
password: req.password,
}));
+ route("/account");
}
}
@@ -1072,7 +1085,10 @@ function BankFrame(Props: any): VNode {
This part of the demo shows how a bank that supports Taler
directly would work. In addition to using your own bank account,
you can also see the transaction history of some{" "}
- <a href="#" onClick={goPublicAccounts(pageStateSetter)}>
+ <a
+ href="/public-accounts"
+ onClick={goPublicAccounts(pageStateSetter)}
+ >
Public Accounts
</a>
.
@@ -1215,7 +1231,7 @@ function PaytoWireTransfer(Props: any): VNode {
name="subject"
id="subject"
placeholder="subject"
- value={submitData?.subject ?? ""}
+ value={submitData?.subject ?? ""}
required
onInput={(e): void => {
submitDataSetter((submitData: any) => ({
@@ -1237,7 +1253,7 @@ function PaytoWireTransfer(Props: any): VNode {
id="amount"
placeholder="amount"
required
- value={submitData?.amount ?? ""}
+ value={submitData?.amount ?? ""}
pattern={amountRegex}
onInput={(e): void => {
submitDataSetter((submitData: any) => ({
@@ -1298,11 +1314,12 @@ function PaytoWireTransfer(Props: any): VNode {
transactionData,
backendState,
pageStateSetter,
- () => submitDataSetter((p) => ({
- amount: undefined,
- iban: undefined,
- subject: undefined,
- })),
+ () =>
+ submitDataSetter((p) => ({
+ amount: undefined,
+ iban: undefined,
+ subject: undefined,
+ })),
);
}}
/>
@@ -1537,7 +1554,7 @@ function QrCodeSection({
//Taler Wallet WebExtension is listening to headers response and tab updates.
//In the SPA there is no header response with the Taler URI so
//this hack manually triggers the tab update after the QR is in the DOM.
- window.location.href = `${window.location.href.split("#")[0]}#`;
+ window.location.hash = `/account/${new Date().getTime()}`;
}, []);
return (
@@ -1786,10 +1803,7 @@ function RegistrationButton(Props: any): VNode {
<button
class="pure-button pure-button-secondary btn-cancel"
onClick={() => {
- pageStateSetter((prevState: PageStateType) => ({
- ...prevState,
- tryRegister: true,
- }));
+ route("/register");
}}
>
{i18n`Register`}
@@ -1816,14 +1830,12 @@ function LoginForm(Props: any): VNode {
ref.current?.focus();
}, []);
- const errors = !submitData ? undefined : undefinedIfEmpty({
- username: !submitData.username
- ? i18n`Missing username`
- : undefined,
- password: !submitData.password
- ? i18n`Missing password`
- : undefined,
- });
+ const errors = !submitData
+ ? undefined
+ : undefinedIfEmpty({
+ username: !submitData.username ? i18n`Missing username` : undefined,
+ password: !submitData.password ? i18n`Missing password` : undefined,
+ });
return (
<div class="login-div">
@@ -1893,7 +1905,7 @@ function LoginForm(Props: any): VNode {
submitDataSetter({
password: "",
repeatPassword: "",
- username:"",
+ username: "",
});
}}
>
@@ -2043,10 +2055,7 @@ function RegistrationForm(Props: any): VNode {
password: "",
repeatPassword: "",
});
- pageStateSetter((prevState: PageStateType) => ({
- ...prevState,
- tryRegister: false,
- }));
+ route("/account");
}}
>
{i18n`Cancel`}
@@ -2069,8 +2078,8 @@ function Transactions(Props: any): VNode {
`access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`,
);
useEffect(() => {
- mutate()
- }, [balanceValue])
+ mutate();
+ }, [balanceValue]);
if (typeof error !== "undefined") {
console.log("transactions not found error", error);
switch (error.status) {
@@ -2152,12 +2161,17 @@ function Account(Props: any): VNode {
// revalidateOnReconnect: false,
});
const [pageState, setPageState] = useContext(PageContext);
- const { withdrawalInProgress, withdrawalId, isLoggedIn, talerWithdrawUri, timestamp } =
- pageState;
+ const {
+ withdrawalInProgress,
+ withdrawalId,
+ isLoggedIn,
+ talerWithdrawUri,
+ timestamp,
+ } = pageState;
const i18n = useTranslator();
useEffect(() => {
- mutate()
- }, [timestamp])
+ mutate();
+ }, [timestamp]);
/**
* This part shows a list of transactions: with 5 elements by
@@ -2294,7 +2308,11 @@ function Account(Props: any): VNode {
<section id="main">
<article>
<h2>{i18n`Latest transactions:`}</h2>
- <Transactions balanceValue={balanceValue} pageNumber="0" accountLabel={accountLabel} />
+ <Transactions
+ balanceValue={balanceValue}
+ pageNumber="0"
+ accountLabel={accountLabel}
+ />
</article>
</section>
</BankFrame>
@@ -2440,50 +2458,39 @@ function PublicHistories(Props: any): VNode {
);
}
-/**
- * If the user is logged in, it displays
- * the balance, otherwise it offers to login.
- */
-export function BankHome(): VNode {
+function PublicHistoriesPage(): VNode {
+ // const [backendState, backendStateSetter] = useBackendState();
+ const [pageState, pageStateSetter] = usePageState();
+ // const i18n = useTranslator();
+ return (
+ <SWRWithoutCredentials baseUrl={getBankBackendBaseUrl()}>
+ <PageContext.Provider value={[pageState, pageStateSetter]}>
+ <BankFrame>
+ <PublicHistories pageStateSetter={pageStateSetter}>
+ <br />
+ <a
+ class="pure-button"
+ onClick={() => {
+ pageStateSetter((prevState: PageStateType) => ({
+ ...prevState,
+ showPublicHistories: false,
+ }));
+ }}
+ >
+ Go back
+ </a>
+ </PublicHistories>
+ </BankFrame>
+ </PageContext.Provider>
+ </SWRWithoutCredentials>
+ );
+}
+
+function RegistrationPage(): VNode {
const [backendState, backendStateSetter] = useBackendState();
const [pageState, pageStateSetter] = usePageState();
const i18n = useTranslator();
-
- if (pageState.showPublicHistories)
- return (
- <SWRWithoutCredentials baseUrl={getBankBackendBaseUrl()}>
- <PageContext.Provider value={[pageState, pageStateSetter]}>
- <BankFrame>
- <PublicHistories pageStateSetter={pageStateSetter}>
- <br />
- <a
- class="pure-button"
- onClick={() => {
- pageStateSetter((prevState: PageStateType) => ({
- ...prevState,
- showPublicHistories: false,
- }));
- }}
- >
- Go back
- </a>
- </PublicHistories>
- </BankFrame>
- </PageContext.Provider>
- </SWRWithoutCredentials>
- );
-
- if (pageState.tryRegister) {
- console.log("allow registrations?", bankUiSettings.allowRegistrations);
- if (bankUiSettings.allowRegistrations)
- return (
- <PageContext.Provider value={[pageState, pageStateSetter]}>
- <BankFrame>
- <RegistrationForm backendStateSetter={backendStateSetter} />
- </BankFrame>
- </PageContext.Provider>
- );
-
+ if (!bankUiSettings.allowRegistrations) {
return (
<PageContext.Provider value={[pageState, pageStateSetter]}>
<BankFrame>
@@ -2492,44 +2499,82 @@ export function BankHome(): VNode {
</PageContext.Provider>
);
}
- if (pageState.isLoggedIn) {
- if (typeof backendState === "undefined") {
- pageStateSetter((prevState) => ({
- ...prevState,
- hasError: true,
- isLoggedIn: false,
- error: {
- title: i18n`Page has a problem: logged in but backend state is lost.`,
- },
- }));
- return <p>Error: waiting for details...</p>;
- }
- console.log("Showing the profile page..");
+ return (
+ <PageContext.Provider value={[pageState, pageStateSetter]}>
+ <BankFrame>
+ <RegistrationForm backendStateSetter={backendStateSetter} />
+ </BankFrame>
+ </PageContext.Provider>
+ );
+}
+
+function AccountPage(): VNode {
+ const [backendState, backendStateSetter] = useBackendState();
+ const [pageState, pageStateSetter] = usePageState();
+ const i18n = useTranslator();
+
+ if (!pageState.isLoggedIn) {
return (
- <SWRWithCredentials
- username={backendState.username}
- password={backendState.password}
- backendUrl={backendState.url}
- >
- <PageContext.Provider value={[pageState, pageStateSetter]}>
- <Account
- accountLabel={backendState.username}
- backendState={backendState}
+ <PageContext.Provider value={[pageState, pageStateSetter]}>
+ <BankFrame>
+ <h1 class="nav">{i18n`Welcome to ${bankUiSettings.bankName}!`}</h1>
+ <LoginForm
+ pageStateSetter={pageStateSetter}
+ backendStateSetter={backendStateSetter}
/>
- </PageContext.Provider>
- </SWRWithCredentials>
+ </BankFrame>
+ </PageContext.Provider>
);
- } // end of logged-in state.
+ }
+ if (typeof backendState === "undefined") {
+ pageStateSetter((prevState) => ({
+ ...prevState,
+ hasError: true,
+ isLoggedIn: false,
+ error: {
+ title: i18n`Page has a problem: logged in but backend state is lost.`,
+ },
+ }));
+ return <p>Error: waiting for details...</p>;
+ }
+ console.log("Showing the profile page..");
return (
- <PageContext.Provider value={[pageState, pageStateSetter]}>
- <BankFrame>
- <h1 class="nav">{i18n`Welcome to ${bankUiSettings.bankName}!`}</h1>
- <LoginForm
- pageStateSetter={pageStateSetter}
- backendStateSetter={backendStateSetter}
+ <SWRWithCredentials
+ username={backendState.username}
+ password={backendState.password}
+ backendUrl={backendState.url}
+ >
+ <PageContext.Provider value={[pageState, pageStateSetter]}>
+ <Account
+ accountLabel={backendState.username}
+ backendState={backendState}
/>
- </BankFrame>
- </PageContext.Provider>
+ </PageContext.Provider>
+ </SWRWithCredentials>
+ );
+}
+
+function Redirect({ to }: { to: string }): VNode {
+ useEffect(() => {
+ debugger;
+ route(to, true);
+ }, []);
+ return <div>being redirected to {to}</div>;
+}
+
+/**
+ * If the user is logged in, it displays
+ * the balance, otherwise it offers to login.
+ */
+export function BankHome(): VNode {
+ const history = createHashHistory();
+ return (
+ <Router history={history}>
+ <Route path="/public-accounts" component={PublicHistoriesPage} />
+ <Route path="/register" component={RegistrationPage} />
+ <Route path="/account/:id*" component={AccountPage} />
+ <Route default component={Redirect} to="/account" />
+ </Router>
);
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9f48e2689..13ac2650e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -105,6 +105,7 @@ importers:
esbuild-sass-plugin: ^2.4.0
eslint: ^8.26.0
eslint-config-preact: ^1.2.0
+ history: 4.10.1
jed: 1.1.1
po2json: ^0.4.5
preact: 10.6.5
@@ -117,6 +118,7 @@ importers:
dependencies:
'@gnu-taler/taler-util': link:../taler-util
date-fns: 2.29.3
+ history: 4.10.1
jed: 1.1.1
preact: 10.6.5
preact-router: 3.2.1_preact@10.6.5