gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (417c07f3f -> 9b04d8bf3)


From: gnunet
Subject: [taler-wallet-core] branch master updated (417c07f3f -> 9b04d8bf3)
Date: Mon, 09 Jan 2023 12:39:02 +0100

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

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

    from 417c07f3f wallet-core: insufficient balance details for p2p payments
     new 14f3d1e06 wallet-core pretty
     new 9b04d8bf3 fix #7152

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/crypto/workers/crypto-dispatcher.test.ts   |  22 ++--
 .../src/crypto/workers/crypto-dispatcher.ts        |  10 +-
 .../src/crypto/workers/nodeThreadWorker.ts         |   4 +-
 packages/taler-wallet-core/src/db.ts               |   6 +-
 .../src/operations/backup/import.ts                |   4 +-
 .../taler-wallet-core/src/operations/common.ts     |   2 +-
 packages/taler-wallet-webextension/package.json    |   2 +-
 .../src/components/AmountField.stories.tsx         |   2 +-
 .../src/components/AmountField.tsx                 |   3 +-
 .../src/components/BankDetailsByPaytoType.tsx      |  35 ++----
 .../src/components/Banner.tsx                      |   3 +-
 .../src/components/Checkbox.tsx                    |   5 +-
 .../src/components/CurrentAlerts.tsx               | 108 +++++++++++++++++++
 .../src/components/ErrorMessage.tsx                |   3 +-
 .../src/components/ErrorTalerOperation.tsx         |   4 +-
 .../src/components/Loading.tsx                     |  96 +++++++++++++----
 .../src/components/LoadingError.tsx                |  30 ------
 .../src/components/MultiActionButton.tsx           |   4 +-
 .../src/components/Part.tsx                        |  10 +-
 .../src/components/PaymentButtons.tsx              |  13 ++-
 .../src/components/PendingTransactions.tsx         |   2 +-
 .../src/components/SelectList.tsx                  |   3 +-
 .../ShowFullContractTermPopup.stories.tsx          |   5 +-
 .../src/components/ShowFullContractTermPopup.tsx   |  16 +--
 .../src/components/TermsOfService/index.ts         |  22 ++--
 .../src/components/TermsOfService/state.ts         |  36 +++----
 .../src/components/TermsOfService/views.tsx        |  34 +-----
 .../src/components/styled/index.tsx                |   2 +-
 .../taler-wallet-webextension/src/context/alert.ts | 118 ++++++++++++++++++++
 .../src/cta/Deposit/index.ts                       |  11 +-
 .../src/cta/Deposit/state.ts                       |  10 +-
 .../src/cta/Deposit/test.ts                        |   8 +-
 .../src/cta/Deposit/views.tsx                      |  18 +---
 .../src/cta/InvoiceCreate/index.ts                 |  14 +--
 .../src/cta/InvoiceCreate/state.ts                 |  37 +++++--
 .../src/cta/InvoiceCreate/views.tsx                |  44 +-------
 .../src/cta/InvoicePay/index.ts                    |  11 +-
 .../src/cta/InvoicePay/state.ts                    |  16 ++-
 .../src/cta/InvoicePay/views.tsx                   |  37 +------
 .../src/cta/Payment/index.ts                       |  11 +-
 .../src/cta/Payment/state.ts                       |  16 ++-
 .../src/cta/Payment/test.ts                        |   6 +-
 .../src/cta/Payment/views.tsx                      |  34 ++----
 .../src/cta/Recovery/index.ts                      |  11 +-
 .../src/cta/Recovery/state.ts                      |  23 ++--
 .../src/cta/Recovery/views.tsx                     |  16 ---
 .../src/cta/Refund/index.ts                        |  16 ++-
 .../src/cta/Refund/state.ts                        |  16 ++-
 .../src/cta/Refund/test.ts                         |   8 +-
 .../src/cta/Refund/views.tsx                       |  29 ++---
 .../taler-wallet-webextension/src/cta/Tip/index.ts |  16 ++-
 .../taler-wallet-webextension/src/cta/Tip/state.ts |  16 ++-
 .../taler-wallet-webextension/src/cta/Tip/test.ts  |   9 +-
 .../src/cta/Tip/views.tsx                          |  28 ++---
 .../src/cta/TransferCreate/index.ts                |  11 +-
 .../src/cta/TransferCreate/state.ts                |  16 ++-
 .../src/cta/TransferCreate/views.tsx               |  32 +-----
 .../src/cta/TransferPickup/index.ts                |  11 +-
 .../src/cta/TransferPickup/state.ts                |  16 ++-
 .../src/cta/TransferPickup/views.tsx               |  35 +-----
 .../src/cta/Withdraw/index.ts                      |  16 ++-
 .../src/cta/Withdraw/state.ts                      |  26 +++--
 .../src/cta/Withdraw/test.ts                       |   8 +-
 .../src/cta/Withdraw/views.tsx                     |  53 +--------
 .../src/hooks/useAsyncAsHook.ts                    |  18 +++-
 .../taler-wallet-webextension/src/hooks/useLang.ts |   1 +
 .../src/hooks/useLocalStorage.ts                   |   5 +-
 .../src/mui/Alert.stories.tsx                      |  33 ++++--
 .../taler-wallet-webextension/src/mui/Alert.tsx    |  10 +-
 .../taler-wallet-webextension/src/mui/Paper.tsx    |   3 -
 .../src/mui/colors/manipulation.ts                 |   2 +-
 .../taler-wallet-webextension/src/platform/api.ts  |   8 +-
 .../src/platform/chrome.ts                         |  60 +++++------
 .../src/platform/firefox.ts                        |   7 +-
 .../src/popup/BalancePage.tsx                      |  24 ++---
 .../src/popup/NoBalanceHelp.tsx                    |   2 +-
 .../taler-wallet-webextension/src/svg/progress.svg |  12 +++
 .../taler-wallet-webextension/src/test-utils.ts    |   5 +-
 .../src/wallet/AddBackupProvider/index.ts          |  15 ++-
 .../src/wallet/AddBackupProvider/views.tsx         |  16 +--
 .../src/wallet/Application.tsx                     |  45 ++++++--
 .../src/wallet/BackupPage.tsx                      |  19 ++--
 .../src/wallet/DepositPage/index.ts                |  10 +-
 .../src/wallet/DepositPage/state.ts                |   7 +-
 .../src/wallet/DepositPage/views.tsx               |  43 ++------
 .../src/wallet/DestinationSelection/index.ts       |  11 +-
 .../src/wallet/DestinationSelection/state.ts       |   8 +-
 .../src/wallet/DestinationSelection/views.tsx      |  29 ++---
 .../src/wallet/EmptyComponentExample/index.ts      |  11 +-
 .../src/wallet/EmptyComponentExample/views.tsx     |  12 ---
 .../src/wallet/ExchangeSelection/index.ts          |   9 +-
 .../src/wallet/ExchangeSelection/state.ts          |  10 +-
 .../src/wallet/ExchangeSelection/views.tsx         |  24 +----
 .../src/wallet/ExchangeSetUrl.tsx                  |   6 +-
 .../src/wallet/History.tsx                         |  15 ++-
 .../src/wallet/ManageAccount/index.ts              |  11 +-
 .../src/wallet/ManageAccount/state.ts              |   7 +-
 .../src/wallet/ManageAccount/views.tsx             |  21 +---
 .../src/wallet/Notifications/index.ts              |  11 +-
 .../src/wallet/Notifications/state.ts              |  11 +-
 .../src/wallet/Notifications/views.tsx             |  12 ---
 .../src/wallet/ProviderAddPage.tsx                 |  10 +-
 .../src/wallet/ProviderDetailPage.tsx              |  45 +++-----
 .../src/wallet/ReserveCreated.tsx                  |   4 +-
 .../src/wallet/Settings.tsx                        |  32 ++----
 .../src/wallet/Transaction.tsx                     | 119 ++++++++++-----------
 .../src/wallet/Welcome.tsx                         |   6 +-
 packages/taler-wallet-webextension/src/wxApi.ts    |  44 ++++++--
 .../taler-wallet-webextension/src/wxBackend.ts     |  31 ++++--
 pnpm-lock.yaml                                     |  21 ++--
 110 files changed, 1100 insertions(+), 1054 deletions(-)
 create mode 100644 
packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
 delete mode 100644 
packages/taler-wallet-webextension/src/components/LoadingError.tsx
 create mode 100644 packages/taler-wallet-webextension/src/context/alert.ts
 create mode 100644 packages/taler-wallet-webextension/src/svg/progress.svg

diff --git 
a/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts 
b/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts
index b63c9bf11..df2e645a7 100644
--- a/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts
@@ -16,11 +16,13 @@
 
 import test from "ava";
 import { CryptoDispatcher, CryptoWorkerFactory } from "./crypto-dispatcher.js";
-import { CryptoWorker, CryptoWorkerResponseMessage } from 
"./cryptoWorkerInterface.js";
+import {
+  CryptoWorker,
+  CryptoWorkerResponseMessage,
+} from "./cryptoWorkerInterface.js";
 import { SynchronousCryptoWorkerFactoryNode } from 
"./synchronousWorkerFactoryNode.js";
 import { processRequestWithImpl } from "./worker-common.js";
 
-
 export class MyCryptoWorker implements CryptoWorker {
   /**
    * Function to be called when we receive a message from the worker thread.
@@ -64,8 +66,8 @@ export class MyCryptoWorker implements CryptoWorker {
           type: "success",
           result: {
             testResult: 42,
-          }
-        }
+          },
+        };
       } else if (msg.operation === "testError") {
         responseMsg = {
           id: msg.id,
@@ -73,8 +75,8 @@ export class MyCryptoWorker implements CryptoWorker {
           error: {
             code: 42,
             hint: "bla",
-          }
-        }
+          },
+        };
       } else if (msg.operation === "testTimeout") {
         // Don't respond
         return;
@@ -98,8 +100,6 @@ export class MyCryptoWorker implements CryptoWorker {
   }
 }
 
-
-
 export class MyCryptoWorkerFactory implements CryptoWorkerFactory {
   startWorker(): CryptoWorker {
     return new MyCryptoWorker();
@@ -111,12 +111,10 @@ export class MyCryptoWorkerFactory implements 
CryptoWorkerFactory {
 }
 
 test("continues after error", async (t) => {
-  const cryptoDisp = new CryptoDispatcher(
-    new MyCryptoWorkerFactory(),
-  );
+  const cryptoDisp = new CryptoDispatcher(new MyCryptoWorkerFactory());
   const resp1 = await cryptoDisp.doRpc("testSuccess", 0, {});
   t.assert((resp1 as any).testResult === 42);
-  const exc = await t.throwsAsync(async() => {
+  const exc = await t.throwsAsync(async () => {
     const resp2 = await cryptoDisp.doRpc("testError", 0, {});
   });
 
diff --git a/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.ts 
b/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.ts
index 940078ea6..f086691e5 100644
--- a/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.ts
@@ -308,11 +308,7 @@ export class CryptoDispatcher {
     }
   }
 
-  doRpc<T>(
-    operation: string,
-    priority: number,
-    req: unknown,
-  ): Promise<T> {
+  doRpc<T>(operation: string, priority: number, req: unknown): Promise<T> {
     if (this.stopped) {
       throw new CryptoApiStoppedError();
     }
@@ -364,7 +360,9 @@ export class CryptoDispatcher {
       const onTimeout = () => {
         // FIXME: Maybe destroy and re-init worker if request is in processing
         // state and really taking too long?
-        logger.warn(`crypto RPC call ('${operation}') has been queued for a 
long time`);
+        logger.warn(
+          `crypto RPC call ('${operation}') has been queued for a long time`,
+        );
         timeoutHandle = timer.after(timeoutMs, onTimeout);
       };
       myProm.promise
diff --git a/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts 
b/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts
index db8bb4737..d9b4399d6 100644
--- a/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/nodeThreadWorker.ts
@@ -27,7 +27,9 @@ import { processRequestWithImpl } from "./worker-common.js";
 
 const logger = new Logger("nodeThreadWorker.ts");
 
-const f = import.meta.url ? url.fileURLToPath(import.meta.url) : 
'__not_available__';
+const f = import.meta.url
+  ? url.fileURLToPath(import.meta.url)
+  : "__not_available__";
 
 const workerCode = `
   // Try loading the glue library for embedded
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 299c7a36c..04fee9495 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -839,8 +839,8 @@ export enum RefreshOperationStatus {
  * Group of refresh operations.  The refreshed coins do not
  * have to belong to the same exchange, but must have the same
  * currency.
- * 
- * FIXME: Should include the currency as a top-level field, 
+ *
+ * FIXME: Should include the currency as a top-level field,
  *        but we need to write a migration for that.
  */
 export interface RefreshGroupRecord {
@@ -857,7 +857,7 @@ export interface RefreshGroupRecord {
 
   /**
    * Currency of this refresh group.
-   * 
+   *
    * FIXME: Write a migration to add this to earlier DB versions.
    */
   currency: string;
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts 
b/packages/taler-wallet-core/src/operations/backup/import.ts
index 805b0c6d3..c7afaea8e 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -778,7 +778,9 @@ export async function importBackup(
             timestampFinished: backupRefreshGroup.timestamp_finish,
             timestampCreated: backupRefreshGroup.timestamp_created,
             refreshGroupId: backupRefreshGroup.refresh_group_id,
-            currency: 
Amounts.currencyOf(backupRefreshGroup.old_coins[0].input_amount),
+            currency: Amounts.currencyOf(
+              backupRefreshGroup.old_coins[0].input_amount,
+            ),
             reason,
             lastErrorPerCoin: {},
             oldCoinPubs: backupRefreshGroup.old_coins.map((x) => x.coin_pub),
diff --git a/packages/taler-wallet-core/src/operations/common.ts 
b/packages/taler-wallet-core/src/operations/common.ts
index cb22105e1..96009eb0b 100644
--- a/packages/taler-wallet-core/src/operations/common.ts
+++ b/packages/taler-wallet-core/src/operations/common.ts
@@ -175,7 +175,7 @@ export async function spendCoins(
     await tx.coins.put(coin);
     await tx.coinAvailability.put(coinAvailability);
   }
-  
+
   await ws.refreshOps.createRefreshGroup(
     ws,
     tx,
diff --git a/packages/taler-wallet-webextension/package.json 
b/packages/taler-wallet-webextension/package.json
index 8a6c19406..87fb27a72 100644
--- a/packages/taler-wallet-webextension/package.json
+++ b/packages/taler-wallet-webextension/package.json
@@ -69,7 +69,7 @@
     "preact-cli": "^3.3.5",
     "preact-render-to-string": "^5.1.19",
     "rimraf": "^3.0.2",
-    "typescript": "^4.8.4"
+    "typescript": "4.9.4"
   },
   "nyc": {
     "include": [
diff --git 
a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx 
b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx
index 9a1d96014..61c4a7661 100644
--- a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx
+++ b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx
@@ -49,7 +49,7 @@ function RenderAmount(): VNode {
     <Fragment>
       <AmountField
         required
-        label={<i18n.Translate>Amount</i18n.Translate>}
+        label={i18n.str`Amount`}
         highestDenom={2000000}
         lowestDenom={0.01}
         handler={handler}
diff --git a/packages/taler-wallet-webextension/src/components/AmountField.tsx 
b/packages/taler-wallet-webextension/src/components/AmountField.tsx
index 2e8942f0d..4936e0604 100644
--- a/packages/taler-wallet-webextension/src/components/AmountField.tsx
+++ b/packages/taler-wallet-webextension/src/components/AmountField.tsx
@@ -21,6 +21,7 @@ import {
   amountMaxValue,
   Amounts,
   Result,
+  TranslatedString,
 } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -37,7 +38,7 @@ export function AmountField({
   highestDenom = 1,
   required,
 }: {
-  label: VNode;
+  label: TranslatedString;
   lowestDenom?: number;
   highestDenom?: number;
   required?: boolean;
diff --git 
a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx 
b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx
index 6f4980aff..d233547a4 100644
--- 
a/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/BankDetailsByPaytoType.tsx
@@ -19,6 +19,7 @@ import {
   Amounts,
   PaytoUri,
   segwitMinAmount,
+  TranslatedString,
 } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
@@ -106,27 +107,18 @@ export function BankDetailsByPaytoType({
   }
 
   const accountPart = !payto.isKnown ? (
-    <Row
-      name={<i18n.Translate>Account</i18n.Translate>}
-      value={payto.targetPath}
-    />
+    <Row name={i18n.str`Account`} value={payto.targetPath} />
   ) : payto.targetType === "x-taler-bank" ? (
     <Fragment>
-      <Row
-        name={<i18n.Translate>Bank host</i18n.Translate>}
-        value={payto.host}
-      />
-      <Row
-        name={<i18n.Translate>Bank account</i18n.Translate>}
-        value={payto.account}
-      />
+      <Row name={i18n.str`Bank host`} value={payto.host} />
+      <Row name={i18n.str`Bank account`} value={payto.account} />
     </Fragment>
   ) : payto.targetType === "iban" ? (
     <Fragment>
       {payto.bic !== undefined ? (
-        <Row name={<i18n.Translate>BIC</i18n.Translate>} value={payto.bic} />
+        <Row name={i18n.str`BIC`} value={payto.bic} />
       ) : undefined}
-      <Row name={<i18n.Translate>IBAN</i18n.Translate>} value={payto.iban} />
+      <Row name={i18n.str`IBAN`} value={payto.iban} />
     </Fragment>
   ) : undefined;
 
@@ -146,19 +138,12 @@ export function BankDetailsByPaytoType({
       <table>
         {accountPart}
         <Row
-          name={<i18n.Translate>Amount</i18n.Translate>}
+          name={i18n.str`Amount`}
           value={<Amount value={amount} hideCurrency />}
         />
-        <Row
-          name={<i18n.Translate>Subject</i18n.Translate>}
-          value={subject}
-          literal
-        />
+        <Row name={i18n.str`Subject`} value={subject} literal />
         {receiver ? (
-          <Row
-            name={<i18n.Translate>Receiver name</i18n.Translate>}
-            value={receiver}
-          />
+          <Row name={i18n.str`Receiver name`} value={receiver} />
         ) : undefined}
       </table>
     </div>
@@ -200,7 +185,7 @@ function Row({
   value,
   literal,
 }: {
-  name: VNode;
+  name: TranslatedString;
   value: string | VNode;
   literal?: boolean;
 }): VNode {
diff --git a/packages/taler-wallet-webextension/src/components/Banner.tsx 
b/packages/taler-wallet-webextension/src/components/Banner.tsx
index a91fd384f..40a4847b8 100644
--- a/packages/taler-wallet-webextension/src/components/Banner.tsx
+++ b/packages/taler-wallet-webextension/src/components/Banner.tsx
@@ -13,6 +13,7 @@
  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 { TranslatedString } from "@gnu-taler/taler-util";
 import { ComponentChildren, Fragment, h, JSX, VNode } from "preact";
 import { Button } from "../mui/Button.js";
 import { Divider } from "../mui/Divider.js";
@@ -20,7 +21,7 @@ import { Grid } from "../mui/Grid.js";
 import { Paper } from "../mui/Paper.js";
 
 interface Props extends JSX.HTMLAttributes<HTMLDivElement> {
-  titleHead?: VNode;
+  titleHead?: VNode | TranslatedString;
   children: ComponentChildren;
   // elements: {
   //   icon?: VNode;
diff --git a/packages/taler-wallet-webextension/src/components/Checkbox.tsx 
b/packages/taler-wallet-webextension/src/components/Checkbox.tsx
index b6fa8b663..70dfab597 100644
--- a/packages/taler-wallet-webextension/src/components/Checkbox.tsx
+++ b/packages/taler-wallet-webextension/src/components/Checkbox.tsx
@@ -14,14 +14,15 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { TranslatedString } from "@gnu-taler/taler-util";
 import { h, VNode } from "preact";
 
 interface Props {
   enabled?: boolean;
   onToggle?: () => Promise<void>;
-  label: VNode;
+  label: TranslatedString;
   name: string;
-  description?: VNode;
+  description?: VNode | TranslatedString;
 }
 export function Checkbox({
   name,
diff --git 
a/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx 
b/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
new file mode 100644
index 000000000..a56c82dee
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
@@ -0,0 +1,108 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { ComponentChildren, Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { useTranslationContext } from "../../../web-util/src/index.browser.js";
+import {
+  ErrorAlert,
+  Alert as AlertNotification,
+  useAlertContext,
+} from "../context/alert.js";
+import { Alert } from "../mui/Alert.js";
+
+/**
+ *
+ * @author sebasjm
+ */
+
+function AlertContext({
+  context,
+  cause,
+}: {
+  cause: unknown;
+  context: undefined | object;
+}): VNode {
+  const [more, setMore] = useState(false);
+  const { i18n } = useTranslationContext();
+  if (!more) {
+    return (
+      <div style={{ display: "flex", justifyContent: "right" }}>
+        <a onClick={() => setMore(true)}>
+          <i18n.Translate>more info</i18n.Translate>
+        </a>
+      </div>
+    );
+  }
+  return (
+    <pre style={{ overflow: "overlay" }}>
+      {JSON.stringify(
+        context === undefined ? { cause } : { context, cause },
+        undefined,
+        2,
+      )}
+    </pre>
+  );
+}
+
+export function ErrorAlertView({
+  error: alert,
+  onClose,
+}: {
+  error: ErrorAlert;
+  onClose?: () => Promise<void>;
+}): VNode {
+  return (
+    <Alert title={alert.message} severity={alert.type} onClose={onClose}>
+      <div style={{ display: "flex", flexDirection: "column" }}>
+        <div>{alert.description}</div>
+        <AlertContext context={alert.context} cause={alert.cause} />
+      </div>
+    </Alert>
+  );
+}
+
+export function AlertView({
+  alert,
+  onClose,
+}: {
+  alert: AlertNotification;
+  onClose?: () => Promise<void>;
+}): VNode {
+  return (
+    <Alert title={alert.message} severity={alert.type} onClose={onClose}>
+      <div style={{ display: "flex", flexDirection: "column" }}>
+        <div>{alert.description}</div>
+      </div>
+    </Alert>
+  );
+}
+
+export function CurrentAlerts(): VNode {
+  const { alerts, removeAlert } = useAlertContext();
+  if (alerts.length === 0) return <Fragment />;
+  return (
+    <Wrapper>
+      {alerts.map((n, i) => (
+        <AlertView key={i} alert={n} onClose={async () => removeAlert(n)} />
+      ))}
+    </Wrapper>
+  );
+}
+
+function Wrapper({ children }: { children: ComponentChildren }): VNode {
+  return <div style={{ margin: "2em" }}>{children}</div>;
+}
diff --git a/packages/taler-wallet-webextension/src/components/ErrorMessage.tsx 
b/packages/taler-wallet-webextension/src/components/ErrorMessage.tsx
index ce8dc0ad1..11f526865 100644
--- a/packages/taler-wallet-webextension/src/components/ErrorMessage.tsx
+++ b/packages/taler-wallet-webextension/src/components/ErrorMessage.tsx
@@ -13,6 +13,7 @@
  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 { TranslatedString } from "@gnu-taler/taler-util";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import arrowDown from "../svg/chevron-down.svg";
@@ -22,7 +23,7 @@ export function ErrorMessage({
   title,
   description,
 }: {
-  title: VNode;
+  title: TranslatedString;
   description?: string | VNode;
 }): VNode | null {
   const [showErrorDetail, setShowErrorDetail] = useState(false);
diff --git 
a/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx 
b/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx
index a7223d2db..f8203f38a 100644
--- a/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx
+++ b/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx
@@ -13,7 +13,7 @@
  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 { TalerErrorDetail } from "@gnu-taler/taler-util";
+import { TalerErrorDetail, TranslatedString } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import arrowDown from "../svg/chevron-down.svg";
@@ -24,7 +24,7 @@ export function ErrorTalerOperation({
   title,
   error,
 }: {
-  title?: VNode;
+  title?: TranslatedString;
   error?: TalerErrorDetail;
 }): VNode | null {
   const { devMode } = useDevContext();
diff --git a/packages/taler-wallet-webextension/src/components/Loading.tsx 
b/packages/taler-wallet-webextension/src/components/Loading.tsx
index f2195b646..3a6daaaa6 100644
--- a/packages/taler-wallet-webextension/src/components/Loading.tsx
+++ b/packages/taler-wallet-webextension/src/components/Loading.tsx
@@ -13,30 +13,88 @@
  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 { css } from "@linaria/core";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { useTranslationContext } from "../context/translation.js";
+import ProgressIcon from "../svg/progress.svg";
 import { CenteredText } from "./styled/index.js";
 
+const fadeIn = css`
+  & {
+    animation: fadein 3s;
+  }
+  @keyframes fadein {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+`;
+
 export function Loading(): VNode {
   const { i18n } = useTranslationContext();
-  const [tooLong, setTooLong] = useState(false);
-  useEffect(() => {
-    const id = setTimeout(() => {
-      setTooLong(true);
-    }, 500);
-    return () => {
-      clearTimeout(id);
-    };
-  });
-  if (tooLong) {
-    return (
-      <section style={{ margin: "auto" }}>
-        <CenteredText>
-          <i18n.Translate>Loading</i18n.Translate>...
-        </CenteredText>
-      </section>
-    );
-  }
-  return <Fragment />;
+  return (
+    <section style={{ margin: "auto" }}>
+      <CenteredText class={fadeIn}>
+        <i18n.Translate>Loading</i18n.Translate>...
+      </CenteredText>
+      {/* <div class={ripple} style={{ "--size": "250px" }}>
+        <div></div>
+        <div></div>
+      </div> */}
+      <div class={fadeIn} dangerouslySetInnerHTML={{ __html: ProgressIcon }} />
+    </section>
+  );
 }
+
+const ripple = css`
+  & {
+    display: inline-block;
+    position: relative;
+    width: var(--size);
+    height: var(--size);
+  }
+  & div {
+    position: absolute;
+    border: 4px solid black;
+    opacity: 1;
+    border-radius: 50%;
+    animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
+  }
+  & div:nth-child(2) {
+    animation-delay: -0.3s;
+  }
+  @keyframes lds-ripple {
+    0% {
+      top: calc(var(--size) / 2);
+      left: calc(var(--size) / 2);
+      width: 0;
+      height: 0;
+      opacity: 0;
+    }
+    14.9% {
+      top: calc(var(--size) / 2);
+      left: calc(var(--size) / 2);
+      width: 0;
+      height: 0;
+      opacity: 0;
+    }
+    15% {
+      top: calc(var(--size) / 2);
+      left: calc(var(--size) / 2);
+      width: 0;
+      height: 0;
+      opacity: 1;
+    }
+    100% {
+      top: 0px;
+      left: 0px;
+      width: var(--size);
+      height: var(--size);
+      opacity: 0;
+    }
+  }
+`;
diff --git a/packages/taler-wallet-webextension/src/components/LoadingError.tsx 
b/packages/taler-wallet-webextension/src/components/LoadingError.tsx
deleted file mode 100644
index 2cd8bee3b..000000000
--- a/packages/taler-wallet-webextension/src/components/LoadingError.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-import { h, VNode } from "preact";
-import { HookError } from "../hooks/useAsyncAsHook.js";
-import { ErrorMessage } from "./ErrorMessage.js";
-import { ErrorTalerOperation } from "./ErrorTalerOperation.js";
-
-export interface Props {
-  title: VNode;
-  error: HookError;
-}
-export function LoadingError({ title, error }: Props): VNode {
-  if (error.operational) {
-    return <ErrorTalerOperation title={title} error={error.details} />;
-  }
-  return <ErrorMessage title={title} description={error.message} />;
-}
diff --git 
a/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx 
b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
index 673ff3dc2..d6a730a4f 100644
--- a/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
+++ b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
@@ -13,7 +13,7 @@
  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 { getUnpackedSettings } from "http2";
+import { TranslatedString } from "@gnu-taler/taler-util";
 import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Button } from "../mui/Button.js";
@@ -21,7 +21,7 @@ import arrowDown from "../svg/chevron-down.svg";
 import { ParagraphClickable } from "./styled/index.js";
 
 export interface Props {
-  label: (s: string) => VNode;
+  label: (s: string) => TranslatedString;
   actions: string[];
   onClick: (s: string) => Promise<void>;
 }
diff --git a/packages/taler-wallet-webextension/src/components/Part.tsx 
b/packages/taler-wallet-webextension/src/components/Part.tsx
index a488ca4dc..1449bcac6 100644
--- a/packages/taler-wallet-webextension/src/components/Part.tsx
+++ b/packages/taler-wallet-webextension/src/components/Part.tsx
@@ -14,7 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { PaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
+import {
+  PaytoUri,
+  stringifyPaytoUri,
+  TranslatedString,
+} from "@gnu-taler/taler-util";
 import { styled } from "@linaria/react";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -27,8 +31,8 @@ import {
 
 export type Kind = "positive" | "negative" | "neutral";
 interface Props {
-  title: VNode | string;
-  text: VNode | string;
+  title: VNode | TranslatedString;
+  text: VNode | TranslatedString;
   kind?: Kind;
   big?: boolean;
   showSign?: boolean;
diff --git 
a/packages/taler-wallet-webextension/src/components/PaymentButtons.tsx 
b/packages/taler-wallet-webextension/src/components/PaymentButtons.tsx
index def1e16eb..30c2ef833 100644
--- a/packages/taler-wallet-webextension/src/components/PaymentButtons.tsx
+++ b/packages/taler-wallet-webextension/src/components/PaymentButtons.tsx
@@ -19,6 +19,7 @@ import {
   Amounts,
   PreparePayResult,
   PreparePayResultType,
+  TranslatedString,
 } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
@@ -109,8 +110,10 @@ export function PaymentButtons({
         <section>
           {payStatus.paid && payStatus.contractTerms.fulfillment_message && (
             <Part
-              title={<i18n.Translate>Merchant message</i18n.Translate>}
-              text={payStatus.contractTerms.fulfillment_message}
+              title={i18n.str`Merchant message`}
+              text={
+                payStatus.contractTerms.fulfillment_message as TranslatedString
+              }
               kind="neutral"
             />
           )}
@@ -131,11 +134,7 @@ function PayWithMobile({ uri }: { uri: string }): VNode {
   return (
     <section>
       <LinkSuccess upperCased onClick={() => setShowQR((qr) => !qr)}>
-        {!showQR ? (
-          <i18n.Translate>Pay with a mobile phone</i18n.Translate>
-        ) : (
-          <i18n.Translate>Hide QR</i18n.Translate>
-        )}
+        {!showQR ? i18n.str`Pay with a mobile phone` : i18n.str`Hide QR`}
       </LinkSuccess>
       {showQR && (
         <div>
diff --git 
a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx 
b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
index e41ff2836..2bba86dba 100644
--- a/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
+++ b/packages/taler-wallet-webextension/src/components/PendingTransactions.tsx
@@ -87,7 +87,7 @@ export function PendingTransactionsView({
       }}
     >
       <Banner
-        titleHead={<i18n.Translate>PENDING OPERATIONS</i18n.Translate>}
+        titleHead={i18n.str`PENDING OPERATIONS`}
         style={{
           backgroundColor: "lightcyan",
           maxHeight: 150,
diff --git a/packages/taler-wallet-webextension/src/components/SelectList.tsx 
b/packages/taler-wallet-webextension/src/components/SelectList.tsx
index 3ceac752e..809698711 100644
--- a/packages/taler-wallet-webextension/src/components/SelectList.tsx
+++ b/packages/taler-wallet-webextension/src/components/SelectList.tsx
@@ -14,6 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { TranslatedString } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { useTranslationContext } from "../context/translation.js";
 import { NiceSelect } from "./styled/index.js";
@@ -21,7 +22,7 @@ import { NiceSelect } from "./styled/index.js";
 interface Props {
   value?: string;
   onChange?: (s: string) => void;
-  label: VNode;
+  label: VNode | TranslatedString;
   list: {
     [label: string]: string;
   };
diff --git 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
index 841583113..8c94e6e60 100644
--- 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
@@ -94,7 +94,10 @@ export const Error = createExample(ErrorView, {
   error: {
     hasError: true,
     message: "message",
-    operational: false,
+    // details: {
+    //   co
+    // },
+    type: "error",
     // details: {
     //   code: 123,
     // },
diff --git 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
index 47c10347c..9871611f2 100644
--- 
a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
+++ 
b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
@@ -22,15 +22,16 @@ import { styled } from "@linaria/react";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Loading } from "../components/Loading.js";
-import { LoadingError } from "../components/LoadingError.js";
 import { Modal } from "../components/Modal.js";
 import { Time } from "../components/Time.js";
+import { alertFromError } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../mui/handlers.js";
 import { compose, StateViewMap } from "../utils/index.js";
 import { Amount } from "./Amount.js";
+import { AlertView } from "./CurrentAlerts.js";
 import { Link } from "./styled/index.js";
 
 const ContractTermsTable = styled.table`
@@ -160,13 +161,12 @@ export function ErrorView({
   const { i18n } = useTranslationContext();
   return (
     <Modal title="Full detail" onClose={hideHandler}>
-      <LoadingError
-        title={
-          <i18n.Translate>
-            Could not load purchase proposal details
-          </i18n.Translate>
-        }
-        error={error}
+      <AlertView
+        alert={alertFromError(
+          i18n.str`Could not load purchase proposal details`,
+          error,
+          { proposalId },
+        )}
       />
     </Modal>
   );
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts 
b/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
index d7716f208..a8c1558d8 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
@@ -15,14 +15,13 @@
  */
 
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ToggleHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
+import { ErrorAlertView } from "../CurrentAlerts.js";
 import { useComponentState } from "./state.js";
 import { TermsState } from "./utils.js";
 import {
-  ErrorAcceptingView,
-  LoadingUriView,
   ShowButtonsAcceptedTosView,
   ShowButtonsNonAcceptedTosView,
   ShowTosContentView,
@@ -35,8 +34,7 @@ export interface Props {
 
 export type State =
   | State.Loading
-  | State.LoadingUriError
-  | State.ErrorAccepting
+  | State.Error
   | State.ShowButtonsAccepted
   | State.ShowButtonsNotAccepted
   | State.ShowContent;
@@ -47,14 +45,9 @@ export namespace State {
     error: undefined;
   }
 
-  export interface LoadingUriError {
-    status: "loading-error";
-    error: HookError;
-  }
-
-  export interface ErrorAccepting {
-    status: "error-accepting";
-    error: HookError;
+  export interface Error {
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -79,11 +72,10 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-error": LoadingUriView,
+  error: ErrorAlertView,
   "show-content": ShowTosContentView,
   "show-buttons-accepted": ShowButtonsAcceptedTosView,
   "show-buttons-not-accepted": ShowButtonsNonAcceptedTosView,
-  "error-accepting": ErrorAcceptingView,
 };
 
 export const TermsOfService = compose(
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts 
b/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
index 3b75965d3..c25c0ed13 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
@@ -16,7 +16,9 @@
 
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { alertFromError, useAlertContext } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 import { buildTermsOfServiceState } from "./utils.js";
@@ -25,9 +27,8 @@ export function useComponentState({ exchangeUrl, onChange }: 
Props): State {
   const api = useBackendContext();
   const readOnly = !onChange;
   const [showContent, setShowContent] = useState<boolean>(readOnly);
-  const [errorAccepting, setErrorAccepting] = useState<Error | undefined>(
-    undefined,
-  );
+  const { i18n } = useTranslationContext();
+  const { pushAlert } = useAlertContext();
 
   /**
    * For the exchange selected, bring the status of the terms of service
@@ -54,22 +55,13 @@ export function useComponentState({ exchangeUrl, onChange 
}: Props): State {
   }
   if (terms.hasError) {
     return {
-      status: "loading-error",
-      error: terms,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        terms,
+      ),
     };
   }
-
-  if (errorAccepting) {
-    return {
-      status: "error-accepting",
-      error: {
-        hasError: true,
-        operational: false,
-        message: errorAccepting.message,
-      },
-    };
-  }
-
   const { state } = terms.response;
 
   async function onUpdate(accepted: boolean): Promise<void> {
@@ -77,13 +69,13 @@ export function useComponentState({ exchangeUrl, onChange 
}: Props): State {
 
     try {
       if (accepted) {
-        api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
+        await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
           exchangeBaseUrl: exchangeUrl,
           etag: state.version,
         });
       } else {
         // mark as not accepted
-        api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
+        await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
           exchangeBaseUrl: exchangeUrl,
           etag: undefined,
         });
@@ -91,11 +83,7 @@ export function useComponentState({ exchangeUrl, onChange }: 
Props): State {
       // setAccepted(accepted);
       if (!readOnly) onChange(accepted); //external update
     } catch (e) {
-      if (e instanceof Error) {
-        //FIXME: uncomment this and display error
-        // setErrorAccepting(e.message);
-        setErrorAccepting(e);
-      }
+      pushAlert(alertFromError(i18n.str`Could not accept terms of service`, 
e));
     }
   }
 
diff --git 
a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx 
b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
index a7e03fd01..0b5a71b3e 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
@@ -14,49 +14,23 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { ExchangeTosStatus } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
-import { LoadingError } from "../../components/LoadingError.js";
-import { useTranslationContext } from "../../context/translation.js";
-import { TermsDocument, TermsState } from "./utils.js";
-import { State } from "./index.js";
 import { CheckboxOutlined } from "../../components/CheckboxOutlined.js";
+import { ExchangeXmlTos } from "../../components/ExchangeToS.js";
 import {
   LinkSuccess,
   TermsOfService,
   WarningBox,
   WarningText,
 } from "../../components/styled/index.js";
-import { ExchangeXmlTos } from "../../components/ExchangeToS.js";
-import { ToggleHandler } from "../../mui/handlers.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
-import { ExchangeTosStatus } from "@gnu-taler/taler-util";
-
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
-export function ErrorAcceptingView({ error }: State.ErrorAccepting): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
+import { State } from "./index.js";
 
 export function ShowButtonsAcceptedTosView({
   termsAccepted,
   showingTermsOfService,
-  terms,
 }: State.ShowButtonsAccepted): VNode {
   const { i18n } = useTranslationContext();
   const ableToReviewTermsOfService =
diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx 
b/packages/taler-wallet-webextension/src/components/styled/index.tsx
index 8e98f75eb..e36502333 100644
--- a/packages/taler-wallet-webextension/src/components/styled/index.tsx
+++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx
@@ -535,7 +535,7 @@ export const LinkDestructive = styled(Link)`
 `;
 
 export const LinkPrimary = styled(Link)`
-  color: rgb(66, 184, 221);
+  color: black;
 `;
 
 export const ButtonPrimary = styled(ButtonVariant)<{ small?: boolean }>`
diff --git a/packages/taler-wallet-webextension/src/context/alert.ts 
b/packages/taler-wallet-webextension/src/context/alert.ts
new file mode 100644
index 000000000..cc98ec1e0
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/context/alert.ts
@@ -0,0 +1,118 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 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 { TranslatedString } from "@gnu-taler/taler-util";
+import { ComponentChildren, createContext, h, VNode } from "preact";
+import { useContext, useState } from "preact/hooks";
+
+export type AlertType = "info" | "warning" | "error" | "success";
+
+export interface Alert {
+  message: TranslatedString;
+  description: TranslatedString | VNode;
+  type: AlertType;
+}
+
+export interface ErrorAlert extends Alert {
+  type: "error";
+  context: object;
+  cause: any;
+}
+
+type Type = {
+  alerts: Alert[];
+  pushAlert: (n: Alert) => void;
+  removeAlert: (n: Alert) => void;
+};
+
+const initial: Type = {
+  alerts: [],
+  pushAlert: () => {
+    null;
+  },
+  removeAlert: () => {
+    null;
+  },
+};
+
+const Context = createContext<Type>(initial);
+
+type AlertWithDate = Alert & { since: Date };
+
+type Props = Partial<Type> & {
+  children: ComponentChildren;
+};
+
+export const AlertProvider = ({ children }: Props): VNode => {
+  const timeout = 3000;
+
+  const [alerts, setAlerts] = useState<AlertWithDate[]>([]);
+
+  const pushAlert = (n: Alert): void => {
+    const entry = { ...n, since: new Date() };
+    setAlerts((ns) => [...ns, entry]);
+    if (n.type !== "error") {
+      setTimeout(() => {
+        setAlerts((ns) => ns.filter((x) => x.since !== entry.since));
+      }, timeout);
+    }
+  };
+
+  const removeAlert = (alert: Alert): void => {
+    setAlerts((ns: AlertWithDate[]) => ns.filter((n) => n !== alert));
+  };
+
+  return h(Context.Provider, {
+    value: { alerts, pushAlert, removeAlert },
+    children,
+  });
+};
+
+export const useAlertContext = (): Type => useContext(Context);
+
+export function alertFromError(
+  message: TranslatedString,
+  error: unknown,
+  ...context: any[]
+): ErrorAlert {
+  let description = "" as TranslatedString;
+
+  const isObject = typeof error === "object" &&
+    error !== null;
+  const hasMessage =
+    isObject &&
+    "message" in error &&
+    typeof error.message === "string";
+
+  if (hasMessage) {
+    description = error.message as TranslatedString;
+  } else {
+    description = `Unknown error: ${String(error)}` as TranslatedString;
+  }
+
+  return {
+    type: "error",
+    message,
+    description,
+    cause: error,
+    context,
+  };
+}
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/index.ts 
b/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
index 9ff3ddd1d..6b228188b 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/index.ts
@@ -15,12 +15,13 @@
  */
 
 import { AmountJson, AmountString } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export interface Props {
   talerDepositUri: string | undefined;
@@ -37,8 +38,8 @@ export namespace State {
     error: undefined;
   }
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
   export interface Ready {
     status: "ready";
@@ -57,7 +58,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   ready: ReadyView,
 };
 
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts 
b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
index dba435611..4cee7cfd0 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts
@@ -16,7 +16,9 @@
 
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
@@ -38,12 +40,16 @@ export function useComponentState({
     });
     return { deposit, uri: talerDepositUri, amount };
   });
+  const { i18n } = useTranslationContext();
 
   if (!info) return { status: "loading", error: undefined };
   if (info.hasError) {
     return {
-      status: "loading-uri",
-      error: info,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        info,
+      ),
     };
   }
 
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts 
b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
index 6a896fb7f..031dcffaa 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts
@@ -50,12 +50,12 @@ describe("Deposit CTA states", () => {
           expect(status).equals("loading");
         },
         ({ status, error }) => {
-          expect(status).equals("loading-uri");
+          expect(status).equals("error");
 
           if (!error) expect.fail();
-          if (!error.hasError) expect.fail();
-          if (error.operational) expect.fail();
-          expect(error.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
+          // if (!error.hasError) expect.fail();
+          // if (error.operational) expect.fail();
+          expect(error.cause?.message).eq("ERROR_NO-URI-FOR-DEPOSIT");
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Deposit/views.tsx
index 2ec305de5..7fa43f878 100644
--- a/packages/taler-wallet-webextension/src/cta/Deposit/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Deposit/views.tsx
@@ -17,7 +17,6 @@
 import { Amounts } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { Amount } from "../../components/Amount.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
 import { SubTitle, WalletAction } from "../../components/styled/index.js";
@@ -30,17 +29,6 @@ import { State } from "./index.js";
  * @author sebasjm
  */
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load deposit status</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function ReadyView(state: State.Ready): VNode {
   const { i18n } = useTranslationContext();
 
@@ -55,7 +43,7 @@ export function ReadyView(state: State.Ready): VNode {
         {Amounts.isNonZero(state.cost) && (
           <Part
             big
-            title={<i18n.Translate>Cost</i18n.Translate>}
+            title={i18n.str`Cost`}
             text={<Amount value={state.cost} />}
             kind="negative"
           />
@@ -63,14 +51,14 @@ export function ReadyView(state: State.Ready): VNode {
         {Amounts.isNonZero(state.fee) && (
           <Part
             big
-            title={<i18n.Translate>Fee</i18n.Translate>}
+            title={i18n.str`Fee`}
             text={<Amount value={state.fee} />}
             kind="negative"
           />
         )}
         <Part
           big
-          title={<i18n.Translate>To be received</i18n.Translate>}
+          title={i18n.str`To be received`}
           text={<Amount value={state.effective} />}
           kind="positive"
         />
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
index 0569e8e5f..f39ab6794 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
@@ -14,16 +14,17 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AmountJson, TalerErrorDetail } from "@gnu-taler/taler-util";
+import { AmountJson } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { State as SelectExchangeState } from 
"../../hooks/useSelectedExchange.js";
 import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { ExchangeSelectionPage } from 
"../../wallet/ExchangeSelection/index.js";
 import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export interface Props {
   amount: string;
@@ -45,8 +46,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -63,13 +64,12 @@ export namespace State {
     requestAmount: AmountJson;
     exchangeUrl: string;
     error: undefined;
-    operationError?: TalerErrorDetail;
   }
 }
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   "no-exchange": NoExchangesView,
   "selecting-exchange": ExchangeSelectionPage,
   ready: ReadyView,
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
index 998270e53..46b1262b1 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
@@ -23,7 +23,9 @@ import {
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { isFuture, parse } from "date-fns";
 import { useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
 import { RecursiveState } from "../../utils/index.js";
@@ -40,6 +42,7 @@ export function useComponentState({
   const hook = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.ListExchanges, {}),
   );
+  const { i18n } = useTranslationContext();
 
   if (!hook) {
     return {
@@ -49,10 +52,19 @@ export function useComponentState({
   }
   if (hook.hasError) {
     return {
-      status: "loading-uri",
-      error: hook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        hook,
+      ),
     };
   }
+  // if (hook.hasError) {
+  //   return {
+  //     status: "loading-uri",
+  //     error: hook,
+  //   };
+  // }
 
   const exchangeList = hook.response.exchanges;
 
@@ -60,10 +72,6 @@ export function useComponentState({
     const [subject, setSubject] = useState<string | undefined>();
     const [timestamp, setTimestamp] = useState<string | undefined>();
 
-    const [operationError, setOperationError] = useState<
-      TalerErrorDetail | undefined
-    >(undefined);
-
     const selectedExchange = useSelectedExchange({
       currency: amount.currency,
       defaultExchange: undefined,
@@ -93,11 +101,19 @@ export function useComponentState({
         error: undefined,
       };
     }
+
     if (hook.hasError) {
       return {
-        status: "loading-uri",
-        error: hook,
+        status: "error",
+        error: alertFromError(
+          i18n.str`Could not load the status of the term of service`,
+          hook,
+        ),
       };
+      // return {
+      //   status: "loading-uri",
+      //   error: hook,
+      // };
     }
 
     const { amountEffective, amountRaw } = hook.response;
@@ -160,8 +176,8 @@ export function useComponentState({
           subject === undefined
             ? undefined
             : !subject
-            ? "Can't be empty"
-            : undefined,
+              ? "Can't be empty"
+              : undefined,
         value: subject ?? "",
         onInput: async (e) => setSubject(e),
       },
@@ -183,7 +199,6 @@ export function useComponentState({
       requestAmount,
       toBeReceived,
       error: undefined,
-      operationError,
     };
   };
 }
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
index 0ef5c697e..10e0e68d5 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
@@ -17,41 +17,24 @@
 import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
-import { QR } from "../../components/QR.js";
 import {
-  Link,
   SubTitle,
   SvgIcon,
   WalletAction,
 } from "../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
-import { Grid } from "../../mui/Grid.js";
 import { TextField } from "../../mui/TextField.js";
 import editIcon from "../../svg/edit_24px.svg";
 import { ExchangeDetails, InvoiceDetails } from "../../wallet/Transaction.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function ReadyView({
   exchangeUrl,
   subject,
   expiration,
-  cancel,
-  operationError,
   create,
   toBeReceived,
   requestAmount,
@@ -59,7 +42,7 @@ export function ReadyView({
 }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
 
-  async function oneDayExpiration() {
+  async function oneDayExpiration(): Promise<void> {
     if (expiration.onInput) {
       expiration.onInput(
         format(new Date().getTime() + 1000 * 60 * 60 * 24, "dd/MM/yyyy"),
@@ -67,14 +50,14 @@ export function ReadyView({
     }
   }
 
-  async function oneWeekExpiration() {
+  async function oneWeekExpiration(): Promise<void> {
     if (expiration.onInput) {
       expiration.onInput(
         format(new Date().getTime() + 1000 * 60 * 60 * 24 * 7, "dd/MM/yyyy"),
       );
     }
   }
-  async function _20DaysExpiration() {
+  async function _20DaysExpiration(): Promise<void> {
     if (expiration.onInput) {
       expiration.onInput(
         format(new Date().getTime() + 1000 * 60 * 60 * 24 * 20, "dd/MM/yyyy"),
@@ -87,16 +70,6 @@ export function ReadyView({
       <SubTitle>
         <i18n.Translate>Digital invoice</i18n.Translate>
       </SubTitle>
-      {operationError && (
-        <ErrorTalerOperation
-          title={
-            <i18n.Translate>
-              Could not finish the invoice creation
-            </i18n.Translate>
-          }
-          error={operationError}
-        />
-      )}
       <section style={{ textAlign: "left" }}>
         <Part
           title={
@@ -125,9 +98,7 @@ export function ReadyView({
             label="Subject"
             variant="filled"
             error={subject.error}
-            helperText={
-              <i18n.Translate>Short description of the invoice</i18n.Translate>
-            }
+            helperText={i18n.str`Short description of the invoice`}
             required
             fullWidth
             value={subject.value}
@@ -171,7 +142,7 @@ export function ReadyView({
         </p>
 
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <InvoiceDetails
               amount={{
@@ -187,11 +158,6 @@ export function ReadyView({
           <i18n.Translate>Create</i18n.Translate>
         </Button>
       </section>
-      <section>
-        <Link upperCased onClick={cancel.onClick}>
-          <i18n.Translate>Cancel</i18n.Translate>
-        </Link>
-      </section>
     </WalletAction>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
index f3de0885d..82b2c7af5 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
@@ -20,12 +20,13 @@ import {
   PreparePayResult,
   TalerErrorDetail,
 } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export interface Props {
   talerPayPullUri: string;
@@ -48,8 +49,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -83,7 +84,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   "no-balance-for-currency": ReadyView,
   "no-enough-balance": ReadyView,
   ready: ReadyView,
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
index c0b97c106..9c4a3162e 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
@@ -25,7 +25,9 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
@@ -36,6 +38,7 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const hook = useAsyncAsHook(async () => {
     const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, 
{
       talerUri: talerPayPullUri,
@@ -63,10 +66,19 @@ export function useComponentState({
   }
   if (hook.hasError) {
     return {
-      status: "loading-uri",
-      error: hook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        hook,
+      ),
     };
   }
+  // if (hook.hasError) {
+  //   return {
+  //     status: "loading-uri",
+  //     error: hook,
+  //   };
+  // }
 
   const { contractTerms, peerPullPaymentIncomingId } = hook.response.p2p;
 
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
index a53fa881a..6a9ab3cf7 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
@@ -17,26 +17,14 @@
 import { Fragment, h, VNode } from "preact";
 import { Amount } from "../../components/Amount.js";
 import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
+import { PaymentButtons } from "../../components/PaymentButtons.js";
 import { Link, SubTitle, WalletAction } from 
"../../components/styled/index.js";
 import { Time } from "../../components/Time.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { PaymentButtons } from "../../components/PaymentButtons";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function ReadyView(
   state: State.Ready | State.NoBalanceForCurrency | State.NoEnoughBalance,
 ): VNode {
@@ -60,25 +48,15 @@ export function ReadyView(
       </SubTitle>
       {operationError && (
         <ErrorTalerOperation
-          title={
-            <i18n.Translate>
-              Could not finish the payment operation
-            </i18n.Translate>
-          }
+          title={i18n.str`Could not finish the payment operation`}
           error={operationError}
         />
       )}
       <section style={{ textAlign: "left" }}>
+        <Part title={i18n.str`Subject`} text={<div>{summary}</div>} />
+        <Part title={i18n.str`Amount`} text={<Amount value={amount} />} />
         <Part
-          title={<i18n.Translate>Subject</i18n.Translate>}
-          text={<div>{summary}</div>}
-        />
-        <Part
-          title={<i18n.Translate>Amount</i18n.Translate>}
-          text={<Amount value={amount} />}
-        />
-        <Part
-          title={<i18n.Translate>Valid until</i18n.Translate>}
+          title={i18n.str`Valid until`}
           text={<Time timestamp={expiration} format="dd MMMM yyyy, HH:mm" />}
           kind="neutral"
         />
@@ -91,11 +69,6 @@ export function ReadyView(
         payHandler={status === "ready" ? state.accept : undefined}
         goToWalletManualWithdraw={state.goToWalletManualWithdraw}
       />
-      <section>
-        <Link upperCased onClick={cancel.onClick}>
-          <i18n.Translate>Cancel</i18n.Translate>
-        </Link>
-      </section>
     </WalletAction>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/index.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
index 2dc6b6741..e844c1706 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
@@ -21,12 +21,13 @@ import {
   PreparePayResultInsufficientBalance,
   PreparePayResultPaymentPossible,
 } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { BaseView, LoadingUriView } from "./views.js";
+import { BaseView } from "./views.js";
 
 export interface Props {
   talerPayUri?: string;
@@ -49,8 +50,8 @@ export namespace State {
     error: undefined;
   }
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   interface BaseInfo {
@@ -86,7 +87,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   "no-balance-for-currency": BaseView,
   "no-enough-balance": BaseView,
   confirmed: BaseView,
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/state.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/state.ts
index d4adf4bcb..6d7ef6b20 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/state.ts
@@ -23,7 +23,9 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { Props, State } from "./index.js";
@@ -36,6 +38,7 @@ export function useComponentState({
 }: Props): State {
   const [payErrMsg, setPayErrMsg] = useState<TalerError | 
undefined>(undefined);
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
 
   const hook = useAsyncAsHook(async () => {
     if (!talerPayUri) throw Error("ERROR_NO-URI-FOR-PAYMENT");
@@ -80,10 +83,19 @@ export function useComponentState({
   if (!hook) return { status: "loading", error: undefined };
   if (hook.hasError) {
     return {
-      status: "loading-uri",
-      error: hook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        hook,
+      ),
     };
   }
+  // if (hook.hasError) {
+  //   return {
+  //     status: "loading-uri",
+  //     error: hook,
+  //   };
+  // }
   const { payStatus } = hook.response;
 
   const amount = Amounts.parseOrThrow(payStatus.amountRaw);
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/test.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
index 077930972..123e95a87 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/test.ts
@@ -54,10 +54,10 @@ describe("Payment CTA states", () => {
           expect(error).undefined;
         },
         ({ status, error }) => {
-          expect(status).equals("loading-uri");
+          expect(status).equals("error");
           if (error === undefined) expect.fail();
-          expect(error.hasError).true;
-          expect(error.operational).false;
+          // expect(error.hasError).true;
+          // expect(error.operational).false;
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
index efc8bcfc4..244ac5886 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
@@ -19,28 +19,17 @@ import {
   Amounts,
   MerchantContractTerms as ContractTerms,
   PreparePayResultType,
+  TranslatedString,
 } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
-import { LoadingError } from "../../components/LoadingError.js";
 import { Part } from "../../components/Part.js";
 import { PaymentButtons } from "../../components/PaymentButtons.js";
-import { Link, SuccessBox, WarningBox } from 
"../../components/styled/index.js";
+import { SuccessBox, WarningBox } from "../../components/styled/index.js";
 import { Time } from "../../components/Time.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { MerchantDetails, PurchaseDetails } from "../../wallet/Transaction.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load pay status</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 type SupportedStates =
   | State.Ready
   | State.Confirmed
@@ -66,17 +55,17 @@ export function BaseView(state: SupportedStates): VNode {
 
       <section style={{ textAlign: "left" }}>
         <Part
-          title={<i18n.Translate>Purchase</i18n.Translate>}
-          text={contractTerms.summary}
+          title={i18n.str`Purchase`}
+          text={contractTerms.summary as TranslatedString}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Merchant</i18n.Translate>}
+          title={i18n.str`Merchant`}
           text={<MerchantDetails merchant={contractTerms.merchant} />}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <PurchaseDetails
               price={price}
@@ -93,14 +82,14 @@ export function BaseView(state: SupportedStates): VNode {
         />
         {contractTerms.order_id && (
           <Part
-            title={<i18n.Translate>Receipt</i18n.Translate>}
-            text={`#${contractTerms.order_id}`}
+            title={i18n.str`Receipt`}
+            text={`#${contractTerms.order_id}` as TranslatedString}
             kind="neutral"
           />
         )}
         {contractTerms.pay_deadline && (
           <Part
-            title={<i18n.Translate>Valid until</i18n.Translate>}
+            title={i18n.str`Valid until`}
             text={
               <Time
                 timestamp={AbsoluteTime.fromTimestamp(
@@ -121,11 +110,6 @@ export function BaseView(state: SupportedStates): VNode {
         payHandler={state.status === "ready" ? state.payHandler : undefined}
         goToWalletManualWithdraw={state.goToWalletManualWithdraw}
       />
-      <section>
-        <Link upperCased onClick={state.cancel}>
-          <i18n.Translate>Cancel</i18n.Translate>
-        </Link>
-      </section>
     </Fragment>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/index.ts 
b/packages/taler-wallet-webextension/src/cta/Recovery/index.ts
index 4a6fc79c9..79056c15b 100644
--- a/packages/taler-wallet-webextension/src/cta/Recovery/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/index.ts
@@ -14,12 +14,13 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export interface Props {
   talerRecoveryUri?: string;
@@ -36,8 +37,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -53,7 +54,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   ready: ReadyView,
 };
 
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts 
b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
index 4fef2c862..078e53bf9 100644
--- a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
@@ -16,7 +16,9 @@
 
 import { parseRecoveryUri } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { Alert } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState({
@@ -25,13 +27,16 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   if (!talerRecoveryUri) {
     return {
-      status: "loading-uri",
+      status: "error",
       error: {
-        operational: false,
-        hasError: true,
-        message: "Missing URI",
+        type: "error",
+        message: i18n.str`Missing URI`,
+        description: i18n.str``,
+        cause: new Error("something"),
+        context: {},
       },
     };
   }
@@ -39,11 +44,13 @@ export function useComponentState({
 
   if (!info) {
     return {
-      status: "loading-uri",
+      status: "error",
       error: {
-        operational: false,
-        hasError: true,
-        message: "Could not be read",
+        type: "error",
+        message: i18n.str`Could not parse the recovery URI`,
+        description: i18n.str``,
+        cause: new Error("something"),
+        context: {},
       },
     };
   }
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Recovery/views.tsx
index 371516932..858349ef3 100644
--- a/packages/taler-wallet-webextension/src/cta/Recovery/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/views.tsx
@@ -15,28 +15,12 @@
  */
 
 import { Fragment, h, VNode } from "preact";
-import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { SubTitle, WalletAction } from "../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={
-        <i18n.Translate>
-          Could not load backup recovery information
-        </i18n.Translate>
-      }
-      error={error}
-    />
-  );
-}
-
 export function ReadyView({ accept, cancel }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
   return (
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/index.ts 
b/packages/taler-wallet-webextension/src/cta/Refund/index.ts
index f79a77680..e90f770ff 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/index.ts
@@ -15,17 +15,13 @@
  */
 
 import { AmountJson, Product } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import {
-  IgnoredView,
-  InProgressView,
-  LoadingUriView,
-  ReadyView,
-} from "./views.js";
+import { IgnoredView, InProgressView, ReadyView } from "./views.js";
 
 export interface Props {
   talerRefundUri?: string;
@@ -47,8 +43,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   interface BaseInfo {
@@ -81,7 +77,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   "in-progress": InProgressView,
   ignored: IgnoredView,
   ready: ReadyView,
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/state.ts 
b/packages/taler-wallet-webextension/src/cta/Refund/state.ts
index 9e3311b65..5a5073ba3 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/state.ts
@@ -17,7 +17,9 @@
 import { Amounts, NotificationType } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
@@ -27,6 +29,7 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const [ignored, setIgnored] = useState(false);
 
   const info = useAsyncAsHook(async () => {
@@ -49,10 +52,19 @@ export function useComponentState({
   }
   if (info.hasError) {
     return {
-      status: "loading-uri",
-      error: info,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        info,
+      ),
     };
   }
+  // if (info.hasError) {
+  //   return {
+  //     status: "loading-uri",
+  //     error: info,
+  //   };
+  // }
 
   const { refund, uri } = info.response;
 
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/test.ts 
b/packages/taler-wallet-webextension/src/cta/Refund/test.ts
index 24d483a9a..8c4daa4d2 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/test.ts
@@ -53,11 +53,11 @@ describe("Refund CTA states", () => {
           expect(error).undefined;
         },
         ({ status, error }) => {
-          expect(status).equals("loading-uri");
+          expect(status).equals("error");
           if (!error) expect.fail();
-          if (!error.hasError) expect.fail();
-          if (error.operational) expect.fail();
-          expect(error.message).eq("ERROR_NO-URI-FOR-REFUND");
+          // if (!error.hasError) expect.fail();
+          // if (error.operational) expect.fail();
+          expect(error.cause?.message).eq("ERROR_NO-URI-FOR-REFUND");
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Refund/views.tsx
index a55bc43dd..16e1c519c 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Refund/views.tsx
@@ -17,26 +17,14 @@
 import { Amounts } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { Amount } from "../../components/Amount.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
+import { ProductList } from "../../components/ProductList.js";
 import { Link, SubTitle, WalletAction } from 
"../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
-import { ProductList } from "../../components/ProductList.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load refund status</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function IgnoredView(state: State.Ignored): VNode {
   const { i18n } = useTranslationContext();
 
@@ -73,13 +61,13 @@ export function InProgressView(state: State.InProgress): 
VNode {
       <section>
         <Part
           big
-          title={<i18n.Translate>Total to refund</i18n.Translate>}
+          title={i18n.str`Total to refund`}
           text={<Amount value={state.awaitingAmount} />}
           kind="negative"
         />
         <Part
           big
-          title={<i18n.Translate>Refunded</i18n.Translate>}
+          title={i18n.str`Refunded`}
           text={<Amount value={state.amount} />}
           kind="negative"
         />
@@ -112,21 +100,21 @@ export function ReadyView(state: State.Ready): VNode {
       <section>
         <Part
           big
-          title={<i18n.Translate>Order amount</i18n.Translate>}
+          title={i18n.str`Order amount`}
           text={<Amount value={state.amount} />}
           kind="neutral"
         />
         {Amounts.isNonZero(state.granted) && (
           <Part
             big
-            title={<i18n.Translate>Already refunded</i18n.Translate>}
+            title={i18n.str`Already refunded`}
             text={<Amount value={state.granted} />}
             kind="neutral"
           />
         )}
         <Part
           big
-          title={<i18n.Translate>Refund offered</i18n.Translate>}
+          title={i18n.str`Refund offered`}
           text={<Amount value={state.awaitingAmount} />}
           kind="positive"
         />
@@ -147,11 +135,6 @@ export function ReadyView(state: State.Ready): VNode {
           </i18n.Translate>
         </Button>
       </section>
-      <section>
-        <Link upperCased onClick={state.cancel}>
-          <i18n.Translate>Cancel</i18n.Translate>
-        </Link>
-      </section>
     </WalletAction>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/index.ts 
b/packages/taler-wallet-webextension/src/cta/Tip/index.ts
index 62e0688be..5e56db7bc 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Tip/index.ts
@@ -15,17 +15,13 @@
  */
 
 import { AmountJson } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import {
-  AcceptedView,
-  IgnoredView,
-  LoadingUriView,
-  ReadyView,
-} from "./views.js";
+import { AcceptedView, IgnoredView, ReadyView } from "./views.js";
 
 export interface Props {
   talerTipUri?: string;
@@ -48,8 +44,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -75,7 +71,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   accepted: AcceptedView,
   ignored: IgnoredView,
   ready: ReadyView,
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/state.ts 
b/packages/taler-wallet-webextension/src/cta/Tip/state.ts
index e83755119..29a9c4c71 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Tip/state.ts
@@ -16,7 +16,9 @@
 
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
@@ -26,6 +28,7 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const tipInfo = useAsyncAsHook(async () => {
     if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP");
     const tip = await api.wallet.call(WalletApiOperation.PrepareTip, {
@@ -42,10 +45,19 @@ export function useComponentState({
   }
   if (tipInfo.hasError) {
     return {
-      status: "loading-uri",
-      error: tipInfo,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        tipInfo,
+      ),
     };
   }
+  // if (tipInfo.hasError) {
+  //   return {
+  //     status: "loading-uri",
+  //     error: tipInfo,
+  //   };
+  // }
 
   const { tip } = tipInfo.response;
 
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/test.ts 
b/packages/taler-wallet-webextension/src/cta/Tip/test.ts
index 5688d82a9..2cc95f424 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Tip/test.ts
@@ -23,8 +23,7 @@ import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { expect } from "chai";
 import { tests } from "../../../../web-util/src/index.browser.js";
-import { mountHook, nullFunction } from "../../test-utils.js";
-import { createWalletApiMock } from "../../test-utils.js";
+import { createWalletApiMock, nullFunction } from "../../test-utils.js";
 import { Props } from "./index.js";
 import { useComponentState } from "./state.js";
 
@@ -47,11 +46,9 @@ describe("Tip CTA states", () => {
           expect(error).undefined;
         },
         ({ status, error }) => {
-          expect(status).equals("loading-uri");
+          expect(status).equals("error");
           if (!error) expect.fail();
-          if (!error.hasError) expect.fail();
-          if (error.operational) expect.fail();
-          expect(error.message).eq("ERROR_NO-URI-FOR-TIP");
+          expect(error.cause?.message).eq("ERROR_NO-URI-FOR-TIP");
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/Tip/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Tip/views.tsx
index fbc93c5ab..000daf19e 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Tip/views.tsx
@@ -14,9 +14,9 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { TranslatedString } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { Amount } from "../../components/Amount.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
 import { Link, SubTitle, WalletAction } from 
"../../components/styled/index.js";
@@ -24,17 +24,6 @@ import { useTranslationContext } from 
"../../context/translation.js";
 import { Button } from "../../mui/Button.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load tip status</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function IgnoredView(state: State.Ignored): VNode {
   const { i18n } = useTranslationContext();
   return (
@@ -66,18 +55,18 @@ export function ReadyView(state: State.Ready): VNode {
           <i18n.Translate>The merchant is offering you a tip</i18n.Translate>
         </p>
         <Part
-          title={<i18n.Translate>Amount</i18n.Translate>}
+          title={i18n.str`Amount`}
           text={<Amount value={state.amount} />}
           kind="positive"
         />
         <Part
-          title={<i18n.Translate>Merchant URL</i18n.Translate>}
-          text={state.merchantBaseUrl}
+          title={i18n.str`Merchant URL`}
+          text={state.merchantBaseUrl as TranslatedString}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Exchange</i18n.Translate>}
-          text={state.exchangeBaseUrl}
+          title={i18n.str`Exchange`}
+          text={state.exchangeBaseUrl as TranslatedString}
           kind="neutral"
         />
       </section>
@@ -92,11 +81,6 @@ export function ReadyView(state: State.Ready): VNode {
           </i18n.Translate>
         </Button>
       </section>
-      <section>
-        <Link upperCased onClick={state.cancel.onClick}>
-          <i18n.Translate>Cancel</i18n.Translate>
-        </Link>
-      </section>
     </WalletAction>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
index 0715bb60e..b191b4efa 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
@@ -15,12 +15,13 @@
  */
 
 import { AmountJson, TalerErrorDetail } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export interface Props {
   amount: string;
@@ -37,8 +38,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -59,7 +60,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   ready: ReadyView,
 };
 
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
index c09a524c8..ecea53848 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
@@ -22,7 +22,9 @@ import {
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { isFuture, parse } from "date-fns";
 import { useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
@@ -33,6 +35,7 @@ export function useComponentState({
 }: Props): State {
   const api = useBackendContext();
   const amount = Amounts.parseOrThrow(amountStr);
+  const { i18n } = useTranslationContext();
 
   const [subject, setSubject] = useState<string | undefined>();
   const [timestamp, setTimestamp] = useState<string | undefined>();
@@ -59,10 +62,19 @@ export function useComponentState({
   }
   if (hook.hasError) {
     return {
-      status: "loading-uri",
-      error: hook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        hook,
+      ),
     };
   }
+  // if (hook.hasError) {
+  //   return {
+  //     status: "loading-uri",
+  //     error: hook,
+  //   };
+  // }
 
   const { amountEffective, amountRaw } = hook.response;
   const debitAmount = Amounts.parseOrThrow(amountRaw);
diff --git 
a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx 
b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
index 0b034e3fb..cee61b3b8 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx
@@ -17,10 +17,8 @@
 import { format } from "date-fns";
 import { h, VNode } from "preact";
 import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
-import { QR } from "../../components/QR.js";
 import { Link, SubTitle, WalletAction } from 
"../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
@@ -28,17 +26,6 @@ import { TextField } from "../../mui/TextField.js";
 import { TransferDetails } from "../../wallet/Transaction.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function ReadyView({
   subject,
   expiration,
@@ -80,11 +67,7 @@ export function ReadyView({
       </SubTitle>
       {operationError && (
         <ErrorTalerOperation
-          title={
-            <i18n.Translate>
-              Could not finish the transfer creation
-            </i18n.Translate>
-          }
+          title={i18n.str`Could not finish the transfer creation`}
           error={operationError}
         />
       )}
@@ -93,9 +76,7 @@ export function ReadyView({
           <TextField
             label="Subject"
             variant="filled"
-            helperText={
-              <i18n.Translate>Short description of the 
transfer</i18n.Translate>
-            }
+            helperText={i18n.str`Short description of the transfer`}
             error={subject.error}
             required
             fullWidth
@@ -138,7 +119,7 @@ export function ReadyView({
           </p>
         </p>
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <TransferDetails
               amount={{
@@ -154,13 +135,6 @@ export function ReadyView({
           <i18n.Translate>Create</i18n.Translate>
         </Button>
       </section>
-      <section>
-        <section>
-          <Link upperCased onClick={cancel.onClick}>
-            <i18n.Translate>Cancel</i18n.Translate>
-          </Link>
-        </section>
-      </section>
     </WalletAction>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
index fe6fb2ada..7bb8785d7 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
@@ -19,12 +19,13 @@ import {
   AmountJson,
   TalerErrorDetail,
 } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export interface Props {
   talerPayPushUri: string;
@@ -41,8 +42,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-uri";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -62,7 +63,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-uri": LoadingUriView,
+  error: ErrorAlertView,
   ready: ReadyView,
 };
 
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
index 82c95b0c6..04fc0e0a7 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
@@ -22,7 +22,9 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
@@ -32,6 +34,7 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const hook = useAsyncAsHook(async () => {
     return await api.wallet.call(WalletApiOperation.CheckPeerPushPayment, {
       talerUri: talerPayPushUri,
@@ -49,10 +52,19 @@ export function useComponentState({
   }
   if (hook.hasError) {
     return {
-      status: "loading-uri",
-      error: hook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the status of the term of service`,
+        hook,
+      ),
     };
   }
+  // if (hook.hasError) {
+  //   return {
+  //     status: "loading-uri",
+  //     error: hook,
+  //   };
+  // }
 
   const { contractTerms, peerPushPaymentIncomingId } = hook.response;
 
diff --git 
a/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx 
b/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx
index c43b0ff52..d2402db3a 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx
@@ -17,7 +17,6 @@
 import { h, VNode } from "preact";
 import { Amount } from "../../components/Amount.js";
 import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
 import { Link, SubTitle, WalletAction } from 
"../../components/styled/index.js";
@@ -26,17 +25,6 @@ import { useTranslationContext } from 
"../../context/translation.js";
 import { Button } from "../../mui/Button.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function ReadyView({
   accept,
   summary,
@@ -54,25 +42,15 @@ export function ReadyView({
       </SubTitle>
       {operationError && (
         <ErrorTalerOperation
-          title={
-            <i18n.Translate>
-              Could not finish the pickup operation
-            </i18n.Translate>
-          }
+          title={i18n.str`Could not finish the pickup operation`}
           error={operationError}
         />
       )}
       <section style={{ textAlign: "left" }}>
+        <Part title={i18n.str`Subject`} text={<div>{summary}</div>} />
+        <Part title={i18n.str`Amount`} text={<Amount value={amount} />} />
         <Part
-          title={<i18n.Translate>Subject</i18n.Translate>}
-          text={<div>{summary}</div>}
-        />
-        <Part
-          title={<i18n.Translate>Amount</i18n.Translate>}
-          text={<Amount value={amount} />}
-        />
-        <Part
-          title={<i18n.Translate>Valid until</i18n.Translate>}
+          title={i18n.str`Valid until`}
           text={<Time timestamp={expiration} format="dd MMMM yyyy, HH:mm" />}
           kind="neutral"
         />
@@ -84,11 +62,6 @@ export function ReadyView({
           </i18n.Translate>
         </Button>
       </section>
-      <section>
-        <Link upperCased onClick={cancel.onClick}>
-          <i18n.Translate>Cancel</i18n.Translate>
-        </Link>
-      </section>
     </WalletAction>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
index 25d4e44e5..7dfc7c141 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
@@ -27,7 +27,9 @@ import {
 
 import { ExchangeSelectionPage } from 
"../../wallet/ExchangeSelection/index.js";
 import { NoExchangesView } from "../../wallet/ExchangeSelection/views.js";
-import { LoadingInfoView, LoadingUriView, SuccessView } from "./views.js";
+import { SuccessView } from "./views.js";
+import { ErrorAlert } from "../../context/alert.js";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 
 export interface PropsFromURI {
   talerWithdrawUri: string | undefined;
@@ -44,7 +46,6 @@ export interface PropsFromParams {
 export type State =
   | State.Loading
   | State.LoadingUriError
-  | State.LoadingInfoError
   | SelectExchangeState.NoExchange
   | SelectExchangeState.Selecting
   | State.Success;
@@ -55,12 +56,8 @@ export namespace State {
     error: undefined;
   }
   export interface LoadingUriError {
-    status: "uri-error";
-    error: HookError;
-  }
-  export interface LoadingInfoError {
-    status: "amount-error";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export type Success = {
@@ -86,8 +83,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "uri-error": LoadingUriView,
-  "amount-error": LoadingInfoView,
+  error: ErrorAlertView,
   "no-exchange": NoExchangesView,
   "selecting-exchange": ExchangeSelectionPage,
   success: SuccessView,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index d1853442b..18c467aae 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -23,7 +23,9 @@ import {
 } from "@gnu-taler/taler-util";
 import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { useSelectedExchange } from "../../hooks/useSelectedExchange.js";
 import { RecursiveState } from "../../utils/index.js";
@@ -35,6 +37,7 @@ export function useComponentStateFromParams({
   onSuccess,
 }: PropsFromParams): RecursiveState<State> {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const uriInfoHook = useAsyncAsHook(async () => {
     const exchanges = await api.wallet.call(
       WalletApiOperation.ListExchanges,
@@ -47,8 +50,11 @@ export function useComponentStateFromParams({
 
   if (uriInfoHook.hasError) {
     return {
-      status: "uri-error",
-      error: uriInfoHook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load the list of exchanges`,
+        uriInfoHook,
+      ),
     };
   }
 
@@ -95,6 +101,7 @@ export function useComponentStateFromURI({
   onSuccess,
 }: PropsFromURI): RecursiveState<State> {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   /**
    * Ask the wallet about the withdraw URI
    */
@@ -123,8 +130,11 @@ export function useComponentStateFromURI({
 
   if (uriInfoHook.hasError) {
     return {
-      status: "uri-error",
-      error: uriInfoHook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load info from URI`,
+        uriInfoHook,
+      ),
     };
   }
 
@@ -194,6 +204,7 @@ function exchangeSelectionState(
   }
 
   return () => {
+    const { i18n } = useTranslationContext();
     const [ageRestricted, setAgeRestricted] = useState(0);
     const currentExchange = selectedExchange.selected;
     const tosNeedToBeAccepted =
@@ -255,8 +266,11 @@ function exchangeSelectionState(
     }
     if (amountHook.hasError) {
       return {
-        status: "amount-error",
-        error: amountHook,
+        status: "error",
+        error: alertFromError(
+          i18n.str`Could not load the withdrawal details`,
+          amountHook,
+        ),
       };
     }
     if (!amountHook.response) {
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
index 3277ac18d..2caa50dca 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
@@ -84,11 +84,11 @@ describe("Withdraw CTA states", () => {
           expect(status).equals("loading");
         },
         ({ status, error }) => {
-          if (status != "uri-error") expect.fail();
+          if (status != "error") expect.fail();
           if (!error) expect.fail();
-          if (!error.hasError) expect.fail();
-          if (error.operational) expect.fail();
-          expect(error.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
+          // if (!error.hasError) expect.fail();
+          // if (error.operational) expect.fail();
+          expect(error.cause?.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
         },
       ],
       TestingContext,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
index 9dbe24b7e..cf87b35bb 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
@@ -19,16 +19,10 @@ import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Amount } from "../../components/Amount.js";
 import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { Part } from "../../components/Part.js";
 import { QR } from "../../components/QR.js";
 import { SelectList } from "../../components/SelectList.js";
-import {
-  Input,
-  Link,
-  LinkSuccess,
-  SvgIcon,
-} from "../../components/styled/index.js";
+import { Input, LinkSuccess, SvgIcon } from "../../components/styled/index.js";
 import { TermsOfService } from "../../components/TermsOfService/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
@@ -36,30 +30,6 @@ import editIcon from "../../svg/edit_24px.svg";
 import { ExchangeDetails, WithdrawDetails } from "../../wallet/Transaction.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={
-        <i18n.Translate>Could not get the info from the URI</i18n.Translate>
-      }
-      error={error}
-    />
-  );
-}
-
-export function LoadingInfoView({ error }: State.LoadingInfoError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not get info of withdrawal</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function SuccessView(state: State.Success): VNode {
   const { i18n } = useTranslationContext();
   const currentTosVersionIsAccepted =
@@ -68,11 +38,7 @@ export function SuccessView(state: State.Success): VNode {
     <Fragment>
       {state.doWithdrawal.error && (
         <ErrorTalerOperation
-          title={
-            <i18n.Translate>
-              Could not finish the withdrawal operation
-            </i18n.Translate>
-          }
+          title={i18n.str`Could not finish the withdrawal operation`}
           error={state.doWithdrawal.error.errorDetail}
         />
       )}
@@ -103,7 +69,7 @@ export function SuccessView(state: State.Success): VNode {
           big
         />
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <WithdrawDetails
               amount={{
@@ -116,7 +82,7 @@ export function SuccessView(state: State.Success): VNode {
         {state.ageRestriction && (
           <Input>
             <SelectList
-              label={<i18n.Translate>Age restriction</i18n.Translate>}
+              label={i18n.str`Age restriction`}
               list={state.ageRestriction.list}
               name="age"
               value={state.ageRestriction.value}
@@ -148,11 +114,6 @@ export function SuccessView(state: State.Success): VNode {
       {state.talerWithdrawUri ? (
         <WithdrawWithMobile talerWithdrawUri={state.talerWithdrawUri} />
       ) : undefined}
-      <section>
-        <Link upperCased onClick={state.cancel}>
-          <i18n.Translate>Cancel</i18n.Translate>
-        </Link>
-      </section>
     </Fragment>
   );
 }
@@ -168,11 +129,7 @@ function WithdrawWithMobile({
   return (
     <section>
       <LinkSuccess upperCased onClick={() => setShowQR((qr) => !qr)}>
-        {!showQR ? (
-          <i18n.Translate>Withdraw to a mobile phone</i18n.Translate>
-        ) : (
-          <i18n.Translate>Hide QR</i18n.Translate>
-        )}
+        {!showQR ? i18n.str`Withdraw to a mobile phone` : i18n.str`Hide QR`}
       </LinkSuccess>
       {showQR && (
         <div>
diff --git a/packages/taler-wallet-webextension/src/hooks/useAsyncAsHook.ts 
b/packages/taler-wallet-webextension/src/hooks/useAsyncAsHook.ts
index 1b2929317..978ea90e1 100644
--- a/packages/taler-wallet-webextension/src/hooks/useAsyncAsHook.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useAsyncAsHook.ts
@@ -16,6 +16,7 @@
 import { TalerErrorDetail } from "@gnu-taler/taler-util";
 import { TalerError } from "@gnu-taler/taler-wallet-core";
 import { useEffect, useMemo, useState } from "preact/hooks";
+import { WalletError } from "../wxApi.js";
 
 export interface HookOk<T> {
   hasError: false;
@@ -26,13 +27,14 @@ export type HookError = HookGenericError | 
HookOperationalError;
 
 export interface HookGenericError {
   hasError: true;
-  operational: false;
+  type: "error";
   message: string;
 }
 
 export interface HookOperationalError {
   hasError: true;
-  operational: true;
+  type: "taler";
+  message: string;
   details: TalerErrorDetail;
 }
 
@@ -68,13 +70,21 @@ export function useAsyncAsHook<T>(
       if (e instanceof TalerError) {
         setHookResponse({
           hasError: true,
-          operational: true,
+          type: "taler",
+          message: e.message,
           details: e.errorDetail,
         });
+      } else if (e instanceof WalletError) {
+        setHookResponse({
+          hasError: true,
+          type: "taler",
+          message: e.message,
+          details: e.errorDetail.errorDetail,
+        });
       } else if (e instanceof Error) {
         setHookResponse({
           hasError: true,
-          operational: false,
+          type: "error",
           message: e.message,
         });
       }
diff --git a/packages/taler-wallet-webextension/src/hooks/useLang.ts 
b/packages/taler-wallet-webextension/src/hooks/useLang.ts
index 269fe6239..b1aa40015 100644
--- a/packages/taler-wallet-webextension/src/hooks/useLang.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useLang.ts
@@ -17,6 +17,7 @@
 import { useNotNullLocalStorage } from "./useLocalStorage.js";
 
 function getBrowserLang(): string | undefined {
+  if (typeof window === "undefined") return undefined;
   if (window.navigator.languages) return window.navigator.languages[0];
   if (window.navigator.language) return window.navigator.language;
   return undefined;
diff --git a/packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts 
b/packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts
index 88b7655b6..387798c96 100644
--- a/packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useLocalStorage.ts
@@ -75,6 +75,9 @@ export function useNotNullLocalStorage(
     }
   };
 
-  const isSaved = window.localStorage.getItem(key) !== null;
+  const isSaved =
+    typeof window === "undefined"
+      ? false
+      : window.localStorage.getItem(key) !== null;
   return [storedValue, setValue, isSaved];
 }
diff --git a/packages/taler-wallet-webextension/src/mui/Alert.stories.tsx 
b/packages/taler-wallet-webextension/src/mui/Alert.stories.tsx
index 62f7a2993..b0c2a2730 100644
--- a/packages/taler-wallet-webextension/src/mui/Alert.stories.tsx
+++ b/packages/taler-wallet-webextension/src/mui/Alert.stories.tsx
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { TranslatedString } from "@gnu-taler/taler-util";
 import { css } from "@linaria/core";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { Alert } from "./Alert.jsx";
@@ -53,16 +54,16 @@ export const BasicExample = (): VNode => (
 
 export const WithTitle = (): VNode => (
   <Wrapper>
-    <Alert title="Warning" severity="warning">
+    <Alert title={"Warning" as TranslatedString} severity="warning">
       this is an warning
     </Alert>
-    <Alert title="Error" severity="error">
+    <Alert title={"Error" as TranslatedString} severity="error">
       this is an error
     </Alert>
-    <Alert title="Success" severity="success">
+    <Alert title={"Success" as TranslatedString} severity="success">
       this is an success
     </Alert>
-    <Alert title="Info" severity="info">
+    <Alert title={"Info" as TranslatedString} severity="info">
       this is an info
     </Alert>
   </Wrapper>
@@ -74,16 +75,32 @@ const showSomething = async function (): Promise<void> {
 
 export const WithAction = (): VNode => (
   <Wrapper>
-    <Alert title="Warning" severity="warning" onClose={showSomething}>
+    <Alert
+      title={"Warning" as TranslatedString}
+      severity="warning"
+      onClose={showSomething}
+    >
       this is an warning
     </Alert>
-    <Alert title="Error" severity="error" onClose={showSomething}>
+    <Alert
+      title={"Error" as TranslatedString}
+      severity="error"
+      onClose={showSomething}
+    >
       this is an error
     </Alert>
-    <Alert title="Success" severity="success" onClose={showSomething}>
+    <Alert
+      title={"Success" as TranslatedString}
+      severity="success"
+      onClose={showSomething}
+    >
       this is an success
     </Alert>
-    <Alert title="Info" severity="info" onClose={showSomething}>
+    <Alert
+      title={"Info" as TranslatedString}
+      severity="info"
+      onClose={showSomething}
+    >
       this is an info
     </Alert>
   </Wrapper>
diff --git a/packages/taler-wallet-webextension/src/mui/Alert.tsx 
b/packages/taler-wallet-webextension/src/mui/Alert.tsx
index 360c3c3cb..b00312a86 100644
--- a/packages/taler-wallet-webextension/src/mui/Alert.tsx
+++ b/packages/taler-wallet-webextension/src/mui/Alert.tsx
@@ -13,6 +13,7 @@
  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 { TranslatedString } from "@gnu-taler/taler-util";
 import { css } from "@linaria/core";
 import { ComponentChildren, h, VNode } from "preact";
 // eslint-disable-next-line import/extensions
@@ -61,7 +62,7 @@ const colorVariant = {
 };
 
 interface Props {
-  title?: string;
+  title?: TranslatedString;
   variant?: "filled" | "outlined" | "standard";
   role?: string;
   onClose?: () => Promise<void>;
@@ -110,20 +111,20 @@ function Message({
   title,
   children,
 }: {
-  title?: string;
+  title?: TranslatedString;
   children: ComponentChildren;
 }): VNode {
   return (
     <div
       class={css`
         padding: 8px 0px;
-        width: 100%;
+        width: 90%;
       `}
     >
       {title && (
         <Typography
           class={css`
-            font-weight: ${theme.typography.fontWeightMedium};
+            font-weight: ${theme.typography.fontWeightBold};
           `}
           gutterBottom
         >
@@ -160,6 +161,7 @@ export function Alert({
         "--color-main": theme.palette[severity].main,
         "--color-light": theme.palette[severity].light,
         // ...(style as any),
+        textAlign: "left",
       }}
       elevation={1}
     >
diff --git a/packages/taler-wallet-webextension/src/mui/Paper.tsx 
b/packages/taler-wallet-webextension/src/mui/Paper.tsx
index 3b5f24bc1..0c805e307 100644
--- a/packages/taler-wallet-webextension/src/mui/Paper.tsx
+++ b/packages/taler-wallet-webextension/src/mui/Paper.tsx
@@ -29,9 +29,6 @@ const borderVariant = {
   `,
 };
 const baseStyle = css`
-  background-color: ${theme.palette.background.paper};
-  color: ${theme.palette.text.primary};
-
   .theme-dark & {
     background-image: var(--gradient-white-elevation);
   }
diff --git a/packages/taler-wallet-webextension/src/mui/colors/manipulation.ts 
b/packages/taler-wallet-webextension/src/mui/colors/manipulation.ts
index 226d3c860..f9bf9eb2b 100644
--- a/packages/taler-wallet-webextension/src/mui/colors/manipulation.ts
+++ b/packages/taler-wallet-webextension/src/mui/colors/manipulation.ts
@@ -57,7 +57,7 @@ export function hexToRgb(color: string): string {
   let colors = color.match(re);
 
   if (colors && colors[0].length === 1) {
-    colors = colors.map((n) => n + n);
+    colors = colors.map((n) => n + n) as RegExpMatchArray;
   }
 
   return colors
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts 
b/packages/taler-wallet-webextension/src/platform/api.ts
index cd09f6438..40993477b 100644
--- a/packages/taler-wallet-webextension/src/platform/api.ts
+++ b/packages/taler-wallet-webextension/src/platform/api.ts
@@ -146,9 +146,9 @@ export interface BackgroundPlatformAPI {
    */
   getPermissionsApi(): CrossBrowserPermissionsApi;
   /**
- * Used by the wallet backend to send notification about new information
- * @param message
- */
+   * Used by the wallet backend to send notification about new information
+   * @param message
+   */
   sendMessageToAllChannels(message: MessageFromBackend): void;
 
   /**
@@ -196,7 +196,6 @@ export interface ForegroundPlatformAPI {
    */
   openWalletURIFromPopup(talerUri: string): void;
 
-
   /**
    * Popup API
    *
@@ -248,5 +247,4 @@ export interface ForegroundPlatformAPI {
   listenToWalletBackground(
     listener: (message: MessageFromBackend) => void,
   ): () => void;
-
 }
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts 
b/packages/taler-wallet-webextension/src/platform/chrome.ts
index e5efdec4e..fc51a65fb 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -14,17 +14,17 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import {
-  classifyTalerUri, Logger,
-  TalerUriType
-} from "@gnu-taler/taler-util";
+import { classifyTalerUri, Logger, TalerUriType } from "@gnu-taler/taler-util";
 import { WalletOperations } from "@gnu-taler/taler-wallet-core";
 import { BackgroundOperations } from "../wxApi.js";
 import {
-  BackgroundPlatformAPI, CrossBrowserPermissionsApi, ForegroundPlatformAPI, 
MessageFromBackend,
+  BackgroundPlatformAPI,
+  CrossBrowserPermissionsApi,
+  ForegroundPlatformAPI,
+  MessageFromBackend,
   MessageFromFrontend,
   MessageResponse,
-  Permissions
+  Permissions,
 } from "./api.js";
 
 const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
@@ -306,13 +306,12 @@ function openWalletPageFromPopup(page: string): void {
   });
 }
 
-
 let nextMessageIndex = 0;
 
 /**
  * To be used by the foreground
- * @param message 
- * @returns 
+ * @param message
+ * @returns
  */
 async function sendMessageToBackground<
   Op extends WalletOperations | BackgroundOperations,
@@ -321,13 +320,13 @@ async function sendMessageToBackground<
 
   return new Promise<any>((resolve, reject) => {
     logger.trace("send operation to the wallet background", message);
-    let timedout = false
+    let timedout = false;
     setTimeout(() => {
-      timedout = true
-      reject("timedout")
+      timedout = true;
+      reject("timedout");
     }, 2000);
     chrome.runtime.sendMessage(messageWithId, (backgroundResponse) => {
-      if (timedout) return false
+      if (timedout) return false;
       if (chrome.runtime.lastError) {
         reject(chrome.runtime.lastError.message);
       } else {
@@ -358,7 +357,6 @@ function listenToWalletBackground(listener: (m: any) => 
void): () => void {
 
 const allPorts: chrome.runtime.Port[] = [];
 
-
 function sendMessageToAllChannels(message: MessageFromBackend): void {
   for (const notif of allPorts) {
     // const message: MessageFromBackend = { type: msg.type };
@@ -578,26 +576,26 @@ function setAlertedIcon(): void {
 
 interface OffscreenCanvasRenderingContext2D
   extends CanvasState,
-  CanvasTransform,
-  CanvasCompositing,
-  CanvasImageSmoothing,
-  CanvasFillStrokeStyles,
-  CanvasShadowStyles,
-  CanvasFilters,
-  CanvasRect,
-  CanvasDrawPath,
-  CanvasUserInterface,
-  CanvasText,
-  CanvasDrawImage,
-  CanvasImageData,
-  CanvasPathDrawingStyles,
-  CanvasTextDrawingStyles,
-  CanvasPath {
+    CanvasTransform,
+    CanvasCompositing,
+    CanvasImageSmoothing,
+    CanvasFillStrokeStyles,
+    CanvasShadowStyles,
+    CanvasFilters,
+    CanvasRect,
+    CanvasDrawPath,
+    CanvasUserInterface,
+    CanvasText,
+    CanvasDrawImage,
+    CanvasImageData,
+    CanvasPathDrawingStyles,
+    CanvasTextDrawingStyles,
+    CanvasPath {
   readonly canvas: OffscreenCanvas;
 }
 declare const OffscreenCanvasRenderingContext2D: {
   prototype: OffscreenCanvasRenderingContext2D;
-  new(): OffscreenCanvasRenderingContext2D;
+  new (): OffscreenCanvasRenderingContext2D;
 };
 
 interface OffscreenCanvas extends EventTarget {
@@ -610,7 +608,7 @@ interface OffscreenCanvas extends EventTarget {
 }
 declare const OffscreenCanvas: {
   prototype: OffscreenCanvas;
-  new(width: number, height: number): OffscreenCanvas;
+  new (width: number, height: number): OffscreenCanvas;
 };
 
 function createCanvas(size: number): OffscreenCanvas {
diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts 
b/packages/taler-wallet-webextension/src/platform/firefox.ts
index a36859a0b..7f6980be7 100644
--- a/packages/taler-wallet-webextension/src/platform/firefox.ts
+++ b/packages/taler-wallet-webextension/src/platform/firefox.ts
@@ -14,7 +14,12 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { BackgroundPlatformAPI, CrossBrowserPermissionsApi, 
ForegroundPlatformAPI, Permissions } from "./api.js";
+import {
+  BackgroundPlatformAPI,
+  CrossBrowserPermissionsApi,
+  ForegroundPlatformAPI,
+  Permissions,
+} from "./api.js";
 import chromePlatform, {
   containsHostPermissions as chromeHostContains,
   removeHostPermissions as chromeHostRemove,
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx 
b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index 8786b2ff7..96f0f6dd9 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -19,12 +19,13 @@ import { WalletApiOperation } from 
"@gnu-taler/taler-wallet-core";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { BalanceTable } from "../components/BalanceTable.js";
+import { ErrorAlertView } from "../components/CurrentAlerts.js";
 import { Loading } from "../components/Loading.js";
-import { LoadingError } from "../components/LoadingError.js";
 import { MultiActionButton } from "../components/MultiActionButton.js";
+import { alertFromError, ErrorAlert } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
-import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
 import { ButtonHandler } from "../mui/handlers.js";
 import { compose, StateViewMap } from "../utils/index.js";
@@ -47,7 +48,7 @@ export namespace State {
 
   export interface Error {
     status: "error";
-    error: HookError;
+    error: ErrorAlert;
   }
 
   export interface Action {
@@ -73,6 +74,7 @@ function useComponentState({
   goToWalletManualWithdraw,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const [addingAction, setAddingAction] = useState(false);
   const state = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.GetBalances, {}),
@@ -94,7 +96,7 @@ function useComponentState({
   if (state.hasError) {
     return {
       status: "error",
-      error: state,
+      error: alertFromError(i18n.str`Could not load the balance`, state),
     };
   }
   if (addingAction) {
@@ -123,7 +125,7 @@ function useComponentState({
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  error: ErrorView,
+  error: ErrorAlertView,
   action: ActionView,
   balance: BalanceView,
 };
@@ -134,16 +136,6 @@ export const BalancePage = compose(
   viewMapping,
 );
 
-function ErrorView({ error }: State.Error): VNode {
-  const { i18n } = useTranslationContext();
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load balance page</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 function ActionView({ cancel }: State.Action): VNode {
   return <AddNewActionView onCancel={cancel.onClick!} />;
 }
@@ -179,7 +171,7 @@ export function BalanceView(state: State.Balances): VNode {
         </Button>
         {currencyWithNonZeroAmount.length > 0 && (
           <MultiActionButton
-            label={(s) => <i18n.Translate>Send {s}</i18n.Translate>}
+            label={(s) => i18n.str`Send ${s}`}
             actions={currencyWithNonZeroAmount}
             onClick={(c) => state.goToWalletDeposit(c)}
           />
diff --git a/packages/taler-wallet-webextension/src/popup/NoBalanceHelp.tsx 
b/packages/taler-wallet-webextension/src/popup/NoBalanceHelp.tsx
index 7d2e15726..5eb31ba46 100644
--- a/packages/taler-wallet-webextension/src/popup/NoBalanceHelp.tsx
+++ b/packages/taler-wallet-webextension/src/popup/NoBalanceHelp.tsx
@@ -33,7 +33,7 @@ export function NoBalanceHelp({
   const { i18n } = useTranslationContext();
   return (
     <Paper class={margin}>
-      <Alert title="Your wallet is empty." severity="info">
+      <Alert title={i18n.str`Your wallet is empty.`} severity="info">
         <Button
           fullWidth
           color="info"
diff --git a/packages/taler-wallet-webextension/src/svg/progress.svg 
b/packages/taler-wallet-webextension/src/svg/progress.svg
new file mode 100644
index 000000000..c7284a545
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/svg/progress.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; 
style="margin:auto;background:#fff;display:block;" viewBox="0 0 100 100" 
preserveAspectRatio="xMidYMid">
+  <defs>
+    <clipPath id="progress-cp" x="0" y="0" width="100" height="100">
+      <rect x="0" y="0" width="0" height="100">
+        <animate attributeName="width" repeatCount="indefinite" dur="2s" 
values="0;100;100" keyTimes="0;0.5;1"></animate>
+        <animate attributeName="x" repeatCount="indefinite" dur="2s" 
values="0;0;100" keyTimes="0;0.5;1"></animate>
+      </rect>
+    </clipPath>
+  </defs>
+  <path fill="none" stroke="darkgrey" stroke-width="1.04" 
d="M10.000000000000004 44.019999999999996L89.99999999999999 
44.019999999999996A5.98 5.98 0 0 1 95.97999999999999 50L95.97999999999999 
50A5.98 5.98 0 0 1 89.99999999999999 55.980000000000004L10.000000000000004 
55.980000000000004A5.98 5.98 0 0 1 4.020000000000003 50L4.020000000000003 
50A5.98 5.98 0 0 1 10.000000000000004 44.019999999999996 Z"></path>
+  <path fill="#0042b2" clip-path="url(#progress-cp)" d="M10.000000000000004 
45.54L90 45.54A4.460000000000001 4.460000000000001 0 0 1 94.46 50L94.46 
50A4.460000000000001 4.460000000000001 0 0 1 90 54.46L10.000000000000004 
54.46A4.460000000000001 4.460000000000001 0 0 1 5.540000000000003 
50L5.540000000000003 50A4.460000000000001 4.460000000000001 0 0 1 
10.000000000000004 45.54 Z"></path>
+</svg>
\ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/test-utils.ts 
b/packages/taler-wallet-webextension/src/test-utils.ts
index 379513782..7e7ddd88d 100644
--- a/packages/taler-wallet-webextension/src/test-utils.ts
+++ b/packages/taler-wallet-webextension/src/test-utils.ts
@@ -32,6 +32,7 @@ import {
 } from "preact";
 import { render as renderToString } from "preact-render-to-string";
 import { BackendProvider } from "./context/backend.js";
+import { TranslationProvider } from "./context/translation.js";
 import { BackgroundApiClient, wxApi } from "./wxApi.js";
 
 // When doing tests we want the requestAnimationFrame to be as fast as 
possible.
@@ -359,10 +360,12 @@ export function createWalletApiMock(): {
   };
 
   function TestingContext({
-    children,
+    children: _cs,
   }: {
     children: ComponentChildren;
   }): VNode {
+    let children = _cs;
+    children = create(TranslationProvider, { children }, children);
     return create(
       BackendProvider,
       {
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
index 10fcd84ce..4ec4c0ffe 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts
@@ -16,8 +16,9 @@
 
 import { TalerErrorDetail } from "@gnu-taler/taler-util";
 import { SyncTermsOfServiceResponse } from "@gnu-taler/taler-wallet-core";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import {
   ButtonHandler,
   TextFieldHandler,
@@ -25,11 +26,7 @@ import {
 } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import {
-  ConfirmProviderView,
-  LoadingUriView,
-  SelectProviderView,
-} from "./views.js";
+import { ConfirmProviderView, SelectProviderView } from "./views.js";
 
 export interface Props {
   onBack: () => Promise<void>;
@@ -50,8 +47,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-error";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface ConfirmProvider {
@@ -77,7 +74,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-error": LoadingUriView,
+  error: ErrorAlertView,
   "select-provider": SelectProviderView,
   "confirm-provider": ConfirmProviderView,
 };
diff --git 
a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx
index b633a595f..c3afc0d33 100644
--- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/views.tsx
@@ -17,12 +17,10 @@
 import { Amounts } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { Checkbox } from "../../components/Checkbox.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import {
   LightText,
   SmallLightText,
   SubTitle,
-  TermsOfService,
   Title,
 } from "../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
@@ -30,17 +28,6 @@ import { Button } from "../../mui/Button.js";
 import { TextField } from "../../mui/TextField.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function ConfirmProviderView({
   url,
   provider,
@@ -88,9 +75,8 @@ export function ConfirmProviderView({
             of service
           </i18n.Translate>
         </p>
-        {/* replace with <TermsOfService /> */}
         <Checkbox
-          label={<i18n.Translate>Accept terms of service</i18n.Translate>}
+          label={i18n.str`Accept terms of service`}
           name="terms"
           onToggle={tos.button.onClick}
           enabled={tos.value}
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx 
b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index 372db847c..46fe02225 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -25,13 +25,17 @@ import { createHashHistory } from "history";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import Router, { route, Route } from "preact-router";
 import { useEffect } from "preact/hooks";
+import { CurrentAlerts } from "../components/CurrentAlerts.js";
 import { LogoHeader } from "../components/LogoHeader.js";
 import PendingTransactions from "../components/PendingTransactions.js";
 import {
+  Link,
+  LinkPrimary,
   SubTitle,
   WalletAction,
   WalletBox,
 } from "../components/styled/index.js";
+import { AlertProvider } from "../context/alert.js";
 import { DevContextProvider } from "../context/devContext.js";
 import { IoCProviderForRuntime } from "../context/iocContext.js";
 import {
@@ -66,6 +70,7 @@ import { QrReaderPage } from "./QrReader.js";
 import { SettingsPage } from "./Settings.js";
 import { TransactionPage } from "./Transaction.js";
 import { WelcomePage } from "./Welcome.js";
+import CloseIcon from "../svg/close_24px.svg";
 
 export function Application(): VNode {
   const { i18n } = useTranslationContext();
@@ -495,15 +500,6 @@ function matchesRoute(url: string, route: string): boolean 
{
   return !result ? false : true;
 }
 
-function shouldShowPendingOperations(url: string): boolean {
-  return [
-    Pages.balanceHistory.pattern,
-    Pages.dev,
-    Pages.settings,
-    Pages.backup,
-  ].some((p) => matchesRoute(url, p));
-}
-
 function CallToActionTemplate({
   title,
   children,
@@ -511,11 +507,35 @@ function CallToActionTemplate({
   title: TranslatedString;
   children: ComponentChildren;
 }): VNode {
+  const { i18n } = useTranslationContext();
   return (
     <WalletAction>
       <LogoHeader />
+      <section style={{ display: "flex", justifyContent: "right", margin: 0 }}>
+        <LinkPrimary href={Pages.balance}>
+          <div
+            style={{
+              height: 24,
+              width: 24,
+              marginLeft: 4,
+              marginRight: 4,
+              border: "1px solid black",
+              borderRadius: 12,
+            }}
+            dangerouslySetInnerHTML={{ __html: CloseIcon }}
+          />
+        </LinkPrimary>
+      </section>
       <SubTitle>{title}</SubTitle>
-      {children}
+      <AlertProvider>
+        <CurrentAlerts />
+        {children}
+      </AlertProvider>
+      <section style={{ display: "flex", justifyContent: "right" }}>
+        <LinkPrimary href={Pages.balance}>
+          <i18n.Translate>Return to wallet</i18n.Translate>
+        </LinkPrimary>
+      </section>
     </WalletAction>
   );
 }
@@ -536,7 +556,10 @@ function WalletTemplate({
       {goToTransaction ? (
         <PendingTransactions goToTransaction={goToTransaction} />
       ) : undefined}
-      <WalletBox>{children}</WalletBox>
+      <CurrentAlerts />
+      <WalletBox>
+        <AlertProvider>{children}</AlertProvider>
+      </WalletBox>
     </Fragment>
   );
 }
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
index 6e987f965..48c9c9cb1 100644
--- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -29,8 +29,8 @@ import {
 } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
+import { AlertView } from "../components/CurrentAlerts.js";
 import { Loading } from "../components/Loading.js";
-import { LoadingError } from "../components/LoadingError.js";
 import { QR } from "../components/QR.js";
 import {
   BoldLight,
@@ -42,6 +42,7 @@ import {
   SmallText,
   WarningBox,
 } from "../components/styled/index.js";
+import { alertFromError } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
@@ -117,9 +118,11 @@ export function BackupPage({ onAddProvider }: Props): 
VNode {
   }
   if (status.hasError) {
     return (
-      <LoadingError
-        title={<i18n.Translate>Could not load backup 
providers</i18n.Translate>}
-        error={status}
+      <AlertView
+        alert={alertFromError(
+          i18n.str`Could not load backup providers`,
+          status,
+        )}
       />
     );
   }
@@ -219,11 +222,9 @@ export function BackupView({
           </div>
           <div>
             <Button variant="contained" onClick={onSyncAll}>
-              {providers.length > 1 ? (
-                <i18n.Translate>Sync all backups</i18n.Translate>
-              ) : (
-                <i18n.Translate>Sync now</i18n.Translate>
-              )}
+              {providers.length > 1
+                ? i18n.str`Sync all backups`
+                : i18n.str`Sync now`}
             </Button>
             <Button variant="contained" color="success" 
onClick={onAddProvider}>
               <i18n.Translate>Add provider</i18n.Translate>
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
index 6ffbccc27..6de406400 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
@@ -15,8 +15,9 @@
  */
 
 import { AmountJson, PaytoUri } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import {
   AmountFieldHandler,
   ButtonHandler,
@@ -27,7 +28,6 @@ import { ManageAccountPage } from "../ManageAccount/index.js";
 import { useComponentState } from "./state.js";
 import {
   AmountOrCurrencyErrorView,
-  LoadingErrorView,
   NoAccountToDepositView,
   NoEnoughBalanceView,
   ReadyView,
@@ -56,8 +56,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-error";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface AddingAccount {
@@ -107,7 +107,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-error": LoadingErrorView,
+  error: ErrorAlertView,
   "amount-or-currency-error": AmountOrCurrencyErrorView,
   "no-enough-balance": NoEnoughBalanceView,
   "no-accounts": NoAccountToDepositView,
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index 02e85a1c7..b597c77be 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -25,7 +25,9 @@ import {
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
@@ -36,6 +38,7 @@ export function useComponentState({
   onSuccess,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const parsed = amountStr === undefined ? undefined : 
Amounts.parse(amountStr);
   const currency = parsed !== undefined ? parsed.currency : currencyStr;
 
@@ -82,8 +85,8 @@ export function useComponentState({
   }
   if (hook.hasError) {
     return {
-      status: "loading-error",
-      error: hook,
+      status: "error",
+      error: alertFromError(i18n.str`Could not load balance information`, 
hook),
     };
   }
   const { accounts, balances } = hook.response;
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
index 6a28f31e1..0d827db43 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
@@ -18,31 +18,13 @@ import { Amounts, PaytoUri } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
 import { AmountField } from "../../components/AmountField.js";
 import { ErrorMessage } from "../../components/ErrorMessage.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { SelectList } from "../../components/SelectList.js";
-import {
-  ErrorText,
-  Input,
-  InputWithLabel,
-  SubTitle,
-  WarningBox,
-} from "../../components/styled/index.js";
+import { Input, SubTitle, WarningBox } from "../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
 import { Grid } from "../../mui/Grid.js";
 import { State } from "./index.js";
 
-export function LoadingErrorView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load deposit balance</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function AmountOrCurrencyErrorView(
   p: State.AmountOrCurrencyError,
 ): VNode {
@@ -50,11 +32,7 @@ export function AmountOrCurrencyErrorView(
 
   return (
     <ErrorMessage
-      title={
-        <i18n.Translate>
-          A currency or an amount should be indicated
-        </i18n.Translate>
-      }
+      title={i18n.str`A currency or an amount should be indicated`}
     />
   );
 }
@@ -66,11 +44,7 @@ export function NoEnoughBalanceView({
 
   return (
     <ErrorMessage
-      title={
-        <i18n.Translate>
-          There is no enough balance to make a deposit for currency {currency}
-        </i18n.Translate>
-      }
+      title={i18n.str`There is no enough balance to make a deposit for 
currency ${currency}`}
     />
   );
 }
@@ -150,7 +124,7 @@ export function ReadyView(state: State.Ready): VNode {
         >
           <Input>
             <SelectList
-              label={<i18n.Translate>Select account</i18n.Translate>}
+              label={i18n.str`Select account`}
               list={state.account.list}
               name="account"
               value={state.account.value}
@@ -171,14 +145,11 @@ export function ReadyView(state: State.Ready): VNode {
         </p>
         <Grid container spacing={2} columns={1}>
           <Grid item xs={1}>
-            <AmountField
-              label={<i18n.Translate>Amount</i18n.Translate>}
-              handler={state.amount}
-            />
+            <AmountField label={i18n.str`Amount`} handler={state.amount} />
           </Grid>
           <Grid item xs={1}>
             <AmountField
-              label={<i18n.Translate>Deposit fee</i18n.Translate>}
+              label={i18n.str`Deposit fee`}
               handler={{
                 value: state.totalFee,
               }}
@@ -186,7 +157,7 @@ export function ReadyView(state: State.Ready): VNode {
           </Grid>
           <Grid item xs={1}>
             <AmountField
-              label={<i18n.Translate>Total deposit</i18n.Translate>}
+              label={i18n.str`Total deposit`}
               handler={{
                 value: state.totalToDeposit,
               }}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
index f1e766a18..bd6b32e78 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/index.ts
@@ -14,12 +14,13 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { AmountFieldHandler, ButtonHandler } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView, SelectCurrencyView } from "./views.js";
+import { ReadyView, SelectCurrencyView } from "./views.js";
 
 export type Props = PropsGet | PropsSend;
 
@@ -49,8 +50,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-error";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface SelectCurrency {
@@ -80,7 +81,7 @@ export type Contact = {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-error": LoadingUriView,
+  error: ErrorAlertView,
   "select-currency": SelectCurrencyView,
   ready: ReadyView,
 };
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
index dd711f406..1fe324c5a 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts
@@ -17,7 +17,9 @@
 import { Amounts } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { assertUnreachable, RecursiveState } from "../../utils/index.js";
 import { Contact, Props, State } from "./index.js";
@@ -58,6 +60,8 @@ export function useComponentState(props: Props): 
RecursiveState<State> {
 
   if (!amount) {
     return () => {
+      // eslint-disable-next-line react-hooks/rules-of-hooks
+      const { i18n } = useTranslationContext();
       // eslint-disable-next-line react-hooks/rules-of-hooks
       const hook = useAsyncAsHook(() =>
         api.wallet.call(WalletApiOperation.ListExchanges, {}),
@@ -71,8 +75,8 @@ export function useComponentState(props: Props): 
RecursiveState<State> {
       }
       if (hook.hasError) {
         return {
-          status: "loading-error",
-          error: hook,
+          status: "error",
+          error: alertFromError(i18n.str`Could not load exchanges`, hook),
         };
       }
       const currencies: Record<string, string> = {};
diff --git 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
index a9a4b2e41..8a7a1fa97 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
@@ -16,7 +16,7 @@
 
 import { styled } from "@linaria/react";
 import { Fragment, h, VNode } from "preact";
-import { LoadingError } from "../../components/LoadingError.js";
+import { AmountField } from "../../components/AmountField.js";
 import { SelectList } from "../../components/SelectList.js";
 import {
   Input,
@@ -25,24 +25,14 @@ import {
   SvgIcon,
 } from "../../components/styled/index.js";
 import { useTranslationContext } from "../../context/translation.js";
-import { Pages } from "../../NavigationBar.js";
-import { Contact, State } from "./index.js";
-import arrowIcon from "../../svg/chevron-down.svg";
-import { AmountField } from "../../components/AmountField.js";
+import { Button } from "../../mui/Button.js";
 import { Grid } from "../../mui/Grid.js";
 import { Paper } from "../../mui/Paper.js";
-import { Button } from "../../mui/Button.js";
+import { Pages } from "../../NavigationBar.js";
+import arrowIcon from "../../svg/chevron-down.svg";
+import bankIcon from "../../svg/ri-bank-line.svg";
 import { assertUnreachable } from "../../utils/index.js";
-
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
+import { Contact, State } from "./index.js";
 
 export function SelectCurrencyView({
   currencies,
@@ -61,7 +51,7 @@ export function SelectCurrencyView({
       <p>
         <Input>
           <SelectList
-            label={<i18n.Translate>Known currencies</i18n.Translate>}
+            label={i18n.str`Known currencies`}
             list={currencies}
             name="lang"
             value={""}
@@ -214,7 +204,7 @@ export function ReadyGetView({
       </h1>
       <Grid container columns={2} justifyContent="space-between">
         <AmountField
-          label={<i18n.Translate>Amount</i18n.Translate>}
+          label={i18n.str`Amount`}
           required
           handler={amountHandler}
         />
@@ -304,7 +294,7 @@ export function ReadySendView({
 
       <div>
         <AmountField
-          label={<i18n.Translate>Amount</i18n.Translate>}
+          label={i18n.str`Amount`}
           required
           handler={amountHandler}
         />
@@ -377,7 +367,6 @@ export function ReadySendView({
     </Container>
   );
 }
-import bankIcon from "../../svg/ri-bank-line.svg";
 
 function RowExample({
   info,
diff --git 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/index.ts 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/index.ts
index 95badb218..afbaf1945 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/index.ts
+++ 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/index.ts
@@ -14,11 +14,12 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export interface Props {
   p: string;
@@ -33,8 +34,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-error";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -48,7 +49,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-error": LoadingUriView,
+  error: ErrorAlertView,
   ready: ReadyView,
 };
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/views.tsx
index 5784a7db5..dc8a42b84 100644
--- 
a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/views.tsx
+++ 
b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/views.tsx
@@ -15,21 +15,9 @@
  */
 
 import { h, VNode } from "preact";
-import { LoadingError } from "../../components/LoadingError.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function ReadyView({ error }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts
index 10e44ce7d..299c236c4 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts
@@ -20,7 +20,9 @@ import {
   ExchangeListItem,
   FeeDescriptionPair,
 } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { State as SelectExchangeState } from 
"../../hooks/useSelectedExchange.js";
 import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js";
@@ -28,7 +30,6 @@ import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
 import {
   ComparingView,
-  ErrorLoadingView,
   NoExchangesView,
   PrivacyContentView,
   ReadyView,
@@ -58,8 +59,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "error-loading";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -99,7 +100,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "error-loading": ErrorLoadingView,
+  error: ErrorAlertView,
   comparing: ComparingView,
   "no-exchange": NoExchangesView,
   "showing-tos": TosContentView,
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
index 3c10febd9..cfb32cbbb 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts
@@ -20,7 +20,9 @@ import {
   WalletApiOperation,
 } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
@@ -31,6 +33,7 @@ export function useComponentState({
   currentExchange,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const initialValue = exchanges.findIndex(
     (e) => e.exchangeBaseUrl === currentExchange,
   );
@@ -84,8 +87,11 @@ export function useComponentState({
   }
   if (hook.hasError) {
     return {
-      status: "error-loading",
-      error: hook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load exchange details info`,
+        hook,
+      ),
     };
   }
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/views.tsx
index 26ff2c0d3..d01ce7ca0 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/views.tsx
@@ -20,7 +20,6 @@ import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { Amount } from "../../components/Amount.js";
 import { ErrorMessage } from "../../components/ErrorMessage.js";
-import { LoadingError } from "../../components/LoadingError.js";
 import { SelectList } from "../../components/SelectList.js";
 import { Input, SvgIcon } from "../../components/styled/index.js";
 import { TermsOfService } from "../../components/TermsOfService/index.js";
@@ -110,17 +109,6 @@ const Container = styled.div`
   }
 `;
 
-export function ErrorLoadingView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load exchange fees</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function PrivacyContentView({
   exchangeUrl,
   onClose,
@@ -156,19 +144,11 @@ export function NoExchangesView({
 }: SelectExchangeState.NoExchange): VNode {
   const { i18n } = useTranslationContext();
   if (!currency) {
-    return (
-      <ErrorMessage
-        title={<i18n.Translate>Could not find any exchange</i18n.Translate>}
-      />
-    );
+    return <ErrorMessage title={i18n.str`Could not find any exchange`} />;
   }
   return (
     <ErrorMessage
-      title={
-        <i18n.Translate>
-          Could not find any exchange for the currency {currency}
-        </i18n.Translate>
-      }
+      title={i18n.str`Could not find any exchange for the currency 
${currency}`}
     />
   );
 }
diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx 
b/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx
index 8c31d8d95..404fc8bee 100644
--- a/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSetUrl.tsx
@@ -140,15 +140,13 @@ export function ExchangeSetUrlPage({
         )}
         {error && (
           <ErrorMessage
-            title={
-              <i18n.Translate>Unable to verify this exchange</i18n.Translate>
-            }
+            title={i18n.str`Unable to verify this exchange`}
             description={error}
           />
         )}
         {confirmationError && (
           <ErrorMessage
-            title={<i18n.Translate>Unable to add this 
exchange</i18n.Translate>}
+            title={i18n.str`Unable to add this exchange`}
             description={confirmationError}
           />
         )}
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.tsx
index 50f634f52..143d3adbb 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -23,8 +23,8 @@ import {
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
+import { AlertView } from "../components/CurrentAlerts.js";
 import { Loading } from "../components/Loading.js";
-import { LoadingError } from "../components/LoadingError.js";
 import {
   CenteredBoldText,
   CenteredText,
@@ -33,6 +33,7 @@ import {
 } from "../components/styled/index.js";
 import { Time } from "../components/Time.js";
 import { TransactionItem } from "../components/TransactionItem.js";
+import { alertFromError } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
@@ -71,13 +72,11 @@ export function HistoryPage({
 
   if (state.hasError) {
     return (
-      <LoadingError
-        title={
-          <i18n.Translate>
-            Could not load the list of transactions
-          </i18n.Translate>
-        }
-        error={state}
+      <AlertView
+        alert={alertFromError(
+          i18n.str`Could not load the list of transactions`,
+          state,
+        )}
       />
     );
   }
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
index 8541821b7..3a00d48ce 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/index.ts
@@ -15,8 +15,9 @@
  */
 
 import { KnownBankAccountsInfo } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import {
   ButtonHandler,
   SelectFieldHandler,
@@ -24,7 +25,7 @@ import {
 } from "../../mui/handlers.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export interface Props {
   currency: string;
@@ -41,8 +42,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-error";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -68,7 +69,7 @@ export type AccountByType = {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-error": LoadingUriView,
+  error: ErrorAlertView,
   ready: ReadyView,
 };
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
index 9690a5c79..176a8d100 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts
@@ -21,7 +21,9 @@ import {
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useState } from "preact/hooks";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { AccountByType, Props, State } from "./index.js";
 
@@ -31,6 +33,7 @@ export function useComponentState({
   onCancel,
 }: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const hook = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),
   );
@@ -47,8 +50,8 @@ export function useComponentState({
   }
   if (hook.hasError) {
     return {
-      status: "loading-error",
-      error: hook,
+      status: "error",
+      error: alertFromError(i18n.str`Could not load known bank accounts`, 
hook),
     };
   }
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
index 3af0d5505..e5be8d17d 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
@@ -23,11 +23,10 @@ import {
 import { styled } from "@linaria/react";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import { LoadingError } from "../../components/LoadingError.js";
+import { ErrorMessage } from "../../components/ErrorMessage.js";
 import { SelectList } from "../../components/SelectList.js";
 import {
   Input,
-  LightText,
   SubTitle,
   SvgIcon,
   WarningText,
@@ -37,10 +36,9 @@ import { Button } from "../../mui/Button.js";
 import { TextFieldHandler } from "../../mui/handlers.js";
 import { TextField } from "../../mui/TextField.js";
 import checkIcon from "../../svg/check_24px.svg";
-import warningIcon from "../../svg/warning_24px.svg";
 import deleteIcon from "../../svg/delete_24px.svg";
+import warningIcon from "../../svg/warning_24px.svg";
 import { State } from "./index.js";
-import { ErrorMessage } from "../../components/ErrorMessage.js";
 
 type AccountType = "bitcoin" | "x-taler-bank" | "iban";
 type ComponentFormByAccountType = {
@@ -80,17 +78,6 @@ const AccountTable = styled.table`
   }
 `;
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 export function ReadyView({
   currency,
   error,
@@ -118,14 +105,14 @@ export function ReadyView({
 
         {error && (
           <ErrorMessage
-            title={<i18n.Translate>Unable add this account</i18n.Translate>}
+            title={i18n.str`Unable add this account`}
             description={error}
           />
         )}
         <p>
           <Input>
             <SelectList
-              label={<i18n.Translate>Select account type</i18n.Translate>}
+              label={i18n.str`Select account type`}
               list={accountType.list}
               name="accountType"
               value={accountType.value}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Notifications/index.ts 
b/packages/taler-wallet-webextension/src/wallet/Notifications/index.ts
index 4697ca549..22b3adb0f 100644
--- a/packages/taler-wallet-webextension/src/wallet/Notifications/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/Notifications/index.ts
@@ -15,11 +15,12 @@
  */
 
 import { UserAttentionUnreadList } from "@gnu-taler/taler-util";
+import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
-import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ErrorAlert } from "../../context/alert.js";
 import { compose, StateViewMap } from "../../utils/index.js";
 import { useComponentState } from "./state.js";
-import { LoadingUriView, ReadyView } from "./views.js";
+import { ReadyView } from "./views.js";
 
 export type Props = object;
 
@@ -32,8 +33,8 @@ export namespace State {
   }
 
   export interface LoadingUriError {
-    status: "loading-error";
-    error: HookError;
+    status: "error";
+    error: ErrorAlert;
   }
 
   export interface BaseInfo {
@@ -49,7 +50,7 @@ export namespace State {
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
-  "loading-error": LoadingUriView,
+  error: ErrorAlertView,
   ready: ReadyView,
 };
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Notifications/state.ts 
b/packages/taler-wallet-webextension/src/wallet/Notifications/state.ts
index 648e490ce..0e06a1e75 100644
--- a/packages/taler-wallet-webextension/src/wallet/Notifications/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/Notifications/state.ts
@@ -15,12 +15,15 @@
  */
 
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { alertFromError } from "../../context/alert.js";
 import { useBackendContext } from "../../context/backend.js";
+import { useTranslationContext } from "../../context/translation.js";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(p: Props): State {
   const api = useBackendContext();
+  const { i18n } = useTranslationContext();
   const hook = useAsyncAsHook(async () => {
     return await api.wallet.call(
       WalletApiOperation.GetUserAttentionRequests,
@@ -34,10 +37,14 @@ export function useComponentState(p: Props): State {
       error: undefined,
     };
   }
+
   if (hook.hasError) {
     return {
-      status: "loading-error",
-      error: hook,
+      status: "error",
+      error: alertFromError(
+        i18n.str`Could not load user attention request`,
+        hook,
+      ),
     };
   }
 
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx 
b/packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx
index 9146d8837..8d0bb34c0 100644
--- a/packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Notifications/views.tsx
@@ -20,7 +20,6 @@ import {
   AttentionType,
 } from "@gnu-taler/taler-util";
 import { Fragment, h, VNode } from "preact";
-import { LoadingError } from "../../components/LoadingError.js";
 import {
   Column,
   DateSeparator,
@@ -37,17 +36,6 @@ import { Pages } from "../../NavigationBar.js";
 import { assertUnreachable } from "../../utils/index.js";
 import { State } from "./index.js";
 
-export function LoadingUriView({ error }: State.LoadingUriError): VNode {
-  const { i18n } = useTranslationContext();
-
-  return (
-    <LoadingError
-      title={<i18n.Translate>Could not load notifications</i18n.Translate>}
-      error={error}
-    />
-  );
-}
-
 const term = 1000 * 60 * 60 * 24;
 function normalizeToDay(x: number): number {
   return Math.round(x / term) * term;
diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
index eb86c9a3f..286a2a88d 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
@@ -127,11 +127,7 @@ export function SetUrlView({
         </Title>
         {error && (
           <ErrorMessage
-            title={
-              <i18n.Translate>
-                Could not get provider information
-              </i18n.Translate>
-            }
+            title={i18n.str`Could not get provider information`}
             description={error}
           />
         )}
@@ -223,7 +219,7 @@ export function ConfirmProviderView({
         </SubTitle>
         <p>
           {Amounts.isZero(provider.annual_fee) ? (
-            <i18n.Translate>free of charge</i18n.Translate>
+            i18n.str`free of charge`
           ) : (
             <i18n.Translate>
               {provider.annual_fee} per year of service
@@ -240,7 +236,7 @@ export function ConfirmProviderView({
           </i18n.Translate>
         </p>
         <Checkbox
-          label={<i18n.Translate>Accept terms of service</i18n.Translate>}
+          label={i18n.str`Accept terms of service`}
           name="terms"
           onToggle={async () => setAccepted((old) => !old)}
           enabled={accepted}
diff --git 
a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
index 46d54e871..9b72c0fae 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx
@@ -23,11 +23,12 @@ import {
   WalletApiOperation,
 } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, VNode } from "preact";
+import { AlertView } from "../components/CurrentAlerts.js";
 import { ErrorMessage } from "../components/ErrorMessage.js";
 import { Loading } from "../components/Loading.js";
-import { LoadingError } from "../components/LoadingError.js";
 import { PaymentStatus, SmallLightText } from "../components/styled/index.js";
 import { Time } from "../components/Time.js";
+import { alertFromError } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
@@ -65,14 +66,11 @@ export function ProviderDetailPage({
   }
   if (state.hasError) {
     return (
-      <LoadingError
-        title={
-          <i18n.Translate>
-            There was an error loading the provider detail for &quot;
-            {providerURL}&quot;
-          </i18n.Translate>
-        }
-        error={state}
+      <AlertView
+        alert={alertFromError(
+          i18n.str`There was an error loading the provider detail for 
&quot;${providerURL}&quot;`,
+          state,
+        )}
       />
     );
   }
@@ -270,9 +268,7 @@ function Error({ info }: { info: ProviderInfo }): VNode {
   if (info.lastError) {
     return (
       <ErrorMessage
-        title={
-          <i18n.Translate>This provider has reported an error</i18n.Translate>
-        }
+        title={i18n.str`This provider has reported an error`}
         description={info.lastError.hint}
       />
     );
@@ -282,32 +278,17 @@ function Error({ info }: { info: ProviderInfo }): VNode {
       case "backup-conflicting-device":
         return (
           <ErrorMessage
-            title={
-              <Fragment>
-                <i18n.Translate>
-                  There is conflict with another backup from{" "}
-                  <b>{info.backupProblem.otherDeviceId}</b>
-                </i18n.Translate>
-              </Fragment>
-            }
+            title={i18n.str`There is conflict with another backup from 
&quot;${info.backupProblem.otherDeviceId}&quot;`}
           />
         );
       case "backup-unreadable":
-        return (
-          <ErrorMessage
-            title={<i18n.Translate>Backup is not readable</i18n.Translate>}
-          />
-        );
+        return <ErrorMessage title={i18n.str`Backup is not readable`} />;
       default:
         return (
           <ErrorMessage
-            title={
-              <Fragment>
-                <i18n.Translate>
-                  Unknown backup problem: {JSON.stringify(info.backupProblem)}
-                </i18n.Translate>
-              </Fragment>
-            }
+            title={i18n.str`Unknown backup problem: ${JSON.stringify(
+              info.backupProblem,
+            )}`}
           />
         );
     }
diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx 
b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
index a259f7c9a..c366f014f 100644
--- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx
@@ -42,8 +42,8 @@ export function ReserveCreated({
   if (!paytoURI) {
     return (
       <ErrorMessage
-        title={<i18n.Translate>Could not parse the payto URI</i18n.Translate>}
-        description={<i18n.Translate>Please check the uri</i18n.Translate>}
+        title={i18n.str`Could not parse the payto URI`}
+        description={i18n.str`Please check the uri`}
       />
     );
   }
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx 
b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
index 768a4ca6a..ed1bc838a 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
@@ -111,13 +111,13 @@ export function SettingsView({
       <section>
         {autoOpenToggle.button.error && (
           <ErrorTalerOperation
-            title={<i18n.Translate>Could not toggle auto-open</i18n.Translate>}
+            title={i18n.str`Could not toggle auto-open`}
             error={autoOpenToggle.button.error.errorDetail}
           />
         )}
         {/* {clipboardToggle.button.error && (
           <ErrorTalerOperation
-            title={<i18n.Translate>Could not toggle clipboard</i18n.Translate>}
+            title={i18n.str`Could not toggle clipboard`}
             error={clipboardToggle.button.error.errorDetail}
           />
         )} */}
@@ -125,11 +125,7 @@ export function SettingsView({
           <i18n.Translate>Navigator</i18n.Translate>
         </SubTitle>
         <Checkbox
-          label={
-            <i18n.Translate>
-              Automatically open wallet based on page content
-            </i18n.Translate>
-          }
+          label={i18n.str`Automatically open wallet based on page content`}
           name="autoOpen"
           description={
             <i18n.Translate>
@@ -142,9 +138,7 @@ export function SettingsView({
         />
         {/* <Checkbox
           label={
-            <i18n.Translate>
-              Automatically check clipboard for Taler URI
-            </i18n.Translate>
+            i18n.str`Automatically check clipboard for Taler URI`
           }
           name="clipboard"
           description={
@@ -241,13 +235,9 @@ export function SettingsView({
           <i18n.Translate>Troubleshooting</i18n.Translate>
         </SubTitle>
         <Checkbox
-          label={<i18n.Translate>Developer mode</i18n.Translate>}
+          label={i18n.str`Developer mode`}
           name="devMode"
-          description={
-            <i18n.Translate>
-              More options and information useful for debugging
-            </i18n.Translate>
-          }
+          description={i18n.str`More options and information useful for 
debugging`}
           enabled={devModeToggle.value!}
           onToggle={devModeToggle.button.onClick!}
         />
@@ -271,7 +261,7 @@ export function SettingsView({
         </SubTitle>
         {coreVersion && (
           <Part
-            title={<i18n.Translate>Wallet Core</i18n.Translate>}
+            title={i18n.str`Wallet Core`}
             text={
               <span>
                 {coreVersion.version}{" "}
@@ -281,7 +271,7 @@ export function SettingsView({
           />
         )}
         <Part
-          title={<i18n.Translate>Web Extension</i18n.Translate>}
+          title={i18n.str`Web Extension`}
           text={
             <span>
               {webexVersion.version}{" "}
@@ -292,15 +282,15 @@ export function SettingsView({
         {coreVersion && (
           <JustInDevMode>
             <Part
-              title={<i18n.Translate>Exchange compatibility</i18n.Translate>}
+              title={i18n.str`Exchange compatibility`}
               text={<span>{coreVersion.exchange}</span>}
             />
             <Part
-              title={<i18n.Translate>Merchant compatibility</i18n.Translate>}
+              title={i18n.str`Merchant compatibility`}
               text={<span>{coreVersion.merchant}</span>}
             />
             <Part
-              title={<i18n.Translate>Bank compatibility</i18n.Translate>}
+              title={i18n.str`Bank compatibility`}
               text={<span>{coreVersion.bank}</span>}
             />
           </JustInDevMode>
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index b7eb4a947..542694490 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -32,6 +32,7 @@ import {
   TransactionRefund,
   TransactionTip,
   TransactionType,
+  TranslatedString,
   WithdrawalType,
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
@@ -43,9 +44,8 @@ import emptyImg from "../../static/img/empty.png";
 import { Amount } from "../components/Amount.js";
 import { BankDetailsByPaytoType } from 
"../components/BankDetailsByPaytoType.js";
 import { CopyButton } from "../components/CopyButton.js";
-import { ErrorTalerOperation } from "../components/ErrorTalerOperation.js";
+import { AlertView, ErrorAlertView } from "../components/CurrentAlerts.js";
 import { Loading } from "../components/Loading.js";
-import { LoadingError } from "../components/LoadingError.js";
 import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js";
 import { QR } from "../components/QR.js";
 import { ShowFullContractTermPopup } from 
"../components/ShowFullContractTermPopup.js";
@@ -60,6 +60,7 @@ import {
   WarningBox,
 } from "../components/styled/index.js";
 import { Time } from "../components/Time.js";
+import { alertFromError } from "../context/alert.js";
 import { useBackendContext } from "../context/backend.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
@@ -98,13 +99,11 @@ export function TransactionPage({
 
   if (state.hasError) {
     return (
-      <LoadingError
-        title={
-          <i18n.Translate>
-            Could not load the transaction information
-          </i18n.Translate>
-        }
-        error={state}
+      <AlertView
+        alert={alertFromError(
+          i18n.str`Could not load transaction information`,
+          state,
+        )}
       />
     );
   }
@@ -199,14 +198,14 @@ export function TransactionView({
     return (
       <Fragment>
         <section style={{ padding: 8, textAlign: "center" }}>
-          <ErrorTalerOperation
-            title={
-              <i18n.Translate>
-                There was an error trying to complete the transaction
-              </i18n.Translate>
-            }
-            error={transaction?.error}
-          />
+          {transaction?.error ? (
+            <ErrorAlertView
+              error={alertFromError(
+                i18n.str`There was an error trying to complete the 
transaction`,
+                transaction.error,
+              )}
+            />
+          ) : undefined}
           {transaction.pending && (
             <WarningBox>
               <i18n.Translate>This transaction is not 
completed</i18n.Translate>
@@ -367,7 +366,7 @@ export function TransactionView({
           </Fragment>
         )}
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <WithdrawDetails
               amount={{
@@ -420,7 +419,7 @@ export function TransactionView({
         <br />
         {transaction.refunds.length > 0 ? (
           <Part
-            title={<i18n.Translate>Refunds</i18n.Translate>}
+            title={i18n.str`Refunds`}
             text={
               <table>
                 {transaction.refunds.map((r, i) => {
@@ -462,7 +461,7 @@ export function TransactionView({
               picked up.
             </i18n.Translate>
             <Part
-              title={<i18n.Translate>Offer</i18n.Translate>}
+              title={i18n.str`Offer`}
               text={<Amount value={pendingRefund} />}
               kind="positive"
             />
@@ -480,17 +479,17 @@ export function TransactionView({
           </InfoBox>
         )}
         <Part
-          title={<i18n.Translate>Merchant</i18n.Translate>}
+          title={i18n.str`Merchant`}
           text={<MerchantDetails merchant={transaction.info.merchant} />}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Invoice ID</i18n.Translate>}
-          text={transaction.info.orderId}
+          title={i18n.str`Invoice ID`}
+          text={transaction.info.orderId as TranslatedString}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <PurchaseDetails
               price={price}
@@ -520,12 +519,12 @@ export function TransactionView({
         </Header>
         {payto && <PartPayto payto={payto} kind="neutral" />}
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={<DepositDetails transaction={transaction} />}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Wire transfer deadline</i18n.Translate>}
+          title={i18n.str`Wire transfer deadline`}
           text={
             <Time
               timestamp={AbsoluteTime.fromTimestamp(
@@ -557,7 +556,7 @@ export function TransactionView({
           {transaction.exchangeBaseUrl}
         </Header>
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={<RefreshDetails transaction={transaction} />}
         />
       </TransactionTemplate>
@@ -578,12 +577,12 @@ export function TransactionView({
           {transaction.merchantBaseUrl}
         </Header>
         {/* <Part
-          title={<i18n.Translate>Merchant</i18n.Translate>}
+          title={i18n.str`Merchant`}
           text={<MerchantDetails merchant={transaction.merchant} />}
           kind="neutral"
         /> */}
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={<TipDetails transaction={transaction} />}
         />
       </TransactionTemplate>
@@ -604,12 +603,12 @@ export function TransactionView({
         </Header>
 
         <Part
-          title={<i18n.Translate>Merchant</i18n.Translate>}
-          text={transaction.info.merchant.name}
+          title={i18n.str`Merchant`}
+          text={transaction.info.merchant.name as TranslatedString}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Original order ID</i18n.Translate>}
+          title={i18n.str`Original order ID`}
           text={
             <a
               href={Pages.balanceTransaction({
@@ -622,12 +621,12 @@ export function TransactionView({
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Purchase summary</i18n.Translate>}
-          text={transaction.info.summary}
+          title={i18n.str`Purchase summary`}
+          text={transaction.info.summary as TranslatedString}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={<RefundDetails transaction={transaction} />}
         />
       </TransactionTemplate>
@@ -683,25 +682,25 @@ export function TransactionView({
 
         {transaction.info.summary ? (
           <Part
-            title={<i18n.Translate>Subject</i18n.Translate>}
-            text={transaction.info.summary}
+            title={i18n.str`Subject`}
+            text={transaction.info.summary as TranslatedString}
             kind="neutral"
           />
         ) : undefined}
         <Part
-          title={<i18n.Translate>Exchange</i18n.Translate>}
-          text={transaction.exchangeBaseUrl}
+          title={i18n.str`Exchange`}
+          text={transaction.exchangeBaseUrl as TranslatedString}
           kind="neutral"
         />
         {transaction.pending /** pending is not-pay */ && (
           <Part
-            title={<i18n.Translate>URI</i18n.Translate>}
+            title={i18n.str`URI`}
             text={<ShowQrWithCopy text={transaction.talerUri} />}
             kind="neutral"
           />
         )}
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <InvoiceDetails
               amount={{
@@ -730,18 +729,18 @@ export function TransactionView({
 
         {transaction.info.summary ? (
           <Part
-            title={<i18n.Translate>Subject</i18n.Translate>}
-            text={transaction.info.summary}
+            title={i18n.str`Subject`}
+            text={transaction.info.summary as TranslatedString}
             kind="neutral"
           />
         ) : undefined}
         <Part
-          title={<i18n.Translate>Exchange</i18n.Translate>}
-          text={transaction.exchangeBaseUrl}
+          title={i18n.str`Exchange`}
+          text={transaction.exchangeBaseUrl as TranslatedString}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <InvoiceDetails
               amount={{
@@ -769,25 +768,25 @@ export function TransactionView({
 
         {transaction.info.summary ? (
           <Part
-            title={<i18n.Translate>Subject</i18n.Translate>}
-            text={transaction.info.summary}
+            title={i18n.str`Subject`}
+            text={transaction.info.summary as TranslatedString}
             kind="neutral"
           />
         ) : undefined}
         <Part
-          title={<i18n.Translate>Exchange</i18n.Translate>}
-          text={transaction.exchangeBaseUrl}
+          title={i18n.str`Exchange`}
+          text={transaction.exchangeBaseUrl as TranslatedString}
           kind="neutral"
         />
         {/* {transaction.pending && ( //pending is not-received 
             )} */}
         <Part
-          title={<i18n.Translate>URI</i18n.Translate>}
+          title={i18n.str`URI`}
           text={<ShowQrWithCopy text={transaction.talerUri} />}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <TransferDetails
               amount={{
@@ -816,18 +815,18 @@ export function TransactionView({
 
         {transaction.info.summary ? (
           <Part
-            title={<i18n.Translate>Subject</i18n.Translate>}
-            text={transaction.info.summary}
+            title={i18n.str`Subject`}
+            text={transaction.info.summary as TranslatedString}
             kind="neutral"
           />
         ) : undefined}
         <Part
-          title={<i18n.Translate>Exchange</i18n.Translate>}
-          text={transaction.exchangeBaseUrl}
+          title={i18n.str`Exchange`}
+          text={transaction.exchangeBaseUrl as TranslatedString}
           kind="neutral"
         />
         <Part
-          title={<i18n.Translate>Details</i18n.Translate>}
+          title={i18n.str`Details`}
           text={
             <TransferDetails
               amount={{
@@ -1245,7 +1244,7 @@ export function PurchaseDetails({
           <td colSpan={2}>
             <PartCollapsible
               big
-              title={<i18n.Translate>Products</i18n.Translate>}
+              title={i18n.str`Products`}
               text={
                 <ListOfProducts>
                   {info.products?.map((p, k) => (
@@ -1274,7 +1273,7 @@ export function PurchaseDetails({
           <td colSpan={2}>
             <PartCollapsible
               big
-              title={<i18n.Translate>Delivery</i18n.Translate>}
+              title={i18n.str`Delivery`}
               text={
                 <DeliveryDetails
                   date={info.delivery_date}
@@ -1508,7 +1507,7 @@ function Header({
   total: AmountJson;
   children: ComponentChildren;
   kind: Kind;
-  type: string;
+  type: TranslatedString;
 }): VNode {
   return (
     <div
diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx 
b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
index 0b64417b8..b243eaa1c 100644
--- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
@@ -91,11 +91,7 @@ export function View({
           <i18n.Translate>Permissions</i18n.Translate>
         </SubTitle>
         <Checkbox
-          label={
-            <i18n.Translate>
-              Automatically open wallet based on page content
-            </i18n.Translate>
-          }
+          label={i18n.str`Automatically open wallet based on page content`}
           name="perm"
           description={
             <i18n.Translate>
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts 
b/packages/taler-wallet-webextension/src/wxApi.ts
index 5c2a577b3..a41372e37 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -25,6 +25,8 @@ import {
   CoreApiResponse,
   Logger,
   NotificationType,
+  TalerErrorCode,
+  TalerErrorDetail,
   WalletDiagnostics,
 } from "@gnu-taler/taler-util";
 import {
@@ -33,11 +35,14 @@ import {
   WalletCoreOpKeys,
   WalletCoreRequestType,
   WalletCoreResponseType,
+  WalletOperations,
 } from "@gnu-taler/taler-wallet-core";
-import { MessageFromBackend, MessageFromFrontendBackground, 
MessageFromFrontendWallet } from "./platform/api.js";
 import {
-  platform,
-} from "./platform/foreground.js";
+  MessageFromBackend,
+  MessageFromFrontendBackground,
+  MessageFromFrontendWallet,
+} from "./platform/api.js";
+import { platform } from "./platform/foreground.js";
 
 /**
  *
@@ -88,6 +93,25 @@ export interface BackgroundApiClient {
   ): Promise<BackgroundOperations[Op]["response"]>;
 }
 
+export class WalletError extends Error {
+  public errorDetail: TalerError;
+
+  constructor(op: string, e: TalerError) {
+    super(`Wallet operation "${op}" failed: ${e.message}`);
+    this.errorDetail = e;
+    // Object.setPrototypeOf(this, WalletError.prototype);
+  }
+}
+export class BackgroundError extends Error {
+  public errorDetail: TalerErrorDetail;
+
+  constructor(op: string, e: TalerErrorDetail) {
+    super(`Background operation "${op}" failed: ${e.message}`);
+    this.errorDetail = e;
+    // Object.setPrototypeOf(this, BackgroundError.prototype);
+  }
+}
+
 /**
  * BackgroundApiClient integration with browser platform
  */
@@ -106,13 +130,18 @@ class BackgroundApiClientImpl implements 
BackgroundApiClient {
 
     try {
       response = await platform.sendMessageToBackground(message);
-    } catch (e) {
+    } catch (error) {
       console.log("Error calling backend");
-      throw new Error(`Error contacting backend: ${e}`);
+      if (error instanceof Error) {
+        throw new BackgroundError(operation, {
+          code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
+        });
+      }
+      throw error;
     }
     logger.info("got response", response);
     if (response.type === "error") {
-      throw TalerError.fromUncheckedDetail(response.error);
+      throw new BackgroundError(operation, response.error);
     }
     return response.result as any;
   }
@@ -140,7 +169,8 @@ class WalletApiClientImpl implements WalletCoreApiClient {
     }
     logger.info("got response", response);
     if (response.type === "error") {
-      throw TalerError.fromUncheckedDetail(response.error);
+      const error = TalerError.fromUncheckedDetail(response.error);
+      throw new WalletError(operation, error);
     }
     return response.result as any;
   }
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts 
b/packages/taler-wallet-webextension/src/wxBackend.ts
index b75e92004..c7b964c28 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -46,9 +46,7 @@ import {
   WalletStoresV1,
 } from "@gnu-taler/taler-wallet-core";
 import { BrowserHttpLib } from "./browserHttpLib.js";
-import {
-  platform,
-} from "./platform/background.js";
+import { platform } from "./platform/background.js";
 import {
   MessageFromBackend,
   MessageFromFrontend,
@@ -199,13 +197,22 @@ async function dispatch<Op extends WalletOperations | 
BackgroundOperations>(
         ),
       };
     }
-    const result = await handler(req.payload);
-    return {
-      type: "response",
-      id: req.id,
-      operation: String(req.operation),
-      result,
-    };
+    try {
+      const result = await handler(req.payload);
+      return {
+        type: "response",
+        id: req.id,
+        operation: String(req.operation),
+        result,
+      };
+    } catch (er) {
+      return {
+        type: "error",
+        id: req.id,
+        error: getErrorDetailFromException(er),
+        operation: String(req.operation),
+      };
+    }
   }
 
   if (req.channel === "wallet") {
@@ -232,7 +239,9 @@ async function dispatch<Op extends WalletOperations | 
BackgroundOperations>(
     id: anyReq.id,
     operation: String(anyReq.operation),
     error: getErrorDetailFromException(
-      Error(`unknown channel ${anyReq.channel}`),
+      Error(
+        `unknown channel ${anyReq.channel}, should be "background" or 
"wallet"`,
+      ),
     ),
   };
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6983ba177..0355f1535 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -649,7 +649,7 @@ importers:
       qrcode-generator: ^1.4.4
       rimraf: ^3.0.2
       tslib: ^2.4.0
-      typescript: ^4.8.4
+      typescript: 4.9.4
     dependencies:
       '@gnu-taler/taler-util': link:../taler-util
       '@gnu-taler/taler-wallet-core': link:../taler-wallet-core
@@ -687,7 +687,7 @@ importers:
       preact-cli: 3.4.1_i2jslynuqxjzp37vlc24guk7gu
       preact-render-to-string: 5.2.6_preact@10.11.3
       rimraf: 3.0.2
-      typescript: 4.8.4
+      typescript: 4.9.4
 
   packages/web-util:
     specifiers:
@@ -8412,7 +8412,7 @@ packages:
       minipass-pipeline: 1.2.4
       mkdirp: 1.0.4
       p-map: 4.0.0
-      promise-inflight: 1.0.1
+      promise-inflight: 1.0.1_bluebird@3.7.2
       rimraf: 3.0.2
       ssri: 8.0.1
       tar: 6.1.11
@@ -18096,15 +18096,6 @@ packages:
     engines: {node: '>=0.4.0'}
     dev: true
 
-  /promise-inflight/1.0.1:
-    resolution: {integrity: 
sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
-    peerDependencies:
-      bluebird: '*'
-    peerDependenciesMeta:
-      bluebird:
-        optional: true
-    dev: true
-
   /promise-inflight/1.0.1_bluebird@3.7.2:
     resolution: {integrity: 
sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
     peerDependencies:
@@ -20691,6 +20682,12 @@ packages:
     hasBin: true
     dev: true
 
+  /typescript/4.9.4:
+    resolution: {integrity: 
sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==}
+    engines: {node: '>=4.2.0'}
+    hasBin: true
+    dev: true
+
   /uglify-js/3.17.4:
     resolution: {integrity: 
sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==}
     engines: {node: '>=0.8.0'}

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