gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant-backoffice] branch master updated: bank: import and conf


From: gnunet
Subject: [taler-merchant-backoffice] branch master updated: bank: import and configure SWR
Date: Mon, 13 Dec 2021 14:33:56 +0100

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

ms pushed a commit to branch master
in repository merchant-backoffice.

The following commit(s) were added to refs/heads/master by this push:
     new 0dc4e08  bank: import and configure SWR
0dc4e08 is described below

commit 0dc4e08923a9c4ee743898b6590a28c8151b160e
Author: ms <ms@taler.net>
AuthorDate: Mon Dec 13 14:33:08 2021 +0100

    bank: import and configure SWR
---
 packages/bank/package.json             |   5 +-
 packages/bank/src/pages/home/index.tsx | 188 +++++++++++++++------------------
 2 files changed, 90 insertions(+), 103 deletions(-)

diff --git a/packages/bank/package.json b/packages/bank/package.json
index cf46a3d..12852c1 100644
--- a/packages/bank/package.json
+++ b/packages/bank/package.json
@@ -32,7 +32,8 @@
     "preact": "^10.5.15",
     "preact-render-to-string": "^5.1.19",
     "preact-router": "^3.2.1",
-    "qrcode-generator": "^1.4.4"
+    "qrcode-generator": "^1.4.4",
+    "swr": "1.1"
   },
   "devDependencies": {
     "@creativebulma/bulma-tooltip": "^1.2.0",
@@ -42,6 +43,8 @@
     "@storybook/addon-links": "6.2.9",
     "@storybook/preact": "6.2.9",
     "@storybook/preset-scss": "^1.0.3",
+    "@testing-library/preact": "^2.0.1",
+    "@testing-library/preact-hooks": "^1.1.0",
     "@types/enzyme": "^3.10.10",
     "@types/jest": "^27.0.2",
     "@typescript-eslint/eslint-plugin": "^5.3.0",
diff --git a/packages/bank/src/pages/home/index.tsx 
b/packages/bank/src/pages/home/index.tsx
index 3cf78b3..1d1ae26 100644
--- a/packages/bank/src/pages/home/index.tsx
+++ b/packages/bank/src/pages/home/index.tsx
@@ -1,6 +1,8 @@
-import { h, Fragment } from "preact";
-import {useState} from "preact/hooks";
+import useSWR, { SWRConfig } from "swr";
+import { h, Fragment, ComponentChildren, VNode } from "preact";
+import { useState, useEffect, StateUpdater } from "preact/hooks";
 import axios from "axios";
+import { Buffer } from 'buffer';
 
 
 /**********************************************
@@ -13,8 +15,8 @@ import axios from "axios";
  */
 interface BackendStateType {
   url: string;
-  username: string | undefined;
-  password: string | undefined;
+  username: string;
+  password: string;
 }
 
 /**
@@ -31,20 +33,23 @@ interface RegistrationRequestType {
 interface PageStateType {
   isLoggedIn: boolean;
   hasProblem: boolean;
+  error?: string;
 }
 
 /**
  * Bank account specific information.
  */
 interface AccountStateType {
-  balance: string | undefined;
+  balance: string;
   /* FIXME: Need history here.  */
+}
 
-  /**
-   * Error message to diplay when one of the
-   * account state entries failed to arrive.
-   */
-  error: string | undefined;
+/*******************
+ * Helpers. *
+ ******************/
+
+const getRootPath = () => {
+  return typeof window !== undefined ? window.location.origin + 
window.location.pathname : '/';
 }
 
 /*******************
@@ -56,59 +61,40 @@ interface AccountStateType {
  * login credentials and backend's
  * base URL.
  */
-function useBackendState(
-  state: BackendStateType = {
-    url: window.location.href, // Should never change.
-    username: undefined,
-    password: undefined,
-}): [
-  BackendStateType,
-  (newState: BackendStateType) => void |
-    ((fn: (oldState: BackendStateType) => void) => void)
+type BackendStateTypeOpt = BackendStateType | undefined;
+function useBackendState(state?: BackendStateType): [
+  BackendStateTypeOpt, StateUpdater<BackendStateTypeOpt>
 ] {
-  return useState<BackendStateType>(state);
+  if (state) return useState<BackendStateTypeOpt>(state);
+  return useState<BackendStateTypeOpt>();
 }
 
-function useAccountState<AccountStateType>(
-  state: AccountStateType = {
-    balance: undefined,
-    error: undefined
-}): [
-  AccountStateType, (fn: (state: AccountStateType) => void) => void
-] {
-  return useState<AccountStateType>(state);
+/**
+ * Keep mere business information, like account balance or
+ * transactions history.
+ */
+type AccountStateTypeOpt = AccountStateType | undefined;
+function useAccountState(state?: AccountStateType): [
+    AccountStateTypeOpt, StateUpdater<AccountStateTypeOpt>] {
+  if (state) return useState<AccountStateTypeOpt>(state);
+  return useState<AccountStateTypeOpt>();
 }
 
 function usePageState(
   state: PageStateType = {
     isLoggedIn: false,
     hasProblem: false,
-}): [
-  PageStateType, (newState: PageStateType) => any |
-    ((fn: (state: PageStateType) => void) => void)
-] {
+}): [PageStateType, StateUpdater<PageStateType>] {
   return useState<PageStateType>(state);
 }
 
-/************
- * Helpers. *
- ***********/
-
-async function post(url: string, data: any): Promise<number> {
-  // Mock 200 OK responses, at this moment.
-  return new Promise(
-    // (res, rej) => {setTimeout(() => {res(404);});}
-    (res, rej) => {setTimeout(() => {res(200);});}
-  );
-}
-
-async function get(url: string): Promise<number> {
-  // Mock 200 OK responses, at this moment.
-  return new Promise(
-    (res, rej) => {setTimeout(() => {res(404);});}
-    // (res, rej) => {setTimeout(() => {res(200);});}
-  );
-}
+/**
+ * Request preparators.
+ *
+ * These functions aim at sanitizing the input received
+ * from users - for example via a HTML form - and create
+ * a HTTP request object out of that.
+ */
 
 /******************
  * HTTP wrappers. *
@@ -163,8 +149,7 @@ async function accountInfoCall(
       }
     }
   };
-  const resp = await get(url)
-  handleResp(resp)
+  handleResp(200)
 }
 
 /**
@@ -177,10 +162,9 @@ async function accountInfoCall(
 async function registrationCall(
   url: string,
   req: RegistrationRequestType,
-  // On success, that will update the username and password to the state.
-  backendStateSetter: (fn: (state: BackendStateType) => void) => void,
-  // Communicate the request outcome to the page.
-  pageStateSetter: (fn: (state: PageStateType) => void) => void,
+  backendStateSetter: StateUpdater<BackendStateTypeOpt>,
+  pageStateSetter: StateUpdater<PageStateType>,
+  // pageStateSetter: (fn: (state: PageStateType) => void) => void,
 ) {
   console.log("Try to register", req);
   var handleResp = (respStatus: number) => {
@@ -189,6 +173,7 @@ async function registrationCall(
         pageStateSetter(state => ({...state, isLoggedIn: true}));
         backendStateSetter(state => ({
          ...state,
+         url: url,
          username: req.username,
          password: req.password,
        }));
@@ -199,8 +184,7 @@ async function registrationCall(
       }
     }
   }
-  var resp = await post(url, req);
-  handleResp(resp);
+  handleResp(200);
 }
 
 /**************************
@@ -214,53 +198,50 @@ export function Account(props: {balance: string}) {
   return <p>Your balance is {props.balance}</p>;
 }
 
+/**
+ * Factor out login credentials.
+ */
+function SWRWithCredentials(props: any): VNode {
+  const { username, password } = props;
+  const headers = new Headers();
+  headers.append(
+    "Authorization",
+    `Basic ${Buffer.from(username + ":" + password).toString("base64")}`
+  );
+  return (
+    <SWRConfig value={{fetcher: (url) => fetch(url, {headers: headers}).then(r 
=> (r.json()))}}>
+      {props.children}
+    </SWRConfig>);
+}
+
 /**
  * If the user is logged in, it displays
  * the balance, otherwise it offers to login.
  */
-export function BankHome() {
-  var [backendState, backendStateSetter] = useBackendState<BackendStateType>();
-  var [pageState, pageStateSetter] = usePageState<PageStateType>();
-  var [accountState, accountStateSetter] = useAccountState<AccountStateType>();
-
-  // Prepare/check registration request.
-  var registrationData: {}; // Untyped collector of user input.
-  let prepareRegistrationRequest = (): RegistrationRequestType => {
-  
-  console.log("Preparing registration request", registrationData);
-    if (!("username" in registrationData)) {
-      pageStateSetter({...pageState, hasProblem: true});
-      return;
-    }
-    if (!("password" in registrationData)) {
-      pageStateSetter({...pageState, hasProblem: true});
-      return;
-    }
-    const u: string = registrationData["username"]
-    const p: string = registrationData["password"]
-    // Here, input is valid.
-    return {username: u, password: p};
-  }
+export function BankHome(): VNode {
+  var [backendState, backendStateSetter] = useBackendState();
+  var [pageState, pageStateSetter] = usePageState();
+  var [accountState, accountStateSetter] = useAccountState();
 
   if (pageState.hasProblem) {
     return <p>Page has a problem.</p>;
   }
 
+  /**
+   * Credentials were correct, now try to render the
+   * bank account page, with balance and transactions
+   * history */
   if (pageState.isLoggedIn) {
-    if (typeof accountState.error !== "undefined") {
-      console.log(accountState);
-      return <p>The page could not load correctly: {accountState.error}</p>;
+    if (typeof backendState === "undefined") {
+      console.log("Credentials not found in state, even after login.");
+      pageStateSetter((state) => ({...state, hasProblem: true}));
+      return <p>Page has a problem</p>;
     }
-    if (typeof accountState.balance === "undefined") {
-      // Need one: request and trigger new state!
-      accountInfoCall(backendState, accountStateSetter);
-      return;
-    }
-
-    return <Fragment>
-      <p>Welcome {backendState.username}!</p>
-      <Account balance={accountState.balance} />
-    </Fragment>;
+    return <SWRWithCredentials
+        username={backendState.username}
+        password={backendState.password}>
+      <p>Hey!</p>
+    </SWRWithCredentials>
 
     /**
      * FIXME: need to offer a Taler withdraw button here.
@@ -275,27 +256,30 @@ export function BankHome() {
      */
   }
   
-  // Proceede collecting registration data.
+  var registrationData: RegistrationRequestType;
   return <div>
     <input type="text"
            placeholder="username"
+          required
            onInput={(e): void => {
             registrationData = {...registrationData, username: 
e.currentTarget.value};
           }}
     / >
     <input type="text"
            placeholder="password"
+          required
            onInput={(e): void => {
             registrationData = {...registrationData, password: 
e.currentTarget.value};
           }}
     / >
 
     <button
-      onClick={() => {registrationCall(
-        backendState.url,
-        prepareRegistrationRequest(),
-        backendStateSetter,
-        pageStateSetter,
-      )}}>Submit</button>
+      onClick={() => {
+       registrationCall(
+          getRootPath(),
+         registrationData,
+          backendStateSetter,
+          pageStateSetter,
+        )}}>Submit</button>
   </div>
 }

-- 
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]