gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 17/19: no-fix: remove 'any' and login status is take


From: gnunet
Subject: [taler-wallet-core] 17/19: no-fix: remove 'any' and login status is taken from backend
Date: Wed, 07 Dec 2022 20:08:45 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

commit d2554bedf3984ba4eb3a52b5649daa9c7c686c39
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Wed Dec 7 15:44:16 2022 -0300

    no-fix: remove 'any' and login status is taken from backend
---
 packages/demobank-ui/src/components/app.tsx        |  5 +-
 packages/demobank-ui/src/context/backend.ts        | 52 +++++++++++++
 packages/demobank-ui/src/context/pageState.ts      |  3 -
 packages/demobank-ui/src/context/translation.ts    |  4 +-
 packages/demobank-ui/src/hooks/backend.ts          | 60 +++++++++-----
 .../demobank-ui/src/pages/home/AccountPage.tsx     | 91 ++++++++--------------
 packages/demobank-ui/src/pages/home/BankFrame.tsx  | 34 ++++----
 packages/demobank-ui/src/pages/home/LoginForm.tsx  | 28 +++----
 .../src/pages/home/PaytoWireTransferForm.tsx       | 86 ++++++++------------
 .../src/pages/home/PublicHistoriesPage.tsx         | 15 ++--
 .../src/pages/home/RegistrationPage.tsx            | 34 +++-----
 .../home/TalerWithdrawalConfirmationQuestion.tsx   | 21 ++---
 .../src/pages/home/TalerWithdrawalQRCode.tsx       | 12 ++-
 .../demobank-ui/src/pages/home/Transactions.tsx    | 12 ++-
 .../src/pages/home/WalletWithdrawForm.tsx          | 15 ++--
 packages/demobank-ui/src/utils.ts                  | 17 ++--
 16 files changed, 264 insertions(+), 225 deletions(-)

diff --git a/packages/demobank-ui/src/components/app.tsx 
b/packages/demobank-ui/src/components/app.tsx
index f3bc3f571..b6b88f910 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -1,4 +1,5 @@
 import { h, FunctionalComponent } from "preact";
+import { BackendStateProvider } from "../context/backend.js";
 import { PageStateProvider } from "../context/pageState.js";
 import { TranslationProvider } from "../context/translation.js";
 import { Routing } from "../pages/Routing.js";
@@ -24,7 +25,9 @@ const App: FunctionalComponent = () => {
   return (
     <TranslationProvider>
       <PageStateProvider>
-        <Routing />
+        <BackendStateProvider>
+          <Routing />
+        </BackendStateProvider>
       </PageStateProvider>
     </TranslationProvider>
   );
diff --git a/packages/demobank-ui/src/context/backend.ts 
b/packages/demobank-ui/src/context/backend.ts
new file mode 100644
index 000000000..b9b7f8527
--- /dev/null
+++ b/packages/demobank-ui/src/context/backend.ts
@@ -0,0 +1,52 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { ComponentChildren, createContext, h, VNode } from "preact";
+import { useContext } from "preact/hooks";
+import { BackendStateHandler, defaultState, useBackendState } from 
"../hooks/backend.js";
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+export type Type = BackendStateHandler;
+
+const initial: Type = {
+  state: defaultState,
+  clear() {
+    null
+  },
+  save(info) {
+    null
+  },
+};
+const Context = createContext<Type>(initial);
+
+export const useBackendContext = (): Type => useContext(Context);
+
+export const BackendStateProvider = ({
+  children,
+}: {
+  children: ComponentChildren;
+}): VNode => {
+  const value = useBackendState();
+
+  return h(Context.Provider, {
+    value,
+    children,
+  });
+};
\ No newline at end of file
diff --git a/packages/demobank-ui/src/context/pageState.ts 
b/packages/demobank-ui/src/context/pageState.ts
index 4ef21b8f0..b954ad20e 100644
--- a/packages/demobank-ui/src/context/pageState.ts
+++ b/packages/demobank-ui/src/context/pageState.ts
@@ -29,7 +29,6 @@ export type Type = {
 };
 const initial: Type = {
   pageState: {
-    isLoggedIn: false,
     isRawPayto: false,
     withdrawalInProgress: false,
   },
@@ -59,7 +58,6 @@ export const PageStateProvider = ({
  */
 function usePageState(
   state: PageStateType = {
-    isLoggedIn: false,
     isRawPayto: false,
     withdrawalInProgress: false,
   },
@@ -98,7 +96,6 @@ function usePageState(
  * Track page state.
  */
 export interface PageStateType {
-  isLoggedIn: boolean;
   isRawPayto: boolean;
   withdrawalInProgress: boolean;
   error?: {
diff --git a/packages/demobank-ui/src/context/translation.ts 
b/packages/demobank-ui/src/context/translation.ts
index 478bdbde0..0a7e9429d 100644
--- a/packages/demobank-ui/src/context/translation.ts
+++ b/packages/demobank-ui/src/context/translation.ts
@@ -20,7 +20,7 @@
  */
 
 import { i18n, setupI18n } from "@gnu-taler/taler-util";
-import { createContext, h, VNode } from "preact";
+import { ComponentChildren, createContext, h, VNode } from "preact";
 import { useContext, useEffect } from "preact/hooks";
 import { hooks } from "@gnu-taler/web-util/lib/index.browser";
 import { strings } from "../i18n/strings.js";
@@ -60,7 +60,7 @@ const Context = createContext<Type>(initial);
 
 interface Props {
   initial?: string;
-  children: any;
+  children: ComponentChildren;
   forceLang?: string;
 }
 
diff --git a/packages/demobank-ui/src/hooks/backend.ts 
b/packages/demobank-ui/src/hooks/backend.ts
index 3b00edee3..967d5ee85 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -1,33 +1,55 @@
 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;
+export type BackendState = LoggedIn | LoggedOut
+
+export interface BackendInfo {
+  url: string;
+  username: string;
+  password: string;
+}
+
+interface LoggedIn extends BackendInfo {
+  status: "loggedIn"
+}
+interface LoggedOut {
+  status: "loggedOut"
 }
 
+export const defaultState: BackendState = { status: "loggedOut" }
+
+export interface BackendStateHandler {
+  state: BackendState,
+  clear(): void;
+  save(info: BackendInfo): void;
+}
 /**
  * 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];
+export function useBackendState(): BackendStateHandler {
+  const [value, update] = hooks.useLocalStorage("backend-state", 
JSON.stringify(defaultState));
+  // const parsed = value !== undefined ? JSON.parse(value) : value;
+  let parsed
+  try {
+    parsed = JSON.parse(value!)
+  } catch {
+    parsed = undefined
+  }
+  const state: BackendState = !parsed?.status ? defaultState : parsed
+
+  return {
+    state,
+    clear() {
+      update(JSON.stringify(defaultState))
+    },
+    save(info) {
+      const nextState: BackendState = { status: "loggedIn", ...info }
+      update(JSON.stringify(nextState))
+    },
+  }
 }
diff --git a/packages/demobank-ui/src/pages/home/AccountPage.tsx 
b/packages/demobank-ui/src/pages/home/AccountPage.tsx
index 2bc05c332..16ff601ec 100644
--- a/packages/demobank-ui/src/pages/home/AccountPage.tsx
+++ b/packages/demobank-ui/src/pages/home/AccountPage.tsx
@@ -16,14 +16,15 @@
 
 import { Amounts, HttpStatusCode } from "@gnu-taler/taler-util";
 import { hooks } from "@gnu-taler/web-util/lib/index.browser";
-import { h, Fragment, VNode } from "preact";
-import { StateUpdater, useEffect, useState } from "preact/hooks";
+import { ComponentChildren, Fragment, h, VNode } from "preact";
+import { StateUpdater, useEffect } from "preact/hooks";
 import useSWR, { SWRConfig, useSWRConfig } from "swr";
+import { useBackendContext } from "../../context/backend.js";
 import { PageStateType, usePageContext } from "../../context/pageState.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { useBackendState } from "../../hooks/backend.js";
+import { BackendInfo } from "../../hooks/backend.js";
 import { bankUiSettings } from "../../settings.js";
-import { getIbanFromPayto } from "../../utils.js";
+import { getIbanFromPayto, prepareHeaders } from "../../utils.js";
 import { BankFrame } from "./BankFrame.js";
 import { LoginForm } from "./LoginForm.js";
 import { PaymentOptions } from "./PaymentOptions.js";
@@ -31,11 +32,10 @@ import { TalerWithdrawalQRCode } from 
"./TalerWithdrawalQRCode.js";
 import { Transactions } from "./Transactions.js";
 
 export function AccountPage(): VNode {
-  const [backendState, backendStateSetter] = useBackendState();
+  const backend = useBackendContext();
   const { i18n } = useTranslationContext();
-  const { pageState, pageStateSetter } = usePageContext();
 
-  if (!pageState.isLoggedIn) {
+  if (backend.state.status === "loggedOut") {
     return (
       <BankFrame>
         <h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
@@ -44,28 +44,9 @@ export function AccountPage(): VNode {
     );
   }
 
-  if (typeof backendState === "undefined") {
-    pageStateSetter((prevState) => ({
-      ...prevState,
-
-      isLoggedIn: false,
-      error: {
-        title: i18n.str`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 (
-    <SWRWithCredentials
-      username={backendState.username}
-      password={backendState.password}
-      backendUrl={backendState.url}
-    >
-      <Account
-        accountLabel={backendState.username}
-        backendState={backendState}
-      />
+    <SWRWithCredentials info={backend.state}>
+      <Account accountLabel={backend.state.username} />
     </SWRWithCredentials>
   );
 }
@@ -73,16 +54,20 @@ export function AccountPage(): VNode {
 /**
  * Factor out login credentials.
  */
-function SWRWithCredentials(props: any): VNode {
-  const { username, password, backendUrl } = props;
-  const headers = new Headers();
-  headers.append("Authorization", `Basic ${btoa(`${username}:${password}`)}`);
-  console.log("Likely backend base URL", backendUrl);
+function SWRWithCredentials({
+  children,
+  info,
+}: {
+  children: ComponentChildren;
+  info: BackendInfo;
+}): VNode {
+  const { username, password, url: backendUrl } = info;
+  const headers = prepareHeaders(username, password);
   return (
     <SWRConfig
       value={{
         fetcher: (url: string) => {
-          return fetch(backendUrl + url || "", { headers }).then((r) => {
+          return fetch(new URL(url, backendUrl).href, { headers }).then((r) => 
{
             if (!r.ok) throw { status: r.status, json: r.json() };
 
             return r.json();
@@ -90,7 +75,7 @@ function SWRWithCredentials(props: any): VNode {
         },
       }}
     >
-      {props.children}
+      {children as any}
     </SWRConfig>
   );
 }
@@ -100,9 +85,9 @@ function SWRWithCredentials(props: any): VNode {
  * is mostly needed to provide the user's credentials to POST
  * to the bank.
  */
-function Account(Props: any): VNode {
+function Account({ accountLabel }: { accountLabel: string }): VNode {
   const { cache } = useSWRConfig();
-  const { accountLabel, backendState } = Props;
+
   // Getting the bank account balance:
   const endpoint = `access-api/accounts/${accountLabel}`;
   const { data, error, mutate } = useSWR(endpoint, {
@@ -112,14 +97,9 @@ function Account(Props: any): VNode {
     // revalidateOnFocus: false,
     // revalidateOnReconnect: false,
   });
+  const backend = useBackendContext();
   const { pageState, pageStateSetter: setPageState } = usePageContext();
-  const {
-    withdrawalInProgress,
-    withdrawalId,
-    isLoggedIn,
-    talerWithdrawUri,
-    timestamp,
-  } = pageState;
+  const { withdrawalId, talerWithdrawUri, timestamp } = pageState;
   const { i18n } = useTranslationContext();
   useEffect(() => {
     mutate();
@@ -129,10 +109,11 @@ function Account(Props: any): VNode {
    * This part shows a list of transactions: with 5 elements by
    * default and offers a "load more" button.
    */
-  const [txPageNumber, setTxPageNumber] = useTransactionPageNumber();
-  const txsPages = [];
-  for (let i = 0; i <= txPageNumber; i++)
-    txsPages.push(<Transactions accountLabel={accountLabel} pageNumber={i} />);
+  // const [txPageNumber, setTxPageNumber] = useTransactionPageNumber();
+  // const txsPages = [];
+  // for (let i = 0; i <= txPageNumber; i++) {
+  //   txsPages.push(<Transactions accountLabel={accountLabel} pageNumber={i} 
/>);
+  // }
 
   if (typeof error !== "undefined") {
     console.log("account error", error, endpoint);
@@ -143,10 +124,10 @@ function Account(Props: any): VNode {
      */
     switch (error.status) {
       case 404: {
+        backend.clear();
         setPageState((prevState: PageStateType) => ({
           ...prevState,
 
-          isLoggedIn: false,
           error: {
             title: i18n.str`Username or account label '${accountLabel}' not 
found.  Won't login.`,
           },
@@ -170,10 +151,9 @@ function Account(Props: any): VNode {
       }
       case HttpStatusCode.Unauthorized:
       case HttpStatusCode.Forbidden: {
+        backend.clear();
         setPageState((prevState: PageStateType) => ({
           ...prevState,
-
-          isLoggedIn: false,
           error: {
             title: i18n.str`Wrong credentials given.`,
           },
@@ -181,10 +161,9 @@ function Account(Props: any): VNode {
         return <p>Wrong credentials...</p>;
       }
       default: {
+        backend.clear();
         setPageState((prevState: PageStateType) => ({
           ...prevState,
-
-          isLoggedIn: false,
           error: {
             title: i18n.str`Account information could not be retrieved.`,
             debug: JSON.stringify(error),
@@ -211,13 +190,11 @@ function Account(Props: any): VNode {
    * the outcome.
    */
   console.log(`maybe new withdrawal ${talerWithdrawUri}`);
-  if (talerWithdrawUri) {
+  if (talerWithdrawUri && withdrawalId) {
     console.log("Bank created a new Taler withdrawal");
     return (
       <BankFrame>
         <TalerWithdrawalQRCode
-          accountLabel={accountLabel}
-          backendState={backendState}
           withdrawalId={withdrawalId}
           talerWithdrawUri={talerWithdrawUri}
         />
@@ -266,7 +243,7 @@ function Account(Props: any): VNode {
           <h2>{i18n.str`Latest transactions:`}</h2>
           <Transactions
             balanceValue={balanceValue}
-            pageNumber="0"
+            pageNumber={0}
             accountLabel={accountLabel}
           />
         </article>
diff --git a/packages/demobank-ui/src/pages/home/BankFrame.tsx 
b/packages/demobank-ui/src/pages/home/BankFrame.tsx
index 3b099e34b..f6b8fbd96 100644
--- a/packages/demobank-ui/src/pages/home/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/home/BankFrame.tsx
@@ -14,15 +14,21 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Fragment, h, VNode } from "preact";
+import { ComponentChildren, Fragment, h, VNode } from "preact";
 import talerLogo from "../../assets/logo-white.svg";
 import { LangSelectorLikePy as LangSelector } from 
"../../components/menu/LangSelector.js";
+import { useBackendContext } from "../../context/backend.js";
 import { PageStateType, usePageContext } from "../../context/pageState.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { bankUiSettings } from "../../settings.js";
 
-export function BankFrame(Props: any): VNode {
+export function BankFrame({
+  children,
+}: {
+  children: ComponentChildren;
+}): VNode {
   const { i18n } = useTranslationContext();
+  const backend = useBackendContext();
   const { pageState, pageStateSetter } = usePageContext();
   console.log("BankFrame state", pageState);
   const logOut = (
@@ -33,9 +39,9 @@ export function BankFrame(Props: any): VNode {
         onClick={() => {
           pageStateSetter((prevState: PageStateType) => {
             const { talerWithdrawUri, withdrawalId, ...rest } = prevState;
+            backend.clear();
             return {
               ...rest,
-              isLoggedIn: false,
               withdrawalInProgress: false,
               error: undefined,
               info: undefined,
@@ -98,10 +104,10 @@ export function BankFrame(Props: any): VNode {
         </nav>
       </div>
       <section id="main" class="content">
-        <ErrorBanner pageState={[pageState, pageStateSetter]} />
-        <StatusBanner pageState={[pageState, pageStateSetter]} />
-        {pageState.isLoggedIn ? logOut : null}
-        {Props.children}
+        <ErrorBanner />
+        <StatusBanner />
+        {backend.state.status === "loggedIn" ? logOut : null}
+        {children}
       </section>
       <section id="footer" class="footer">
         <div class="footer">
@@ -127,9 +133,9 @@ function maybeDemoContent(content: VNode): VNode {
   return <Fragment />;
 }
 
-function ErrorBanner(Props: any): VNode | null {
-  const [pageState, pageStateSetter] = Props.pageState;
-  // const { i18n } = useTranslationContext();
+function ErrorBanner(): VNode | null {
+  const { pageState, pageStateSetter } = usePageContext();
+
   if (!pageState.error) return null;
 
   const rval = (
@@ -144,7 +150,7 @@ function ErrorBanner(Props: any): VNode | null {
             class="pure-button"
             value="Clear"
             onClick={async () => {
-              pageStateSetter((prev: any) => ({ ...prev, error: undefined }));
+              pageStateSetter((prev) => ({ ...prev, error: undefined }));
             }}
           />
         </div>
@@ -156,8 +162,8 @@ function ErrorBanner(Props: any): VNode | null {
   return rval;
 }
 
-function StatusBanner(Props: any): VNode | null {
-  const [pageState, pageStateSetter] = Props.pageState;
+function StatusBanner(): VNode | null {
+  const { pageState, pageStateSetter } = usePageContext();
   if (!pageState.info) return null;
 
   const rval = (
@@ -172,7 +178,7 @@ function StatusBanner(Props: any): VNode | null {
             class="pure-button"
             value="Clear"
             onClick={async () => {
-              pageStateSetter((prev: any) => ({ ...prev, info: undefined }));
+              pageStateSetter((prev) => ({ ...prev, info: undefined }));
             }}
           />
         </div>
diff --git a/packages/demobank-ui/src/pages/home/LoginForm.tsx 
b/packages/demobank-ui/src/pages/home/LoginForm.tsx
index f60c9f600..f31f91190 100644
--- a/packages/demobank-ui/src/pages/home/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/home/LoginForm.tsx
@@ -16,10 +16,10 @@
 
 import { h, VNode } from "preact";
 import { route } from "preact-router";
-import { StateUpdater, useEffect, useRef, useState } from "preact/hooks";
-import { PageStateType, usePageContext } from "../../context/pageState.js";
+import { useEffect, useRef, useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { BackendStateType, useBackendState } from "../../hooks/backend.js";
+import { BackendStateHandler } from "../../hooks/backend.js";
 import { bankUiSettings } from "../../settings.js";
 import { getBankBackendBaseUrl, undefinedIfEmpty } from "../../utils.js";
 import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
@@ -28,8 +28,7 @@ import { ShowInputErrorLabel } from 
"./ShowInputErrorLabel.js";
  * Collect and submit login data.
  */
 export function LoginForm(): VNode {
-  const [backendState, backendStateSetter] = useBackendState();
-  const { pageState, pageStateSetter } = usePageContext();
+  const backend = useBackendContext();
   const [username, setUsername] = useState<string | undefined>();
   const [password, setPassword] = useState<string | undefined>();
   const { i18n } = useTranslationContext();
@@ -93,11 +92,7 @@ export function LoginForm(): VNode {
             disabled={!!errors}
             onClick={() => {
               if (!username || !password) return;
-              loginCall(
-                { username, password },
-                backendStateSetter,
-                pageStateSetter,
-              );
+              loginCall({ username, password }, backend);
               setUsername(undefined);
               setPassword(undefined);
             }}
@@ -129,21 +124,16 @@ async function loginCall(
    * FIXME: figure out if the two following
    * functions can be retrieved from the state.
    */
-  backendStateSetter: StateUpdater<BackendStateType | undefined>,
-  pageStateSetter: StateUpdater<PageStateType>,
+  backend: BackendStateHandler,
 ): Promise<void> {
   /**
    * Optimistically setting the state as 'logged in', and
    * let the Account component request the balance to check
    * whether the credentials are valid.  */
-  pageStateSetter((prevState) => ({ ...prevState, isLoggedIn: true }));
-  let baseUrl = getBankBackendBaseUrl();
-  if (!baseUrl.endsWith("/")) baseUrl += "/";
 
-  backendStateSetter((prevState) => ({
-    ...prevState,
-    url: baseUrl,
+  backend.save({
+    url: getBankBackendBaseUrl(),
     username: req.username,
     password: req.password,
-  }));
+  });
 }
diff --git a/packages/demobank-ui/src/pages/home/PaytoWireTransferForm.tsx 
b/packages/demobank-ui/src/pages/home/PaytoWireTransferForm.tsx
index 45e7cf5ca..e4fe386ff 100644
--- a/packages/demobank-ui/src/pages/home/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/home/PaytoWireTransferForm.tsx
@@ -18,9 +18,10 @@ import { Amounts, parsePaytoUri } from 
"@gnu-taler/taler-util";
 import { hooks } from "@gnu-taler/web-util/lib/index.browser";
 import { h, VNode } from "preact";
 import { StateUpdater, useEffect, useRef, useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { PageStateType, usePageContext } from "../../context/pageState.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { BackendStateType, useBackendState } from "../../hooks/backend.js";
+import { BackendState } from "../../hooks/backend.js";
 import { prepareHeaders, undefinedIfEmpty } from "../../utils.js";
 import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";
 
@@ -31,7 +32,7 @@ export function PaytoWireTransferForm({
   focus?: boolean;
   currency?: string;
 }): VNode {
-  const [backendState, backendStateSetter] = useBackendState();
+  const backend = useBackendContext();
   const { pageState, pageStateSetter } = usePageContext(); // NOTE: used for 
go-back button?
 
   const [submitData, submitDataSetter] = useWireTransferRequestType();
@@ -81,7 +82,7 @@ export function PaytoWireTransferForm({
               required
               pattern={ibanRegex}
               onInput={(e): void => {
-                submitDataSetter((submitData: any) => ({
+                submitDataSetter((submitData) => ({
                   ...submitData,
                   iban: e.currentTarget.value,
                 }));
@@ -102,7 +103,7 @@ export function PaytoWireTransferForm({
               value={submitData?.subject ?? ""}
               required
               onInput={(e): void => {
-                submitDataSetter((submitData: any) => ({
+                submitDataSetter((submitData) => ({
                   ...submitData,
                   subject: e.currentTarget.value,
                 }));
@@ -133,7 +134,7 @@ export function PaytoWireTransferForm({
               required
               value={submitData?.amount ?? ""}
               onInput={(e): void => {
-                submitDataSetter((submitData: any) => ({
+                submitDataSetter((submitData) => ({
                   ...submitData,
                   amount: e.currentTarget.value,
                 }));
@@ -179,7 +180,7 @@ export function PaytoWireTransferForm({
                 };
                 return await createTransactionCall(
                   transactionData,
-                  backendState,
+                  backend.state,
                   pageStateSetter,
                   () =>
                     submitDataSetter((p) => ({
@@ -209,7 +210,7 @@ export function PaytoWireTransferForm({
             href="/account"
             onClick={() => {
               console.log("switch to raw payto form");
-              pageStateSetter((prevState: any) => ({
+              pageStateSetter((prevState) => ({
                 ...prevState,
                 isRawPayto: true,
               }));
@@ -283,7 +284,7 @@ export function PaytoWireTransferForm({
 
               return await createTransactionCall(
                 transactionData,
-                backendState,
+                backend.state,
                 pageStateSetter,
                 () => rawPaytoInputSetter(undefined),
               );
@@ -295,7 +296,7 @@ export function PaytoWireTransferForm({
             href="/account"
             onClick={() => {
               console.log("switch to wire-transfer-form");
-              pageStateSetter((prevState: any) => ({
+              pageStateSetter((prevState) => ({
                 ...prevState,
                 isRawPayto: false,
               }));
@@ -345,7 +346,7 @@ function useWireTransferRequestType(
  */
 async function createTransactionCall(
   req: TransactionRequestType,
-  backendState: BackendStateType | undefined,
+  backendState: BackendState,
   pageStateSetter: StateUpdater<PageStateType>,
   /**
    * Optional since the raw payto form doesn't have
@@ -353,13 +354,30 @@ async function createTransactionCall(
    */
   cleanUpForm: () => void,
 ): Promise<void> {
-  let res: any;
+  if (backendState.status === "loggedOut") {
+    console.log("No credentials found.");
+    pageStateSetter((prevState) => ({
+      ...prevState,
+
+      error: {
+        title: "No credentials found.",
+      },
+    }));
+    return;
+  }
+  let res: Response;
   try {
-    res = await postToBackend(
-      `access-api/accounts/${getUsername(backendState)}/transactions`,
-      backendState,
-      JSON.stringify(req),
+    const { username, password } = backendState;
+    const headers = prepareHeaders(username, password);
+    const url = new URL(
+      `access-api/accounts/${backendState.username}/transactions`,
+      backendState.url,
     );
+    res = await fetch(url.href, {
+      method: "POST",
+      headers,
+      body: JSON.stringify(req),
+    });
   } catch (error) {
     console.log("Could not POST transaction request to the bank", error);
     pageStateSetter((prevState) => ({
@@ -402,41 +420,3 @@ async function createTransactionCall(
   // be discarded.
   cleanUpForm();
 }
-
-/**
- * Get username from the backend state, and throw
- * exception if not found.
- */
-function getUsername(backendState: BackendStateType | undefined): string {
-  if (typeof backendState === "undefined")
-    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;
-}
-
-/**
- * Helps extracting the credentials from the state
- * and wraps the actual call to 'fetch'.  Should be
- * enclosed in a try-catch block by the caller.
- */
-async function postToBackend(
-  uri: string,
-  backendState: BackendStateType | undefined,
-  body: string,
-): Promise<any> {
-  if (typeof backendState === "undefined")
-    throw Error("Credentials can't be found in a undefined backend state.");
-
-  const { username, password } = backendState;
-  const headers = prepareHeaders(username, password);
-  // Backend URL must have been stored _with_ a final slash.
-  const url = new URL(uri, backendState.url);
-  return await fetch(url.href, {
-    method: "POST",
-    headers,
-    body,
-  });
-}
diff --git a/packages/demobank-ui/src/pages/home/PublicHistoriesPage.tsx 
b/packages/demobank-ui/src/pages/home/PublicHistoriesPage.tsx
index 215dc7321..a8028f3bf 100644
--- a/packages/demobank-ui/src/pages/home/PublicHistoriesPage.tsx
+++ b/packages/demobank-ui/src/pages/home/PublicHistoriesPage.tsx
@@ -15,7 +15,7 @@
  */
 
 import { hooks } from "@gnu-taler/web-util/lib/index.browser";
-import { Fragment, h, VNode } from "preact";
+import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { route } from "preact-router";
 import { StateUpdater } from "preact/hooks";
 import useSWR, { SWRConfig } from "swr";
@@ -35,8 +35,13 @@ export function PublicHistoriesPage(): VNode {
   );
 }
 
-function SWRWithoutCredentials(Props: any): VNode {
-  const { baseUrl } = Props;
+function SWRWithoutCredentials({
+  baseUrl,
+  children,
+}: {
+  children: ComponentChildren;
+  baseUrl: string;
+}): VNode {
   console.log("Base URL", baseUrl);
   return (
     <SWRConfig
@@ -49,7 +54,7 @@ function SWRWithoutCredentials(Props: any): VNode {
           }),
       }}
     >
-      {Props.children}
+      {children as any}
     </SWRConfig>
   );
 }
@@ -93,7 +98,7 @@ function PublicHistories(): VNode {
     }
   }
   if (!data) return <p>Waiting public accounts list...</p>;
-  const txs: any = {};
+  const txs: Record<string, h.JSX.Element> = {};
   const accountsBar = [];
 
   /**
diff --git a/packages/demobank-ui/src/pages/home/RegistrationPage.tsx 
b/packages/demobank-ui/src/pages/home/RegistrationPage.tsx
index 9a120cb4f..08e9bd480 100644
--- a/packages/demobank-ui/src/pages/home/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/home/RegistrationPage.tsx
@@ -16,9 +16,10 @@
 import { Fragment, h, VNode } from "preact";
 import { route } from "preact-router";
 import { StateUpdater, useState } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { PageStateType, usePageContext } from "../../context/pageState.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { BackendStateType, useBackendState } from "../../hooks/backend.js";
+import { BackendStateHandler } from "../../hooks/backend.js";
 import { bankUiSettings } from "../../settings.js";
 import { getBankBackendBaseUrl, undefinedIfEmpty } from "../../utils.js";
 import { BankFrame } from "./BankFrame.js";
@@ -44,7 +45,7 @@ export function RegistrationPage(): VNode {
  * Collect and submit registration data.
  */
 function RegistrationForm(): VNode {
-  const [backendState, backendStateSetter] = useBackendState();
+  const backend = useBackendContext();
   const { pageState, pageStateSetter } = usePageContext();
   const [username, setUsername] = useState<string | undefined>();
   const [password, setPassword] = useState<string | undefined>();
@@ -132,7 +133,7 @@ function RegistrationForm(): VNode {
                   if (!username || !password) return;
                   registrationCall(
                     { username, password },
-                    backendStateSetter, // will store BE URL, if OK.
+                    backend, // will store BE URL, if OK.
                     pageStateSetter,
                   );
 
@@ -177,23 +178,17 @@ async function registrationCall(
    * functions can be retrieved somewhat from
    * the state.
    */
-  backendStateSetter: StateUpdater<BackendStateType | undefined>,
+  backend: BackendStateHandler,
   pageStateSetter: StateUpdater<PageStateType>,
 ): Promise<void> {
-  let baseUrl = getBankBackendBaseUrl();
-  /**
-   * If the base URL doesn't end with slash and the path
-   * is not empty, then the concatenation made by URL()
-   * drops the last path element.
-   */
-  if (!baseUrl.endsWith("/")) baseUrl += "/";
+  const url = getBankBackendBaseUrl();
 
   const headers = new Headers();
   headers.append("Content-Type", "application/json");
-  const url = new URL("access-api/testing/register", baseUrl);
+  const registerEndpoint = new URL("access-api/testing/register", url);
   let res: Response;
   try {
-    res = await fetch(url.href, {
+    res = await fetch(registerEndpoint.href, {
       method: "POST",
       body: JSON.stringify({
         username: req.username,
@@ -203,7 +198,7 @@ async function registrationCall(
     });
   } catch (error) {
     console.log(
-      `Could not POST new registration to the bank (${url.href})`,
+      `Could not POST new registration to the bank (${registerEndpoint.href})`,
       error,
     );
     pageStateSetter((prevState) => ({
@@ -239,16 +234,11 @@ async function registrationCall(
     }
   } else {
     // registration was ok
-    pageStateSetter((prevState) => ({
-      ...prevState,
-      isLoggedIn: true,
-    }));
-    backendStateSetter((prevState) => ({
-      ...prevState,
-      url: baseUrl,
+    backend.save({
+      url,
       username: req.username,
       password: req.password,
-    }));
+    });
     route("/account");
   }
 }
diff --git 
a/packages/demobank-ui/src/pages/home/TalerWithdrawalConfirmationQuestion.tsx 
b/packages/demobank-ui/src/pages/home/TalerWithdrawalConfirmationQuestion.tsx
index e3d8957b8..4fd46878b 100644
--- 
a/packages/demobank-ui/src/pages/home/TalerWithdrawalConfirmationQuestion.tsx
+++ 
b/packages/demobank-ui/src/pages/home/TalerWithdrawalConfirmationQuestion.tsx
@@ -1,17 +1,18 @@
 import { Fragment, h, VNode } from "preact";
 import { StateUpdater } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { PageStateType, usePageContext } from "../../context/pageState.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { BackendStateType } from "../../hooks/backend.js";
+import { BackendState } from "../../hooks/backend.js";
 import { prepareHeaders } from "../../utils.js";
 
 /**
  * Additional authentication required to complete the operation.
  * Not providing a back button, only abort.
  */
-export function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
+export function TalerWithdrawalConfirmationQuestion(): VNode {
   const { pageState, pageStateSetter } = usePageContext();
-  const { backendState } = Props;
+  const backend = useBackendContext();
   const { i18n } = useTranslationContext();
   const captchaNumbers = {
     a: Math.floor(Math.random() * 10),
@@ -57,7 +58,7 @@ export function TalerWithdrawalConfirmationQuestion(Props: 
any): VNode {
                       (captchaNumbers.a + captchaNumbers.b).toString()
                     ) {
                       confirmWithdrawalCall(
-                        backendState,
+                        backend.state,
                         pageState.withdrawalId,
                         pageStateSetter,
                       );
@@ -79,7 +80,7 @@ export function TalerWithdrawalConfirmationQuestion(Props: 
any): VNode {
                   class="pure-button pure-button-secondary btn-cancel"
                   onClick={async () =>
                     await abortWithdrawalCall(
-                      backendState,
+                      backend.state,
                       pageState.withdrawalId,
                       pageStateSetter,
                     )
@@ -116,11 +117,11 @@ export function 
TalerWithdrawalConfirmationQuestion(Props: any): VNode {
  * 'page state' and let the related components refresh.
  */
 async function confirmWithdrawalCall(
-  backendState: BackendStateType | undefined,
+  backendState: BackendState,
   withdrawalId: string | undefined,
   pageStateSetter: StateUpdater<PageStateType>,
 ): Promise<void> {
-  if (typeof backendState === "undefined") {
+  if (backendState.status === "loggedOut") {
     console.log("No credentials found.");
     pageStateSetter((prevState) => ({
       ...prevState,
@@ -211,11 +212,11 @@ async function confirmWithdrawalCall(
  * Abort a withdrawal operation via the Access API's /abort.
  */
 async function abortWithdrawalCall(
-  backendState: BackendStateType | undefined,
+  backendState: BackendState,
   withdrawalId: string | undefined,
   pageStateSetter: StateUpdater<PageStateType>,
 ): Promise<void> {
-  if (typeof backendState === "undefined") {
+  if (backendState.status === "loggedOut") {
     console.log("No credentials found.");
     pageStateSetter((prevState) => ({
       ...prevState,
@@ -237,7 +238,7 @@ async function abortWithdrawalCall(
     }));
     return;
   }
-  let res: any;
+  let res: Response;
   try {
     const { username, password } = backendState;
     const headers = prepareHeaders(username, password);
diff --git a/packages/demobank-ui/src/pages/home/TalerWithdrawalQRCode.tsx 
b/packages/demobank-ui/src/pages/home/TalerWithdrawalQRCode.tsx
index da4ccc45e..848a9c45c 100644
--- a/packages/demobank-ui/src/pages/home/TalerWithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/home/TalerWithdrawalQRCode.tsx
@@ -1,5 +1,6 @@
 import { Fragment, h, VNode } from "preact";
 import useSWR from "swr";
+import { useBackendContext } from "../../context/backend.js";
 import { PageStateType, usePageContext } from "../../context/pageState.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { QrCodeSection } from "./QrCodeSection.js";
@@ -10,10 +11,15 @@ import { TalerWithdrawalConfirmationQuestion } from 
"./TalerWithdrawalConfirmati
  * permit the passing of exchange and reserve details to
  * the bank.  Poll the backend until such operation is done.
  */
-export function TalerWithdrawalQRCode(Props: any): VNode {
+export function TalerWithdrawalQRCode({
+  withdrawalId,
+  talerWithdrawUri,
+}: {
+  withdrawalId: string;
+  talerWithdrawUri: string;
+}): VNode {
   // turns true when the wallet POSTed the reserve details:
   const { pageState, pageStateSetter } = usePageContext();
-  const { withdrawalId, talerWithdrawUri, backendState } = Props;
   const { i18n } = useTranslationContext();
   const abortButton = (
     <a
@@ -93,5 +99,5 @@ export function TalerWithdrawalQRCode(Props: any): VNode {
    * Wallet POSTed the withdrawal details!  Ask the
    * user to authorize the operation (here CAPTCHA).
    */
-  return <TalerWithdrawalConfirmationQuestion backendState={backendState} />;
+  return <TalerWithdrawalConfirmationQuestion />;
 }
diff --git a/packages/demobank-ui/src/pages/home/Transactions.tsx 
b/packages/demobank-ui/src/pages/home/Transactions.tsx
index eb344403f..c0bb86024 100644
--- a/packages/demobank-ui/src/pages/home/Transactions.tsx
+++ b/packages/demobank-ui/src/pages/home/Transactions.tsx
@@ -10,14 +10,20 @@ export function Transactions({
   pageNumber,
   accountLabel,
   balanceValue,
-}: any): VNode {
+}: {
+  pageNumber: number;
+  accountLabel: string;
+  balanceValue?: string;
+}): VNode {
   const { i18n } = useTranslationContext();
   const { data, error, mutate } = useSWR(
     `access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`,
   );
   useEffect(() => {
-    mutate();
-  }, [balanceValue]);
+    if (balanceValue) {
+      mutate();
+    }
+  }, [balanceValue ?? ""]);
   if (typeof error !== "undefined") {
     console.log("transactions not found error", error);
     switch (error.status) {
diff --git a/packages/demobank-ui/src/pages/home/WalletWithdrawForm.tsx 
b/packages/demobank-ui/src/pages/home/WalletWithdrawForm.tsx
index 842f14a5f..ee43d2006 100644
--- a/packages/demobank-ui/src/pages/home/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/home/WalletWithdrawForm.tsx
@@ -16,9 +16,10 @@
 
 import { h, VNode } from "preact";
 import { StateUpdater, useEffect, useRef } from "preact/hooks";
+import { useBackendContext } from "../../context/backend.js";
 import { PageStateType, usePageContext } from "../../context/pageState.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { BackendStateType, useBackendState } from "../../hooks/backend.js";
+import { BackendState } from "../../hooks/backend.js";
 import { prepareHeaders, validateAmount } from "../../utils.js";
 
 export function WalletWithdrawForm({
@@ -28,10 +29,10 @@ export function WalletWithdrawForm({
   currency?: string;
   focus?: boolean;
 }): VNode {
-  const [backendState, backendStateSetter] = useBackendState();
+  const backend = useBackendContext();
   const { pageState, pageStateSetter } = usePageContext();
   const { i18n } = useTranslationContext();
-  let submitAmount = "5.00";
+  let submitAmount: string | undefined = "5.00";
 
   const ref = useRef<HTMLInputElement>(null);
   useEffect(() => {
@@ -83,7 +84,7 @@ export function WalletWithdrawForm({
               if (!submitAmount && currency) return;
               createWithdrawalCall(
                 `${currency}:${submitAmount}`,
-                backendState,
+                backend.state,
                 pageStateSetter,
               );
             }}
@@ -105,10 +106,10 @@ export function WalletWithdrawForm({
  * the user about the operation's outcome.  (2) use POST helper.  */
 async function createWithdrawalCall(
   amount: string,
-  backendState: BackendStateType | undefined,
+  backendState: BackendState,
   pageStateSetter: StateUpdater<PageStateType>,
 ): Promise<void> {
-  if (typeof backendState === "undefined") {
+  if (backendState?.status === "loggedOut") {
     console.log("Page has a problem: no credentials found in the state.");
     pageStateSetter((prevState) => ({
       ...prevState,
@@ -120,7 +121,7 @@ async function createWithdrawalCall(
     return;
   }
 
-  let res: any;
+  let res: Response;
   try {
     const { username, password } = backendState;
     const headers = prepareHeaders(username, password);
diff --git a/packages/demobank-ui/src/utils.ts 
b/packages/demobank-ui/src/utils.ts
index 23cade0e8..d74f4d129 100644
--- a/packages/demobank-ui/src/utils.ts
+++ b/packages/demobank-ui/src/utils.ts
@@ -1,9 +1,11 @@
+import { canonicalizeBaseUrl } from "@gnu-taler/taler-util";
+
 /**
  * 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 {
+export function validateAmount(maybeAmount: string | undefined): string | 
undefined {
   const amountRegex = "^[0-9]+(.[0-9]+)?$";
   if (!maybeAmount) {
     console.log(`Entered amount (${maybeAmount}) mismatched <input> pattern.`);
@@ -15,7 +17,7 @@ export function validateAmount(maybeAmount: string): any {
     const re = RegExp(amountRegex);
     if (!re.test(maybeAmount)) {
       console.log(`Not using invalid amount '${maybeAmount}'.`);
-      return false;
+      return;
     }
   }
   return maybeAmount;
@@ -33,18 +35,19 @@ export function getIbanFromPayto(url: string): string {
   return iban;
 }
 
+const maybeRootPath = "https://bank.demo.taler.net/demobanks/default/";;
+
 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;
+  } else {
+    console.log(`using bank base URL (${maybeRootPath})`);
   }
-  const maybeRootPath = "https://bank.demo.taler.net/demobanks/default/";;
-  if (!maybeRootPath.endsWith("/")) return `${maybeRootPath}/`;
-  console.log(`using bank base URL (${maybeRootPath})`);
-  return maybeRootPath;
+  return canonicalizeBaseUrl(overrideUrl ? overrideUrl : maybeRootPath)
+
 }
 
 export function undefinedIfEmpty<T extends object>(obj: T): T | undefined {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]