gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 03/03: deposit design from belen, feature missing: k


From: gnunet
Subject: [taler-wallet-core] 03/03: deposit design from belen, feature missing: kyc
Date: Mon, 10 Jan 2022 20:07:42 +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 fb22009ec4799a624f00c228fbd7435b44c1cbac
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Jan 10 16:04:53 2022 -0300

    deposit design from belen, feature missing: kyc
---
 .../.storybook/preview.js                          |   4 +-
 .../taler-wallet-webextension/clean_and_build.sh   |   1 +
 .../src/NavigationBar.tsx                          |  15 +-
 .../src/components/BalanceTable.tsx                |  28 +-
 .../src/components/Loading.tsx                     |  20 ++
 .../src/components/MultiActionButton.tsx           |  95 +++++++
 .../src/components/styled/index.tsx                |  64 +++--
 .../src/context/devContext.ts                      |   1 +
 .../src/cta/Deposit.stories.tsx                    | 168 ++++++++++++
 .../src/cta/{Pay.tsx => Deposit.tsx}               | 206 ++-------------
 packages/taler-wallet-webextension/src/cta/Pay.tsx |  29 +-
 .../src/popup/Balance.stories.tsx                  | 293 ++++++++-------------
 .../src/popup/BalancePage.tsx                      |  73 +++--
 .../src/popup/DeveloperPage.tsx                    |   7 +-
 .../src/popup/History.stories.tsx                  | 213 ---------------
 .../src/popup/History.tsx                          | 148 -----------
 .../src/popup/index.stories.tsx                    |   5 +-
 .../src/popupEntryPoint.tsx                        |  30 ++-
 .../taler-wallet-webextension/src/renderHtml.tsx   |   7 +-
 .../taler-wallet-webextension/src/test-utils.ts    |   3 +-
 .../{popup => wallet}/AddNewActionView.stories.tsx |   2 +-
 .../src/{popup => wallet}/AddNewActionView.tsx     |   0
 .../src/wallet/Balance.stories.tsx                 | 177 ++++++++-----
 .../src/wallet/BalancePage.tsx                     |  89 ++++---
 .../src/wallet/CreateManualWithdraw.tsx            |  12 +-
 .../src/wallet/DepositPage.tsx                     | 104 +++++---
 .../src/wallet/History.stories.tsx                 | 119 ++++++---
 .../src/wallet/History.tsx                         | 227 ++++++++++------
 .../LastActivityPage.stories.tsx}                  |  12 +-
 .../src/wallet/LastActivityPage.tsx                |  35 +++
 .../src/wallet/ManualWithdrawPage.tsx              |  16 +-
 .../src/wallet/Transaction.tsx                     |  13 +-
 .../src/wallet/index.stories.tsx                   |  19 +-
 .../src/walletEntryPoint.tsx                       | 285 ++++++++++----------
 packages/taler-wallet-webextension/src/wxApi.ts    |   2 +-
 35 files changed, 1261 insertions(+), 1261 deletions(-)

diff --git a/packages/taler-wallet-webextension/.storybook/preview.js 
b/packages/taler-wallet-webextension/.storybook/preview.js
index 6331a7fa..b770d7b6 100644
--- a/packages/taler-wallet-webextension/.storybook/preview.js
+++ b/packages/taler-wallet-webextension/.storybook/preview.js
@@ -48,7 +48,7 @@ export const decorators = [
         const isTestingHeader = (/.*\/header\/?.*/.test(kind));
         if (isTestingHeader) {
           // simple box with correct width and height
-          return <div style={{ width: 400, height: 320 }}>
+          return <div style={{ width: "fit-content" }}>
             <Story />
           </div>
         }
@@ -90,7 +90,7 @@ export const decorators = [
           font-family: Arial, Helvetica, sans-serif;
         }`}
         </style>
-        <div style={{ width: 400, border: 'black solid 1px' }}>
+        <div style={{ border: 'black solid 1px', width: "fit-content" }}>
           <Body />
         </div>
       </div>
diff --git a/packages/taler-wallet-webextension/clean_and_build.sh 
b/packages/taler-wallet-webextension/clean_and_build.sh
index be20d80d..0cfbe094 100755
--- a/packages/taler-wallet-webextension/clean_and_build.sh
+++ b/packages/taler-wallet-webextension/clean_and_build.sh
@@ -1,5 +1,6 @@
 #!/usr/bin/env bash
 # This file is in the public domain.
+set -e
 [ "also-wallet" == "$1" ] && { pnpm -C ../taler-wallet-core/ compile || exit 
1; }
 [ "also-util" == "$1" ] && { pnpm -C ../taler-util/ prepare || exit 1; }
 pnpm clean && pnpm compile && rm -rf extension/ && ./pack.sh
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx 
b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index c02e4898..44e8af78 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -28,18 +28,18 @@ import { i18n } from "@gnu-taler/taler-util";
 import { ComponentChildren, h, VNode } from "preact";
 import Match from "preact-router/match";
 import { PopupNavigation } from "./components/styled";
-import { useDevContext } from "./context/devContext";
 
 export enum Pages {
   welcome = "/welcome",
   balance = "/balance",
-  manual_withdraw = "/manual-withdraw",
+  balance_history = "/balance/history/:currency",
+  manual_withdraw = "/manual-withdraw/:currency?",
   deposit = "/deposit/:currency",
   settings = "/settings",
   dev = "/dev",
   cta = "/cta/:action",
   backup = "/backup",
-  history = "/history",
+  last_activity = "/last-activity",
   transaction = "/transaction/:tid",
   provider_detail = "/provider/:pid",
   provider_add = "/provider/add",
@@ -78,7 +78,10 @@ export function NavBar({ devMode, path }: { path: string; 
devMode: boolean }) {
     <PopupNavigation devMode={devMode}>
       <div>
         <Tab target="/balance" current={path}>{i18n.str`Balance`}</Tab>
-        <Tab target="/history" current={path}>{i18n.str`History`}</Tab>
+        <Tab
+          target="/last-activity"
+          current={path}
+        >{i18n.str`Last Activity`}</Tab>
         <Tab target="/backup" current={path}>{i18n.str`Backup`}</Tab>
         <Tab target="/settings" current={path}>{i18n.str`Settings`}</Tab>
         {devMode && <Tab target="/dev" current={path}>{i18n.str`Dev`}</Tab>}
@@ -87,8 +90,8 @@ export function NavBar({ devMode, path }: { path: string; 
devMode: boolean }) {
   );
 }
 
-export function WalletNavBar() {
-  const { devMode } = useDevContext();
+export function WalletNavBar({ devMode }: { devMode: boolean }) {
+  // const { devMode } = useDevContext();
   return (
     <Match>
       {({ path }: any) => {
diff --git a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx 
b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
index 05a7d28d..c69625cd 100644
--- a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
+++ b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
@@ -14,31 +14,28 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { amountFractionalBase, Amounts, Balance } from "@gnu-taler/taler-util";
+import { Amounts, amountToPretty, Balance } from "@gnu-taler/taler-util";
 import { h, VNode } from "preact";
-import {
-  ButtonPrimary,
-  TableWithRoundRows as TableWithRoundedRows,
-} from "./styled";
+import { TableWithRoundRows as TableWithRoundedRows } from "./styled";
 
 export function BalanceTable({
   balances,
-  goToWalletDeposit,
+  goToWalletHistory,
 }: {
   balances: Balance[];
-  goToWalletDeposit: (currency: string) => void;
+  goToWalletHistory: (currency: string) => void;
 }): VNode {
-  const currencyFormatter = new Intl.NumberFormat("en-US");
   return (
     <TableWithRoundedRows>
       {balances.map((entry, idx) => {
         const av = Amounts.parseOrThrow(entry.available);
 
-        const v = currencyFormatter.format(
-          av.value + av.fraction / amountFractionalBase,
-        );
         return (
-          <tr key={idx}>
+          <tr
+            key={idx}
+            onClick={() => goToWalletHistory(av.currency)}
+            style={{ cursor: "pointer" }}
+          >
             <td>{av.currency}</td>
             <td
               style={{
@@ -47,12 +44,7 @@ export function BalanceTable({
                 width: "100%",
               }}
             >
-              {v}
-            </td>
-            <td>
-              <ButtonPrimary onClick={() => goToWalletDeposit(av.currency)}>
-                Deposit
-              </ButtonPrimary>
+              {Amounts.stringifyValue(av)}
             </td>
           </tr>
         );
diff --git a/packages/taler-wallet-webextension/src/components/Loading.tsx 
b/packages/taler-wallet-webextension/src/components/Loading.tsx
new file mode 100644
index 00000000..34edac55
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/Loading.tsx
@@ -0,0 +1,20 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+import { h, VNode } from "preact";
+
+export function Loading(): VNode {
+  return <div>Loading...</div>;
+}
diff --git 
a/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx 
b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
new file mode 100644
index 00000000..70d53640
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
@@ -0,0 +1,95 @@
+import { h, VNode } from "preact";
+import arrowDown from "../../static/img/chevron-down.svg";
+import { ButtonBoxPrimary, ButtonPrimary, ParagraphClickable } from "./styled";
+import { useState } from "preact/hooks";
+
+export interface Props {
+  label: (s: string) => string;
+  actions: string[];
+  onClick: (s: string) => void;
+}
+
+/**
+ * functionality: it will receive a list of actions, take the first actions as
+ * the first chosen action
+ * the user may change the chosen action
+ * when the user click the button it will call onClick with the chosen action
+ * as argument
+ *
+ * visually: it is a primary button with a select handler on the right
+ *
+ * @returns
+ */
+export function MultiActionButton({
+  label,
+  actions,
+  onClick: doClick,
+}: Props): VNode {
+  const defaultAction = actions.length > 0 ? actions[0] : "";
+
+  const [opened, setOpened] = useState(false);
+  const [selected, setSelected] = useState<string>(defaultAction);
+
+  const canChange = actions.length > 1;
+  const options = canChange ? actions.filter((a) => a !== selected) : [];
+  function select(m: string): void {
+    setSelected(m);
+    setOpened(false);
+  }
+
+  if (!canChange) {
+    return (
+      <ButtonPrimary onClick={() => doClick(selected)}>
+        {label(selected)}
+      </ButtonPrimary>
+    );
+  }
+
+  return (
+    <div style={{ position: "relative", display: "inline-block" }}>
+      {opened && (
+        <div
+          style={{
+            position: "absolute",
+            bottom: 32 + 5,
+            right: 0,
+            marginLeft: 8,
+            marginRight: 8,
+            borderRadius: 5,
+            border: "1px solid blue",
+            background: "white",
+            boxShadow: "0px 8px 16px 0px rgba(0,0,0,0.2)",
+            zIndex: 1,
+          }}
+        >
+          {options.map((m) => (
+            <ParagraphClickable key={m} onClick={() => select(m)}>
+              {label(m)}
+            </ParagraphClickable>
+          ))}
+        </div>
+      )}
+      <ButtonPrimary
+        onClick={() => doClick(selected)}
+        style={{
+          borderTopRightRadius: 0,
+          borderBottomRightRadius: 0,
+          marginRight: 0,
+        }}
+      >
+        {label(selected)}
+      </ButtonPrimary>
+
+      <ButtonBoxPrimary
+        onClick={() => setOpened((s) => !s)}
+        style={{
+          marginLeft: 0,
+          borderTopLeftRadius: 0,
+          borderBottomLeftRadius: 0,
+        }}
+      >
+        <img style={{ height: 14 }} src={arrowDown} />
+      </ButtonBoxPrimary>
+    </div>
+  );
+}
diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx 
b/packages/taler-wallet-webextension/src/components/styled/index.tsx
index 216a1fab..2d16b496 100644
--- a/packages/taler-wallet-webextension/src/components/styled/index.tsx
+++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx
@@ -43,7 +43,7 @@ export const WalletAction = styled.div`
   }
   section {
     margin-bottom: 2em;
-    & button {
+    button {
       margin-right: 8px;
       margin-left: 8px;
     }
@@ -92,6 +92,10 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>`
       border-bottom: 1px solid black;
       border-top: 1px solid black;
     }
+    button {
+      margin-right: 8px;
+      margin-left: 8px;
+    }
   }
 
   & > header {
@@ -123,7 +127,7 @@ export const WalletBox = styled.div<{ noPadding?: boolean 
}>`
     justify-content: space-between;
     display: flex;
     background-color: #f7f7f7;
-    & button {
+    button {
       margin-right: 8px;
       margin-left: 8px;
     }
@@ -136,9 +140,9 @@ export const Middle = styled.div`
   height: 100%;
 `;
 
-export const PopupBox = styled.div<{ noPadding?: boolean }>`
+export const PopupBox = styled.div<{ noPadding?: boolean; devMode: boolean }>`
   height: 290px;
-  width: 400px;
+  width: ${({ devMode }) => (!devMode ? "400px" : "500px")};
   display: flex;
   flex-direction: column;
   justify-content: space-between;
@@ -156,6 +160,10 @@ export const PopupBox = styled.div<{ noPadding?: boolean 
}>`
       border-bottom: 1px solid black;
       border-top: 1px solid black;
     }
+    button {
+      margin-right: 8px;
+      margin-left: 8px;
+    }
   }
 
   & > section[data-expanded] {
@@ -196,7 +204,7 @@ export const PopupBox = styled.div<{ noPadding?: boolean }>`
     flex-direction: row;
     justify-content: space-between;
     display: flex;
-    & button {
+    button {
       margin-right: 8px;
       margin-left: 8px;
     }
@@ -363,11 +371,11 @@ export const CenteredDialog = styled.div`
 
 export const Button = styled.button<{ upperCased?: boolean }>`
   display: inline-block;
-  zoom: 1;
+  /* zoom: 1; */
   line-height: normal;
   white-space: nowrap;
-  vertical-align: middle;
-  text-align: center;
+  vertical-align: middle; //check this
+  /* text-align: center; */
   cursor: pointer;
   user-select: none;
   box-sizing: border-box;
@@ -379,7 +387,7 @@ export const Button = styled.button<{ upperCased?: boolean 
}>`
   /* color: #444; rgba not supported (IE 8) */
   color: rgba(0, 0, 0, 0.8); /* rgba supported */
   border: 1px solid #999; /*IE 6/7/8*/
-  border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/
+  /* border: none rgba(0, 0, 0, 0); IE9 + everything else */
   background-color: "#e6e6e6";
   text-decoration: none;
   border-radius: 2px;
@@ -401,11 +409,11 @@ export const Button = styled.button<{ upperCased?: 
boolean }>`
   }
 
   :hover {
-    filter: alpha(opacity=90);
+    filter: alpha(opacity=80);
     background-image: linear-gradient(
       transparent,
-      rgba(0, 0, 0, 0.05) 40%,
-      rgba(0, 0, 0, 0.1)
+      rgba(0, 0, 0, 0.1) 40%,
+      rgba(0, 0, 0, 0.2)
     );
   }
 `;
@@ -415,7 +423,7 @@ export const Link = styled.a<{ upperCased?: boolean }>`
   zoom: 1;
   line-height: normal;
   white-space: nowrap;
-  vertical-align: middle;
+  /* vertical-align: middle; */
   text-align: center;
   cursor: pointer;
   user-select: none;
@@ -463,8 +471,8 @@ export const FontIcon = styled.div`
   /* vertical-align: text-top; */
 `;
 export const ButtonBox = styled(Button)`
-  padding: 0.5em;
-  font-size: x-small;
+  padding: 8px;
+  /* font-size: small; */
 
   & > ${FontIcon} {
     width: 1em;
@@ -472,12 +480,13 @@ export const ButtonBox = styled(Button)`
     display: inline;
     line-height: 0px;
   }
-  background-color: transparent;
+  background-color: #f7f7f7;
 
   border: 1px solid;
   border-radius: 4px;
   border-color: black;
   color: black;
+  /* text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); */
   /* -webkit-border-horizontal-spacing: 0px;
   -webkit-border-vertical-spacing: 0px; */
 `;
@@ -499,6 +508,7 @@ export const LinkPrimary = styled(Link)`
 export const ButtonPrimary = styled(ButtonVariant)<{ small?: boolean }>`
   font-size: ${({ small }) => (small ? "small" : "inherit")};
   background-color: rgb(66, 184, 221);
+  border-color: rgb(66, 184, 221);
 `;
 export const ButtonBoxPrimary = styled(ButtonBox)`
   color: rgb(66, 184, 221);
@@ -714,6 +724,7 @@ export const InputWithLabel = styled.div<{ invalid?: 
boolean }>`
     border-top-right-radius: 0.25em;
     border-color: ${({ invalid }) => (!invalid ? "lightgray" : "red")};
   }
+  margin-bottom: 16px;
 `;
 
 export const ErrorText = styled.div`
@@ -772,13 +783,13 @@ export const PopupNavigation = styled.div<{ devMode?: 
boolean }>`
   display: flex;
 
   & > div {
-    width: 400px;
+    width: ${({ devMode }) => (!devMode ? "400px" : "500px")};
   }
 
   & > div > a {
     color: #f8faf7;
     display: inline-block;
-    width: calc(400px / ${({ devMode }) => (!devMode ? 4 : 5)});
+    width: 100px;
     text-align: center;
     text-decoration: none;
     vertical-align: middle;
@@ -804,10 +815,9 @@ export const NiceSelect = styled.div`
     box-shadow: none;
 
     background-image: ${image};
-    background-position: right 0.5rem center;
+    background-position: right 8px center;
     background-repeat: no-repeat;
     background-size: 1.5em 1.5em;
-    padding-right: 2.5rem;
 
     background-color: white;
 
@@ -967,3 +977,17 @@ export const StyledCheckboxLabel = styled.div`
     box-shadow: 0 0 0 0.05em #fff, 0 0 0.15em 0.1em currentColor;
   }
 `;
+
+export const ParagraphClickable = styled.p`
+  cursor: pointer;
+  margin: 0px;
+  padding: 8px 16px;
+  :hover {
+    filter: alpha(opacity=80);
+    background-image: linear-gradient(
+      transparent,
+      rgba(0, 0, 0, 0.1) 40%,
+      rgba(0, 0, 0, 0.2)
+    );
+  }
+`;
diff --git a/packages/taler-wallet-webextension/src/context/devContext.ts 
b/packages/taler-wallet-webextension/src/context/devContext.ts
index 7ed6858a..4b8ba2bc 100644
--- a/packages/taler-wallet-webextension/src/context/devContext.ts
+++ b/packages/taler-wallet-webextension/src/context/devContext.ts
@@ -42,5 +42,6 @@ export const DevContextProvider = ({ children }: { children: 
any }): VNode => {
   const [value, setter] = useLocalStorage("devMode");
   const devMode = value === "true";
   const toggleDevMode = () => setter((v) => (!v ? "true" : undefined));
+  children = children.length === 1 && typeof children === "function" ? 
children({ devMode }) : children;
   return h(Context.Provider, { value: { devMode, toggleDevMode }, children });
 };
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit.stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Deposit.stories.tsx
new file mode 100644
index 00000000..df5947bb
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/Deposit.stories.tsx
@@ -0,0 +1,168 @@
+/*
+ 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { ContractTerms, PreparePayResultType } from "@gnu-taler/taler-util";
+import { createExample } from "../test-utils";
+import { PaymentRequestView as TestedComponent } from "./Deposit";
+
+export default {
+  title: "cta/deposit",
+  component: TestedComponent,
+  argTypes: {},
+};
+
+export const NoBalance = createExample(TestedComponent, {
+  payStatus: {
+    status: PreparePayResultType.InsufficientBalance,
+    noncePriv: "",
+    proposalId: "proposal1234",
+    contractTerms: {
+      merchant: {
+        name: "someone",
+      },
+      summary: "some beers",
+      amount: "USD:10",
+    } as Partial<ContractTerms> as any,
+    amountRaw: "USD:10",
+  },
+});
+
+export const NoEnoughBalance = createExample(TestedComponent, {
+  payStatus: {
+    status: PreparePayResultType.InsufficientBalance,
+    noncePriv: "",
+    proposalId: "proposal1234",
+    contractTerms: {
+      merchant: {
+        name: "someone",
+      },
+      summary: "some beers",
+      amount: "USD:10",
+    } as Partial<ContractTerms> as any,
+    amountRaw: "USD:10",
+  },
+  balance: {
+    currency: "USD",
+    fraction: 40000000,
+    value: 9,
+  },
+});
+
+export const PaymentPossible = createExample(TestedComponent, {
+  uri: 
"taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0",
+  payStatus: {
+    status: PreparePayResultType.PaymentPossible,
+    amountEffective: "USD:10",
+    amountRaw: "USD:10",
+    noncePriv: "",
+    contractTerms: {
+      nonce: "123213123",
+      merchant: {
+        name: "someone",
+      },
+      amount: "USD:10",
+      summary: "some beers",
+    } as Partial<ContractTerms> as any,
+    contractTermsHash: "123456",
+    proposalId: "proposal1234",
+  },
+});
+
+export const PaymentPossibleWithFee = createExample(TestedComponent, {
+  uri: 
"taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0",
+  payStatus: {
+    status: PreparePayResultType.PaymentPossible,
+    amountEffective: "USD:10.20",
+    amountRaw: "USD:10",
+    noncePriv: "",
+    contractTerms: {
+      nonce: "123213123",
+      merchant: {
+        name: "someone",
+      },
+      amount: "USD:10",
+      summary: "some beers",
+    } as Partial<ContractTerms> as any,
+    contractTermsHash: "123456",
+    proposalId: "proposal1234",
+  },
+});
+
+export const AlreadyConfirmedWithFullfilment = createExample(TestedComponent, {
+  payStatus: {
+    status: PreparePayResultType.AlreadyConfirmed,
+    amountEffective: "USD:10",
+    amountRaw: "USD:10",
+    contractTerms: {
+      merchant: {
+        name: "someone",
+      },
+      fulfillment_message:
+        "congratulations! you are looking at the fulfillment message! ",
+      summary: "some beers",
+      amount: "USD:10",
+    } as Partial<ContractTerms> as any,
+    contractTermsHash: "123456",
+    proposalId: "proposal1234",
+    paid: false,
+  },
+});
+
+export const AlreadyConfirmedWithoutFullfilment = createExample(
+  TestedComponent,
+  {
+    payStatus: {
+      status: PreparePayResultType.AlreadyConfirmed,
+      amountEffective: "USD:10",
+      amountRaw: "USD:10",
+      contractTerms: {
+        merchant: {
+          name: "someone",
+        },
+        summary: "some beers",
+        amount: "USD:10",
+      } as Partial<ContractTerms> as any,
+      contractTermsHash: "123456",
+      proposalId: "proposal1234",
+      paid: false,
+    },
+  },
+);
+
+export const AlreadyPaid = createExample(TestedComponent, {
+  payStatus: {
+    status: PreparePayResultType.AlreadyConfirmed,
+    amountEffective: "USD:10",
+    amountRaw: "USD:10",
+    contractTerms: {
+      merchant: {
+        name: "someone",
+      },
+      fulfillment_message:
+        "congratulations! you are looking at the fulfillment message! ",
+      summary: "some beers",
+      amount: "USD:10",
+    } as Partial<ContractTerms> as any,
+    contractTermsHash: "123456",
+    proposalId: "proposal1234",
+    paid: true,
+  },
+});
diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx 
b/packages/taler-wallet-webextension/src/cta/Deposit.tsx
similarity index 53%
copy from packages/taler-wallet-webextension/src/cta/Pay.tsx
copy to packages/taler-wallet-webextension/src/cta/Deposit.tsx
index d7419d41..3696b0c2 100644
--- a/packages/taler-wallet-webextension/src/cta/Pay.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Deposit.tsx
@@ -26,8 +26,8 @@
 
 import {
   AmountJson,
-  AmountLike,
   Amounts,
+  amountToPretty,
   ConfirmPayResult,
   ConfirmPayResultDone,
   ConfirmPayResultType,
@@ -43,11 +43,8 @@ import { useEffect, useState } from "preact/hooks";
 import { ErrorTalerOperation } from "../components/ErrorTalerOperation";
 import { LogoHeader } from "../components/LogoHeader";
 import { Part } from "../components/Part";
-import { QR } from "../components/QR";
 import {
-  ButtonSuccess,
   ErrorBox,
-  LinkSuccess,
   SuccessBox,
   WalletAction,
   WarningBox,
@@ -57,57 +54,10 @@ import * as wxApi from "../wxApi";
 
 interface Props {
   talerPayUri?: string;
-  goToWalletManualWithdraw: () => void;
+  goBack: () => void;
 }
 
-// export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) 
{
-//   const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
-//   let message;
-//   if (fulfillmentUrl) {
-//     message = (
-//       <span>
-//         You have already paid for this article. Click{" "}
-//         <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to 
view it again.
-//       </span>
-//     );
-//   } else {
-//     message = <span>
-//       You have already paid for this article:{" "}
-//       <em>
-//         {payStatus.contractTerms.fulfillment_message ?? "no message given"}
-//       </em>
-//     </span>;
-//   }
-//   return <section class="main">
-//     <h1>GNU Taler Wallet</h1>
-//     <article class="fade">
-//       {message}
-//     </article>
-//   </section>
-// }
-
-const doPayment = async (
-  payStatus: PreparePayResult,
-): Promise<ConfirmPayResultDone> => {
-  if (payStatus.status !== "payment-possible") {
-    throw Error(`invalid state: ${payStatus.status}`);
-  }
-  const proposalId = payStatus.proposalId;
-  const res = await wxApi.confirmPay(proposalId, undefined);
-  if (res.type !== ConfirmPayResultType.Done) {
-    throw Error("payment pending");
-  }
-  const fu = res.contractTerms.fulfillment_url;
-  if (fu) {
-    document.location.href = fu;
-  }
-  return res;
-};
-
-export function PayPage({
-  talerPayUri,
-  goToWalletManualWithdraw,
-}: Props): VNode {
+export function DepositPage({ talerPayUri, goBack }: Props): VNode {
   const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(
     undefined,
   );
@@ -193,15 +143,15 @@ export function PayPage({
   }
 
   const onClick = async (): Promise<void> => {
-    try {
-      const res = await doPayment(payStatus);
-      setPayResult(res);
-    } catch (e) {
-      console.error(e);
-      if (e instanceof Error) {
-        setPayErrMsg(e.message);
-      }
-    }
+    // try {
+    //   const res = await doPayment(payStatus);
+    //   setPayResult(res);
+    // } catch (e) {
+    //   console.error(e);
+    //   if (e instanceof Error) {
+    //     setPayErrMsg(e.message);
+    //   }
+    // }
   };
 
   return (
@@ -210,7 +160,6 @@ export function PayPage({
       payStatus={payStatus}
       payResult={payResult}
       onClick={onClick}
-      goToWalletManualWithdraw={goToWalletManualWithdraw}
       balance={foundAmount}
     />
   );
@@ -222,7 +171,6 @@ export interface PaymentRequestViewProps {
   onClick: () => void;
   payErrMsg?: string;
   uri: string;
-  goToWalletManualWithdraw: () => void;
   balance: AmountJson | undefined;
 }
 export function PaymentRequestView({
@@ -230,129 +178,16 @@ export function PaymentRequestView({
   payStatus,
   payResult,
   onClick,
-  goToWalletManualWithdraw,
   balance,
 }: PaymentRequestViewProps): VNode {
   let totalFees: AmountJson = Amounts.getZero(payStatus.amountRaw);
   const contractTerms: ContractTerms = payStatus.contractTerms;
 
-  if (!contractTerms) {
-    return (
-      <span>
-        Error: did not get contract terms from merchant or wallet backend.
-      </span>
-    );
-  }
-
-  if (payStatus.status === PreparePayResultType.PaymentPossible) {
-    const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw);
-    const amountEffective: AmountJson = Amounts.parseOrThrow(
-      payStatus.amountEffective,
-    );
-    totalFees = Amounts.sub(amountEffective, amountRaw).amount;
-  }
-
-  // let merchantName: VNode;
-  // if (contractTerms.merchant && contractTerms.merchant.name) {
-  //   merchantName = <strong>{contractTerms.merchant.name}</strong>;
-  // } else {
-  // merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
-  // }
-
-  function Alternative(): VNode {
-    const [showQR, setShowQR] = useState<boolean>(false);
-    const privateUri =
-      payStatus.status !== PreparePayResultType.AlreadyConfirmed
-        ? `${uri}&n=${payStatus.noncePriv}`
-        : uri;
-    return (
-      <section>
-        <LinkSuccess upperCased onClick={() => setShowQR((qr) => !qr)}>
-          {!showQR ? i18n.str`Pay with a mobile phone` : i18n.str`Hide QR`}
-        </LinkSuccess>
-        {showQR && (
-          <div>
-            <QR text={privateUri} />
-            Scan the QR code or <a href={privateUri}>click here</a>
-          </div>
-        )}
-      </section>
-    );
-  }
-
-  function ButtonsSection(): VNode {
-    if (payResult) {
-      if (payResult.type === ConfirmPayResultType.Pending) {
-        return (
-          <section>
-            <div>
-              <p>Processing...</p>
-            </div>
-          </section>
-        );
-      }
-      return <Fragment />;
-    }
-    if (payStatus.status === PreparePayResultType.PaymentPossible) {
-      return (
-        <Fragment>
-          <section>
-            <ButtonSuccess upperCased onClick={onClick}>
-              {i18n.str`Pay`} {amountToString(payStatus.amountEffective)}
-            </ButtonSuccess>
-          </section>
-          <Alternative />
-        </Fragment>
-      );
-    }
-    if (payStatus.status === PreparePayResultType.InsufficientBalance) {
-      return (
-        <Fragment>
-          <section>
-            {balance ? (
-              <WarningBox>
-                Your balance of {amountToString(balance)} is not enough to pay
-                for this purchase
-              </WarningBox>
-            ) : (
-              <WarningBox>
-                Your balance is not enough to pay for this purchase.
-              </WarningBox>
-            )}
-          </section>
-          <section>
-            <ButtonSuccess upperCased onClick={goToWalletManualWithdraw}>
-              {i18n.str`Withdraw digital cash`}
-            </ButtonSuccess>
-          </section>
-          <Alternative />
-        </Fragment>
-      );
-    }
-    if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
-      return (
-        <Fragment>
-          <section>
-            {payStatus.paid && contractTerms.fulfillment_message && (
-              <Part
-                title="Merchant message"
-                text={contractTerms.fulfillment_message}
-                kind="neutral"
-              />
-            )}
-          </section>
-          {!payStatus.paid && <Alternative />}
-        </Fragment>
-      );
-    }
-    return <span />;
-  }
-
   return (
     <WalletAction>
       <LogoHeader />
 
-      <h2>{i18n.str`Digital cash payment`}</h2>
+      <h2>{i18n.str`Digital cash deposit`}</h2>
       {payStatus.status === PreparePayResultType.AlreadyConfirmed &&
         (payStatus.paid ? (
           <SuccessBox> Already paid </SuccessBox>
@@ -375,14 +210,16 @@ export function PaymentRequestView({
             <Part
               big
               title="Total to pay"
-              text={amountToString(payStatus.amountEffective)}
+              text={amountToPretty(
+                Amounts.parseOrThrow(payStatus.amountEffective),
+              )}
               kind="negative"
             />
           )}
         <Part
           big
           title="Purchase amount"
-          text={amountToString(payStatus.amountRaw)}
+          text={amountToPretty(Amounts.parseOrThrow(payStatus.amountRaw))}
           kind="neutral"
         />
         {Amounts.isNonZero(totalFees) && (
@@ -390,7 +227,7 @@ export function PaymentRequestView({
             <Part
               big
               title="Fee"
-              text={amountToString(totalFees)}
+              text={amountToPretty(totalFees)}
               kind="negative"
             />
           </Fragment>
@@ -409,13 +246,6 @@ export function PaymentRequestView({
           />
         )}
       </section>
-      <ButtonsSection />
     </WalletAction>
   );
 }
-
-function amountToString(text: AmountLike): string {
-  const aj = Amounts.jsonifyAmount(text);
-  const amount = Amounts.stringifyValue(aj, 2);
-  return `${amount} ${aj.currency}`;
-}
diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx 
b/packages/taler-wallet-webextension/src/cta/Pay.tsx
index d7419d41..e61d3a9d 100644
--- a/packages/taler-wallet-webextension/src/cta/Pay.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Pay.tsx
@@ -57,35 +57,10 @@ import * as wxApi from "../wxApi";
 
 interface Props {
   talerPayUri?: string;
-  goToWalletManualWithdraw: () => void;
+  goToWalletManualWithdraw: (currency?: string) => void;
+  goBack: () => void;
 }
 
-// export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) 
{
-//   const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
-//   let message;
-//   if (fulfillmentUrl) {
-//     message = (
-//       <span>
-//         You have already paid for this article. Click{" "}
-//         <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to 
view it again.
-//       </span>
-//     );
-//   } else {
-//     message = <span>
-//       You have already paid for this article:{" "}
-//       <em>
-//         {payStatus.contractTerms.fulfillment_message ?? "no message given"}
-//       </em>
-//     </span>;
-//   }
-//   return <section class="main">
-//     <h1>GNU Taler Wallet</h1>
-//     <article class="fade">
-//       {message}
-//     </article>
-//   </section>
-// }
-
 const doPayment = async (
   payStatus: PreparePayResult,
 ): Promise<ConfirmPayResultDone> => {
diff --git a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx 
b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx
index a4988cf2..1af3b585 100644
--- a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample, NullLink } from "../test-utils";
+import { createExample } from "../test-utils";
 import { BalanceView as TestedComponent } from "./BalancePage";
 
 export default {
@@ -28,211 +28,124 @@ export default {
   argTypes: {},
 };
 
-export const NotYetLoaded = createExample(TestedComponent, {});
-
-export const GotError = createExample(TestedComponent, {
-  balance: {
-    hasError: true,
-    message: "Network error",
-  },
-  Linker: NullLink,
-});
-
 export const EmptyBalance = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [],
-    },
-  },
-  Linker: NullLink,
+  balances: [],
 });
 
 export const SomeCoins = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:10.5",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:0",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-      ],
+  balances: [
+    {
+      available: "USD:10.5",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
+  ],
 });
 
-export const SomeCoinsAndIncomingMoney = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:2.23",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:5.11",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-      ],
+export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, {
+  balances: [
+    {
+      available: "EUR:1",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
-});
-
-export const SomeCoinsAndOutgoingMoney = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:2.23",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:0",
-          pendingOutgoing: "USD:5.11",
-          requiresUserInput: false,
-        },
-      ],
+    {
+      available: "TESTKUDOS:2000",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
-});
-
-export const SomeCoinsAndMovingMoney = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:2.23",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:2",
-          pendingOutgoing: "USD:5.11",
-          requiresUserInput: false,
-        },
-      ],
+    {
+      available: "JPY:4",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:15",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
+  ],
 });
 
-export const SomeCoinsInTwoCurrencies = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:2",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:5.1",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "EUR:4",
-          hasPendingTransactions: false,
-          pendingIncoming: "EUR:0",
-          pendingOutgoing: "EUR:3.01",
-          requiresUserInput: false,
-        },
-      ],
+export const NoCoinsInTreeCurrencies = createExample(TestedComponent, {
+  balances: [
+    {
+      available: "EUR:3",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
-});
-
-export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:1",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:0",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "TESTKUDOS:2000",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:0",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "EUR:4",
-          hasPendingTransactions: false,
-          pendingIncoming: "EUR:15",
-          pendingOutgoing: "EUR:0",
-          requiresUserInput: false,
-        },
-      ],
+    {
+      available: "USD:2",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "ARS:1",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:15",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
+  ],
 });
 
 export const SomeCoinsInFiveCurrencies = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:13451",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:0",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "EUR:202.02",
-          hasPendingTransactions: false,
-          pendingIncoming: "EUR:0",
-          pendingOutgoing: "EUR:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "ARS:30",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:0",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "JPY:51223233",
-          hasPendingTransactions: false,
-          pendingIncoming: "EUR:0",
-          pendingOutgoing: "EUR:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "JPY:51223233",
-          hasPendingTransactions: false,
-          pendingIncoming: "EUR:0",
-          pendingOutgoing: "EUR:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "DEMOKUDOS:6",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:0",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "TESTKUDOS:6",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:5",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-      ],
+  balances: [
+    {
+      available: "USD:0",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "ARS:13451",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "EUR:202.02",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:0",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "JPY:0",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:0",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "JPY:51223233",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:0",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "DEMOKUDOS:6",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "TESTKUDOS:6",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:5",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
+  ],
 });
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx 
b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index 3eb5f427..014d3b18 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -14,70 +14,81 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { BalancesResponse, i18n } from "@gnu-taler/taler-util";
+import { Amounts, Balance, i18n } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { BalanceTable } from "../components/BalanceTable";
 import { ButtonPrimary, ErrorBox } from "../components/styled";
-import { HookResponse, useAsyncAsHook } from "../hooks/useAsyncAsHook";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
 import { PageLink } from "../renderHtml";
 import * as wxApi from "../wxApi";
+import { MultiActionButton } from "../components/MultiActionButton";
+import { Loading } from "../components/Loading";
+
 interface Props {
   goToWalletDeposit: (currency: string) => void;
+  goToWalletHistory: (currency: string) => void;
   goToWalletManualWithdraw: () => void;
 }
 export function BalancePage({
   goToWalletManualWithdraw,
   goToWalletDeposit,
+  goToWalletHistory,
 }: Props): VNode {
   const state = useAsyncAsHook(wxApi.getBalance);
+  const balances = !state || state.hasError ? [] : state.response.balances;
+
+  if (!state) {
+    return <Loading />;
+  }
+
+  if (state.hasError) {
+    return (
+      <Fragment>
+        <ErrorBox>{state.message}</ErrorBox>
+        <p>
+          Click <PageLink pageName="welcome">here</PageLink> for help and
+          diagnostics.
+        </p>
+      </Fragment>
+    );
+  }
+
   return (
     <BalanceView
-      balance={state}
-      Linker={PageLink}
+      balances={balances}
       goToWalletManualWithdraw={goToWalletManualWithdraw}
       goToWalletDeposit={goToWalletDeposit}
+      goToWalletHistory={goToWalletHistory}
     />
   );
 }
 export interface BalanceViewProps {
-  balance: HookResponse<BalancesResponse>;
-  Linker: typeof PageLink;
+  balances: Balance[];
   goToWalletManualWithdraw: () => void;
   goToWalletDeposit: (currency: string) => void;
+  goToWalletHistory: (currency: string) => void;
 }
 
 export function BalanceView({
-  balance,
-  Linker,
+  balances,
   goToWalletManualWithdraw,
   goToWalletDeposit,
+  goToWalletHistory,
 }: BalanceViewProps): VNode {
-  if (!balance) {
-    return <div>Loading...</div>;
-  }
+  const currencyWithNonZeroAmount = balances
+    .filter((b) => !Amounts.isZero(b.available))
+    .map((b) => b.available.split(":")[0]);
 
-  if (balance.hasError) {
-    return (
-      <Fragment>
-        <ErrorBox>{balance.message}</ErrorBox>
-        <p>
-          Click <Linker pageName="welcome">here</Linker> for help and
-          diagnostics.
-        </p>
-      </Fragment>
-    );
-  }
-  if (balance.response.balances.length === 0) {
+  if (balances.length === 0) {
     return (
       <Fragment>
         <p>
           <i18n.Translate>
             You have no balance to show. Need some{" "}
-            <Linker pageName="/welcome">help</Linker> getting started?
+            <PageLink pageName="/welcome">help</PageLink> getting started?
           </i18n.Translate>
         </p>
         <footer style={{ justifyContent: "space-between" }}>
-          <div />
           <ButtonPrimary onClick={goToWalletManualWithdraw}>
             Withdraw
           </ButtonPrimary>
@@ -90,15 +101,21 @@ export function BalanceView({
     <Fragment>
       <section>
         <BalanceTable
-          balances={balance.response.balances}
-          goToWalletDeposit={goToWalletDeposit}
+          balances={balances}
+          goToWalletHistory={goToWalletHistory}
         />
       </section>
       <footer style={{ justifyContent: "space-between" }}>
-        <div />
         <ButtonPrimary onClick={goToWalletManualWithdraw}>
           Withdraw
         </ButtonPrimary>
+        {currencyWithNonZeroAmount.length > 0 && (
+          <MultiActionButton
+            label={(s) => `Deposit ${s}`}
+            actions={currencyWithNonZeroAmount}
+            onClick={(c) => goToWalletDeposit(c)}
+          />
+        )}
       </footer>
     </Fragment>
   );
diff --git a/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx 
b/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
index aec288de..840398a4 100644
--- a/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
@@ -86,10 +86,6 @@ export function View({
   return (
     <div>
       <p>Debug tools:</p>
-      <button onClick={openExtensionPage("/static/popup.html")}>
-        wallet tab
-      </button>
-
       <button onClick={confirmReset}>reset</button>
       <br />
       <button onClick={onExportDatabase}>export database</button>
@@ -109,7 +105,8 @@ export function View({
               "yyyy/MM/dd_HH:mm",
             )}.json`}
           >
-            click here
+            {" "}
+            click here{" "}
           </a>
           to download
         </div>
diff --git a/packages/taler-wallet-webextension/src/popup/History.stories.tsx 
b/packages/taler-wallet-webextension/src/popup/History.stories.tsx
deleted file mode 100644
index 43d39da8..00000000
--- a/packages/taler-wallet-webextension/src/popup/History.stories.tsx
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- 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/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import {
-  PaymentStatus,
-  TransactionCommon,
-  TransactionDeposit,
-  TransactionPayment,
-  TransactionRefresh,
-  TransactionRefund,
-  TransactionTip,
-  TransactionType,
-  TransactionWithdrawal,
-  WithdrawalType,
-} from "@gnu-taler/taler-util";
-import { createExample } from "../test-utils";
-import { HistoryView as TestedComponent } from "./History";
-
-export default {
-  title: "popup/history/list",
-  component: TestedComponent,
-};
-
-const commonTransaction = {
-  amountRaw: "USD:10",
-  amountEffective: "USD:9",
-  pending: false,
-  timestamp: {
-    t_ms: new Date().getTime(),
-  },
-  transactionId: "12",
-} as TransactionCommon;
-
-const exampleData = {
-  withdraw: {
-    ...commonTransaction,
-    type: TransactionType.Withdrawal,
-    exchangeBaseUrl: "http://exchange.demo.taler.net";,
-    withdrawalDetails: {
-      reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
-      confirmed: false,
-      exchangePaytoUris: ["payto://x-taler-bank/bank/account"],
-      type: WithdrawalType.ManualTransfer,
-    },
-  } as TransactionWithdrawal,
-  payment: {
-    ...commonTransaction,
-    amountEffective: "USD:11",
-    type: TransactionType.Payment,
-    info: {
-      contractTermsHash: "ASDZXCASD",
-      merchant: {
-        name: "the merchant",
-      },
-      orderId: "2021.167-03NPY6MCYMVGT",
-      products: [],
-      summary: "the summary",
-      fulfillmentMessage: "",
-    },
-    proposalId: "1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0",
-    status: PaymentStatus.Accepted,
-  } as TransactionPayment,
-  deposit: {
-    ...commonTransaction,
-    type: TransactionType.Deposit,
-    depositGroupId: "#groupId",
-    targetPaytoUri: "payto://x-taler-bank/bank/account",
-  } as TransactionDeposit,
-  refresh: {
-    ...commonTransaction,
-    type: TransactionType.Refresh,
-    exchangeBaseUrl: "http://exchange.taler";,
-  } as TransactionRefresh,
-  tip: {
-    ...commonTransaction,
-    type: TransactionType.Tip,
-    merchantBaseUrl: "http://merchant.taler";,
-  } as TransactionTip,
-  refund: {
-    ...commonTransaction,
-    type: TransactionType.Refund,
-    refundedTransactionId:
-      "payment:1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0",
-    info: {
-      contractTermsHash: "ASDZXCASD",
-      merchant: {
-        name: "the merchant",
-      },
-      orderId: "2021.167-03NPY6MCYMVGT",
-      products: [],
-      summary: "the summary",
-      fulfillmentMessage: "",
-    },
-  } as TransactionRefund,
-};
-
-export const EmptyWithBalance = createExample(TestedComponent, {
-  list: [],
-  balances: [
-    {
-      available: "TESTKUDOS:10",
-      pendingIncoming: "TESTKUDOS:0",
-      pendingOutgoing: "TESTKUDOS:0",
-      hasPendingTransactions: false,
-      requiresUserInput: false,
-    },
-  ],
-});
-
-export const EmptyWithNoBalance = createExample(TestedComponent, {
-  list: [],
-  balances: [],
-});
-
-export const One = createExample(TestedComponent, {
-  list: [exampleData.withdraw],
-  balances: [
-    {
-      available: "USD:10",
-      pendingIncoming: "USD:0",
-      pendingOutgoing: "USD:0",
-      hasPendingTransactions: false,
-      requiresUserInput: false,
-    },
-  ],
-});
-
-export const OnePending = createExample(TestedComponent, {
-  list: [
-    {
-      ...exampleData.withdraw,
-      pending: true,
-    },
-  ],
-  balances: [
-    {
-      available: "USD:10",
-      pendingIncoming: "USD:0",
-      pendingOutgoing: "USD:0",
-      hasPendingTransactions: false,
-      requiresUserInput: false,
-    },
-  ],
-});
-
-export const Several = createExample(TestedComponent, {
-  list: [
-    exampleData.withdraw,
-    exampleData.payment,
-    exampleData.withdraw,
-    exampleData.payment,
-    exampleData.refresh,
-    exampleData.refund,
-    exampleData.tip,
-    exampleData.deposit,
-  ],
-  balances: [
-    {
-      available: "TESTKUDOS:10",
-      pendingIncoming: "TESTKUDOS:0",
-      pendingOutgoing: "TESTKUDOS:0",
-      hasPendingTransactions: false,
-      requiresUserInput: false,
-    },
-  ],
-});
-
-export const SeveralWithTwoCurrencies = createExample(TestedComponent, {
-  list: [
-    exampleData.withdraw,
-    exampleData.payment,
-    exampleData.withdraw,
-    exampleData.payment,
-    exampleData.refresh,
-    exampleData.refund,
-    exampleData.tip,
-    exampleData.deposit,
-  ],
-  balances: [
-    {
-      available: "TESTKUDOS:10",
-      pendingIncoming: "TESTKUDOS:0",
-      pendingOutgoing: "TESTKUDOS:0",
-      hasPendingTransactions: false,
-      requiresUserInput: false,
-    },
-    {
-      available: "USD:10",
-      pendingIncoming: "USD:0",
-      pendingOutgoing: "USD:0",
-      hasPendingTransactions: false,
-      requiresUserInput: false,
-    },
-  ],
-});
diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx 
b/packages/taler-wallet-webextension/src/popup/History.tsx
deleted file mode 100644
index 2dfddb8c..00000000
--- a/packages/taler-wallet-webextension/src/popup/History.tsx
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
-
- 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.
-
- 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
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-import {
-  AmountString,
-  Balance,
-  i18n,
-  Transaction,
-  TransactionsResponse,
-} from "@gnu-taler/taler-util";
-import { Fragment, h, VNode } from "preact";
-import { useEffect, useState } from "preact/hooks";
-import { ButtonPrimary } from "../components/styled";
-import { TransactionItem } from "../components/TransactionItem";
-import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
-import * as wxApi from "../wxApi";
-import { AddNewActionView } from "./AddNewActionView";
-
-export function HistoryPage(): VNode {
-  const [transactions, setTransactions] = useState<
-    TransactionsResponse | undefined
-  >(undefined);
-  const balance = useAsyncAsHook(wxApi.getBalance);
-  const balanceWithoutError = balance?.hasError
-    ? []
-    : balance?.response.balances || [];
-
-  useEffect(() => {
-    const fetchData = async (): Promise<void> => {
-      const res = await wxApi.getTransactions();
-      setTransactions(res);
-    };
-    fetchData();
-  }, []);
-
-  const [addingAction, setAddingAction] = useState(false);
-
-  if (addingAction) {
-    return <AddNewActionView onCancel={() => setAddingAction(false)} />;
-  }
-
-  if (!transactions) {
-    return <div>Loading ...</div>;
-  }
-
-  return (
-    <HistoryView
-      balances={balanceWithoutError}
-      list={[...transactions.transactions].reverse()}
-      onAddNewAction={() => setAddingAction(true)}
-    />
-  );
-}
-
-function amountToString(c: AmountString): string {
-  const idx = c.indexOf(":");
-  return `${c.substring(idx + 1)} ${c.substring(0, idx)}`;
-}
-
-export function HistoryView({
-  list,
-  balances,
-  onAddNewAction,
-}: {
-  list: Transaction[];
-  balances: Balance[];
-  onAddNewAction: () => void;
-}): VNode {
-  const multiCurrency = balances.length > 1;
-  return (
-    <Fragment>
-      <header>
-        {balances.length > 0 ? (
-          <Fragment>
-            {multiCurrency ? (
-              <div class="title">
-                Balance:{" "}
-                <ul style={{ margin: 0 }}>
-                  {balances.map((b, i) => (
-                    <li key={i}>{b.available}</li>
-                  ))}
-                </ul>
-              </div>
-            ) : (
-              <div class="title">
-                Balance: <span>{amountToString(balances[0].available)}</span>
-              </div>
-            )}
-          </Fragment>
-        ) : (
-          <div />
-        )}
-        <div>
-          <ButtonPrimary onClick={onAddNewAction}>
-            <b>+</b>
-          </ButtonPrimary>
-        </div>
-      </header>
-      {list.length === 0 ? (
-        <section data-expanded data-centered>
-          <p>
-            <i18n.Translate>
-              You have no history yet, here you will be able to check your last
-              transactions.
-            </i18n.Translate>
-          </p>
-        </section>
-      ) : (
-        <section>
-          {list.slice(0, 3).map((tx, i) => (
-            <TransactionItem key={i} tx={tx} multiCurrency={multiCurrency} />
-          ))}
-        </section>
-      )}
-      <footer style={{ justifyContent: "space-around" }}>
-        {list.length > 0 && (
-          <a
-            target="_blank"
-            rel="noopener noreferrer"
-            style={{ color: "darkgreen", textDecoration: "none" }}
-            href={
-              // eslint-disable-next-line no-undef
-              typeof chrome !== "undefined" && chrome.extension
-                ? // eslint-disable-next-line no-undef
-                  chrome.extension.getURL(`/static/wallet.html#/history`)
-                : "#"
-            }
-          >
-            VIEW MORE TRANSACTIONS
-          </a>
-        )}
-      </footer>
-    </Fragment>
-  );
-}
diff --git a/packages/taler-wallet-webextension/src/popup/index.stories.tsx 
b/packages/taler-wallet-webextension/src/popup/index.stories.tsx
index c3e60c4e..3abb8002 100644
--- a/packages/taler-wallet-webextension/src/popup/index.stories.tsx
+++ b/packages/taler-wallet-webextension/src/popup/index.stories.tsx
@@ -19,11 +19,10 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import * as a1 from "./AddNewActionView.stories";
+import * as a1 from "../wallet/AddNewActionView.stories";
 import * as a2 from "./Balance.stories";
 import * as a3 from "./DeveloperPage.stories";
-import * as a4 from "./History.stories";
 import * as a5 from "./Popup.stories";
 import * as a6 from "./TalerActionFound.stories";
 
-export default [a1, a2, a3, a4, a5, a6];
+export default [a1, a2, a3, a5, a6];
diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx 
b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
index 27372db5..908349e8 100644
--- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
@@ -33,13 +33,13 @@ import { Pages, WalletNavBar } from "./NavigationBar";
 import { BackupPage } from "./wallet/BackupPage";
 import { BalancePage } from "./popup/BalancePage";
 import { DeveloperPage } from "./popup/DeveloperPage";
-import { HistoryPage } from "./popup/History";
 import { ProviderAddPage } from "./wallet/ProviderAddPage";
 import { ProviderDetailPage } from "./wallet/ProviderDetailPage";
 import { SettingsPage } from "./popup/Settings";
 import { TalerActionFound } from "./popup/TalerActionFound";
 import { ExchangeAddPage } from "./wallet/ExchangeAddPage";
 import { IoCProviderForRuntime } from "./context/iocContext";
+import { LastActivityPage } from "./wallet/LastActivityPage";
 
 function main(): void {
   try {
@@ -77,12 +77,13 @@ function CheckTalerActionComponent(): VNode {
 
 function Application() {
   return (
-    <div>
-      <DevContextProvider>
+    // <div>
+    <DevContextProvider>
+      {({ devMode }: { devMode: boolean }) => (
         <IoCProviderForRuntime>
-          <WalletNavBar />
+          <WalletNavBar devMode={devMode} />
           <CheckTalerActionComponent />
-          <PopupBox>
+          <PopupBox devMode={devMode}>
             <Router history={createHashHistory()}>
               <Route path={Pages.dev} component={DeveloperPage} />
 
@@ -90,10 +91,14 @@ function Application() {
                 path={Pages.balance}
                 component={BalancePage}
                 goToWalletManualWithdraw={() =>
-                  goToWalletPage(Pages.manual_withdraw)
+                  goToWalletPage(
+                    Pages.manual_withdraw.replace(":currency?", ""),
+                  )
                 }
-                goToWalletDeposit={(currency: string) =>
-                  goToWalletPage(Pages.deposit.replace(":currency", currency))
+                goToWalletHistory={(currency: string) =>
+                  goToWalletPage(
+                    Pages.balance_history.replace(":currency", currency),
+                  )
                 }
               />
               <Route path={Pages.settings} component={SettingsPage} />
@@ -114,6 +119,8 @@ function Application() {
                 }}
               />
 
+              <Route path={Pages.last_activity} component={LastActivityPage} />
+
               <Route
                 path={Pages.transaction}
                 component={({ tid }: { tid: string }) =>
@@ -121,8 +128,6 @@ function Application() {
                 }
               />
 
-              <Route path={Pages.history} component={HistoryPage} />
-
               <Route
                 path={Pages.backup}
                 component={BackupPage}
@@ -157,8 +162,9 @@ function Application() {
             </Router>
           </PopupBox>
         </IoCProviderForRuntime>
-      </DevContextProvider>
-    </div>
+      )}
+    </DevContextProvider>
+    // </div>
   );
 }
 
diff --git a/packages/taler-wallet-webextension/src/renderHtml.tsx 
b/packages/taler-wallet-webextension/src/renderHtml.tsx
index 15986d5d..ba98ae23 100644
--- a/packages/taler-wallet-webextension/src/renderHtml.tsx
+++ b/packages/taler-wallet-webextension/src/renderHtml.tsx
@@ -162,7 +162,12 @@ export function PageLink(props: {
   children?: ComponentChildren;
 }): VNode {
   // eslint-disable-next-line no-undef
-  const url = 
chrome.extension.getURL(`/static/wallet.html#/${props.pageName}`);
+
+  const url =
+    typeof chrome === "undefined"
+      ? undefined
+      : // eslint-disable-next-line no-undef
+        chrome.extension?.getURL(`/static/wallet.html#/${props.pageName}`);
   return (
     <a class="actionLink" href={url} target="_blank" rel="noopener noreferrer">
       {props.children}
diff --git a/packages/taler-wallet-webextension/src/test-utils.ts 
b/packages/taler-wallet-webextension/src/test-utils.ts
index fbb7c56f..8c721a9d 100644
--- a/packages/taler-wallet-webextension/src/test-utils.ts
+++ b/packages/taler-wallet-webextension/src/test-utils.ts
@@ -117,5 +117,6 @@ export function mountBrowser<T>(callback: () => T, 
Context?: ({ children }: { ch
   }
 }
 
+const nullTestFunction = {} as TestFunction
 export const justBrowser_it: PendingTestFunction | TestFunction =
-  typeof window === 'undefined' ? it.skip : it
\ No newline at end of file
+  typeof it === 'undefined' ? nullTestFunction : (typeof window === 
'undefined' ? it.skip : it)
\ No newline at end of file
diff --git 
a/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx
similarity index 96%
copy from 
packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx
copy to 
packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx
index 6ee56ef7..54e4eb1f 100644
--- a/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx
@@ -23,7 +23,7 @@ import { createExample } from "../test-utils";
 import { AddNewActionView as TestedComponent } from "./AddNewActionView";
 
 export default {
-  title: "popup/add new action",
+  title: "wallet/add new action",
   component: TestedComponent,
   argTypes: {
     setDeviceName: () => Promise.resolve(),
diff --git a/packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx 
b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx
similarity index 100%
rename from packages/taler-wallet-webextension/src/popup/AddNewActionView.tsx
rename to packages/taler-wallet-webextension/src/wallet/AddNewActionView.tsx
diff --git a/packages/taler-wallet-webextension/src/wallet/Balance.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Balance.stories.tsx
index 2432c31e..6c670b01 100644
--- a/packages/taler-wallet-webextension/src/wallet/Balance.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Balance.stories.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { createExample, NullLink } from "../test-utils";
+import { createExample } from "../test-utils";
 import { BalanceView as TestedComponent } from "./BalancePage";
 
 export default {
@@ -28,83 +28,124 @@ export default {
   argTypes: {},
 };
 
-export const NotYetLoaded = createExample(TestedComponent, {});
-
-export const GotError = createExample(TestedComponent, {
-  balance: {
-    hasError: true,
-    message: "Network error",
-  },
-  Linker: NullLink,
+export const EmptyBalance = createExample(TestedComponent, {
+  balances: [],
 });
 
-export const EmptyBalance = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [],
+export const SomeCoins = createExample(TestedComponent, {
+  balances: [
+    {
+      available: "USD:10.5",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
+  ],
 });
 
-export const SomeCoins = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:10.5",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:0",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-      ],
+export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, {
+  balances: [
+    {
+      available: "EUR:1",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "TESTKUDOS:2000",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
+    {
+      available: "JPY:4",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:15",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
+    },
+  ],
 });
 
-export const SomeCoinsAndIncomingMoney = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:2.23",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:5.11",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-      ],
+export const NoCoinsInTreeCurrencies = createExample(TestedComponent, {
+  balances: [
+    {
+      available: "EUR:3",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "USD:2",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
+    {
+      available: "ARS:1",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:15",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
+    },
+  ],
 });
 
-export const SomeCoinsInTwoCurrencies = createExample(TestedComponent, {
-  balance: {
-    hasError: false,
-    response: {
-      balances: [
-        {
-          available: "USD:2",
-          hasPendingTransactions: false,
-          pendingIncoming: "USD:5",
-          pendingOutgoing: "USD:0",
-          requiresUserInput: false,
-        },
-        {
-          available: "EUR:4",
-          hasPendingTransactions: false,
-          pendingIncoming: "EUR:5",
-          pendingOutgoing: "EUR:0",
-          requiresUserInput: false,
-        },
-      ],
+export const SomeCoinsInFiveCurrencies = createExample(TestedComponent, {
+  balances: [
+    {
+      available: "USD:0",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "ARS:13451",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "EUR:202.02",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:0",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "JPY:0",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:0",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "JPY:51223233",
+      hasPendingTransactions: false,
+      pendingIncoming: "EUR:0",
+      pendingOutgoing: "EUR:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "DEMOKUDOS:6",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
+    },
+    {
+      available: "TESTKUDOS:6",
+      hasPendingTransactions: false,
+      pendingIncoming: "USD:5",
+      pendingOutgoing: "USD:0",
+      requiresUserInput: false,
     },
-  },
-  Linker: NullLink,
+  ],
 });
diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx 
b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
index 33182a38..5fa08f8a 100644
--- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
@@ -14,68 +14,87 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { BalancesResponse, i18n } from "@gnu-taler/taler-util";
+import { Amounts, Balance, i18n } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { BalanceTable } from "../components/BalanceTable";
-import { ButtonPrimary, Centered, ErrorBox } from "../components/styled";
-import { HookResponse, useAsyncAsHook } from "../hooks/useAsyncAsHook";
+import { Loading } from "../components/Loading";
+import { MultiActionButton } from "../components/MultiActionButton";
+import {
+  ButtonPrimary,
+  Centered,
+  ErrorBox,
+  SuccessBox,
+} from "../components/styled";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
 import { PageLink } from "../renderHtml";
 import * as wxApi from "../wxApi";
 
+interface Props {
+  goToWalletDeposit: (currency: string) => void;
+  goToWalletHistory: (currency: string) => void;
+  goToWalletManualWithdraw: () => void;
+}
+
 export function BalancePage({
   goToWalletManualWithdraw,
   goToWalletDeposit,
-}: {
-  goToWalletDeposit: (currency: string) => void;
-  goToWalletManualWithdraw: () => void;
-}): VNode {
+  goToWalletHistory,
+}: Props): VNode {
   const state = useAsyncAsHook(wxApi.getBalance);
+
+  const balances = !state || state.hasError ? [] : state.response.balances;
+
+  if (!state) {
+    return <Loading />;
+  }
+
+  if (state.hasError) {
+    return (
+      <Fragment>
+        <ErrorBox>{state.message}</ErrorBox>
+        <p>
+          Click <PageLink pageName="welcome">here</PageLink> for help and
+          diagnostics.
+        </p>
+      </Fragment>
+    );
+  }
+
   return (
     <BalanceView
-      balance={state}
-      Linker={PageLink}
+      balances={balances}
       goToWalletManualWithdraw={goToWalletManualWithdraw}
       goToWalletDeposit={goToWalletDeposit}
+      goToWalletHistory={goToWalletHistory}
     />
   );
 }
 
 export interface BalanceViewProps {
-  balance: HookResponse<BalancesResponse>;
-  Linker: typeof PageLink;
+  balances: Balance[];
   goToWalletManualWithdraw: () => void;
   goToWalletDeposit: (currency: string) => void;
+  goToWalletHistory: (currency: string) => void;
 }
 
 export function BalanceView({
-  balance,
-  Linker,
+  balances,
   goToWalletManualWithdraw,
   goToWalletDeposit,
+  goToWalletHistory,
 }: BalanceViewProps): VNode {
-  if (!balance) {
-    return <div>Loading...</div>;
-  }
+  const currencyWithNonZeroAmount = balances
+    .filter((b) => !Amounts.isZero(b.available))
+    .map((b) => b.available.split(":")[0]);
 
-  if (balance.hasError) {
-    return (
-      <Fragment>
-        <ErrorBox>{balance.message}</ErrorBox>
-        <p>
-          Click <Linker pageName="welcome">here</Linker> for help and
-          diagnostics.
-        </p>
-      </Fragment>
-    );
-  }
-  if (balance.response.balances.length === 0) {
+  if (balances.length === 0) {
     return (
       <Fragment>
         <p>
           <Centered style={{ marginTop: 100 }}>
             <i18n.Translate>
               You have no balance to show. Need some{" "}
-              <Linker pageName="/welcome">help</Linker> getting started?
+              <PageLink pageName="/welcome">help</PageLink> getting started?
             </i18n.Translate>
           </Centered>
         </p>
@@ -93,15 +112,21 @@ export function BalanceView({
     <Fragment>
       <section>
         <BalanceTable
-          balances={balance.response.balances}
-          goToWalletDeposit={goToWalletDeposit}
+          balances={balances}
+          goToWalletHistory={goToWalletHistory}
         />
       </section>
       <footer style={{ justifyContent: "space-between" }}>
-        <div />
         <ButtonPrimary onClick={goToWalletManualWithdraw}>
           Withdraw
         </ButtonPrimary>
+        {currencyWithNonZeroAmount.length > 0 && (
+          <MultiActionButton
+            label={(s) => `Deposit ${s}`}
+            actions={currencyWithNonZeroAmount}
+            onClick={(c) => goToWalletDeposit(c)}
+          />
+        )}
       </footer>
     </Fragment>
   );
diff --git 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
index 36feeb76..f32a2aa5 100644
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
@@ -41,12 +41,14 @@ export interface Props {
   exchangeList: Record<string, string>;
   onCreate: (exchangeBaseUrl: string, amount: AmountJson) => Promise<void>;
   onAddExchange: () => void;
+  initialCurrency?: string;
 }
 
 export function CreateManualWithdraw({
   initialAmount,
   exchangeList,
   error,
+  initialCurrency,
   onCreate,
   onAddExchange,
 }: Props): VNode {
@@ -61,8 +63,16 @@ export function CreateManualWithdraw({
     {} as Record<string, string>,
   );
 
+  const foundExchangeForCurrency = exchangeSelectList.findIndex(
+    (e) => exchangeList[e] === initialCurrency,
+  );
+
   const initialExchange =
-    exchangeSelectList.length > 0 ? exchangeSelectList[0] : "";
+    foundExchangeForCurrency !== -1
+      ? exchangeSelectList[foundExchangeForCurrency]
+      : exchangeSelectList.length > 0
+      ? exchangeSelectList[0]
+      : "";
 
   const [exchange, setExchange] = useState(initialExchange || "");
   const [currency, setCurrency] = useState(exchangeList[initialExchange] ?? 
"");
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx
index 5c931394..9e15daa9 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx
@@ -23,23 +23,24 @@ import {
 import { DepositFee } from 
"@gnu-taler/taler-wallet-core/src/operations/deposits";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
-import { Part } from "../components/Part";
+import { Loading } from "../components/Loading";
 import { SelectList } from "../components/SelectList";
 import {
+  ButtonBoxWarning,
   ButtonPrimary,
   ErrorText,
   Input,
   InputWithLabel,
+  WarningBox,
 } from "../components/styled";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
 import * as wxApi from "../wxApi";
 
 interface Props {
   currency: string;
+  onSuccess: (currency: string) => void;
 }
-export function DepositPage({ currency }: Props): VNode {
-  const [success, setSuccess] = useState(false);
-
+export function DepositPage({ currency, onSuccess }: Props): VNode {
   const state = useAsyncAsHook(async () => {
     const balance = await wxApi.getBalance();
     const bs = balance.balances.filter((b) => 
b.available.startsWith(currency));
@@ -63,7 +64,7 @@ export function DepositPage({ currency }: Props): VNode {
 
   async function doSend(account: string, amount: AmountString): Promise<void> {
     await wxApi.createDepositGroup(account, amount);
-    setSuccess(true);
+    onSuccess(currency);
   }
 
   async function getFeeForAmount(
@@ -73,8 +74,8 @@ export function DepositPage({ currency }: Props): VNode {
     return await wxApi.getFeeForDeposit(account, amount);
   }
 
-  if (accounts.length === 0) return <div>loading..</div>;
-  if (success) return <div>deposit created</div>;
+  if (accounts.length === 0) return <Loading />;
+
   return (
     <View
       knownBankAccounts={accounts}
@@ -105,8 +106,17 @@ export function View({
   const [accountIdx, setAccountIdx] = useState(0);
   const [amount, setAmount] = useState<number | undefined>(undefined);
   const [fee, setFee] = useState<DepositFee | undefined>(undefined);
+  function updateAmount(num: number | undefined) {
+    setAmount(num);
+    setFee(undefined);
+  }
+  const feeHasBeenCalculated = fee !== undefined;
   const currency = balance.currency;
   const amountStr: AmountString = `${currency}:${amount}`;
+  const feeSum =
+    fee !== undefined
+      ? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount
+      : Amounts.getZero(currency);
 
   const account = knownBankAccounts.length
     ? knownBankAccounts[accountIdx]
@@ -126,7 +136,12 @@ export function View({
     return <div>no balance</div>;
   }
   if (!knownBankAccounts || !knownBankAccounts.length) {
-    return <div>there is no known bank account to send money to</div>;
+    return (
+      <WarningBox>
+        <p>There is no known bank account to send money to</p>
+        <ButtonBoxWarning>Withdraw</ButtonBoxWarning>
+      </WarningBox>
+    );
   }
   const parsedAmount =
     amount === undefined ? undefined : Amounts.parse(amountStr);
@@ -136,9 +151,16 @@ export function View({
     : !parsedAmount
     ? "Invalid amount"
     : Amounts.cmp(balance, parsedAmount) === -1
-    ? `To much, your current balance is ${balance.value}`
+    ? `To much, your current balance is ${Amounts.stringifyValue(balance)}`
     : undefined;
 
+  const totalToDeposit = parsedAmount
+    ? Amounts.sub(parsedAmount, feeSum).amount
+    : Amounts.getZero(currency);
+
+  const unableToDeposit =
+    Amounts.isZero(totalToDeposit) && feeHasBeenCalculated;
+
   return (
     <Fragment>
       <h2>Send {currency} to your account</h2>
@@ -153,7 +175,7 @@ export function View({
           />
         </Input>
         <InputWithLabel invalid={!!error}>
-          <label>Amount to send</label>
+          <label>Amount</label>
           <div>
             <span>{currency}</span>
             <input
@@ -161,11 +183,10 @@ export function View({
               value={amount}
               onInput={(e) => {
                 const num = parseFloat(e.currentTarget.value);
-                console.log(num);
                 if (!Number.isNaN(num)) {
-                  setAmount(num);
+                  updateAmount(num);
                 } else {
-                  setAmount(undefined);
+                  updateAmount(undefined);
                   setFee(undefined);
                 }
               }}
@@ -173,40 +194,41 @@ export function View({
           </div>
           {error && <ErrorText>{error}</ErrorText>}
         </InputWithLabel>
-        {!error && fee && (
-          <div style={{ textAlign: "center" }}>
-            <Part
-              title="Exchange fee"
-              text={Amounts.stringify(Amounts.sum([fee.wire, 
fee.coin]).amount)}
-              kind="negative"
-            />
-            <Part
-              title="Change cost"
-              text={Amounts.stringify(fee.refresh)}
-              kind="negative"
-            />
-            {parsedAmount && (
-              <Part
-                title="Total received"
-                text={Amounts.stringify(
-                  Amounts.sub(
-                    parsedAmount,
-                    Amounts.sum([fee.wire, fee.coin]).amount,
-                  ).amount,
-                )}
-                kind="positive"
-              />
-            )}
-          </div>
-        )}
+        {
+          <Fragment>
+            <InputWithLabel>
+              <label>Deposit fee</label>
+              <div>
+                <span>{currency}</span>
+                <input
+                  type="number"
+                  disabled
+                  value={Amounts.stringifyValue(feeSum)}
+                />
+              </div>
+            </InputWithLabel>
+
+            <InputWithLabel>
+              <label>Total deposit</label>
+              <div>
+                <span>{currency}</span>
+                <input
+                  type="number"
+                  disabled
+                  value={Amounts.stringifyValue(totalToDeposit)}
+                />
+              </div>
+            </InputWithLabel>
+          </Fragment>
+        }
       </section>
       <footer>
         <div />
         <ButtonPrimary
-          disabled={!parsedAmount}
+          disabled={unableToDeposit}
           onClick={() => onSend(accountURI, amountStr)}
         >
-          Send
+          Deposit {Amounts.stringifyValue(totalToDeposit)} {currency}
         </ButtonPrimary>
       </footer>
     </Fragment>
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index ce4b0fb7..3f550175 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -35,7 +35,7 @@ import { HistoryView as TestedComponent } from "./History";
 import { createExample } from "../test-utils";
 
 export default {
-  title: "wallet/history/list",
+  title: "wallet/balance/history",
   component: TestedComponent,
 };
 
@@ -114,8 +114,13 @@ const exampleData = {
   } as TransactionRefund,
 };
 
-export const Empty = createExample(TestedComponent, {
-  list: [],
+export const NoBalance = createExample(TestedComponent, {
+  transactions: [],
+  balances: [],
+});
+
+export const SomeBalanceWithNoTransactions = createExample(TestedComponent, {
+  transactions: [],
   balances: [
     {
       available: "TESTKUDOS:10",
@@ -127,16 +132,24 @@ export const Empty = createExample(TestedComponent, {
   ],
 });
 
-export const EmptyWithNoBalance = createExample(TestedComponent, {
-  list: [],
-  balances: [],
+export const OneSimpleTransaction = createExample(TestedComponent, {
+  transactions: [exampleData.withdraw],
+  balances: [
+    {
+      available: "USD:10",
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      hasPendingTransactions: false,
+      requiresUserInput: false,
+    },
+  ],
 });
 
-export const One = createExample(TestedComponent, {
-  list: [exampleData.withdraw],
+export const TwoTransactionsAndZeroBalance = createExample(TestedComponent, {
+  transactions: [exampleData.withdraw, exampleData.deposit],
   balances: [
     {
-      available: "USD:10",
+      available: "USD:0",
       pendingIncoming: "USD:0",
       pendingOutgoing: "USD:0",
       hasPendingTransactions: false,
@@ -145,8 +158,8 @@ export const One = createExample(TestedComponent, {
   ],
 });
 
-export const OnePending = createExample(TestedComponent, {
-  list: [
+export const OneTransactionPending = createExample(TestedComponent, {
+  transactions: [
     {
       ...exampleData.withdraw,
       pending: true,
@@ -163,8 +176,8 @@ export const OnePending = createExample(TestedComponent, {
   ],
 });
 
-export const Several = createExample(TestedComponent, {
-  list: [
+export const SomeTransactions = createExample(TestedComponent, {
+  transactions: [
     exampleData.withdraw,
     exampleData.payment,
     exampleData.withdraw,
@@ -183,38 +196,82 @@ export const Several = createExample(TestedComponent, {
   ],
   balances: [
     {
-      available: "TESTKUDOS:10",
-      pendingIncoming: "TESTKUDOS:0",
-      pendingOutgoing: "TESTKUDOS:0",
+      available: "USD:10",
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
       hasPendingTransactions: false,
       requiresUserInput: false,
     },
   ],
 });
 
-export const SeveralWithTwoCurrencies = createExample(TestedComponent, {
-  list: [
-    exampleData.withdraw,
-    exampleData.payment,
-    exampleData.withdraw,
-    exampleData.payment,
-    exampleData.refresh,
-    exampleData.refund,
-    exampleData.tip,
-    exampleData.deposit,
-  ],
+export const SomeTransactionsWithTwoCurrencies = createExample(
+  TestedComponent,
+  {
+    transactions: [
+      exampleData.withdraw,
+      exampleData.payment,
+      exampleData.withdraw,
+      exampleData.payment,
+      exampleData.refresh,
+      exampleData.refund,
+      exampleData.tip,
+      exampleData.deposit,
+    ],
+    balances: [
+      {
+        available: "USD:0",
+        pendingIncoming: "USD:0",
+        pendingOutgoing: "USD:0",
+        hasPendingTransactions: false,
+        requiresUserInput: false,
+      },
+      {
+        available: "TESTKUDOS:10",
+        pendingIncoming: "TESTKUDOS:0",
+        pendingOutgoing: "TESTKUDOS:0",
+        hasPendingTransactions: false,
+        requiresUserInput: false,
+      },
+    ],
+  },
+);
+
+export const FiveOfficialCurrencies = createExample(TestedComponent, {
+  transactions: [exampleData.withdraw],
   balances: [
     {
-      available: "TESTKUDOS:10",
+      available: "USD:1000",
+      pendingIncoming: "USD:0",
+      pendingOutgoing: "USD:0",
+      hasPendingTransactions: false,
+      requiresUserInput: false,
+    },
+    {
+      available: "EUR:881",
       pendingIncoming: "TESTKUDOS:0",
       pendingOutgoing: "TESTKUDOS:0",
       hasPendingTransactions: false,
       requiresUserInput: false,
     },
     {
-      available: "USD:10",
-      pendingIncoming: "USD:0",
-      pendingOutgoing: "USD:0",
+      available: "COL:4043000.5",
+      pendingIncoming: "TESTKUDOS:0",
+      pendingOutgoing: "TESTKUDOS:0",
+      hasPendingTransactions: false,
+      requiresUserInput: false,
+    },
+    {
+      available: "JPY:11564450.6",
+      pendingIncoming: "TESTKUDOS:0",
+      pendingOutgoing: "TESTKUDOS:0",
+      hasPendingTransactions: false,
+      requiresUserInput: false,
+    },
+    {
+      available: "GBP:736",
+      pendingIncoming: "TESTKUDOS:0",
+      pendingOutgoing: "TESTKUDOS:0",
       hasPendingTransactions: false,
       requiresUserInput: false,
     },
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.tsx
index 58db0360..7912d169 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -15,21 +15,38 @@
  */
 
 import {
-  AmountString,
+  Amounts,
   Balance,
   NotificationType,
   Transaction,
 } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { ButtonPrimary, DateSeparator } from "../components/styled";
+import { Loading } from "../components/Loading";
+import {
+  ButtonBoxPrimary,
+  ButtonBoxWarning,
+  ButtonPrimary,
+  DateSeparator,
+  ErrorBox,
+  NiceSelect,
+  WarningBox,
+} from "../components/styled";
 import { Time } from "../components/Time";
 import { TransactionItem } from "../components/TransactionItem";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
-import { AddNewActionView } from "../popup/AddNewActionView";
 import * as wxApi from "../wxApi";
 
-export function HistoryPage(): VNode {
+interface Props {
+  currency?: string;
+  goToWalletDeposit: (currency: string) => void;
+  goToWalletManualWithdraw: (currency?: string) => void;
+}
+export function HistoryPage({
+  currency,
+  goToWalletManualWithdraw,
+  goToWalletDeposit,
+}: Props): VNode {
   const balance = useAsyncAsHook(wxApi.getBalance);
   const balanceWithoutError = balance?.hasError
     ? []
@@ -39,110 +56,166 @@ export function HistoryPage(): VNode {
     NotificationType.WithdrawGroupFinished,
   ]);
 
-  const [addingAction, setAddingAction] = useState(false);
-
-  if (addingAction) {
-    return <AddNewActionView onCancel={() => setAddingAction(false)} />;
+  if (!transactionQuery || !balance) {
+    return <Loading />;
   }
 
-  if (!transactionQuery) {
-    return <div>Loading ...</div>;
-  }
   if (transactionQuery.hasError) {
-    return <div>There was an error loading the transactions.</div>;
+    return (
+      <Fragment>
+        <ErrorBox>{transactionQuery.message}</ErrorBox>
+        <p>There was an error loading the transactions.</p>
+      </Fragment>
+    );
   }
 
   return (
     <HistoryView
       balances={balanceWithoutError}
-      list={[...transactionQuery.response.transactions].reverse()}
-      onAddNewAction={() => setAddingAction(true)}
+      defaultCurrency={currency}
+      goToWalletManualWithdraw={goToWalletManualWithdraw}
+      goToWalletDeposit={goToWalletDeposit}
+      transactions={[...transactionQuery.response.transactions].reverse()}
     />
   );
 }
 
-function amountToString(c: AmountString): string {
-  const idx = c.indexOf(":");
-  return `${c.substring(idx + 1)} ${c.substring(0, idx)}`;
-}
-
 const term = 1000 * 60 * 60 * 24;
 function normalizeToDay(x: number): number {
   return Math.round(x / term) * term;
 }
 
 export function HistoryView({
-  list,
+  defaultCurrency,
+  transactions,
   balances,
-  onAddNewAction,
+  goToWalletManualWithdraw,
+  goToWalletDeposit,
 }: {
-  list: Transaction[];
+  goToWalletDeposit: (currency: string) => void;
+  goToWalletManualWithdraw: (currency?: string) => void;
+  defaultCurrency?: string;
+  transactions: Transaction[];
   balances: Balance[];
-  onAddNewAction: () => void;
 }): VNode {
-  const byDate = list.reduce((rv, x) => {
-    const theDate =
-      x.timestamp.t_ms === "never" ? 0 : normalizeToDay(x.timestamp.t_ms);
-    if (theDate) {
-      (rv[theDate] = rv[theDate] || []).push(x);
-    }
+  const currencies = balances.map((b) => b.available.split(":")[0]);
 
-    return rv;
-  }, {} as { [x: string]: Transaction[] });
+  const defaultCurrencyIndex = currencies.findIndex(
+    (c) => c === defaultCurrency,
+  );
+  const [currencyIndex, setCurrencyIndex] = useState(
+    defaultCurrencyIndex === -1 ? 0 : defaultCurrencyIndex,
+  );
+  const selectedCurrency =
+    currencies.length > 0 ? currencies[currencyIndex] : undefined;
+
+  const currencyAmount = balances[currencyIndex]
+    ? Amounts.jsonifyAmount(balances[currencyIndex].available)
+    : undefined;
+
+  const byDate = transactions
+    .filter((t) => t.amountRaw.split(":")[0] === selectedCurrency)
+    .reduce((rv, x) => {
+      const theDate =
+        x.timestamp.t_ms === "never" ? 0 : normalizeToDay(x.timestamp.t_ms);
+      if (theDate) {
+        (rv[theDate] = rv[theDate] || []).push(x);
+      }
+
+      return rv;
+    }, {} as { [x: string]: Transaction[] });
+  const datesWithTransaction = Object.keys(byDate);
 
   const multiCurrency = balances.length > 1;
 
+  if (balances.length === 0 || !selectedCurrency) {
+    return (
+      <WarningBox>
+        <p>
+          You have <b>no balance</b>. Withdraw some founds into your wallet
+        </p>
+        <ButtonBoxWarning onClick={() => goToWalletManualWithdraw()}>
+          Withdraw
+        </ButtonBoxWarning>
+      </WarningBox>
+    );
+  }
   return (
     <Fragment>
-      <header>
-        {balances.length > 0 ? (
-          <Fragment>
-            {balances.length === 1 && (
-              <div class="title">
-                Balance: <span>{amountToString(balances[0].available)}</span>
-              </div>
-            )}
-            {balances.length > 1 && (
-              <div class="title">
-                Balance:{" "}
-                <ul style={{ margin: 0 }}>
-                  {balances.map((b, i) => (
-                    <li key={i}>{b.available}</li>
-                  ))}
-                </ul>
-              </div>
-            )}
-          </Fragment>
-        ) : (
-          <div />
-        )}
-        <div>
-          <ButtonPrimary onClick={onAddNewAction}>
-            <b>+</b>
+      <section>
+        <p
+          style={{
+            display: "flex",
+            justifyContent: "space-between",
+            alignItems: "center",
+          }}
+        >
+          {currencies.length === 1 ? (
+            <div style={{ fontSize: "large" }}>{selectedCurrency}</div>
+          ) : (
+            <NiceSelect>
+              <select
+                value={currencyIndex}
+                onChange={(e) => {
+                  setCurrencyIndex(Number(e.currentTarget.value));
+                }}
+              >
+                {currencies.map((currency, index) => {
+                  return (
+                    <option value={index} key={currency}>
+                      {currency}
+                    </option>
+                  );
+                })}
+              </select>
+            </NiceSelect>
+          )}
+          {currencyAmount && (
+            <h2 style={{ margin: 0 }}>
+              {Amounts.stringifyValue(currencyAmount)}
+            </h2>
+          )}
+        </p>
+        <div style={{ marginLeft: "auto", width: "fit-content" }}>
+          <ButtonPrimary
+            onClick={() => goToWalletManualWithdraw(selectedCurrency)}
+          >
+            Withdraw
           </ButtonPrimary>
+          {currencyAmount && Amounts.isNonZero(currencyAmount) && (
+            <ButtonBoxPrimary
+              onClick={() => goToWalletDeposit(selectedCurrency)}
+            >
+              Deposit
+            </ButtonBoxPrimary>
+          )}
         </div>
-      </header>
-      <section>
-        {Object.keys(byDate).map((d, i) => {
-          return (
-            <Fragment key={i}>
-              <DateSeparator>
-                <Time
-                  timestamp={{ t_ms: Number.parseInt(d, 10) }}
-                  format="dd MMMM yyyy"
-                />
-              </DateSeparator>
-              {byDate[d].map((tx, i) => (
-                <TransactionItem
-                  key={i}
-                  tx={tx}
-                  multiCurrency={multiCurrency}
-                />
-              ))}
-            </Fragment>
-          );
-        })}
       </section>
+      {datesWithTransaction.length === 0 ? (
+        <section>There is no history for this currency</section>
+      ) : (
+        <section>
+          {datesWithTransaction.map((d, i) => {
+            return (
+              <Fragment key={i}>
+                <DateSeparator>
+                  <Time
+                    timestamp={{ t_ms: Number.parseInt(d, 10) }}
+                    format="dd MMMM yyyy"
+                  />
+                </DateSeparator>
+                {byDate[d].map((tx, i) => (
+                  <TransactionItem
+                    key={i}
+                    tx={tx}
+                    multiCurrency={multiCurrency}
+                  />
+                ))}
+              </Fragment>
+            );
+          })}
+        </section>
+      )}
     </Fragment>
   );
 }
diff --git 
a/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx
similarity index 76%
rename from 
packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx
rename to 
packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx
index 6ee56ef7..e729c298 100644
--- a/packages/taler-wallet-webextension/src/popup/AddNewActionView.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/LastActivityPage.stories.tsx
@@ -20,14 +20,14 @@
  */
 
 import { createExample } from "../test-utils";
-import { AddNewActionView as TestedComponent } from "./AddNewActionView";
+import { queryToSlashKeys } from "../utils/index";
+import { LastActivityPage as TestedComponent } from "./LastActivityPage";
 
 export default {
-  title: "popup/add new action",
+  title: "wallet/last activity",
   component: TestedComponent,
-  argTypes: {
-    setDeviceName: () => Promise.resolve(),
-  },
 };
 
-export const Initial = createExample(TestedComponent, {});
+export const InitialState = createExample(TestedComponent, {
+  onVerify: queryToSlashKeys,
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx
new file mode 100644
index 00000000..8ec4c875
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/LastActivityPage.tsx
@@ -0,0 +1,35 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { ButtonPrimary } from "../components/styled";
+import { AddNewActionView } from "./AddNewActionView";
+
+export function LastActivityPage(): VNode {
+  const [addingAction, setAddingAction] = useState(false);
+
+  if (addingAction) {
+    return <AddNewActionView onCancel={() => setAddingAction(false)} />;
+  }
+
+  return (
+    <section>
+      <div />
+      <ButtonPrimary onClick={() => setAddingAction(true)}>+</ButtonPrimary>
+    </section>
+  );
+}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
index b3e8a2c2..c7958eb8 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
@@ -14,7 +14,7 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 
-import { VNode, h } from "preact";
+import { VNode, h, Fragment } from "preact";
 import { useState } from "preact/hooks";
 import { CreateManualWithdraw } from "./CreateManualWithdraw";
 import * as wxApi from "../wxApi";
@@ -29,8 +29,10 @@ import { route } from "preact-router";
 import { Pages } from "../NavigationBar";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
 import { ExchangeAddPage } from "./ExchangeAddPage";
+import { Loading } from "../components/Loading";
+import { ErrorBox } from "../components/styled";
 
-export function ManualWithdrawPage(): VNode {
+export function ManualWithdrawPage({ currency }: { currency?: string }): VNode 
{
   const [success, setSuccess] = useState<
     | {
         response: AcceptManualWithdrawalResult;
@@ -86,10 +88,15 @@ export function ManualWithdrawPage(): VNode {
   }
 
   if (!state) {
-    return <div>loading...</div>;
+    return <Loading />;
   }
   if (state.hasError) {
-    return <div>There was an error getting the known exchanges</div>;
+    return (
+      <Fragment>
+        <ErrorBox>{state.message}</ErrorBox>
+        <p>There was an error getting the known exchanges</p>
+      </Fragment>
+    );
   }
   const exchangeList = state.response.exchanges.reduce(
     (p, c) => ({
@@ -105,6 +112,7 @@ export function ManualWithdrawPage(): VNode {
       error={error}
       exchangeList={exchangeList}
       onCreate={doCreate}
+      initialCurrency={currency}
     />
   );
 }
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 8172e02a..21bfc943 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -73,7 +73,7 @@ export function TransactionPage({ tid }: { tid: string }): 
VNode {
   }
 
   if (state.hasError) {
-    route(Pages.history);
+    route(Pages.balance);
     return (
       <div>
         <i18n.Translate>
@@ -84,7 +84,16 @@ export function TransactionPage({ tid }: { tid: string }): 
VNode {
   }
 
   function goToHistory(): void {
-    route(Pages.history);
+    const currency =
+      state !== undefined && !state.hasError
+        ? Amounts.parseOrThrow(state.response.amountRaw).currency
+        : undefined;
+
+    if (currency) {
+      route(Pages.balance_history.replace(":currency", currency));
+    } else {
+      route(Pages.balance);
+    }
   }
 
   return (
diff --git a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
index 644ab1c5..55f350d4 100644
--- a/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/index.stories.tsx
@@ -33,5 +33,22 @@ import * as a11 from "./ReserveCreated.stories";
 import * as a12 from "./Settings.stories";
 import * as a13 from "./Transaction.stories";
 import * as a14 from "./Welcome.stories";
+import * as a15 from "./AddNewActionView.stories";
 
-export default [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14];
+export default [
+  a1,
+  a2,
+  a3,
+  a4,
+  a5,
+  a6,
+  a7,
+  a8,
+  a9,
+  a10,
+  a11,
+  a12,
+  a13,
+  a14,
+  a15,
+];
diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx 
b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
index 93889287..b54d49de 100644
--- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
@@ -22,31 +22,32 @@
 
 import { setupI18n } from "@gnu-taler/taler-util";
 import { createHashHistory } from "history";
-import { Fragment, h, render, VNode } from "preact";
+import { h, render, VNode } from "preact";
 import Router, { route, Route } from "preact-router";
-import { useEffect } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
 import { LogoHeader } from "./components/LogoHeader";
+import { SuccessBox, WalletBox } from "./components/styled";
 import { DevContextProvider } from "./context/devContext";
+import { IoCProviderForRuntime } from "./context/iocContext";
 import { PayPage } from "./cta/Pay";
 import { RefundPage } from "./cta/Refund";
 import { TipPage } from "./cta/Tip";
 import { WithdrawPage } from "./cta/Withdraw";
 import { strings } from "./i18n/strings";
 import { Pages, WalletNavBar } from "./NavigationBar";
+import { DeveloperPage } from "./popup/DeveloperPage";
+import { BackupPage } from "./wallet/BackupPage";
 import { BalancePage } from "./wallet/BalancePage";
+import { DepositPage } from "./wallet/DepositPage";
+import { ExchangeAddPage } from "./wallet/ExchangeAddPage";
 import { HistoryPage } from "./wallet/History";
+import { LastActivityPage } from "./wallet/LastActivityPage";
+import { ManualWithdrawPage } from "./wallet/ManualWithdrawPage";
+import { ProviderAddPage } from "./wallet/ProviderAddPage";
+import { ProviderDetailPage } from "./wallet/ProviderDetailPage";
 import { SettingsPage } from "./wallet/Settings";
 import { TransactionPage } from "./wallet/Transaction";
 import { WelcomePage } from "./wallet/Welcome";
-import { BackupPage } from "./wallet/BackupPage";
-import { DeveloperPage } from "./popup/DeveloperPage";
-import { ManualWithdrawPage } from "./wallet/ManualWithdrawPage";
-import { WalletBox } from "./components/styled";
-import { ProviderDetailPage } from "./wallet/ProviderDetailPage";
-import { ProviderAddPage } from "./wallet/ProviderAddPage";
-import { ExchangeAddPage } from "./wallet/ExchangeAddPage";
-import { DepositPage } from "./wallet/DepositPage";
-import { IoCProviderForRuntime } from "./context/iocContext";
 
 function main(): void {
   try {
@@ -71,140 +72,156 @@ if (document.readyState === "loading") {
   main();
 }
 
-function withLogoAndNavBar(Component: any) {
-  return function withLogoAndNavBarComponent(props: any): VNode {
-    return (
-      <Fragment>
-        <LogoHeader />
-        <WalletNavBar />
-        <WalletBox>
-          <Component {...props} />
-        </WalletBox>
-      </Fragment>
-    );
-  };
-}
-
 function Application(): VNode {
+  const [globalNotification, setGlobalNotification] = useState<
+    string | undefined
+  >(undefined);
   return (
     <div>
       <DevContextProvider>
-        <IoCProviderForRuntime>
-          <Router history={createHashHistory()}>
-            <Route
-              path={Pages.welcome}
-              component={withLogoAndNavBar(WelcomePage)}
-            />
-
-            <Route
-              path={Pages.history}
-              component={withLogoAndNavBar(HistoryPage)}
-            />
-            <Route
-              path={Pages.transaction}
-              component={withLogoAndNavBar(TransactionPage)}
-            />
-            <Route
-              path={Pages.balance}
-              component={withLogoAndNavBar(BalancePage)}
-              goToWalletManualWithdraw={() => route(Pages.manual_withdraw)}
-              goToWalletDeposit={(currency: string) =>
-                route(Pages.deposit.replace(":currency", currency))
-              }
-            />
-            <Route
-              path={Pages.settings}
-              component={withLogoAndNavBar(SettingsPage)}
-            />
-            <Route
-              path={Pages.backup}
-              component={withLogoAndNavBar(BackupPage)}
-              onAddProvider={() => {
-                route(Pages.provider_add);
-              }}
-            />
-            <Route
-              path={Pages.provider_detail}
-              component={withLogoAndNavBar(ProviderDetailPage)}
-              onBack={() => {
-                route(Pages.backup);
-              }}
-            />
-            <Route
-              path={Pages.provider_add}
-              component={withLogoAndNavBar(ProviderAddPage)}
-              onBack={() => {
-                route(Pages.backup);
-              }}
-            />
-
-            <Route
-              path={Pages.exchange_add}
-              component={withLogoAndNavBar(ExchangeAddPage)}
-              onBack={() => {
-                route(Pages.balance);
-              }}
-            />
-
-            <Route
-              path={Pages.manual_withdraw}
-              component={withLogoAndNavBar(ManualWithdrawPage)}
-            />
-
-            <Route
-              path={Pages.deposit}
-              component={withLogoAndNavBar(DepositPage)}
-            />
-            <Route
-              path={Pages.reset_required}
-              component={() => <div>no yet implemented</div>}
-            />
-            <Route
-              path={Pages.payback}
-              component={() => <div>no yet implemented</div>}
-            />
-            <Route
-              path={Pages.return_coins}
-              component={() => <div>no yet implemented</div>}
-            />
-
-            <Route
-              path={Pages.dev}
-              component={withLogoAndNavBar(DeveloperPage)}
-            />
-
-            {/** call to action */}
-            <Route
-              path={Pages.pay}
-              component={PayPage}
-              goToWalletManualWithdraw={() =>
-                goToWalletPage(Pages.manual_withdraw)
-              }
-            />
-            <Route path={Pages.refund} component={RefundPage} />
-            <Route path={Pages.tips} component={TipPage} />
-            <Route path={Pages.withdraw} component={WithdrawPage} />
-
-            <Route default component={Redirect} to={Pages.history} />
-          </Router>
-        </IoCProviderForRuntime>
+        {({ devMode }: { devMode: boolean }) => (
+          <IoCProviderForRuntime>
+            <LogoHeader />
+            <WalletNavBar devMode={devMode} />
+            <WalletBox>
+              {globalNotification && (
+                <SuccessBox onClick={() => setGlobalNotification(undefined)}>
+                  <div>{globalNotification}</div>
+                </SuccessBox>
+              )}
+              <Router history={createHashHistory()}>
+                <Route path={Pages.welcome} component={WelcomePage} />
+
+                <Route
+                  path={Pages.balance}
+                  component={BalancePage}
+                  goToWalletManualWithdraw={() =>
+                    route(Pages.manual_withdraw.replace(":currency?", ""))
+                  }
+                  goToWalletDeposit={(currency: string) =>
+                    route(Pages.deposit.replace(":currency", currency))
+                  }
+                  goToWalletHistory={(currency: string) =>
+                    route(Pages.balance_history.replace(":currency", currency))
+                  }
+                />
+                <Route
+                  path={Pages.balance_history}
+                  component={HistoryPage}
+                  goToWalletDeposit={(currency: string) =>
+                    route(Pages.deposit.replace(":currency", currency))
+                  }
+                  goToWalletManualWithdraw={(currency?: string) =>
+                    route(
+                      Pages.manual_withdraw.replace(
+                        ":currency?",
+                        currency || "",
+                      ),
+                    )
+                  }
+                />
+                <Route
+                  path={Pages.last_activity}
+                  component={LastActivityPage}
+                />
+                <Route path={Pages.transaction} component={TransactionPage} />
+                <Route path={Pages.settings} component={SettingsPage} />
+                <Route
+                  path={Pages.backup}
+                  component={BackupPage}
+                  onAddProvider={() => {
+                    route(Pages.provider_add);
+                  }}
+                />
+                <Route
+                  path={Pages.provider_detail}
+                  component={ProviderDetailPage}
+                  onBack={() => {
+                    route(Pages.backup);
+                  }}
+                />
+                <Route
+                  path={Pages.provider_add}
+                  component={ProviderAddPage}
+                  onBack={() => {
+                    route(Pages.backup);
+                  }}
+                />
+
+                <Route
+                  path={Pages.exchange_add}
+                  component={ExchangeAddPage}
+                  onBack={() => {
+                    route(Pages.balance);
+                  }}
+                />
+
+                <Route
+                  path={Pages.manual_withdraw}
+                  component={ManualWithdrawPage}
+                />
+
+                <Route
+                  path={Pages.deposit}
+                  component={DepositPage}
+                  onSuccess={(currency: string) => {
+                    route(Pages.balance_history.replace(":currency", 
currency));
+                    setGlobalNotification(
+                      "All done, your transaction is in progress",
+                    );
+                  }}
+                />
+                <Route
+                  path={Pages.reset_required}
+                  component={() => <div>no yet implemented</div>}
+                />
+                <Route
+                  path={Pages.payback}
+                  component={() => <div>no yet implemented</div>}
+                />
+                <Route
+                  path={Pages.return_coins}
+                  component={() => <div>no yet implemented</div>}
+                />
+
+                <Route path={Pages.dev} component={DeveloperPage} />
+
+                {/** call to action */}
+                <Route
+                  path={Pages.pay}
+                  component={PayPage}
+                  goToWalletManualWithdraw={(currency?: string) =>
+                    route(
+                      Pages.manual_withdraw.replace(
+                        ":currency?",
+                        currency || "",
+                      ),
+                    )
+                  }
+                  goBack={() => route(Pages.balance)}
+                />
+                <Route
+                  path={Pages.pay}
+                  component={PayPage}
+                  goBack={() => route(Pages.balance)}
+                />
+                <Route path={Pages.refund} component={RefundPage} />
+                <Route path={Pages.tips} component={TipPage} />
+                <Route path={Pages.withdraw} component={WithdrawPage} />
+
+                <Route default component={Redirect} to={Pages.balance} />
+              </Router>
+            </WalletBox>
+          </IoCProviderForRuntime>
+        )}
       </DevContextProvider>
     </div>
   );
 }
 
-function goToWalletPage(page: Pages | string): null {
-  // eslint-disable-next-line no-undef
-  chrome.tabs.create({
-    active: true,
-    // eslint-disable-next-line no-undef
-    url: chrome.extension.getURL(`/static/wallet.html#${page}`),
-  });
-  return null;
-}
-
 function Redirect({ to }: { to: string }): null {
   useEffect(() => {
+    console.log("go some wrong route");
     route(to, true);
   });
   return null;
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts 
b/packages/taler-wallet-webextension/src/wxApi.ts
index 5fe30bc4..dc96efc7 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -24,7 +24,7 @@
 import {
   AcceptExchangeTosRequest,
   AcceptManualWithdrawalResult, AcceptTipRequest, AcceptWithdrawalResponse,
-  AddExchangeRequest, AmountJson, AmountString, ApplyRefundResponse, 
BalancesResponse, ConfirmPayResult,
+  AddExchangeRequest, AmountString, ApplyRefundResponse, BalancesResponse, 
ConfirmPayResult,
   CoreApiResponse, CreateDepositGroupRequest, CreateDepositGroupResponse, 
DeleteTransactionRequest, ExchangesListRespose,
   GetExchangeTosResult, GetExchangeWithdrawalInfo,
   GetFeeForDepositRequest,

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