gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: reserveCreated new design


From: gnunet
Subject: [taler-wallet-core] branch master updated: reserveCreated new design
Date: Tue, 16 Nov 2021 18:01:55 +0100

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

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

The following commit(s) were added to refs/heads/master by this push:
     new a994009d reserveCreated new design
a994009d is described below

commit a994009d2f094c4d9c12da68dac3abb28bdef4b3
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Nov 16 13:59:53 2021 -0300

    reserveCreated new design
---
 packages/taler-util/src/payto.ts                   |  45 ++++-
 .../src/NavigationBar.tsx                          |   6 +-
 .../src/components/Checkbox.tsx                    |   5 +-
 .../src/components/CheckboxOutlined.tsx            |   5 +-
 .../src/components/DebugCheckbox.tsx               |   4 +-
 .../src/components/Diagnostics.tsx                 |  75 ++++-----
 .../src/components/EditableText.tsx                |  47 +++---
 .../src/components/ExchangeToS.tsx                 |  14 +-
 .../src/components/SelectList.tsx                  |  10 +-
 .../src/{cta/payback.tsx => components/Time.tsx}   |  39 +++--
 .../src/components/TransactionItem.tsx             |  17 +-
 .../src/components/styled/index.tsx                |  87 +++++-----
 packages/taler-wallet-webextension/src/cta/Pay.tsx |  16 +-
 .../taler-wallet-webextension/src/cta/Refund.tsx   |  20 +--
 packages/taler-wallet-webextension/src/cta/Tip.tsx |  13 +-
 .../src/cta/Withdraw.stories.tsx                   | 115 ++++---------
 .../taler-wallet-webextension/src/cta/Withdraw.tsx |  13 +-
 .../taler-wallet-webextension/src/cta/payback.tsx  |   5 +-
 .../src/cta/reset-required.tsx                     |   6 +-
 .../src/cta/return-coins.tsx                       |   5 +-
 .../src/popup/BackupPage.tsx                       |  11 +-
 .../src/popup/BalancePage.tsx                      |  25 ++-
 .../taler-wallet-webextension/src/popup/Debug.tsx  |   7 +-
 .../src/popup/History.tsx                          |   4 +-
 .../taler-wallet-webextension/src/renderHtml.tsx   |  19 ++-
 .../src/wallet/BackupPage.tsx                      |  12 +-
 .../src/wallet/BalancePage.tsx                     |  18 +-
 .../src/wallet/CreateManualWithdraw.stories.tsx    |  21 +--
 .../src/wallet/CreateManualWithdraw.tsx            |  89 +++++++---
 .../src/wallet/History.tsx                         |  37 +++--
 .../src/wallet/ManualWithdrawPage.tsx              |  55 +++----
 .../src/wallet/ProviderDetailPage.tsx              | 119 +++++++-------
 .../src/wallet/ReserveCreated.stories.tsx          |  26 ++-
 .../src/wallet/ReserveCreated.tsx                  | 183 +++++++++++++++------
 .../src/wallet/Transaction.tsx                     |  92 +++++------
 .../src/wallet/Welcome.tsx                         |   7 +-
 .../taler-wallet-webextension/static/wallet.html   |  20 ++-
 37 files changed, 719 insertions(+), 573 deletions(-)

diff --git a/packages/taler-util/src/payto.ts b/packages/taler-util/src/payto.ts
index 504db533..fc338055 100644
--- a/packages/taler-util/src/payto.ts
+++ b/packages/taler-util/src/payto.ts
@@ -16,12 +16,31 @@
 
 import { URLSearchParams } from "./url.js";
 
-interface PaytoUri {
+export type PaytoUri = PaytoUriUnknown | PaytoUriIBAN | PaytoUriTalerBank;
+
+interface PaytoUriGeneric {
   targetType: string;
   targetPath: string;
   params: { [name: string]: string };
 }
 
+interface PaytoUriUnknown extends PaytoUriGeneric {
+  isKnown: false;
+}
+
+interface PaytoUriIBAN extends PaytoUriGeneric {
+  isKnown: true;
+  targetType: 'iban',
+  iban: string;
+}
+
+interface PaytoUriTalerBank extends PaytoUriGeneric {
+  isKnown: true;
+  targetType: 'x-taler-bank',
+  host: string;
+  account: string;
+}
+
 const paytoPfx = "payto://";
 
 /**
@@ -63,9 +82,33 @@ export function parsePaytoUri(s: string): PaytoUri | 
undefined {
     params[v] = k;
   });
 
+  if (targetType === 'x-taler-bank') {
+    const parts = targetPath.split('/')
+    const host = parts[0]
+    const account = parts[1]
+    return {
+      targetPath,
+      targetType,
+      params,
+      isKnown: true,
+      host, account,
+    };
+
+  }
+  if (targetType === 'iban') {
+    return {
+      isKnown: true,
+      targetPath,
+      targetType,
+      params,
+      iban: targetPath
+    };
+
+  }
   return {
     targetPath,
     targetType,
     params,
+    isKnown: false
   };
 }
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx 
b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index f206fa2d..56704fb5 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -25,10 +25,10 @@
  * Imports.
  */
 import { i18n } from "@gnu-taler/taler-util";
-import { ComponentChildren, JSX, h } from "preact";
+import { ComponentChildren, h, VNode } from "preact";
 import Match from "preact-router/match";
-import { useDevContext } from "./context/devContext";
 import { PopupNavigation } from "./components/styled";
+import { useDevContext } from "./context/devContext";
 
 export enum Pages {
   welcome = "/welcome",
@@ -59,7 +59,7 @@ interface TabProps {
   children?: ComponentChildren;
 }
 
-function Tab(props: TabProps): JSX.Element {
+function Tab(props: TabProps): VNode {
   let cssClass = "";
   if (props.current?.startsWith(props.target)) {
     cssClass = "active";
diff --git a/packages/taler-wallet-webextension/src/components/Checkbox.tsx 
b/packages/taler-wallet-webextension/src/components/Checkbox.tsx
index 276ac9ff..59e84f4b 100644
--- a/packages/taler-wallet-webextension/src/components/Checkbox.tsx
+++ b/packages/taler-wallet-webextension/src/components/Checkbox.tsx
@@ -14,8 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { JSX } from "preact/jsx-runtime";
-import { h } from "preact";
+import { h, VNode } from "preact";
 
 interface Props {
   enabled: boolean;
@@ -30,7 +29,7 @@ export function Checkbox({
   onToggle,
   label,
   description,
-}: Props): JSX.Element {
+}: Props): VNode {
   return (
     <div>
       <input
diff --git 
a/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx 
b/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx
index 2fc8316f..3b9519f3 100644
--- a/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx
+++ b/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx
@@ -14,9 +14,8 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { JSX } from "preact/jsx-runtime";
 import { Outlined, StyledCheckboxLabel } from "./styled/index";
-import { h } from "preact";
+import { h, VNode } from "preact";
 
 interface Props {
   enabled: boolean;
@@ -47,7 +46,7 @@ export function CheckboxOutlined({
   enabled,
   onToggle,
   label,
-}: Props): JSX.Element {
+}: Props): VNode {
   return (
     <Outlined>
       <StyledCheckboxLabel onClick={onToggle}>
diff --git 
a/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx 
b/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx
index 952df15a..b5707580 100644
--- a/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx
+++ b/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx
@@ -14,7 +14,7 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { JSX, h } from "preact";
+import { h, VNode } from "preact";
 
 export function DebugCheckbox({
   enabled,
@@ -22,7 +22,7 @@ export function DebugCheckbox({
 }: {
   enabled: boolean;
   onToggle: () => void;
-}): JSX.Element {
+}): VNode {
   return (
     <div>
       <input
diff --git a/packages/taler-wallet-webextension/src/components/Diagnostics.tsx 
b/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
index 0f8afd52..d368a10b 100644
--- a/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
+++ b/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
@@ -15,8 +15,7 @@
  */
 
 import { WalletDiagnostics } from "@gnu-taler/taler-util";
-import { h } from "preact";
-import { JSX } from "preact/jsx-runtime";
+import { Fragment, h, VNode } from "preact";
 import { PageLink } from "../renderHtml";
 
 interface Props {
@@ -24,51 +23,47 @@ interface Props {
   diagnostics: WalletDiagnostics | undefined;
 }
 
-export function Diagnostics({
-  timedOut,
-  diagnostics,
-}: Props): JSX.Element | null {
+export function Diagnostics({ timedOut, diagnostics }: Props): VNode {
   if (timedOut) {
     return <p>Diagnostics timed out. Could not talk to the wallet backend.</p>;
   }
 
   if (diagnostics) {
     if (diagnostics.errors.length === 0) {
-      return null;
-    } else {
-      return (
-        <div
-          style={{
-            borderLeft: "0.5em solid red",
-            paddingLeft: "1em",
-            paddingTop: "0.2em",
-            paddingBottom: "0.2em",
-          }}
-        >
-          <p>Problems detected:</p>
-          <ol>
-            {diagnostics.errors.map((errMsg) => (
-              <li key={errMsg}>{errMsg}</li>
-            ))}
-          </ol>
-          {diagnostics.firefoxIdbProblem ? (
-            <p>
-              Please check in your <code>about:config</code> settings that you
-              have IndexedDB enabled (check the preference name{" "}
-              <code>dom.indexedDB.enabled</code>).
-            </p>
-          ) : null}
-          {diagnostics.dbOutdated ? (
-            <p>
-              Your wallet database is outdated. Currently automatic migration 
is
-              not supported. Please go{" "}
-              <PageLink pageName="/reset-required">here</PageLink> to reset the
-              wallet database.
-            </p>
-          ) : null}
-        </div>
-      );
+      return <Fragment />;
     }
+    return (
+      <div
+        style={{
+          borderLeft: "0.5em solid red",
+          paddingLeft: "1em",
+          paddingTop: "0.2em",
+          paddingBottom: "0.2em",
+        }}
+      >
+        <p>Problems detected:</p>
+        <ol>
+          {diagnostics.errors.map((errMsg) => (
+            <li key={errMsg}>{errMsg}</li>
+          ))}
+        </ol>
+        {diagnostics.firefoxIdbProblem ? (
+          <p>
+            Please check in your <code>about:config</code> settings that you
+            have IndexedDB enabled (check the preference name{" "}
+            <code>dom.indexedDB.enabled</code>).
+          </p>
+        ) : null}
+        {diagnostics.dbOutdated ? (
+          <p>
+            Your wallet database is outdated. Currently automatic migration is
+            not supported. Please go{" "}
+            <PageLink pageName="/reset-required">here</PageLink> to reset the
+            wallet database.
+          </p>
+        ) : null}
+      </div>
+    );
   }
 
   return <p>Running diagnostics ...</p>;
diff --git a/packages/taler-wallet-webextension/src/components/EditableText.tsx 
b/packages/taler-wallet-webextension/src/components/EditableText.tsx
index 8b3e6d37..72bfbe80 100644
--- a/packages/taler-wallet-webextension/src/components/EditableText.tsx
+++ b/packages/taler-wallet-webextension/src/components/EditableText.tsx
@@ -14,9 +14,8 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { h } from "preact";
+import { h, VNode } from "preact";
 import { useRef, useState } from "preact/hooks";
-import { JSX } from "preact/jsx-runtime";
 
 interface Props {
   value: string;
@@ -31,31 +30,35 @@ export function EditableText({
   onChange,
   label,
   description,
-}: Props): JSX.Element {
+}: Props): VNode {
   const [editing, setEditing] = useState(false);
   const ref = useRef<HTMLInputElement>(null);
   let InputText;
   if (!editing) {
-    InputText = () => (
-      <div style={{ display: "flex", justifyContent: "space-between" }}>
-        <p>{value}</p>
-        <button onClick={() => setEditing(true)}>edit</button>
-      </div>
-    );
+    InputText = function InputToEdit(): VNode {
+      return (
+        <div style={{ display: "flex", justifyContent: "space-between" }}>
+          <p>{value}</p>
+          <button onClick={() => setEditing(true)}>edit</button>
+        </div>
+      );
+    };
   } else {
-    InputText = () => (
-      <div style={{ display: "flex", justifyContent: "space-between" }}>
-        <input value={value} ref={ref} type="text" id={`text-${name}`} />
-        <button
-          onClick={() => {
-            if (ref.current)
-              onChange(ref.current.value).then((r) => setEditing(false));
-          }}
-        >
-          confirm
-        </button>
-      </div>
-    );
+    InputText = function InputEditing(): VNode {
+      return (
+        <div style={{ display: "flex", justifyContent: "space-between" }}>
+          <input value={value} ref={ref} type="text" id={`text-${name}`} />
+          <button
+            onClick={() => {
+              if (ref.current)
+                onChange(ref.current.value).then(() => setEditing(false));
+            }}
+          >
+            confirm
+          </button>
+        </div>
+      );
+    };
   }
   return (
     <div>
diff --git a/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx 
b/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx
index 6d2731cd..a71108c5 100644
--- a/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx
+++ b/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx
@@ -13,12 +13,10 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { Fragment, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { JSXInternal } from "preact/src/jsx";
-import { h } from "preact";
 
-export function ExchangeXmlTos({ doc }: { doc: Document }) {
+export function ExchangeXmlTos({ doc }: { doc: Document }): VNode {
   const termsNode = doc.querySelector("[ids=terms-of-service]");
   if (!termsNode) {
     return (
@@ -70,7 +68,7 @@ function renderChild(child: Element): VNode {
     default:
       return (
         <div style={{ color: "red", display: "hidden" }}>
-          unknown tag {child.nodeName} <a></a>
+          unknown tag {child.nodeName}
         </div>
       );
   }
@@ -81,10 +79,10 @@ function renderChild(child: Element): VNode {
  * @returns
  */
 function AnchorWithOpenState(
-  props: JSXInternal.HTMLAttributes<HTMLAnchorElement>,
-) {
+  props: h.JSX.HTMLAttributes<HTMLAnchorElement>,
+): VNode {
   const [open, setOpen] = useState<boolean>(false);
-  function doClick(e: JSXInternal.TargetedMouseEvent<HTMLAnchorElement>) {
+  function doClick(e: h.JSX.TargetedMouseEvent<HTMLAnchorElement>): void {
     setOpen(!open);
     e.preventDefault();
   }
diff --git a/packages/taler-wallet-webextension/src/components/SelectList.tsx 
b/packages/taler-wallet-webextension/src/components/SelectList.tsx
index f89ba19b..78dd2feb 100644
--- a/packages/taler-wallet-webextension/src/components/SelectList.tsx
+++ b/packages/taler-wallet-webextension/src/components/SelectList.tsx
@@ -14,9 +14,8 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { JSX } from "preact/jsx-runtime";
+import { Fragment, h, VNode } from "preact";
 import { NiceSelect } from "./styled/index";
-import { h } from "preact";
 
 interface Props {
   value?: string;
@@ -34,13 +33,12 @@ export function SelectList({
   name,
   value,
   list,
-  canBeNull,
   onChange,
   label,
   description,
-}: Props): JSX.Element {
+}: Props): VNode {
   return (
-    <div>
+    <Fragment>
       <label
         htmlFor={`text-${name}`}
         style={{ marginLeft: "0.5em", fontWeight: "bold" }}
@@ -84,6 +82,6 @@ export function SelectList({
           {description}
         </span>
       )}
-    </div>
+    </Fragment>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/cta/payback.tsx 
b/packages/taler-wallet-webextension/src/components/Time.tsx
similarity index 52%
copy from packages/taler-wallet-webextension/src/cta/payback.tsx
copy to packages/taler-wallet-webextension/src/components/Time.tsx
index 60cb8c51..452b0833 100644
--- a/packages/taler-wallet-webextension/src/cta/payback.tsx
+++ b/packages/taler-wallet-webextension/src/components/Time.tsx
@@ -1,6 +1,6 @@
 /*
  This file is part of TALER
- (C) 2017 Inria
+ (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
@@ -14,19 +14,28 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { JSX } from "preact/jsx-runtime";
-import { h } from "preact";
+import { Timestamp } from "@gnu-taler/taler-util";
+import { formatISO, format } from "date-fns";
+import { h, VNode } from "preact";
 
-/**
- * View and edit auditors.
- *
- * @author Florian Dold
- */
-
-/**
- * Imports.
- */
-
-export function makePaybackPage(): JSX.Element {
-  return <div>not implemented</div>;
+export function Time({
+  timestamp,
+  format: formatString,
+}: {
+  timestamp: Timestamp | undefined;
+  format: string;
+}): VNode {
+  return (
+    <time
+      dateTime={
+        !timestamp || timestamp.t_ms === "never"
+          ? undefined
+          : formatISO(timestamp.t_ms)
+      }
+    >
+      {!timestamp || timestamp.t_ms === "never"
+        ? "never"
+        : format(timestamp.t_ms, formatString)}
+    </time>
+  );
 }
diff --git 
a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx 
b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
index 1917d562..99ca8638 100644
--- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
+++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
@@ -20,8 +20,7 @@ import {
   Transaction,
   TransactionType,
 } from "@gnu-taler/taler-util";
-import { format, formatDistance } from "date-fns";
-import { h } from "preact";
+import { h, VNode } from "preact";
 import imageBank from "../../static/img/ri-bank-line.svg";
 import imageHandHeart from "../../static/img/ri-hand-heart-line.svg";
 import imageRefresh from "../../static/img/ri-refresh-line.svg";
@@ -36,11 +35,12 @@ import {
   LargeText,
   LightText,
 } from "./styled/index";
+import { Time } from "./Time";
 
 export function TransactionItem(props: {
   tx: Transaction;
   multiCurrency: boolean;
-}): JSX.Element {
+}): VNode {
   const tx = props.tx;
   switch (tx.type) {
     case TransactionType.Withdrawal:
@@ -125,10 +125,7 @@ export function TransactionItem(props: {
   }
 }
 
-function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
-  const date = new Date(props.timestamp.t_ms);
-  const dateStr = format(date, "dd MMM, hh:mm");
-
+function TransactionLayout(props: TransactionLayoutProps): VNode {
   return (
     <HistoryRow href={Pages.transaction.replace(":tid", props.id)}>
       <img src={props.iconPath} />
@@ -146,7 +143,9 @@ function TransactionLayout(props: TransactionLayoutProps): 
JSX.Element {
             Waiting for confirmation
           </LightText>
         )}
-        <SmallLightText style={{ marginTop: 5 }}>{dateStr}</SmallLightText>
+        <SmallLightText style={{ marginTop: 5 }}>
+          <Time timestamp={props.timestamp} format="dd MMM, hh:mm" />
+        </SmallLightText>
       </Column>
       <TransactionAmount
         pending={props.pending}
@@ -177,7 +176,7 @@ interface TransactionAmountProps {
   multiCurrency: boolean;
 }
 
-function TransactionAmount(props: TransactionAmountProps): JSX.Element {
+function TransactionAmount(props: TransactionAmountProps): VNode {
   const [currency, amount] = props.amount.split(":");
   let sign: string;
   switch (props.debitCreditIndicator) {
diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx 
b/packages/taler-wallet-webextension/src/components/styled/index.tsx
index 8b36dbd3..2db7c61f 100644
--- a/packages/taler-wallet-webextension/src/components/styled/index.tsx
+++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx
@@ -85,7 +85,7 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>`
     overflow: auto;
 
     table td {
-      padding: 5px 10px;
+      padding: 5px 5px;
     }
     table tr {
       border-bottom: 1px solid black;
@@ -328,7 +328,8 @@ const ButtonVariant = styled(Button)`
   text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
 `;
 
-export const ButtonPrimary = styled(ButtonVariant)`
+export const ButtonPrimary = styled(ButtonVariant)<{ small?: boolean }>`
+  font-size: ${({ small }) => (small ? "small" : "inherit")};
   background-color: rgb(66, 184, 221);
 `;
 export const ButtonBoxPrimary = styled(ButtonBox)`
@@ -501,29 +502,40 @@ export const Input = styled.div<{ invalid?: boolean }>`
 `;
 
 export const InputWithLabel = styled.div<{ invalid?: boolean }>`
+  /* display: flex; */
+
   & label {
     display: block;
+    font-weight: bold;
+    margin-left: 0.5em;
     padding: 5px;
     color: ${({ invalid }) => (!invalid ? "inherit" : "red")};
   }
-  & > div {
-    position: relative;
-    display: flex;
-    top: 0px;
-    bottom: 0px;
-
-    & > div {
-      position: absolute;
-      background-color: lightgray;
-      padding: 5px;
-      margin: 2px;
-    }
 
-    & > input {
-      flex: 1;
-      padding: 5px;
-      border-color: ${({ invalid }) => (!invalid ? "inherit" : "red")};
-    }
+  & div {
+    line-height: 24px;
+    display: flex;
+  }
+  & div > span {
+    background-color: lightgray;
+    box-sizing: border-box;
+    border-bottom-left-radius: 0.25em;
+    border-top-left-radius: 0.25em;
+    height: 2em;
+    display: inline-block;
+    padding-left: 0.5em;
+    padding-right: 0.5em;
+    align-items: center;
+    display: flex;
+  }
+  & input {
+    border-width: 1px;
+    box-sizing: border-box;
+    height: 2em;
+    /* border-color: lightgray; */
+    border-bottom-right-radius: 0.25em;
+    border-top-right-radius: 0.25em;
+    border-color: ${({ invalid }) => (!invalid ? "lightgray" : "red")};
   }
 `;
 
@@ -535,6 +547,7 @@ export const ErrorBox = styled.div`
   flex-direction: column;
   /* margin: 0.5em; */
   padding: 1em;
+  margin: 1em;
   /* width: 100%; */
   color: #721c24;
   background: #f8d7da;
@@ -592,6 +605,8 @@ export const PopupNavigation = styled.div<{ devMode?: 
boolean }>`
   }
 `;
 
+const image = `url("data:image/svg+xml,%3csvg 
xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath 
stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' 
stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`;
+
 export const NiceSelect = styled.div`
   & > select {
     -webkit-appearance: none;
@@ -600,11 +615,18 @@ export const NiceSelect = styled.div`
     appearance: none;
     outline: 0;
     box-shadow: none;
-    background-image: none;
+
+    background-image: ${image};
+    background-position: right 0.5rem center;
+    background-repeat: no-repeat;
+    background-size: 1.5em 1.5em;
+    padding-right: 2.5rem;
+
     background-color: white;
 
-    flex: 1;
-    padding: 0.5em 1em;
+    border-radius: 0.25rem;
+    font-size: 1em;
+    padding: 0.5em 3em 0.5em 1em;
     cursor: pointer;
   }
 
@@ -613,27 +635,6 @@ export const NiceSelect = styled.div`
   /* width: 10em; */
   overflow: hidden;
   border-radius: 0.25em;
-
-  &::after {
-    content: "\u25BC";
-    position: absolute;
-    top: 0;
-    right: 0;
-    padding: 0.5em 1em;
-    cursor: pointer;
-    pointer-events: none;
-    -webkit-transition: 0.25s all ease;
-    -o-transition: 0.25s all ease;
-    transition: 0.25s all ease;
-  }
-
-  &:hover::after {
-    /* color: #f39c12; */
-  }
-
-  &::-ms-expand {
-    display: none;
-  }
 `;
 
 export const Outlined = styled.div`
diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx 
b/packages/taler-wallet-webextension/src/cta/Pay.tsx
index 1023013d..d5861c47 100644
--- a/packages/taler-wallet-webextension/src/cta/Pay.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Pay.tsx
@@ -36,7 +36,7 @@ import {
   PreparePayResult,
   PreparePayResultType,
 } from "@gnu-taler/taler-util";
-import { h, Fragment, JSX, VNode } from "preact";
+import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { LogoHeader } from "../components/LogoHeader";
 import { Part } from "../components/Part";
@@ -100,7 +100,7 @@ const doPayment = async (
   return res;
 };
 
-export function PayPage({ talerPayUri }: Props): JSX.Element {
+export function PayPage({ talerPayUri }: Props): VNode {
   const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(
     undefined,
   );
@@ -159,7 +159,7 @@ export function PayPage({ talerPayUri }: Props): 
JSX.Element {
     return <span>Loading payment information ...</span>;
   }
 
-  const onClick = async () => {
+  const onClick = async (): Promise<void> => {
     try {
       const res = await doPayment(payStatus);
       setPayResult(res);
@@ -198,7 +198,7 @@ export function PaymentRequestView({
   onClick,
   payErrMsg,
   balance,
-}: PaymentRequestViewProps) {
+}: PaymentRequestViewProps): VNode {
   let totalFees: AmountJson = Amounts.getZero(payStatus.amountRaw);
   const contractTerms: ContractTerms = payStatus.contractTerms;
 
@@ -225,7 +225,7 @@ export function PaymentRequestView({
     merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
   }
 
-  function Alternative() {
+  function Alternative(): VNode {
     const [showQR, setShowQR] = useState<boolean>(false);
     const privateUri =
       payStatus.status !== PreparePayResultType.AlreadyConfirmed
@@ -246,7 +246,7 @@ export function PaymentRequestView({
     );
   }
 
-  function ButtonsSection() {
+  function ButtonsSection(): VNode {
     if (payResult) {
       if (payResult.type === ConfirmPayResultType.Pending) {
         return (
@@ -257,7 +257,7 @@ export function PaymentRequestView({
           </section>
         );
       }
-      return null;
+      return <Fragment />;
     }
     if (payErrMsg) {
       return (
@@ -392,7 +392,7 @@ export function PaymentRequestView({
   );
 }
 
-function amountToString(text: AmountLike) {
+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/Refund.tsx 
b/packages/taler-wallet-webextension/src/cta/Refund.tsx
index aa11dca6..cecd1ac0 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Refund.tsx
@@ -20,12 +20,11 @@
  * @author Florian Dold
  */
 
-import * as wxApi from "../wxApi";
-import { AmountView } from "../renderHtml";
-import { ApplyRefundResponse, Amounts } from "@gnu-taler/taler-util";
+import { Amounts, ApplyRefundResponse } from "@gnu-taler/taler-util";
+import { h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
-import { JSX } from "preact/jsx-runtime";
-import { h } from "preact";
+import { AmountView } from "../renderHtml";
+import * as wxApi from "../wxApi";
 
 interface Props {
   talerRefundUri?: string;
@@ -33,7 +32,7 @@ interface Props {
 export interface ViewProps {
   applyResult: ApplyRefundResponse;
 }
-export function View({ applyResult }: ViewProps) {
+export function View({ applyResult }: ViewProps): VNode {
   return (
     <section class="main">
       <h1>GNU Taler Wallet</h1>
@@ -58,7 +57,7 @@ export function View({ applyResult }: ViewProps) {
     </section>
   );
 }
-export function RefundPage({ talerRefundUri }: Props): JSX.Element {
+export function RefundPage({ talerRefundUri }: Props): VNode {
   const [applyResult, setApplyResult] = useState<
     ApplyRefundResponse | undefined
   >(undefined);
@@ -71,9 +70,10 @@ export function RefundPage({ talerRefundUri }: Props): 
JSX.Element {
         const result = await wxApi.applyRefund(talerRefundUri);
         setApplyResult(result);
       } catch (e) {
-        console.error(e);
-        setErrMsg(e.message);
-        console.log("err message", e.message);
+        if (e instanceof Error) {
+          setErrMsg(e.message);
+          console.log("err message", e.message);
+        }
       }
     };
     doFetch();
diff --git a/packages/taler-wallet-webextension/src/cta/Tip.tsx 
b/packages/taler-wallet-webextension/src/cta/Tip.tsx
index 0a1c1238..5a9ab720 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Tip.tsx
@@ -20,12 +20,11 @@
  * @author Florian Dold <dold@taler.net>
  */
 
-import { useEffect, useState } from "preact/hooks";
 import { PrepareTipResult } from "@gnu-taler/taler-util";
+import { h, VNode } from "preact";
+import { useEffect, useState } from "preact/hooks";
 import { AmountView } from "../renderHtml";
 import * as wxApi from "../wxApi";
-import { JSX } from "preact/jsx-runtime";
-import { h } from "preact";
 
 interface Props {
   talerTipUri?: string;
@@ -35,7 +34,11 @@ export interface ViewProps {
   onAccept: () => void;
   onIgnore: () => void;
 }
-export function View({ prepareTipResult, onAccept, onIgnore }: ViewProps) {
+export function View({
+  prepareTipResult,
+  onAccept,
+  onIgnore,
+}: ViewProps): VNode {
   return (
     <section class="main">
       <h1>GNU Taler Wallet</h1>
@@ -64,7 +67,7 @@ export function View({ prepareTipResult, onAccept, onIgnore 
}: ViewProps) {
   );
 }
 
-export function TipPage({ talerTipUri }: Props): JSX.Element {
+export function TipPage({ talerTipUri }: Props): VNode {
   const [updateCounter, setUpdateCounter] = useState<number>(0);
   const [prepareTipResult, setPrepareTipResult] = useState<
     PrepareTipResult | undefined
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx
index 90df2a27..54ae19c6 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx
@@ -19,10 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { amountFractionalBase, Amounts } from "@gnu-taler/taler-util";
-import { ExchangeRecord } from "@gnu-taler/taler-wallet-core";
-import { ExchangeWithdrawDetails } from 
"@gnu-taler/taler-wallet-core/src/operations/withdraw";
-import { getMaxListeners } from "process";
+import { amountFractionalBase } from "@gnu-taler/taler-util";
 import { createExample } from "../test-utils";
 import { View as TestedComponent } from "./Withdraw";
 
@@ -793,12 +790,6 @@ export const NewTerms = createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -810,7 +801,9 @@ export const NewTerms = createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "xml",
@@ -834,12 +827,6 @@ export const TermsReviewingPLAIN = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -851,7 +838,9 @@ export const TermsReviewingPLAIN = 
createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "plain",
@@ -876,12 +865,6 @@ export const TermsReviewingHTML = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -893,7 +876,9 @@ export const TermsReviewingHTML = 
createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "html",
@@ -935,12 +920,6 @@ export const TermsReviewingPDF = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -952,7 +931,9 @@ export const TermsReviewingPDF = 
createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "pdf",
@@ -979,12 +960,6 @@ export const TermsReviewingXML = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -996,7 +971,9 @@ export const TermsReviewingXML = 
createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "xml",
@@ -1021,12 +998,6 @@ export const NewTermsAccepted = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -1037,7 +1008,9 @@ export const NewTermsAccepted = 
createExample(TestedComponent, {
     value: 2,
     fraction: 10000000,
   },
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "xml",
@@ -1062,12 +1035,6 @@ export const TermsShowAgainXML = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -1079,7 +1046,9 @@ export const TermsShowAgainXML = 
createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "xml",
@@ -1105,12 +1074,6 @@ export const TermsChanged = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -1122,7 +1085,9 @@ export const TermsChanged = 
createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "xml",
@@ -1146,12 +1111,6 @@ export const TermsNotFound = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -1163,7 +1122,9 @@ export const TermsNotFound = 
createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     status: "notfound",
   },
@@ -1183,12 +1144,6 @@ export const TermsAlreadyAccepted = 
createExample(TestedComponent, {
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: amountFractionalBase * 0.5,
@@ -1200,7 +1155,9 @@ export const TermsAlreadyAccepted = 
createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     status: "accepted",
   },
@@ -1220,12 +1177,6 @@ export const WithoutFee = createExample(TestedComponent, 
{
     },
   ],
   exchangeBaseUrl: "exchange.demo.taler.net",
-  details: {
-    content: "",
-    contentType: "",
-    currentEtag: "",
-    acceptedEtag: undefined,
-  },
   withdrawalFee: {
     currency: "USD",
     fraction: 0,
@@ -1237,7 +1188,9 @@ export const WithoutFee = createExample(TestedComponent, {
     fraction: 10000000,
   },
 
-  onSwitchExchange: async () => {},
+  onSwitchExchange: async () => {
+    null;
+  },
   terms: {
     value: {
       type: "xml",
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx 
b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
index 603dafcd..8258717b 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx
@@ -29,9 +29,8 @@ import {
   i18n,
   WithdrawUriInfoResponse,
 } from "@gnu-taler/taler-util";
-import { VNode, h } from "preact";
+import { VNode, h, Fragment } from "preact";
 import { useState } from "preact/hooks";
-import { Fragment } from "preact/jsx-runtime";
 import { CheckboxOutlined } from "../components/CheckboxOutlined";
 import { ExchangeXmlTos } from "../components/ExchangeToS";
 import { LogoHeader } from "../components/LogoHeader";
@@ -60,7 +59,6 @@ interface Props {
 }
 
 export interface ViewProps {
-  details: GetExchangeTosResult;
   withdrawalFee: AmountJson;
   exchangeBaseUrl: string;
   amount: AmountJson;
@@ -112,14 +110,13 @@ interface TermsDocumentPdf {
   location: URL;
 }
 
-function amountToString(text: AmountJson) {
+function amountToString(text: AmountJson): string {
   const aj = Amounts.jsonifyAmount(text);
   const amount = Amounts.stringifyValue(aj);
   return `${amount} ${aj.currency}`;
 }
 
 export function View({
-  details,
   withdrawalFee,
   exchangeBaseUrl,
   knownExchanges,
@@ -132,7 +129,7 @@ export function View({
   onAccept,
   reviewed,
   confirmed,
-}: ViewProps) {
+}: ViewProps): VNode {
   const needsReview = terms.status === "changed" || terms.status === "new";
 
   const [switchingExchange, setSwitchingExchange] = useState<
@@ -309,7 +306,7 @@ export function WithdrawPageWithParsedURI({
 }: {
   uri: string;
   uriInfo: WithdrawUriInfoResponse;
-}) {
+}): VNode {
   const [customExchange, setCustomExchange] = useState<string | undefined>(
     undefined,
   );
@@ -407,7 +404,7 @@ export function WithdrawPageWithParsedURI({
   return (
     <View
       onWithdraw={onWithdraw}
-      details={details.tos}
+      // details={details.tos}
       amount={withdrawAmount}
       exchangeBaseUrl={exchange}
       withdrawalFee={details.info.withdrawFee} //FIXME
diff --git a/packages/taler-wallet-webextension/src/cta/payback.tsx 
b/packages/taler-wallet-webextension/src/cta/payback.tsx
index 60cb8c51..1c81b48a 100644
--- a/packages/taler-wallet-webextension/src/cta/payback.tsx
+++ b/packages/taler-wallet-webextension/src/cta/payback.tsx
@@ -14,8 +14,7 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { JSX } from "preact/jsx-runtime";
-import { h } from "preact";
+import { h, VNode } from "preact";
 
 /**
  * View and edit auditors.
@@ -27,6 +26,6 @@ import { h } from "preact";
  * Imports.
  */
 
-export function makePaybackPage(): JSX.Element {
+export function makePaybackPage(): VNode {
   return <div>not implemented</div>;
 }
diff --git a/packages/taler-wallet-webextension/src/cta/reset-required.tsx 
b/packages/taler-wallet-webextension/src/cta/reset-required.tsx
index 3949318c..75c4c196 100644
--- a/packages/taler-wallet-webextension/src/cta/reset-required.tsx
+++ b/packages/taler-wallet-webextension/src/cta/reset-required.tsx
@@ -20,7 +20,7 @@
  * @author Florian Dold
  */
 
-import { Component, JSX, h } from "preact";
+import { Component, h, VNode } from "preact";
 import * as wxApi from "../wxApi";
 
 interface State {
@@ -45,7 +45,7 @@ class ResetNotification extends Component<any, State> {
     const res = await wxApi.checkUpgrade();
     this.setState({ resetRequired: res.dbResetRequired });
   }
-  render(): JSX.Element {
+  render(): VNode {
     if (this.state.resetRequired) {
       return (
         <div>
@@ -92,6 +92,6 @@ class ResetNotification extends Component<any, State> {
 /**
  * @deprecated to be removed
  */
-export function createResetRequiredPage(): JSX.Element {
+export function createResetRequiredPage(): VNode {
   return <ResetNotification />;
 }
diff --git a/packages/taler-wallet-webextension/src/cta/return-coins.tsx 
b/packages/taler-wallet-webextension/src/cta/return-coins.tsx
index 548202ca..55f0297d 100644
--- a/packages/taler-wallet-webextension/src/cta/return-coins.tsx
+++ b/packages/taler-wallet-webextension/src/cta/return-coins.tsx
@@ -14,8 +14,7 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { JSX } from "preact/jsx-runtime";
-import { h } from "preact";
+import { h, VNode } from "preact";
 /**
  * Return coins to own bank account.
  *
@@ -25,6 +24,6 @@ import { h } from "preact";
 /**
  * Imports.
  */
-export function createReturnCoinsPage(): JSX.Element {
+export function createReturnCoinsPage(): VNode {
   return <span>Not implemented yet.</span>;
 }
diff --git a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx 
b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx
index 894c8a79..ae93f8a4 100644
--- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx
@@ -24,18 +24,18 @@ import {
   formatDuration,
   intervalToDuration,
 } from "date-fns";
-import { Fragment, JSX, VNode, h } from "preact";
+import { Fragment, h, VNode } from "preact";
 import {
   BoldLight,
   ButtonPrimary,
   ButtonSuccess,
   Centered,
-  CenteredText,
   CenteredBoldText,
+  CenteredText,
   PopupBox,
   RowBorderGray,
-  SmallText,
   SmallLightText,
+  SmallText,
 } from "../components/styled";
 import { useBackupStatus } from "../hooks/useBackupStatus";
 import { Pages } from "../NavigationBar";
@@ -72,8 +72,9 @@ export function BackupView({
   return (
     <PopupBox>
       <section>
-        {providers.map((provider) => (
+        {providers.map((provider, idx) => (
           <BackupLayout
+            key={idx}
             status={provider.paymentStatus}
             timestamp={provider.lastSuccessfulBackupTimestamp}
             id={provider.syncProviderBaseUrl}
@@ -117,7 +118,7 @@ interface TransactionLayoutProps {
   active: boolean;
 }
 
-function BackupLayout(props: TransactionLayoutProps): JSX.Element {
+function BackupLayout(props: TransactionLayoutProps): VNode {
   const date = !props.timestamp ? undefined : new Date(props.timestamp.t_ms);
   const dateStr = date?.toLocaleString([], {
     dateStyle: "medium",
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx 
b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index 2913f60e..a23c81cd 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -18,17 +18,14 @@ import {
   amountFractionalBase,
   Amounts,
   Balance,
-  BalancesResponse,
   i18n,
 } from "@gnu-taler/taler-util";
-import { JSX, h, Fragment } from "preact";
-import { ErrorMessage } from "../components/ErrorMessage";
+import { h, VNode } from "preact";
 import {
-  PopupBox,
-  Centered,
   ButtonPrimary,
   ErrorBox,
   Middle,
+  PopupBox,
 } from "../components/styled/index";
 import { BalancesHook, useBalances } from "../hooks/useBalances";
 import { PageLink, renderAmount } from "../renderHtml";
@@ -37,7 +34,7 @@ export function BalancePage({
   goToWalletManualWithdraw,
 }: {
   goToWalletManualWithdraw: () => void;
-}) {
+}): VNode {
   const balance = useBalances();
   return (
     <BalanceView
@@ -53,11 +50,11 @@ export interface BalanceViewProps {
   goToWalletManualWithdraw: () => void;
 }
 
-function formatPending(entry: Balance): JSX.Element {
-  let incoming: JSX.Element | undefined;
-  let payment: JSX.Element | undefined;
+function formatPending(entry: Balance): VNode {
+  let incoming: VNode | undefined;
+  let payment: VNode | undefined;
 
-  const available = Amounts.parseOrThrow(entry.available);
+  // const available = Amounts.parseOrThrow(entry.available);
   const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming);
   const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing);
 
@@ -105,8 +102,8 @@ export function BalanceView({
   balance,
   Linker,
   goToWalletManualWithdraw,
-}: BalanceViewProps) {
-  function Content() {
+}: BalanceViewProps): VNode {
+  function Content(): VNode {
     if (!balance) {
       return <span />;
     }
@@ -139,7 +136,7 @@ export function BalanceView({
     return (
       <section data-expanded data-centered>
         <table style={{ width: "100%" }}>
-          {balance.response.balances.map((entry) => {
+          {balance.response.balances.map((entry, idx) => {
             const av = Amounts.parseOrThrow(entry.available);
             // Create our number formatter.
             let formatter;
@@ -168,7 +165,7 @@ export function BalanceView({
             const fontSize =
               v.length < 8 ? "3em" : v.length < 13 ? "2em" : "1em";
             return (
-              <tr>
+              <tr key={idx}>
                 <td
                   style={{
                     height: 50,
diff --git a/packages/taler-wallet-webextension/src/popup/Debug.tsx 
b/packages/taler-wallet-webextension/src/popup/Debug.tsx
index 8722c1cf..b0e8543f 100644
--- a/packages/taler-wallet-webextension/src/popup/Debug.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Debug.tsx
@@ -14,12 +14,12 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { JSX, h } from "preact";
+import { h, VNode } from "preact";
 import { Diagnostics } from "../components/Diagnostics";
 import { useDiagnostics } from "../hooks/useDiagnostics.js";
 import * as wxApi from "../wxApi";
 
-export function DeveloperPage(props: any): JSX.Element {
+export function DeveloperPage(): VNode {
   const [status, timedOut] = useDiagnostics();
   return (
     <div>
@@ -36,6 +36,7 @@ export function DeveloperPage(props: any): JSX.Element {
 
 export function reload(): void {
   try {
+    // eslint-disable-next-line no-undef
     chrome.runtime.reload();
     window.close();
   } catch (e) {
@@ -57,7 +58,9 @@ export async function confirmReset(): Promise<void> {
 
 export function openExtensionPage(page: string) {
   return () => {
+    // eslint-disable-next-line no-undef
     chrome.tabs.create({
+      // eslint-disable-next-line no-undef
       url: chrome.extension.getURL(page),
     });
   };
diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx 
b/packages/taler-wallet-webextension/src/popup/History.tsx
index 8fe6de16..2228271d 100644
--- a/packages/taler-wallet-webextension/src/popup/History.tsx
+++ b/packages/taler-wallet-webextension/src/popup/History.tsx
@@ -21,14 +21,14 @@ import {
   Transaction,
   TransactionsResponse,
 } from "@gnu-taler/taler-util";
-import { h, JSX } from "preact";
+import { h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { PopupBox } from "../components/styled";
 import { TransactionItem } from "../components/TransactionItem";
 import { useBalances } from "../hooks/useBalances";
 import * as wxApi from "../wxApi";
 
-export function HistoryPage(props: any): JSX.Element {
+export function HistoryPage(): VNode {
   const [transactions, setTransactions] = useState<
     TransactionsResponse | undefined
   >(undefined);
diff --git a/packages/taler-wallet-webextension/src/renderHtml.tsx 
b/packages/taler-wallet-webextension/src/renderHtml.tsx
index 9c2a794d..15986d5d 100644
--- a/packages/taler-wallet-webextension/src/renderHtml.tsx
+++ b/packages/taler-wallet-webextension/src/renderHtml.tsx
@@ -28,13 +28,13 @@ import {
   Amounts,
   amountFractionalBase,
 } from "@gnu-taler/taler-util";
-import { Component, ComponentChildren, JSX, h } from "preact";
+import { Component, ComponentChildren, h, VNode } from "preact";
 
 /**
  * Render amount as HTML, which non-breaking space between
  * decimal value and currency.
  */
-export function renderAmount(amount: AmountJson | string): JSX.Element {
+export function renderAmount(amount: AmountJson | string): VNode {
   let a;
   if (typeof amount === "string") {
     a = Amounts.parse(amount);
@@ -56,13 +56,13 @@ export const AmountView = ({
   amount,
 }: {
   amount: AmountJson | string;
-}): JSX.Element => renderAmount(amount);
+}): VNode => renderAmount(amount);
 
 /**
  * Abbreviate a string to a given length, and show the full
  * string on hover as a tooltip.
  */
-export function abbrev(s: string, n = 5): JSX.Element {
+export function abbrev(s: string, n = 5): VNode {
   let sAbbrev = s;
   if (s.length > n) {
     sAbbrev = s.slice(0, n) + "..";
@@ -92,7 +92,7 @@ export class Collapsible extends Component<CollapsibleProps, 
CollapsibleState> {
     super(props);
     this.state = { collapsed: props.initiallyCollapsed };
   }
-  render(): JSX.Element {
+  render(): VNode {
     const doOpen = (e: any): void => {
       this.setState({ collapsed: false });
       e.preventDefault();
@@ -132,19 +132,19 @@ interface ExpanderTextProps {
 /**
  * Show a heading with a toggle to show/hide the expandable content.
  */
-export function ExpanderText({ text }: ExpanderTextProps): JSX.Element {
+export function ExpanderText({ text }: ExpanderTextProps): VNode {
   return <span>{text}</span>;
 }
 
 export interface LoadingButtonProps
-  extends JSX.HTMLAttributes<HTMLButtonElement> {
+  extends h.JSX.HTMLAttributes<HTMLButtonElement> {
   isLoading: boolean;
 }
 
 export function ProgressButton({
   isLoading,
   ...rest
-}: LoadingButtonProps): JSX.Element {
+}: LoadingButtonProps): VNode {
   return (
     <button class="pure-button pure-button-primary" type="button" {...rest}>
       {isLoading ? (
@@ -160,7 +160,8 @@ export function ProgressButton({
 export function PageLink(props: {
   pageName: string;
   children?: ComponentChildren;
-}): JSX.Element {
+}): VNode {
+  // eslint-disable-next-line no-undef
   const url = 
chrome.extension.getURL(`/static/wallet.html#/${props.pageName}`);
   return (
     <a class="actionLink" href={url} target="_blank" rel="noopener noreferrer">
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
index c3be0203..f0ae38e0 100644
--- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -24,18 +24,17 @@ import {
   formatDuration,
   intervalToDuration,
 } from "date-fns";
-import { Fragment, JSX, VNode, h } from "preact";
+import { Fragment, h, VNode } from "preact";
 import {
   BoldLight,
   ButtonPrimary,
   ButtonSuccess,
   Centered,
-  CenteredText,
   CenteredBoldText,
-  PopupBox,
+  CenteredText,
   RowBorderGray,
-  SmallText,
   SmallLightText,
+  SmallText,
   WalletBox,
 } from "../components/styled";
 import { useBackupStatus } from "../hooks/useBackupStatus";
@@ -73,8 +72,9 @@ export function BackupView({
   return (
     <WalletBox>
       <section>
-        {providers.map((provider) => (
+        {providers.map((provider, idx) => (
           <BackupLayout
+            key={idx}
             status={provider.paymentStatus}
             timestamp={provider.lastSuccessfulBackupTimestamp}
             id={provider.syncProviderBaseUrl}
@@ -118,7 +118,7 @@ interface TransactionLayoutProps {
   active: boolean;
 }
 
-function BackupLayout(props: TransactionLayoutProps): JSX.Element {
+function BackupLayout(props: TransactionLayoutProps): VNode {
   const date = !props.timestamp ? undefined : new Date(props.timestamp.t_ms);
   const dateStr = date?.toLocaleString([], {
     dateStyle: "medium",
diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx 
b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
index f3c08a3e..9a284767 100644
--- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
@@ -21,7 +21,7 @@ import {
   BalancesResponse,
   i18n,
 } from "@gnu-taler/taler-util";
-import { JSX, h } from "preact";
+import { h, VNode } from "preact";
 import { ButtonPrimary, Centered, WalletBox } from 
"../components/styled/index";
 import { BalancesHook, useBalances } from "../hooks/useBalances";
 import { PageLink, renderAmount } from "../renderHtml";
@@ -30,7 +30,7 @@ export function BalancePage({
   goToWalletManualWithdraw,
 }: {
   goToWalletManualWithdraw: () => void;
-}) {
+}): VNode {
   const balance = useBalances();
   return (
     <BalanceView
@@ -51,7 +51,7 @@ export function BalanceView({
   balance,
   Linker,
   goToWalletManualWithdraw,
-}: BalanceViewProps) {
+}: BalanceViewProps): VNode {
   if (!balance) {
     return <span />;
   }
@@ -85,13 +85,13 @@ export function BalanceView({
   );
 }
 
-function formatPending(entry: Balance): JSX.Element {
-  let incoming: JSX.Element | undefined;
-  let payment: JSX.Element | undefined;
+function formatPending(entry: Balance): VNode {
+  let incoming: VNode | undefined;
+  let payment: VNode | undefined;
 
-  const available = Amounts.parseOrThrow(entry.available);
+  // const available = Amounts.parseOrThrow(entry.available);
   const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming);
-  const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing);
+  // const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing);
 
   if (!Amounts.isZero(pendingIncoming)) {
     incoming = (
@@ -128,7 +128,7 @@ function ShowBalances({
 }: {
   wallet: BalancesResponse;
   onWithdraw: () => void;
-}) {
+}): VNode {
   return (
     <WalletBox>
       <section>
diff --git 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
index 6eab8dc3..300e9cd5 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx
@@ -28,26 +28,27 @@ export default {
   argTypes: {},
 };
 
-export const InitialState = createExample(TestedComponent, {});
+// ,
+const exchangeList = {
+  "http://exchange.taler:8081": "COL",
+  "http://exchange.tal": "EUR",
+};
 
-export const WithExchangeFilled = createExample(TestedComponent, {
-  currency: "COL",
-  initialExchange: "http://exchange.taler:8081";,
+export const InitialState = createExample(TestedComponent, {
+  exchangeList,
 });
 
-export const WithExchangeAndAmountFilled = createExample(TestedComponent, {
-  currency: "COL",
-  initialExchange: "http://exchange.taler:8081";,
+export const WithAmountInitialized = createExample(TestedComponent, {
   initialAmount: "10",
+  exchangeList,
 });
 
 export const WithExchangeError = createExample(TestedComponent, {
-  initialExchange: "http://exchange.tal";,
   error: "The exchange url seems invalid",
+  exchangeList,
 });
 
 export const WithAmountError = createExample(TestedComponent, {
-  currency: "COL",
-  initialExchange: "http://exchange.taler:8081";,
   initialAmount: "e",
+  exchangeList,
 });
diff --git 
a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx 
b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
index b48dcbaf..140ac2d4 100644
--- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx
@@ -20,9 +20,10 @@
  */
 
 import { AmountJson, Amounts } from "@gnu-taler/taler-util";
-import { VNode, h } from "preact";
-import { useEffect, useRef, useState } from "preact/hooks";
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
 import { ErrorMessage } from "../components/ErrorMessage";
+import { SelectList } from "../components/SelectList";
 import {
   ButtonPrimary,
   Input,
@@ -33,32 +34,56 @@ import {
 
 export interface Props {
   error: string | undefined;
-  currency: string | undefined;
-  initialExchange?: string;
   initialAmount?: string;
-  onExchangeChange: (exchange: string) => void;
+  exchangeList: Record<string, string>;
   onCreate: (exchangeBaseUrl: string, amount: AmountJson) => Promise<void>;
 }
 
 export function CreateManualWithdraw({
-  onExchangeChange,
-  initialExchange,
   initialAmount,
+  exchangeList,
   error,
-  currency,
   onCreate,
 }: Props): VNode {
+  const exchangeSelectList = Object.keys(exchangeList);
+  const currencySelectList = Object.values(exchangeList);
+  const exchangeMap = exchangeSelectList.reduce(
+    (p, c) => ({ ...p, [c]: `${c} (${exchangeList[c]})` }),
+    {} as Record<string, string>,
+  );
+  const currencyMap = currencySelectList.reduce(
+    (p, c) => ({ ...p, [c]: c }),
+    {} as Record<string, string>,
+  );
+
+  const initialExchange =
+    exchangeSelectList.length > 0 ? exchangeSelectList[0] : "";
+
   const [exchange, setExchange] = useState(initialExchange || "");
+  const [currency, setCurrency] = useState(exchangeList[initialExchange] ?? 
"");
+
   const [amount, setAmount] = useState(initialAmount || "");
   const parsedAmount = Amounts.parse(`${currency}:${amount}`);
 
-  let timeout = useRef<number | undefined>(undefined);
-  useEffect(() => {
-    if (timeout) window.clearTimeout(timeout.current);
-    timeout.current = window.setTimeout(async () => {
-      onExchangeChange(exchange);
-    }, 1000);
-  }, [exchange]);
+  function changeExchange(exchange: string): void {
+    setExchange(exchange);
+    setCurrency(exchangeList[exchange]);
+  }
+
+  function changeCurrency(currency: string): void {
+    setCurrency(currency);
+    const found = Object.entries(exchangeList).find((e) => e[1] === currency);
+
+    if (found) {
+      setExchange(found[0]);
+    } else {
+      setExchange("");
+    }
+  }
+
+  if (!initialExchange) {
+    return <div>There is no known exchange where to withdraw, add one</div>;
+  }
 
   return (
     <WalletBox>
@@ -73,26 +98,38 @@ export function CreateManualWithdraw({
           withdraw the coins
         </LightText>
         <p>
-          <Input invalid={!!exchange && !currency}>
-            <label>Exchange</label>
-            <input
-              type="text"
-              placeholder="https://";
+          <Input>
+            <SelectList
+              label="Currency"
+              list={currencyMap}
+              name="currency"
+              value={currency}
+              onChange={changeCurrency}
+            />
+          </Input>
+          <Input>
+            <SelectList
+              label="Exchange"
+              list={exchangeMap}
+              name="currency"
               value={exchange}
-              onChange={(e) => setExchange(e.currentTarget.value)}
+              onChange={changeExchange}
             />
-            <small>http://exchange.taler:8081</small>
           </Input>
+          {/* <p style={{ display: "flex", justifyContent: "right" }}>
+            <a href="" style={{ marginLeft: "auto" }}>
+              Add new exchange
+            </a>
+          </p> */}
           {currency && (
             <InputWithLabel invalid={!!amount && !parsedAmount}>
               <label>Amount</label>
               <div>
-                <div>{currency}</div>
+                <span>{currency}</span>
                 <input
                   type="number"
-                  style={{ paddingLeft: `${currency.length}em` }}
                   value={amount}
-                  onChange={(e) => setAmount(e.currentTarget.value)}
+                  onInput={(e) => setAmount(e.currentTarget.value)}
                 />
               </div>
             </InputWithLabel>
@@ -105,7 +142,7 @@ export function CreateManualWithdraw({
           disabled={!parsedAmount || !exchange}
           onClick={() => onCreate(exchange, parsedAmount!)}
         >
-          Create
+          Start withdrawal
         </ButtonPrimary>
       </footer>
     </WalletBox>
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.tsx
index aabe50a2..6b1a2185 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -20,15 +20,15 @@ import {
   Transaction,
   TransactionsResponse,
 } from "@gnu-taler/taler-util";
-import { format } from "date-fns";
-import { Fragment, h, JSX } from "preact";
+import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { DateSeparator, WalletBox } from "../components/styled";
+import { Time } from "../components/Time";
 import { TransactionItem } from "../components/TransactionItem";
 import { useBalances } from "../hooks/useBalances";
 import * as wxApi from "../wxApi";
 
-export function HistoryPage(props: any): JSX.Element {
+export function HistoryPage(): VNode {
   const [transactions, setTransactions] = useState<
     TransactionsResponse | undefined
   >(undefined);
@@ -57,24 +57,30 @@ export function HistoryPage(props: any): JSX.Element {
   );
 }
 
-function amountToString(c: AmountString) {
+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,
   balances,
 }: {
   list: Transaction[];
   balances: Balance[];
-}) {
-  const byDate = list.reduce(function (rv, x) {
+}): VNode {
+  const byDate = list.reduce((rv, x) => {
     const theDate =
-      x.timestamp.t_ms === "never"
-        ? "never"
-        : format(x.timestamp.t_ms, "dd MMMM yyyy");
-    (rv[theDate] = rv[theDate] || []).push(x);
+      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[] });
 
@@ -93,8 +99,8 @@ export function HistoryView({
             <div class="title">
               Balance:{" "}
               <ul style={{ margin: 0 }}>
-                {balances.map((b) => (
-                  <li>{b.available}</li>
+                {balances.map((b, i) => (
+                  <li key={i}>{b.available}</li>
                 ))}
               </ul>
             </div>
@@ -105,7 +111,12 @@ export function HistoryView({
         {Object.keys(byDate).map((d, i) => {
           return (
             <Fragment key={i}>
-              <DateSeparator>{d}</DateSeparator>
+              <DateSeparator>
+                <Time
+                  timestamp={{ t_ms: Number.parseInt(d, 10) }}
+                  format="dd MMMM yyyy"
+                />
+              </DateSeparator>
               {byDate[d].map((tx, i) => (
                 <TransactionItem
                   key={i}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
index 102978f9..1af4e8d8 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
@@ -26,44 +26,31 @@ import {
 import { ReserveCreated } from "./ReserveCreated.js";
 import { route } from "preact-router";
 import { Pages } from "../NavigationBar.js";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
 
-interface Props {}
-
-export function ManualWithdrawPage({}: Props): VNode {
+export function ManualWithdrawPage(): VNode {
   const [success, setSuccess] = useState<
-    AcceptManualWithdrawalResult | undefined
+    | {
+        response: AcceptManualWithdrawalResult;
+        exchangeBaseUrl: string;
+        amount: AmountJson;
+      }
+    | undefined
   >(undefined);
-  const [currency, setCurrency] = useState<string | undefined>(undefined);
   const [error, setError] = useState<string | undefined>(undefined);
 
-  async function onExchangeChange(exchange: string | undefined): Promise<void> 
{
-    if (!exchange) return;
-    try {
-      const r = await fetch(`${exchange}/keys`);
-      const j = await r.json();
-      if (j.currency) {
-        await wxApi.addExchange({
-          exchangeBaseUrl: `${exchange}/`,
-          forceUpdate: true,
-        });
-        setCurrency(j.currency);
-      }
-    } catch (e) {
-      setError("The exchange url seems invalid");
-      setCurrency(undefined);
-    }
-  }
+  const knownExchangesHook = useAsyncAsHook(() => wxApi.listExchanges());
 
   async function doCreate(
     exchangeBaseUrl: string,
     amount: AmountJson,
   ): Promise<void> {
     try {
-      const resp = await wxApi.acceptManualWithdrawal(
+      const response = await wxApi.acceptManualWithdrawal(
         exchangeBaseUrl,
         Amounts.stringify(amount),
       );
-      setSuccess(resp);
+      setSuccess({ exchangeBaseUrl, response, amount });
     } catch (e) {
       if (e instanceof Error) {
         setError(e.message);
@@ -77,8 +64,10 @@ export function ManualWithdrawPage({}: Props): VNode {
   if (success) {
     return (
       <ReserveCreated
-        reservePub={success.reservePub}
-        paytos={success.exchangePaytoUris}
+        reservePub={success.response.reservePub}
+        payto={success.response.exchangePaytoUris[0]}
+        exchangeBaseUrl={success.exchangeBaseUrl}
+        amount={success.amount}
         onBack={() => {
           route(Pages.balance);
         }}
@@ -86,12 +75,22 @@ export function ManualWithdrawPage({}: Props): VNode {
     );
   }
 
+  if (!knownExchangesHook || knownExchangesHook.hasError) {
+    return <div>No Known exchanges</div>;
+  }
+  const exchangeList = knownExchangesHook.response.exchanges.reduce(
+    (p, c) => ({
+      ...p,
+      [c.exchangeBaseUrl]: c.currency,
+    }),
+    {} as Record<string, string>,
+  );
+
   return (
     <CreateManualWithdraw
       error={error}
-      currency={currency}
+      exchangeList={exchangeList}
       onCreate={doCreate}
-      onExchangeChange={onExchangeChange}
     />
   );
 }
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
index bd64b076..1c14c6e0 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
@@ -14,23 +14,23 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 
-import { i18n, Timestamp } from "@gnu-taler/taler-util";
+import { i18n } from "@gnu-taler/taler-util";
 import {
   ProviderInfo,
   ProviderPaymentStatus,
   ProviderPaymentType,
 } from "@gnu-taler/taler-wallet-core";
-import { format, formatDuration, intervalToDuration } from "date-fns";
-import { Fragment, VNode, h } from "preact";
+import { Fragment, h, VNode } from "preact";
 import { ErrorMessage } from "../components/ErrorMessage";
 import {
   Button,
   ButtonDestructive,
   ButtonPrimary,
   PaymentStatus,
-  WalletBox,
   SmallLightText,
+  WalletBox,
 } from "../components/styled";
+import { Time } from "../components/Time";
 import { useProviderStatus } from "../hooks/useProviderStatus";
 
 interface Props {
@@ -97,10 +97,7 @@ export function ProviderView({
       </header>
       <section>
         <p>
-          <b>Last backup:</b>{" "}
-          {lb == null || lb.t_ms == "never"
-            ? "never"
-            : format(lb.t_ms, "dd MMM yyyy")}{" "}
+          <b>Last backup:</b> <Time timestamp={lb} format="dd MMMM yyyy" />
         </p>
         <ButtonPrimary onClick={onSync}>
           <i18n.Translate>Back up</i18n.Translate>
@@ -128,7 +125,7 @@ export function ProviderView({
             <table>
               <thead>
                 <tr>
-                  <td></td>
+                  <td>&nbsp;</td>
                   <td>
                     <i18n.Translate>old</i18n.Translate>
                   </td>
@@ -174,32 +171,32 @@ export function ProviderView({
   );
 }
 
-function daysSince(d?: Timestamp) {
-  if (!d || d.t_ms === "never") return "never synced";
-  const duration = intervalToDuration({
-    start: d.t_ms,
-    end: new Date(),
-  });
-  const str = formatDuration(duration, {
-    delimiter: ", ",
-    format: [
-      duration?.years
-        ? i18n.str`years`
-        : duration?.months
-        ? i18n.str`months`
-        : duration?.days
-        ? i18n.str`days`
-        : duration?.hours
-        ? i18n.str`hours`
-        : duration?.minutes
-        ? i18n.str`minutes`
-        : i18n.str`seconds`,
-    ],
-  });
-  return `synced ${str} ago`;
-}
+// function daysSince(d?: Timestamp): string {
+//   if (!d || d.t_ms === "never") return "never synced";
+//   const duration = intervalToDuration({
+//     start: d.t_ms,
+//     end: new Date(),
+//   });
+//   const str = formatDuration(duration, {
+//     delimiter: ", ",
+//     format: [
+//       duration?.years
+//         ? i18n.str`years`
+//         : duration?.months
+//         ? i18n.str`months`
+//         : duration?.days
+//         ? i18n.str`days`
+//         : duration?.hours
+//         ? i18n.str`hours`
+//         : duration?.minutes
+//         ? i18n.str`minutes`
+//         : i18n.str`seconds`,
+//     ],
+//   });
+//   return `synced ${str} ago`;
+// }
 
-function Error({ info }: { info: ProviderInfo }) {
+function Error({ info }: { info: ProviderInfo }): VNode {
   if (info.lastError) {
     return <ErrorMessage title={info.lastError.hint} />;
   }
@@ -234,45 +231,45 @@ function Error({ info }: { info: ProviderInfo }) {
         );
     }
   }
-  return null;
+  return <Fragment />;
 }
 
-function colorByStatus(status: ProviderPaymentType) {
-  switch (status) {
-    case ProviderPaymentType.InsufficientBalance:
-      return "rgb(223, 117, 20)";
-    case ProviderPaymentType.Unpaid:
-      return "rgb(202, 60, 60)";
-    case ProviderPaymentType.Paid:
-      return "rgb(28, 184, 65)";
-    case ProviderPaymentType.Pending:
-      return "gray";
-    case ProviderPaymentType.InsufficientBalance:
-      return "rgb(202, 60, 60)";
-    case ProviderPaymentType.TermsChanged:
-      return "rgb(202, 60, 60)";
-  }
-}
+// function colorByStatus(status: ProviderPaymentType): string {
+//   switch (status) {
+//     case ProviderPaymentType.InsufficientBalance:
+//       return "rgb(223, 117, 20)";
+//     case ProviderPaymentType.Unpaid:
+//       return "rgb(202, 60, 60)";
+//     case ProviderPaymentType.Paid:
+//       return "rgb(28, 184, 65)";
+//     case ProviderPaymentType.Pending:
+//       return "gray";
+//     // case ProviderPaymentType.InsufficientBalance:
+//     //   return "rgb(202, 60, 60)";
+//     case ProviderPaymentType.TermsChanged:
+//       return "rgb(202, 60, 60)";
+//   }
+// }
 
-function descriptionByStatus(status: ProviderPaymentStatus) {
+function descriptionByStatus(status: ProviderPaymentStatus): VNode {
   switch (status.type) {
     // return i18n.str`no enough balance to make the payment`
     // return i18n.str`not paid yet`
     case ProviderPaymentType.Paid:
     case ProviderPaymentType.TermsChanged:
       if (status.paidUntil.t_ms === "never") {
-        return i18n.str`service paid`;
-      } else {
-        return (
-          <Fragment>
-            <b>Backup valid until:</b>{" "}
-            {format(status.paidUntil.t_ms, "dd MMM yyyy")}
-          </Fragment>
-        );
+        return <span>{i18n.str`service paid`}</span>;
       }
+      return (
+        <Fragment>
+          <b>Backup valid until:</b>{" "}
+          <Time timestamp={status.paidUntil} format="dd MMM yyyy" />
+        </Fragment>
+      );
+
     case ProviderPaymentType.Unpaid:
     case ProviderPaymentType.InsufficientBalance:
     case ProviderPaymentType.Pending:
-      return "";
+      return <span />;
   }
 }
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
index c552b19b..8d7b65b3 100644
--- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
@@ -28,10 +28,26 @@ export default {
   argTypes: {},
 };
 
-export const InitialState = createExample(TestedComponent, {
-  reservePub: "ASLKDJQWLKEJASLKDJSADLKASJDLKSADJ",
-  paytos: [
+export const TalerBank = createExample(TestedComponent, {
+  reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
+  payto:
     
"payto://x-taler-bank/bank.taler:5882/exchangeminator?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
-    
"payto://x-taler-bank/international-bank.com/myaccount?amount=COL%3A1&message=Taler+Withdrawal+TYQTE7VA4M9GZQ4TR06YBNGA05AJGMFNSK4Q62NXR2FKNDB1J4EX",
-  ],
+  amount: {
+    currency: "USD",
+    value: 10,
+    fraction: 0,
+  },
+  exchangeBaseUrl: "https://exchange.demo.taler.net";,
+});
+
+export const IBAN = createExample(TestedComponent, {
+  reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
+  payto:
+    
"payto://iban/ASDQWEASDZXCASDQWE?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
+  amount: {
+    currency: "USD",
+    value: 10,
+    fraction: 0,
+  },
+  exchangeBaseUrl: "https://exchange.demo.taler.net";,
 });
diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx 
b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
index 9008e975..a72026ab 100644
--- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
@@ -1,66 +1,155 @@
-import { h, Fragment, VNode } from "preact";
-import { useState } from "preact/hooks";
+import {
+  AmountJson,
+  Amounts,
+  parsePaytoUri,
+  PaytoUri,
+} from "@gnu-taler/taler-util";
+import { Fragment, h, VNode } from "preact";
+import { useEffect, useState } from "preact/hooks";
 import { QR } from "../components/QR";
-import { ButtonBox, FontIcon, WalletBox } from "../components/styled";
+import {
+  ButtonDestructive,
+  ButtonPrimary,
+  WalletBox,
+  WarningBox,
+} from "../components/styled";
 export interface Props {
   reservePub: string;
-  paytos: string[];
+  payto: string;
+  exchangeBaseUrl: string;
+  amount: AmountJson;
   onBack: () => void;
 }
 
-export function ReserveCreated({ reservePub, paytos, onBack }: Props): VNode {
-  const [opened, setOpened] = useState(-1);
+interface BankDetailsProps {
+  payto: PaytoUri;
+  exchangeBaseUrl: string;
+  subject: string;
+  amount: string;
+}
+
+function Row({
+  name,
+  value,
+  literal,
+}: {
+  name: string;
+  value: string;
+  literal?: boolean;
+}): VNode {
+  const [copied, setCopied] = useState(false);
+  function copyText(): void {
+    navigator.clipboard.writeText(value);
+    setCopied(true);
+  }
+  useEffect(() => {
+    setTimeout(() => {
+      setCopied(false);
+    }, 1000);
+  }, [copied]);
+  return (
+    <tr>
+      <td>
+        {!copied ? (
+          <ButtonPrimary small onClick={copyText}>
+            &nbsp; Copy &nbsp;
+          </ButtonPrimary>
+        ) : (
+          <ButtonPrimary small disabled>
+            Copied
+          </ButtonPrimary>
+        )}
+      </td>
+      <td>
+        <b>{name}</b>
+      </td>
+      {literal ? (
+        <td>
+          <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
+            {value}
+          </pre>
+        </td>
+      ) : (
+        <td>{value}</td>
+      )}
+    </tr>
+  );
+}
+
+function BankDetailsByPaytoType({
+  payto,
+  subject,
+  exchangeBaseUrl,
+  amount,
+}: BankDetailsProps): VNode {
+  const firstPart = !payto.isKnown ? (
+    <Fragment>
+      <Row name="Account" value={payto.targetPath} />
+      <Row name="Exchange" value={exchangeBaseUrl} />
+    </Fragment>
+  ) : payto.targetType === "x-taler-bank" ? (
+    <Fragment>
+      <Row name="Bank host" value={payto.host} />
+      <Row name="Bank account" value={payto.account} />
+      <Row name="Exchange" value={exchangeBaseUrl} />
+    </Fragment>
+  ) : payto.targetType === "iban" ? (
+    <Fragment>
+      <Row name="IBAN" value={payto.iban} />
+      <Row name="Exchange" value={exchangeBaseUrl} />
+    </Fragment>
+  ) : undefined;
+  return (
+    <table>
+      {firstPart}
+      <Row name="Amount" value={amount} />
+      <Row name="Subject" value={subject} literal />
+    </table>
+  );
+}
+export function ReserveCreated({
+  reservePub,
+  payto,
+  onBack,
+  exchangeBaseUrl,
+  amount,
+}: Props): VNode {
+  const paytoURI = parsePaytoUri(payto);
+  // const url = new URL(paytoURI?.targetPath);
+  if (!paytoURI) {
+    return <div>could not parse payto uri from exchange {payto}</div>;
+  }
   return (
     <WalletBox>
       <section>
-        <h2>Reserve created!</h2>
-        <p>
-          Now you need to send money to the exchange to one of the following
-          accounts
-        </p>
+        <h1>Bank transfer details</h1>
         <p>
-          To complete the setup of the reserve, you must now initiate a wire
-          transfer using the given wire transfer subject and crediting the
-          specified amount to the indicated account of the exchange.
+          Please wire <b>{Amounts.stringify(amount)}</b> to:
         </p>
+        <BankDetailsByPaytoType
+          amount={Amounts.stringify(amount)}
+          exchangeBaseUrl={exchangeBaseUrl}
+          payto={paytoURI}
+          subject={reservePub}
+        />
       </section>
       <section>
-        <ul>
-          {paytos.map((href, idx) => {
-            const url = new URL(href);
-            return (
-              <li key={idx}>
-                <p>
-                  <a
-                    href=""
-                    onClick={(e) => {
-                      setOpened((o) => (o === idx ? -1 : idx));
-                      e.preventDefault();
-                    }}
-                  >
-                    {url.pathname}
-                  </a>
-                  {opened === idx && (
-                    <Fragment>
-                      <p>
-                        If your system supports RFC 8905, you can do this by
-                        opening <a href={href}>this URI</a> or scan the QR with
-                        your wallet
-                      </p>
-                      <QR text={href} />
-                    </Fragment>
-                  )}
-                </p>
-              </li>
-            );
-          })}
-        </ul>
+        <p>
+          <WarningBox>
+            Make sure to use the correct subject, otherwise the money will not
+            arrive in this wallet.
+          </WarningBox>
+        </p>
+        <p>
+          Alternative, you can also scan this QR code or open{" "}
+          <a href={payto}>this link</a> if you have a banking app installed 
that
+          supports RFC 8905
+        </p>
+        <QR text={payto} />
       </section>
       <footer>
-        <ButtonBox onClick={onBack}>
-          <FontIcon>&#x2190;</FontIcon>
-        </ButtonBox>
         <div />
+        <ButtonDestructive onClick={onBack}>Cancel withdraw</ButtonDestructive>
       </footer>
     </WalletBox>
   );
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 7de6982e..1472efb4 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -21,28 +21,27 @@ import {
   Transaction,
   TransactionType,
 } from "@gnu-taler/taler-util";
-import { format } from "date-fns";
-import { JSX, VNode, h } from "preact";
+import { h, VNode } from "preact";
 import { route } from "preact-router";
 import { useEffect, useState } from "preact/hooks";
 import emptyImg from "../../static/img/empty.png";
 import { ErrorMessage } from "../components/ErrorMessage";
 import { Part } from "../components/Part";
 import {
-  ButtonBox,
-  ButtonBoxDestructive,
+  Button,
+  ButtonDestructive,
   ButtonPrimary,
-  FontIcon,
   ListOfProducts,
   RowBorderGray,
   SmallLightText,
   WalletBox,
   WarningBox,
 } from "../components/styled";
+import { Time } from "../components/Time";
 import { Pages } from "../NavigationBar";
 import * as wxApi from "../wxApi";
 
-export function TransactionPage({ tid }: { tid: string }): JSX.Element {
+export function TransactionPage({ tid }: { tid: string }): VNode {
   const [transaction, setTransaction] = useState<Transaction | undefined>(
     undefined,
   );
@@ -70,8 +69,8 @@ export function TransactionPage({ tid }: { tid: string }): 
JSX.Element {
   return (
     <TransactionView
       transaction={transaction}
-      onDelete={() => wxApi.deleteTransaction(tid).then((_) => history.go(-1))}
-      onRetry={() => wxApi.retryTransaction(tid).then((_) => history.go(-1))}
+      onDelete={() => wxApi.deleteTransaction(tid).then(() => history.go(-1))}
+      onRetry={() => wxApi.retryTransaction(tid).then(() => history.go(-1))}
       onBack={() => {
         route(Pages.history);
       }}
@@ -91,42 +90,42 @@ export function TransactionView({
   onDelete,
   onRetry,
   onBack,
-}: WalletTransactionProps) {
-  function TransactionTemplate({ children }: { children: VNode[] }) {
+}: WalletTransactionProps): VNode {
+  function TransactionTemplate({ children }: { children: VNode[] }): VNode {
     return (
       <WalletBox>
         <section style={{ padding: 8, textAlign: "center" }}>
           <ErrorMessage title={transaction?.error?.hint} />
           {transaction.pending && (
-            <WarningBox>This transaction is not completed</WarningBox>
+            <WarningBox>
+              This transaction is not completed
+              <a href="">more info...</a>
+            </WarningBox>
           )}
         </section>
         <section>
           <div style={{ textAlign: "center" }}>{children}</div>
         </section>
         <footer>
-          <ButtonBox onClick={onBack}>
-            <i18n.Translate>
-              {" "}
-              <FontIcon>&#x2190;</FontIcon>{" "}
-            </i18n.Translate>
-          </ButtonBox>
+          <Button onClick={onBack}>
+            <i18n.Translate> &lt; Back </i18n.Translate>
+          </Button>
           <div>
             {transaction?.error ? (
               <ButtonPrimary onClick={onRetry}>
                 <i18n.Translate>retry</i18n.Translate>
               </ButtonPrimary>
             ) : null}
-            <ButtonBoxDestructive onClick={onDelete}>
-              <i18n.Translate>&#x1F5D1;</i18n.Translate>
-            </ButtonBoxDestructive>
+            <ButtonDestructive onClick={onDelete}>
+              <i18n.Translate> Forget </i18n.Translate>
+            </ButtonDestructive>
           </div>
         </footer>
       </WalletBox>
     );
   }
 
-  function amountToString(text: AmountLike) {
+  function amountToString(text: AmountLike): string {
     const aj = Amounts.jsonifyAmount(text);
     const amount = Amounts.stringifyValue(aj);
     return `${amount} ${aj.currency}`;
@@ -140,23 +139,26 @@ export function TransactionView({
     return (
       <TransactionTemplate>
         <h2>Withdrawal</h2>
-        <div>
-          {transaction.timestamp.t_ms === "never"
-            ? "never"
-            : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")}
-        </div>
+        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
+          big
           title="Total withdrawn"
           text={amountToString(transaction.amountEffective)}
           kind="positive"
         />
         <Part
+          big
           title="Chosen amount"
           text={amountToString(transaction.amountRaw)}
           kind="neutral"
         />
-        <Part title="Exchange fee" text={amountToString(fee)} kind="negative" 
/>
+        <Part
+          big
+          title="Exchange fee"
+          text={amountToString(fee)}
+          kind="negative"
+        />
         <Part
           title="Exchange"
           text={new URL(transaction.exchangeBaseUrl).hostname}
@@ -166,7 +168,9 @@ export function TransactionView({
     );
   }
 
-  const showLargePic = () => {};
+  const showLargePic = (): void => {
+    return;
+  };
 
   if (transaction.type === TransactionType.Payment) {
     const fee = Amounts.sub(
@@ -177,11 +181,7 @@ export function TransactionView({
     return (
       <TransactionTemplate>
         <h2>Payment </h2>
-        <div>
-          {transaction.timestamp.t_ms === "never"
-            ? "never"
-            : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")}
-        </div>
+        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -241,11 +241,7 @@ export function TransactionView({
     return (
       <TransactionTemplate>
         <h2>Deposit </h2>
-        <div>
-          {transaction.timestamp.t_ms === "never"
-            ? "never"
-            : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")}
-        </div>
+        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -272,11 +268,7 @@ export function TransactionView({
     return (
       <TransactionTemplate>
         <h2>Refresh</h2>
-        <div>
-          {transaction.timestamp.t_ms === "never"
-            ? "never"
-            : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")}
-        </div>
+        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -303,11 +295,7 @@ export function TransactionView({
     return (
       <TransactionTemplate>
         <h2>Tip</h2>
-        <div>
-          {transaction.timestamp.t_ms === "never"
-            ? "never"
-            : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")}
-        </div>
+        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -334,11 +322,7 @@ export function TransactionView({
     return (
       <TransactionTemplate>
         <h2>Refund</h2>
-        <div>
-          {transaction.timestamp.t_ms === "never"
-            ? "never"
-            : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")}
-        </div>
+        <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
         <br />
         <Part
           big
@@ -391,5 +375,5 @@ export function TransactionView({
     );
   }
 
-  return <div></div>;
+  return <div />;
 }
diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx 
b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
index 0b8e5c60..a6dd040e 100644
--- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
@@ -20,16 +20,15 @@
  * @author Florian Dold
  */
 
-import { JSX } from "preact/jsx-runtime";
 import { Checkbox } from "../components/Checkbox";
 import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
 import { Diagnostics } from "../components/Diagnostics";
 import { WalletBox } from "../components/styled";
 import { useDiagnostics } from "../hooks/useDiagnostics";
 import { WalletDiagnostics } from "@gnu-taler/taler-util";
-import { h } from "preact";
+import { h, VNode } from "preact";
 
-export function WelcomePage() {
+export function WelcomePage(): VNode {
   const [permissionsEnabled, togglePermissions] = useExtendedPermissions();
   const [diagnostics, timedOut] = useDiagnostics();
   return (
@@ -53,7 +52,7 @@ export function View({
   togglePermissions,
   diagnostics,
   timedOut,
-}: ViewProps): JSX.Element {
+}: ViewProps): VNode {
   return (
     <WalletBox>
       <h1>Browser Extension Installed!</h1>
diff --git a/packages/taler-wallet-webextension/static/wallet.html 
b/packages/taler-wallet-webextension/static/wallet.html
index a1c069d7..f9dd8a19 100644
--- a/packages/taler-wallet-webextension/static/wallet.html
+++ b/packages/taler-wallet-webextension/static/wallet.html
@@ -2,11 +2,27 @@
 <html>
   <head>
     <meta charset="utf-8" />
-    <link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
-    <link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
     <link rel="stylesheet" type="text/css" href="/dist/popupEntryPoint.css" />
     <link rel="icon" href="/static/img/icon.png" />
     <script src="/dist/walletEntryPoint.js"></script>
+    <style>
+      html {
+        font-family: sans-serif; /* 1 */
+      }
+      h1 {
+        font-size: 2em;
+      }
+      input {
+        font: inherit;
+      }
+      body {
+        margin: 0;
+        font-size: 100%;
+        padding: 0;
+        background-color: #f8faf7;
+        font-family: Arial, Helvetica, sans-serif;
+      }
+    </style>
   </head>
 
   <body>

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