gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 01/02: sharing components in web-util


From: gnunet
Subject: [taler-wallet-core] 01/02: sharing components in web-util
Date: Sun, 05 Nov 2023 22:20:33 +0100

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

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

commit b58d53dd93bd8e97aecc28fae788c5c7051fd73d
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Sun Nov 5 18:04:22 2023 -0300

    sharing components in web-util
---
 packages/aml-backoffice-ui/src/App.tsx             |  11 +-
 packages/aml-backoffice-ui/src/Dashboard.tsx       |  26 +++--
 packages/aml-backoffice-ui/src/account.ts          | 128 ---------------------
 .../src/context/config.ts                          |  18 +--
 .../aml-backoffice-ui/src/handlers/Caption.tsx     |   7 +-
 packages/aml-backoffice-ui/src/hooks/useBackend.ts |  27 +++--
 .../aml-backoffice-ui/src/hooks/useCaseDetails.ts  | 103 ++++-------------
 packages/aml-backoffice-ui/src/hooks/useCases.ts   | 126 +++++++++-----------
 packages/aml-backoffice-ui/src/hooks/useOfficer.ts |  20 ++--
 .../aml-backoffice-ui/src/pages/CaseDetails.tsx    |  83 ++++++-------
 packages/aml-backoffice-ui/src/pages/Cases.tsx     |  46 ++++----
 .../aml-backoffice-ui/src/pages/NewFormEntry.tsx   |  19 ++-
 packages/aml-backoffice-ui/src/pages/Officer.tsx   |   4 +-
 .../aml-backoffice-ui/src/pages/UnlockAccount.tsx  |   3 +-
 packages/aml-backoffice-ui/src/utils/errors.tsx    |  77 -------------
 .../demobank-ui/src/components/Cashouts/index.ts   |   2 +-
 .../demobank-ui/src/components/Cashouts/views.tsx  |   5 +-
 .../src/components/EmptyComponentExample/index.ts  |   5 +-
 packages/demobank-ui/src/components/Routing.tsx    |   4 -
 .../src/components/Transactions/index.ts           |   5 +-
 packages/demobank-ui/src/components/app.tsx        |   1 +
 packages/demobank-ui/src/context/config.ts         |   2 +-
 .../demobank-ui/src/pages/AccountPage/index.ts     |   4 +-
 .../demobank-ui/src/pages/AccountPage/views.tsx    |   2 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       |  10 +-
 packages/demobank-ui/src/pages/LoginForm.tsx       |   6 +-
 .../demobank-ui/src/pages/OperationState/index.ts  |   4 +-
 .../demobank-ui/src/pages/OperationState/views.tsx |   8 +-
 .../src/pages/PaytoWireTransferForm.tsx            |   4 +-
 .../demobank-ui/src/pages/PublicHistoriesPage.tsx  |   2 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |   2 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     |   4 +-
 .../demobank-ui/src/pages/ShowAccountDetails.tsx   |   6 +-
 .../src/pages/UpdateAccountPassword.tsx            |   8 +-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |   4 +-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |   4 +-
 .../src/pages/WithdrawalOperationPage.tsx          |   2 +-
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |   6 +-
 packages/demobank-ui/src/pages/admin/Account.tsx   |   4 +-
 .../demobank-ui/src/pages/admin/AccountForm.tsx    |   4 +-
 .../demobank-ui/src/pages/admin/AccountList.tsx    |   4 +-
 packages/demobank-ui/src/pages/admin/AdminHome.tsx |   2 +-
 .../src/pages/admin/CreateNewAccount.tsx           |   4 +-
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |  10 +-
 .../src/pages/business/CreateCashout.tsx           |  10 +-
 .../src/pages/business/ShowCashoutDetails.tsx      |  10 +-
 packages/demobank-ui/tailwind.config.js            |   8 +-
 .../{demobank-ui => web-util}/src/assets/lang.svg  |   0
 .../src/components/Attention.tsx                   |  13 +--
 .../src/components/CopyButton.tsx                  |   2 -
 .../src/components/ErrorLoading.tsx                |   5 +-
 .../src/components/LangSelector.tsx                |  12 +-
 .../src/components/Loading.tsx                     |   0
 .../src/components/ShowInputErrorLabel.tsx         |   0
 .../src/components/ShowLocalNotification.tsx       |  24 ++--
 packages/web-util/src/components/index.ts          |   7 ++
 .../src/declaration.d.ts}                          |  30 +++--
 57 files changed, 342 insertions(+), 605 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/App.tsx 
b/packages/aml-backoffice-ui/src/App.tsx
index 600131219..0e29279ff 100644
--- a/packages/aml-backoffice-ui/src/App.tsx
+++ b/packages/aml-backoffice-ui/src/App.tsx
@@ -1,12 +1,19 @@
 import { TranslationProvider } from "@gnu-taler/web-util/browser";
 import { h, VNode } from "preact";
-import { Dashboard } from "./Dashboard.js";
+import { ExchangeAmlFrame, Main } from "./Dashboard.js";
 import "./scss/main.css";
+import { ExchangeApiProvider } from "./context/config.js";
+import { getInitialBackendBaseURL } from "./hooks/useBackend.js";
 
 export function App(): VNode {
+  const baseUrl = getInitialBackendBaseURL();
   return (
     <TranslationProvider source={{}}>
-      <Dashboard />
+      <ExchangeApiProvider baseUrl={baseUrl} frameOnError={ExchangeAmlFrame}>
+        <ExchangeAmlFrame>
+          <Main />
+        </ExchangeAmlFrame>
+      </ExchangeApiProvider>
     </TranslationProvider>
   );
 }
diff --git a/packages/aml-backoffice-ui/src/Dashboard.tsx 
b/packages/aml-backoffice-ui/src/Dashboard.tsx
index 6794ca1f8..bd8a48c45 100644
--- a/packages/aml-backoffice-ui/src/Dashboard.tsx
+++ b/packages/aml-backoffice-ui/src/Dashboard.tsx
@@ -182,7 +182,7 @@ function LeftMenu() {
   );
 }
 
-export function Dashboard({
+export function ExchangeAmlFrame({
   children,
 }: {
   children?: ComponentChildren;
@@ -211,21 +211,25 @@ export function Dashboard({
           }}
         />
         <Notifications />
-        <main class="py-10 px-4 sm:px-6 lg:px-8">
-          <div class="mx-auto max-w-3xl">
-            <Router
-              pageList={pageList}
-              onNotFound={() => {
-                return <div>not found</div>;
-              }}
-            />
-          </div>
-        </main>
+        {children}
       </div>
     </Fragment>
   );
 }
 
+export function Main(): VNode {
+  return <main class="py-10 px-4 sm:px-6 lg:px-8">
+    <div class="mx-auto max-w-3xl">
+      <Router
+        pageList={pageList}
+        onNotFound={() => {
+          return <div>not found</div>;
+        }}
+      />
+    </div>
+  </main>
+}
+
 const pageList = Object.values(Pages);
 
 function NavigationBar({
diff --git a/packages/aml-backoffice-ui/src/account.ts 
b/packages/aml-backoffice-ui/src/account.ts
deleted file mode 100644
index 615d843c4..000000000
--- a/packages/aml-backoffice-ui/src/account.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import {
-  Amounts,
-  TalerSignaturePurpose,
-  amountToBuffer,
-  bufferForUint32,
-  buildSigPS,
-  createEddsaKeyPair,
-  decodeCrock,
-  decryptWithDerivedKey,
-  eddsaGetPublic,
-  eddsaSign,
-  encodeCrock,
-  encryptWithDerivedKey,
-  getRandomBytesF,
-  hash,
-  hashTruncate32,
-  stringToBytes,
-  timestampRoundedToBuffer
-} from "@gnu-taler/taler-util";
-import { AmlExchangeBackend } from "./types.js";
-
-export interface Account {
-  accountId: AccountId;
-  signingKey: SigningKey;
-}
-
-/**
- * Restore previous session and unlock account with password
- *
- * @param salt string from which crypto params will be derived
- * @param key secured private key
- * @param password password for the private key
- * @returns
- */
-export async function unlockAccount(
-  account: LockedAccount,
-  password: string,
-): Promise<Account> {
-  const rawKey = decodeCrock(account);
-  const rawPassword = stringToBytes(password);
-
-  const signingKey = (await decryptWithDerivedKey(
-    rawKey,
-    rawPassword,
-    password,
-  ).catch((e: Error) => {
-    throw new UnwrapKeyError(e.message);
-  })) as SigningKey;
-
-  const publicKey = eddsaGetPublic(signingKey);
-
-  const accountId = encodeCrock(publicKey) as AccountId;
-
-  return { accountId, signingKey };
-}
-
-export function buildQuerySignature(key: SigningKey): string {
-  const sigBlob = buildSigPS(
-    TalerSignaturePurpose.TALER_SIGNATURE_AML_QUERY,
-  ).build();
-
-  return encodeCrock(eddsaSign(sigBlob, key));
-}
-
-export function buildDecisionSignature(
-  key: SigningKey,
-  decision: AmlExchangeBackend.AmlDecision,
-): string {
-  const zero = new Uint8Array(new ArrayBuffer(64))
-
-  const sigBlob = 
buildSigPS(TalerSignaturePurpose.TALER_SIGNATURE_AML_DECISION)
-    //TODO: new need the null terminator, also in the exchange
-    .put(hash(stringToBytes(decision.justification)))//check null
-    .put(timestampRoundedToBuffer(decision.decision_time))
-    .put(amountToBuffer(decision.new_threshold))
-    .put(decodeCrock(decision.h_payto))
-    .put(zero) //kyc_requirement
-    .put(bufferForUint32(decision.new_state))
-    .build();
-
-  return encodeCrock(eddsaSign(sigBlob, key));
-}
-
-declare const opaque_Account: unique symbol;
-export type LockedAccount = string & { [opaque_Account]: true };
-
-declare const opaque_AccountId: unique symbol;
-export type AccountId = string & { [opaque_AccountId]: true };
-
-declare const opaque_SigningKey: unique symbol;
-export type SigningKey = Uint8Array & { [opaque_SigningKey]: true };
-
-/**
- * Create new account (secured private key)
- * secured with the given password
- *
- * @param sessionId
- * @param password
- * @returns
- */
-export async function createNewAccount(
-  password: string,
-): Promise<Account & { safe: LockedAccount }> {
-  const { eddsaPriv, eddsaPub } = createEddsaKeyPair();
-
-  const key = stringToBytes(password);
-
-  const protectedPrivKey = await encryptWithDerivedKey(
-    getRandomBytesF(24),
-    key,
-    eddsaPriv,
-    password,
-  );
-
-  const signingKey = eddsaPriv as SigningKey;
-  const accountId = encodeCrock(eddsaPub) as AccountId;
-  const safe = encodeCrock(protectedPrivKey) as LockedAccount;
-
-  return { accountId, signingKey, safe };
-}
-
-export class UnwrapKeyError extends Error {
-  public cause: string;
-  constructor(cause: string) {
-    super(`Recovering private key failed on: ${cause}`);
-    this.cause = cause;
-  }
-}
diff --git a/packages/demobank-ui/src/context/config.ts 
b/packages/aml-backoffice-ui/src/context/config.ts
similarity index 82%
copy from packages/demobank-ui/src/context/config.ts
copy to packages/aml-backoffice-ui/src/context/config.ts
index a31d914b8..2866717de 100644
--- a/packages/demobank-ui/src/context/config.ts
+++ b/packages/aml-backoffice-ui/src/context/config.ts
@@ -14,11 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { TalerCorebankApi, TalerCoreBankHttpClient, TalerError } from 
"@gnu-taler/taler-util";
+import { TalerExchangeApi, TalerExchangeHttpClient, TalerError } from 
"@gnu-taler/taler-util";
 import { BrowserHttpLib, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { ComponentChildren, createContext, FunctionComponent, h, VNode } from 
"preact";
 import { useContext, useEffect, useState } from "preact/hooks";
-import { ErrorLoading } from "../components/ErrorLoading.js";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
 
 /**
  *
@@ -27,20 +27,20 @@ import { ErrorLoading } from 
"../components/ErrorLoading.js";
 
 export type Type = {
   url: URL,
-  config: TalerCorebankApi.Config,
-  api: TalerCoreBankHttpClient,
+  config: TalerExchangeApi.ExchangeVersionResponse,
+  api: TalerExchangeHttpClient,
 };
 
 const Context = createContext<Type>(undefined as any);
 
-export const useBankCoreApiContext = (): Type => useContext(Context);
+export const useExchangeApiContext = (): Type => useContext(Context);
 
 export type ConfigResult = undefined
-  | { type: "ok", config: TalerCorebankApi.Config }
-  | { type: "incompatible", result: TalerCorebankApi.Config, supported: string 
}
+  | { type: "ok", config: TalerExchangeApi.ExchangeVersionResponse }
+  | { type: "incompatible", result: TalerExchangeApi.ExchangeVersionResponse, 
supported: string }
   | { type: "error", error: TalerError }
 
-export const BankCoreApiProvider = ({
+export const ExchangeApiProvider = ({
   baseUrl,
   children,
   frameOnError,
@@ -52,7 +52,7 @@ export const BankCoreApiProvider = ({
   const [checked, setChecked] = useState<ConfigResult>()
   const { i18n } = useTranslationContext();
   const url = new URL(baseUrl)
-  const api = new TalerCoreBankHttpClient(url.href, new BrowserHttpLib())
+  const api = new TalerExchangeHttpClient(url.href, new BrowserHttpLib())
   useEffect(() => {
     api.getConfig()
       .then((resp) => {
diff --git a/packages/aml-backoffice-ui/src/handlers/Caption.tsx 
b/packages/aml-backoffice-ui/src/handlers/Caption.tsx
index fbf154d89..8facddec3 100644
--- a/packages/aml-backoffice-ui/src/handlers/Caption.tsx
+++ b/packages/aml-backoffice-ui/src/handlers/Caption.tsx
@@ -1,11 +1,8 @@
+import { TranslatedString } from "@gnu-taler/taler-util";
 import { VNode, h } from "preact";
 import {
-  IconAddon,
-  InputLine,
-  LabelWithTooltipMaybeRequired,
-  UIFormProps,
+  LabelWithTooltipMaybeRequired
 } from "./InputLine.js";
-import { TranslatedString } from "@gnu-taler/taler-util";
 
 interface Props {
   label: TranslatedString;
diff --git a/packages/aml-backoffice-ui/src/hooks/useBackend.ts 
b/packages/aml-backoffice-ui/src/hooks/useBackend.ts
index b9d66fca6..95277a915 100644
--- a/packages/aml-backoffice-ui/src/hooks/useBackend.ts
+++ b/packages/aml-backoffice-ui/src/hooks/useBackend.ts
@@ -1,3 +1,4 @@
+import { canonicalizeBaseUrl } from "@gnu-taler/taler-util";
 import {
   HttpResponseOk,
   RequestOptions,
@@ -5,9 +6,6 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { useCallback } from "preact/hooks";
 import { uiSettings } from "../settings.js";
-import { canonicalizeBaseUrl } from "@gnu-taler/taler-util";
-import { useOfficer } from "./useOfficer.js";
-import { buildQuerySignature } from "../account.js";
 
 interface useBackendType {
   request: <T>(
@@ -35,7 +33,7 @@ export function usePublicBackend(): useBackendType {
   );
 
   const fetcher = useCallback(
-    function fetcherImpl<T>([endpoint, talerAmlOfficerSignature]: 
[string,string]): Promise<HttpResponseOk<T>> {
+    function fetcherImpl<T>([endpoint, talerAmlOfficerSignature]: [string, 
string]): Promise<HttpResponseOk<T>> {
       return requestHandler<T>(baseUrl, endpoint, {
         talerAmlOfficerSignature
       });
@@ -66,18 +64,29 @@ export function usePublicBackend(): useBackendType {
 export function getInitialBackendBaseURL(): string {
   const overrideUrl =
     typeof localStorage !== "undefined"
-      ? localStorage.getItem("exchange-aml-base-url")
+      ? localStorage.getItem("exchange-base-url")
       : undefined;
+
+  let result: string;
+
   if (!overrideUrl) {
     //normal path
     if (!uiSettings.backendBaseURL) {
       console.error(
         "ERROR: backendBaseURL was overridden by a setting file and missing. 
Setting value to 'window.origin'",
       );
-      return canonicalizeBaseUrl(window.origin);
+      result = window.origin
+    } else {
+      result = uiSettings.backendBaseURL;
     }
-    return canonicalizeBaseUrl(uiSettings.backendBaseURL);
+  } else {
+    // testing/development path
+    result = overrideUrl
+  }
+  try {
+    return canonicalizeBaseUrl(result)
+  } catch (e) {
+    //fall back
+    return canonicalizeBaseUrl(window.origin)
   }
-  // testing/development path
-  return canonicalizeBaseUrl(overrideUrl);
 }
diff --git a/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts 
b/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts
index 980a35f21..9db1e2aec 100644
--- a/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts
+++ b/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts
@@ -1,34 +1,29 @@
 
 import {
   HttpResponse,
-  HttpResponseOk,
-  RequestError
+  HttpResponseOk
 } from "@gnu-taler/web-util/browser";
 import { AmlExchangeBackend } from "../types.js";
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import { AmountString, OfficerAccount, PaytoString, TalerExchangeApi, 
TalerExchangeResultByMethod, TalerHttpError } from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook, useSWRConfig } from "swr";
-import { AccountId } from "../account.js";
+import { useExchangeApiContext } from "../context/config.js";
 import { usePublicBackend } from "./useBackend.js";
+import { useOfficer } from "./useOfficer.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-export function useCaseDetails(
-  account: AccountId,
-  paytoHash: string,
-  signature: string | undefined,
-): HttpResponse<
-  AmlExchangeBackend.AmlDecisionDetails,
-  AmlExchangeBackend.AmlError
-> {
-  const { fetcher } = usePublicBackend();
+export function useCaseDetails(paytoHash: string) {
+  const officer = useOfficer();
+  const session = officer.state === "ready" ? officer.account : undefined;
 
-  const { data, error } = useSWR<
-  HttpResponseOk<AmlExchangeBackend.AmlDecisionDetails>,
-  RequestError<AmlExchangeBackend.AmlError>
->(    [
-  `aml/${account}/decision/${(paytoHash)}`,
-  signature,
-],
-fetcher, {
+  const { api } = useExchangeApiContext();
+
+  async function fetcher([officer, account]: [OfficerAccount, PaytoString]) {
+    return await api.getDecisionDetails(officer, account)
+  }
+
+  const { data, error } = 
useSWR<TalerExchangeResultByMethod<"getDecisionDetails">, TalerHttpError>(
+    !session ? undefined : [session, paytoHash], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -41,11 +36,11 @@ fetcher, {
   });
 
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
 
-const example1: AmlExchangeBackend.AmlDecisionDetails = {
+const example1: TalerExchangeApi.AmlDecisionDetails = {
   aml_history: [
     {
       justification: "Lack of documentation",
@@ -54,7 +49,7 @@ const example1: AmlExchangeBackend.AmlDecisionDetails = {
         t_s: Date.now() / 1000,
       },
       new_state: 2,
-      new_threshold: "USD:0",
+      new_threshold: "USD:0" as AmountString,
     },
     {
       justification: "Doing a transfer of high amount",
@@ -63,7 +58,7 @@ const example1: AmlExchangeBackend.AmlDecisionDetails = {
         t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 6,
       },
       new_state: 1,
-      new_threshold: "USD:2000",
+      new_threshold: "USD:2000" as AmountString,
     },
     {
       justification: "Account is known to the system",
@@ -72,7 +67,7 @@ const example1: AmlExchangeBackend.AmlDecisionDetails = {
         t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 9,
       },
       new_state: 0,
-      new_threshold: "USD:100",
+      new_threshold: "USD:100" as AmountString,
     },
   ],
   kyc_attributes: [
@@ -103,60 +98,4 @@ const example1: AmlExchangeBackend.AmlDecisionDetails = {
   ],
 };
 
-export const exampleResponse: 
HttpResponse<AmlExchangeBackend.AmlDecisionDetails,AmlExchangeBackend.AmlError> 
= {
-  ok: true,
-  data: example1,
-}
-
-
-export function useAmlCasesAPI(): AmlCaseAPI {
-  const { request } = usePublicBackend();
-  const mutateAll = useMatchMutate();
-
-  const updateDecision = async (
-    officer: AccountId,
-    data: AmlExchangeBackend.AmlDecision,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`aml/${officer}/decision`, {
-      method: "POST",
-      data,
-      contentType: "json",
-    });
-    await mutateAll(/.*aml.*/);
-    return res;
-  };
-
-  return {
-    updateDecision,
-  };
-}
-
-export interface AmlCaseAPI {
-  updateDecision: (
-    officer: AccountId,
-    data: AmlExchangeBackend.AmlDecision,
-  ) => Promise<HttpResponseOk<void>>;
-}
-
 
-function useMatchMutate(): (
-  re: RegExp,
-  value?: unknown,
-) => Promise<any> {
-  const { cache, mutate } = useSWRConfig();
-
-  if (!(cache instanceof Map)) {
-    throw new Error(
-      "matchMutate requires the cache provider to be a Map instance",
-    );
-  }
-
-  return function matchRegexMutate(re: RegExp, value?: unknown) {
-    const allKeys = Array.from(cache.keys());
-    const keys = allKeys.filter((key) => re.test(key));
-    const mutations = keys.map((key) => {
-      return mutate(key, value, true);
-    });
-    return Promise.all(mutations);
-  };
-}
diff --git a/packages/aml-backoffice-ui/src/hooks/useCases.ts 
b/packages/aml-backoffice-ui/src/hooks/useCases.ts
index c07bd5f18..2a133f46d 100644
--- a/packages/aml-backoffice-ui/src/hooks/useCases.ts
+++ b/packages/aml-backoffice-ui/src/hooks/useCases.ts
@@ -1,16 +1,13 @@
-import { useEffect, useState } from "preact/hooks";
+import { useState } from "preact/hooks";
 
-import { AmlExchangeBackend } from "../types.js";
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  HttpResponsePaginated
 } from "@gnu-taler/web-util/browser";
+import { AmlExchangeBackend } from "../types.js";
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import { AmountString, OfficerAccount, TalerExchangeApi, 
TalerExchangeResultByMethod, TalerHttpError } from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook } from "swr";
-import { usePublicBackend } from "./useBackend.js";
-import { AccountId, buildQuerySignature } from "../account.js";
+import { useExchangeApiContext } from "../context/config.js";
 import { useOfficer } from "./useOfficer.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
@@ -22,59 +19,49 @@ const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1;
  * @param args
  * @returns
  */
-export function useCases(
-  account: AccountId,
-  state: AmlExchangeBackend.AmlState,
-  signature: string | undefined,
-): HttpResponsePaginated<
-  AmlExchangeBackend.AmlRecords,
-  AmlExchangeBackend.AmlError
-> {
-  const { paginatedFetcher } = usePublicBackend();
+export function useCases(state: AmlExchangeBackend.AmlState) {
+  const officer = useOfficer();
+  const session = officer.state === "ready" ? officer.account : undefined;
+  const { api } = useExchangeApiContext();
 
-  const [page, setPage] = useState(1);
+  const [offset, setOffet] = useState<string>();
+
+  async function fetcher([officer, state, offset]: [OfficerAccount, 
AmlExchangeBackend.AmlState, string | undefined]) {
+    return await api.getDecisionsByState(officer, state, {
+      order: "asc", offset, limit: MAX_RESULT_SIZE
+    })
+  }
 
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<AmlExchangeBackend.AmlRecords>,
-    RequestError<AmlExchangeBackend.AmlError>
-  >(
-    [
-      `aml/${account}/decisions/${AmlExchangeBackend.AmlState[state]}`,
-      page,
-      PAGE_SIZE,
-      signature,
-    ],
-    paginatedFetcher,
+  const { data, error } = 
useSWR<TalerExchangeResultByMethod<"getDecisionsByState">, TalerHttpError>(
+    !session ? undefined : [session, state, offset],
+    fetcher,
   );
 
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<AmlExchangeBackend.AmlRecords, AmlExchangeBackend.AmlError>
-  >({ loading: true });
+  // const [lastAfter, setLastAfter] = useState<
+  //   HttpResponse<AmlExchangeBackend.AmlRecords, AmlExchangeBackend.AmlError>
+  // >({ loading: true });
 
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData]);
+  // useEffect(() => {
+  //   if (afterData) setLastAfter(afterData);
+  // }, [afterData]);
 
-  if (afterError) {
-    return afterError.cause;
-  }
+  // if (afterError) {
+  //   return afterError.cause;
+  // }
 
   // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data && afterData.data.records.length < PAGE_SIZE;
-  const isReachingStart = false;
+  const isLastPage =
+    data && data.type === "ok" && data.body.records.length < PAGE_SIZE;
+  const isFirstPage = !offset;
 
   const pagination = {
-    isReachingEnd,
-    isReachingStart,
+    isLastPage,
+    isFirstPage,
     loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data && afterData.data.records.length < MAX_RESULT_SIZE) {
-        setPage(page + 1);
+      if (isLastPage || data?.type !== "ok") return;
+      const list = data.body.records
+      if (list.length < MAX_RESULT_SIZE) {
+        // setOffset(list[list.length-1].account_name);
       }
     },
     loadMorePrev: () => {
@@ -82,65 +69,62 @@ export function useCases(
     },
   };
 
-  const records = !afterData
-    ? []
-    : ((afterData ?? lastAfter).data ?? { records: [] }).records;
-  if (loadingAfter) return { loading: true, data: { records } };
-  if (afterData) {
-    return { ok: true, data: { records }, ...pagination };
+  // const public_accountslist = data?.type !== "ok" ? [] : 
data.body.public_accounts;
+  if (data) {
+    if (data.type === "fail") {
+      return { data }
+    }
+    return { data, pagination }
+  }
+  if (error) {
+    return error;
   }
-  return { loading: true };
+  return undefined;
 }
 
-const example1: AmlExchangeBackend.AmlRecords = {
+const example1: TalerExchangeApi.AmlRecords = {
   records: [
     {
       current_state: 0,
       h_payto: "QWEQWEQWEQWEWQE",
       rowid: 1,
-      threshold: "USD 100",
+      threshold: "USD 100" as AmountString,
     },
     {
       current_state: 1,
       h_payto: "ASDASDASD",
       rowid: 1,
-      threshold: "USD 100",
+      threshold: "USD 100" as AmountString,
     },
     {
       current_state: 2,
       h_payto: "ZXCZXCZXCXZC",
       rowid: 1,
-      threshold: "USD 1000",
+      threshold: "USD 1000" as AmountString,
     },
     {
       current_state: 0,
       h_payto: "QWEQWEQWEQWEWQE",
       rowid: 1,
-      threshold: "USD 100",
+      threshold: "USD 100" as AmountString,
     },
     {
       current_state: 1,
       h_payto: "ASDASDASD",
       rowid: 1,
-      threshold: "USD 100",
+      threshold: "USD 100" as AmountString,
     },
     {
       current_state: 2,
       h_payto: "ZXCZXCZXCXZC",
       rowid: 1,
-      threshold: "USD 1000",
+      threshold: "USD 1000" as AmountString,
     },
   ].map((e, idx) => {
     e.rowid = idx;
-    e.threshold = `${e.threshold}${idx}`;
+    e.threshold = `${e.threshold}${idx}` as AmountString;
     return e;
   }),
 };
 
-export const exampleResponse: 
HttpResponsePaginated<AmlExchangeBackend.AmlRecords,AmlExchangeBackend.AmlError>
 = {
-  ok: true,
-  data: example1,
-  loadMore: () => {},
-  loadMorePrev: () => {},  
-}
 
diff --git a/packages/aml-backoffice-ui/src/hooks/useOfficer.ts 
b/packages/aml-backoffice-ui/src/hooks/useOfficer.ts
index 4ec43569b..0747170e8 100644
--- a/packages/aml-backoffice-ui/src/hooks/useOfficer.ts
+++ b/packages/aml-backoffice-ui/src/hooks/useOfficer.ts
@@ -1,16 +1,14 @@
 import {
   AbsoluteTime,
   Codec,
+  LockedAccount,
+  OfficerAccount,
   buildCodecForObject,
   codecForAbsoluteTime,
   codecForString,
+  createNewOfficerAccount,
+  unlockOfficerAccount,
 } from "@gnu-taler/taler-util";
-import {
-  Account,
-  LockedAccount,
-  createNewAccount,
-  unlockAccount,
-} from "../account.js";
 import {
   buildStorageKey,
   useLocalStorage,
@@ -43,7 +41,7 @@ interface OfficerLocked {
 }
 interface OfficerReady {
   state: "ready";
-  account: Account;
+  account: OfficerAccount;
   forget: () => void;
   lock: () => void;
 }
@@ -52,7 +50,7 @@ const OFFICER_KEY = buildStorageKey("officer", 
codecForOfficer());
 const ACCOUNT_KEY = "account";
 
 export function useOfficer(): OfficerState {
-  const accountStorage = useMemoryStorage<Account>(ACCOUNT_KEY);
+  const accountStorage = useMemoryStorage<OfficerAccount>(ACCOUNT_KEY);
   const officerStorage = useLocalStorage(OFFICER_KEY);
 
   const officer = officerStorage.value;
@@ -62,13 +60,13 @@ export function useOfficer(): OfficerState {
     return {
       state: "not-found",
       create: async (pwd: string) => {
-        const { accountId, safe, signingKey } = await createNewAccount(pwd);
+        const { id, safe, signingKey } = await createNewOfficerAccount(pwd);
         officerStorage.update({
           account: safe,
           when: AbsoluteTime.now(),
         });
 
-        accountStorage.update({ accountId, signingKey });
+        accountStorage.update({ id, signingKey });
       },
     };
   }
@@ -80,7 +78,7 @@ export function useOfficer(): OfficerState {
         officerStorage.reset();
       },
       tryUnlock: async (pwd: string) => {
-        const ac = await unlockAccount(officer.account, pwd);
+        const ac = await unlockOfficerAccount(officer.account, pwd);
         accountStorage.update(ac);
       },
     };
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx 
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index ce820d612..f618a3592 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -1,24 +1,23 @@
-import { Fragment, VNode, h } from "preact";
 import {
   AbsoluteTime,
   AmountJson,
   Amounts,
+  PaytoString,
+  TalerError,
   TranslatedString,
+  assertUnreachable,
 } from "@gnu-taler/taler-util";
-import { format } from "date-fns";
+import { ErrorLoading, Loading, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { ArrowDownCircleIcon, ClockIcon } from "@heroicons/react/20/solid";
+import { format } from "date-fns";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { NiceForm } from "../NiceForm.js";
 import { FlexibleForm } from "../forms/index.js";
 import { UIFormField } from "../handlers/forms.js";
+import { useCaseDetails } from "../hooks/useCaseDetails.js";
 import { Pages } from "../pages.js";
 import { AmlExchangeBackend } from "../types.js";
-import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { useOfficer } from "../hooks/useOfficer.js";
-import { buildQuerySignature } from "../account.js";
-import { useCaseDetails } from "../hooks/useCaseDetails.js";
-import { handleNotOkResult } from "../utils/errors.js";
 
 type AmlEvent = AmlFormEvent | KycCollectionEvent | KycExpirationEvent;
 type AmlFormEvent = {
@@ -85,30 +84,33 @@ function getEventsFromAmlHistory(
   return ae.concat(ke).sort(selectSooner);
 }
 
-export function CaseDetails({ account: paytoHash }: { account: string }) {
+export function CaseDetails({ account }: { account: string }) {
   const [selected, setSelected] = useState<AmlEvent | undefined>(undefined);
 
-  const officer = useOfficer();
   const { i18n } = useTranslationContext();
-  if (officer.state !== "ready") {
-    return <HandleAccountNotReady officer={officer} />;
+  const details = useCaseDetails(account)
+  if (!details) {
+    return <Loading />
   }
-  const signature =
-    officer.state === "ready"
-      ? buildQuerySignature(officer.account.signingKey)
-      : undefined;
-  const details = useCaseDetails(officer.account.accountId, paytoHash, 
signature)
-  if (!details.ok && !details.loading) {
-    return handleNotOkResult(i18n)(details);
+  if (details instanceof TalerError) {
+    return <ErrorLoading error={details} />
   }
-  const aml_history = details.loading ? [] : details.data.aml_history
-  const kyc_attributes = details.loading ? [] : details.data.kyc_attributes
-  const events = getEventsFromAmlHistory(aml_history,kyc_attributes);
-  
+  if (details.type === "fail") {
+    switch (details.case) {
+      case "unauthorized":
+      case "officer-not-found":
+      case "officer-disabled": return <div />
+      default: assertUnreachable(details)
+    }
+  }
+  const { aml_history, kyc_attributes } = details.body
+
+  const events = getEventsFromAmlHistory(aml_history, kyc_attributes);
+
   return (
     <div>
       <a
-        href={Pages.newFormEntry.url({ account: paytoHash })}
+        href={Pages.newFormEntry.url({ account })}
         class="m-4 block rounded-md w-fit border-0 px-3 py-2 text-center 
text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700"
       >
         New AML form
@@ -287,23 +289,22 @@ function ShowConsolidated({
       },
       Object.entries(cons.kyc).length > 0
         ? {
-            title: "KYC" as TranslatedString,
-            fields: Object.entries(cons.kyc).map(([key, field]) => {
-              const result: UIFormField = {
-                type: "text",
-                props: {
-                  label: key as TranslatedString,
-                  name: `kyc.${key}.value`,
-                  help: `${field.provider} since ${
-                    field.since.t_ms === "never"
-                      ? "never"
-                      : format(field.since.t_ms, "dd/MM/yyyy")
+          title: "KYC" as TranslatedString,
+          fields: Object.entries(cons.kyc).map(([key, field]) => {
+            const result: UIFormField = {
+              type: "text",
+              props: {
+                label: key as TranslatedString,
+                name: `kyc.${key}.value`,
+                help: `${field.provider} since ${field.since.t_ms === "never"
+                  ? "never"
+                  : format(field.since.t_ms, "dd/MM/yyyy")
                   }` as TranslatedString,
-                },
-              };
-              return result;
-            }),
-          }
+              },
+            };
+            return result;
+          }),
+        }
         : undefined,
     ],
   };
@@ -319,7 +320,7 @@ function ShowConsolidated({
         key={`${String(Date.now())}`}
         form={form}
         initial={cons}
-        onUpdate={() => {}}
+        onUpdate={() => { }}
       />
     </Fragment>
   );
diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx 
b/packages/aml-backoffice-ui/src/pages/Cases.tsx
index 990c0d2d4..5f79db71e 100644
--- a/packages/aml-backoffice-ui/src/pages/Cases.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx
@@ -1,4 +1,5 @@
-import { TranslatedString } from "@gnu-taler/taler-util";
+import { TalerError, TranslatedString, assertUnreachable } from 
"@gnu-taler/taler-util";
+import { ErrorLoading, Loading, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { createNewForm } from "../handlers/forms.js";
@@ -7,34 +8,37 @@ import { useOfficer } from "../hooks/useOfficer.js";
 import { Pages } from "../pages.js";
 import { AmlExchangeBackend } from "../types.js";
 import { amlStateConverter } from "./CaseDetails.js";
-import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
-import { buildQuerySignature } from "../account.js";
-import { handleNotOkResult } from "../utils/errors.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
 
 export function Cases() {
-  const officer = useOfficer();
   const { i18n } = useTranslationContext();
-  if (officer.state !== "ready") {
-    return <HandleAccountNotReady officer={officer} />;
-  }
-  const form = createNewForm<{
-    state: AmlExchangeBackend.AmlState;
-  }>();
 
-  const signature =
-    officer.state === "ready"
-      ? buildQuerySignature(officer.account.signingKey)
-      : undefined;
+  const form = createNewForm<{ state: AmlExchangeBackend.AmlState }>();
+
 
   const initial = AmlExchangeBackend.AmlState.pending;
   const [stateFilter, setStateFilter] = useState(initial);
-  const list = useCases(officer.account.accountId, stateFilter, signature);
 
-  if (!list.ok && !list.loading) {
-    return handleNotOkResult(i18n)(list);
+  const list = useCases(stateFilter);
+
+  if (!list) {
+    return <Loading />
+  }
+
+  if (list instanceof TalerError) {
+    return <ErrorLoading error={list} />
   }
-  const records = list.loading ? [] : list.data.records
+
+  if (list.data.type === "fail") {
+    switch (list.data.case) {
+      case "unauthorized":
+      case "officer-not-found":
+      case "officer-disabled": return <div />
+      default: assertUnreachable(list.data)
+    }
+  }
+
+  const { records } = list.data.body
+
   return (
     <div>
       <div class="px-4 sm:px-6 lg:px-8">
@@ -52,7 +56,7 @@ export function Cases() {
             onUpdate={(v) => {
               setStateFilter(v.state ?? initial);
             }}
-            onSubmit={(v) => {}}
+            onSubmit={(v) => { }}
           >
             <form.InputChoiceHorizontal
               name="state"
diff --git a/packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx 
b/packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx
index 429cfb9ca..fa79bb476 100644
--- a/packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx
+++ b/packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx
@@ -2,12 +2,11 @@ import { VNode, h } from "preact";
 import { allForms } from "./AntiMoneyLaunderingForm.js";
 import { Pages } from "../pages.js";
 import { NiceForm } from "../NiceForm.js";
-import { AbsoluteTime, Amounts, TalerProtocolTimestamp } from 
"@gnu-taler/taler-util";
+import { AbsoluteTime, Amounts, TalerExchangeApi, TalerProtocolTimestamp } 
from "@gnu-taler/taler-util";
 import { AmlExchangeBackend } from "../types.js";
-import { useAmlCasesAPI } from "../hooks/useCaseDetails.js";
 import { useOfficer } from "../hooks/useOfficer.js";
 import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
-import { buildDecisionSignature, buildQuerySignature } from "../account.js";
+import { useExchangeApiContext } from "../context/config.js";
 
 export function NewFormEntry({
   account,
@@ -40,21 +39,21 @@ export function NewFormEntry({
     state: AmlExchangeBackend.AmlState.pending,
     threshold: Amounts.parseOrThrow("KUDOS:1000"),
   };
-  const api = useAmlCasesAPI()
-  
+  const { api } = useExchangeApiContext()
+
   return (
     <NiceForm
       initial={initial}
       form={showingFrom(initial)}
       onSubmit={(formValue) => {
         if (formValue.state === undefined || formValue.threshold === 
undefined) return;
-        
+
         const justification = {
           index: selectedForm,
           name: formName,
           value: formValue
         }
-        const decision: AmlExchangeBackend.AmlDecision = {
+        const decision: TalerExchangeApi.AmlDecision = {
           justification: JSON.stringify(justification),
           decision_time: TalerProtocolTimestamp.now(),
           h_payto: account,
@@ -63,9 +62,9 @@ export function NewFormEntry({
           officer_sig: "",
           kyc_requirements: undefined
         }
-        const signature = buildDecisionSignature(officer.account.signingKey, 
decision);
-        decision.officer_sig = signature
-        api.updateDecision(officer.account.accountId, decision);
+        // const signature = 
buildDecisionSignature(officer.account.signingKey, decision);
+        // decision.officer_sig = signature
+        api.addDecisionDetails(officer.account, decision);
 
         // alert(JSON.stringify(formValue));
       }}
diff --git a/packages/aml-backoffice-ui/src/pages/Officer.tsx 
b/packages/aml-backoffice-ui/src/pages/Officer.tsx
index 5320369e4..4af34805a 100644
--- a/packages/aml-backoffice-ui/src/pages/Officer.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Officer.tsx
@@ -14,12 +14,12 @@ export function Officer() {
         Public key
       </h1>
       <div class="max-w-xl text-base leading-7 text-gray-700 lg:max-w-lg">
-        <p class="mt-6 font-mono break-all">{officer.account.accountId}</p>
+        <p class="mt-6 font-mono break-all">{officer.account.id}</p>
       </div>
       <p>
         <a
           href={`mailto:aml@exchange.taler.net?body=${encodeURIComponent(
-            `I want my AML account\n\n\nPubKey: ${officer.account.accountId}`,
+            `I want my AML account\n\n\nPubKey: ${officer.account.id}`,
           )}`}
           target="_blank"
           rel="noreferrer"
diff --git a/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx 
b/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
index 39f8addd3..83d8767fb 100644
--- a/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
+++ b/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
@@ -1,7 +1,6 @@
-import { TranslatedString } from "@gnu-taler/taler-util";
+import { TranslatedString, UnwrapKeyError } from "@gnu-taler/taler-util";
 import { notifyError, notifyInfo } from "@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
-import { UnwrapKeyError } from "../account.js";
 import { createNewForm } from "../handlers/forms.js";
 
 export function UnlockAccount({
diff --git a/packages/aml-backoffice-ui/src/utils/errors.tsx 
b/packages/aml-backoffice-ui/src/utils/errors.tsx
deleted file mode 100644
index b67d61a5f..000000000
--- a/packages/aml-backoffice-ui/src/utils/errors.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import {
-  ErrorType,
-  HttpResponse,
-  HttpResponsePaginated,
-  notifyError,
-  useTranslationContext,
-} from "@gnu-taler/web-util/browser";
-import { VNode, h } from "preact";
-import { Loading } from "./Loading.js";
-import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
-import { AmlExchangeBackend } from "../types.js";
-
-export function handleNotOkResult<Error extends AmlExchangeBackend.AmlError>(
-  i18n: ReturnType<typeof useTranslationContext>["i18n"],
-): <T>(
-  result: HttpResponsePaginated<T, Error> | HttpResponse<T, Error>,
-) => VNode {
-  return function handleNotOkResult2<T>(
-    result: HttpResponsePaginated<T, Error> | HttpResponse<T, Error>,
-  ): VNode {
-    if (result.loading) return <Loading />;
-    if (!result.ok) {
-      switch (result.type) {
-        case ErrorType.TIMEOUT: {
-          notifyError(i18n.str`Request timeout, try again later.`, undefined);
-          break;
-        }
-        case ErrorType.CLIENT: {
-          if (result.status === HttpStatusCode.Unauthorized) {
-            notifyError(i18n.str`Wrong credentials`, undefined);
-            return <div> not authorized</div>;
-          }
-          const errorData = result.payload;
-          notifyError(
-            i18n.str`Could not load due to a client error`,
-            errorData.hint as TranslatedString,
-            JSON.stringify(result),
-          );
-          break;
-        }
-        case ErrorType.SERVER: {
-          notifyError(
-            i18n.str`Server returned with error`,
-            result.payload.hint as TranslatedString,
-            JSON.stringify(result.payload),
-          );
-          break;
-        }
-        case ErrorType.UNREADABLE: {
-          notifyError(
-            i18n.str`Unexpected error.`,
-            `Response from ${result.info?.url} is unreadable, http status: 
${result.status}` as TranslatedString,
-            JSON.stringify(result),
-          );
-          break;
-        }
-        case ErrorType.UNEXPECTED: {
-          notifyError(
-            i18n.str`Unexpected error.`,
-            `Diagnostic from ${result.info?.url} is "${result.message}"` as 
TranslatedString,
-            JSON.stringify(result),
-          );
-          break;
-        }
-        default: {
-          assertUnreachable(result);
-        }
-      }
-
-      return <div>error</div>;
-    }
-    return <div />;
-  };
-}
-export function assertUnreachable(x: never): never {
-  throw new Error("Didn't expect to get here");
-}
diff --git a/packages/demobank-ui/src/components/Cashouts/index.ts 
b/packages/demobank-ui/src/components/Cashouts/index.ts
index 09839e753..6cbb1247d 100644
--- a/packages/demobank-ui/src/components/Cashouts/index.ts
+++ b/packages/demobank-ui/src/components/Cashouts/index.ts
@@ -15,7 +15,7 @@
  */
 
 import { HttpError, utils } from "@gnu-taler/web-util/browser";
-import { Loading } from "../Loading.js";
+import { Loading } from "@gnu-taler/web-util/browser";
 // import { compose, StateViewMap } from "../../utils/index.js";
 // import { wxApi } from "../../wxApi.js";
 import { AbsoluteTime, AmountJson, TalerCoreBankErrorsByMethod, 
TalerCorebankApi, TalerError } from "@gnu-taler/taler-util";
diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx 
b/packages/demobank-ui/src/components/Cashouts/views.tsx
index 89f173b0d..76a3a90df 100644
--- a/packages/demobank-ui/src/components/Cashouts/views.tsx
+++ b/packages/demobank-ui/src/components/Cashouts/views.tsx
@@ -18,10 +18,9 @@ import { Fragment, h, VNode } from "preact";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { State } from "./index.js";
 import { format } from "date-fns";
-import { Amounts } from "@gnu-taler/taler-util";
+import { Amounts, assertUnreachable } from "@gnu-taler/taler-util";
 import { RenderAmount } from "../../pages/PaytoWireTransferForm.js";
-import { assertUnreachable } from "../Routing.js";
-import { Attention } from "../Attention.js";
+import { Attention } from "@gnu-taler/web-util/browser";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
diff --git a/packages/demobank-ui/src/components/EmptyComponentExample/index.ts 
b/packages/demobank-ui/src/components/EmptyComponentExample/index.ts
index 013904ff3..d80e6bdf9 100644
--- a/packages/demobank-ui/src/components/EmptyComponentExample/index.ts
+++ b/packages/demobank-ui/src/components/EmptyComponentExample/index.ts
@@ -14,10 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Loading } from "../../components/Loading.js";
-import { HookError, utils } from "@gnu-taler/web-util/browser";
-//import { compose, StateViewMap } from "../../utils/index.js";
-//import { wxApi } from "../../wxApi.js";
+import { HookError, Loading, utils } from "@gnu-taler/web-util/browser";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
 
diff --git a/packages/demobank-ui/src/components/Routing.tsx 
b/packages/demobank-ui/src/components/Routing.tsx
index c94e74201..65a7b6e86 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -323,7 +323,3 @@ function Redirect({ to }: { to: string }): VNode {
   }, []);
   return <div>being redirected to {to}</div>;
 }
-
-export function assertUnreachable(x: never): never {
-  throw new Error("Didn't expect to get here");
-}
diff --git a/packages/demobank-ui/src/components/Transactions/index.ts 
b/packages/demobank-ui/src/components/Transactions/index.ts
index 3c4fb5ce9..b95aa4cb1 100644
--- a/packages/demobank-ui/src/components/Transactions/index.ts
+++ b/packages/demobank-ui/src/components/Transactions/index.ts
@@ -14,10 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpError, utils } from "@gnu-taler/web-util/browser";
-import { Loading } from "../Loading.js";
-// import { compose, StateViewMap } from "../../utils/index.js";
-// import { wxApi } from "../../wxApi.js";
+import { Loading, utils } from "@gnu-taler/web-util/browser";
 import { AbsoluteTime, AmountJson, TalerError } from "@gnu-taler/taler-util";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
diff --git a/packages/demobank-ui/src/components/app.tsx 
b/packages/demobank-ui/src/components/app.tsx
index 55e1178fe..f79bd96b0 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -72,6 +72,7 @@ function getInitialBackendBaseURL(): string {
       ? localStorage.getItem("bank-base-url")
       : undefined;
   let result: string;
+
   if (!overrideUrl) {
     //normal path
     if (!bankUiSettings.backendBaseURL) {
diff --git a/packages/demobank-ui/src/context/config.ts 
b/packages/demobank-ui/src/context/config.ts
index a31d914b8..a55af719d 100644
--- a/packages/demobank-ui/src/context/config.ts
+++ b/packages/demobank-ui/src/context/config.ts
@@ -18,7 +18,7 @@ import { TalerCorebankApi, TalerCoreBankHttpClient, 
TalerError } from "@gnu-tale
 import { BrowserHttpLib, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { ComponentChildren, createContext, FunctionComponent, h, VNode } from 
"preact";
 import { useContext, useEffect, useState } from "preact/hooks";
-import { ErrorLoading } from "../components/ErrorLoading.js";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
 
 /**
  *
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts 
b/packages/demobank-ui/src/pages/AccountPage/index.ts
index 87ed878b0..3a3001ad4 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -16,8 +16,8 @@
 
 import { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError } from 
"@gnu-taler/taler-util";
 import { utils } from "@gnu-taler/web-util/browser";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
-import { Loading } from "../../components/Loading.js";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
 import { LoginForm } from "../LoginForm.js";
 import { useComponentState } from "./state.js";
 import { InvalidIbanView, ReadyView } from "./views.js";
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx 
b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 8fff37624..0f5236192 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -16,7 +16,7 @@
 
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
-import { Attention } from "../../components/Attention.js";
+import { Attention } from "@gnu-taler/web-util/browser";
 import { Transactions } from "../../components/Transactions/index.js";
 import { useSettings } from "../../hooks/settings.js";
 import { PaymentOptions } from "../PaymentOptions.js";
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx 
b/packages/demobank-ui/src/pages/BankFrame.tsx
index c0babd0c9..5561d7b42 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -14,15 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts, TalerError, TranslatedString, parsePaytoUri, 
stringifyPaytoUri } from "@gnu-taler/taler-util";
-import { notifyError, notifyException, useNotifications, useTranslationContext 
} from "@gnu-taler/web-util/browser";
+import { Amounts, TalerError, TranslatedString } from "@gnu-taler/taler-util";
+import { Attention, LangSelector, Loading, notifyError, notifyException, 
useNotifications, useTranslationContext } from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, VNode, h } from "preact";
 import { useEffect, useErrorBoundary, useState } from "preact/hooks";
 import logo from "../assets/logo-2021.svg";
-import { Attention } from "../components/Attention.js";
-import { CopyButton } from "../components/CopyButton.js";
-import { LangSelector } from "../components/LangSelector.js";
-import { Loading } from "../components/Loading.js";
 import { useAccountDetails } from "../hooks/access.js";
 import { useBackendState } from "../hooks/backend.js";
 import { getAllBooleanSettings, getLabelForSetting, useSettings } from 
"../hooks/settings.js";
@@ -179,7 +175,7 @@ export function BankFrame({
                               </li>
                               : undefined}
                             <li>
-                              <LangSelector />
+                              <LangSelector supportedLangs={["en", "es", 
"de"]} />
                             </li>
                             <li>
                               <div class="text-xs font-semibold leading-6 
text-gray-400">
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx 
b/packages/demobank-ui/src/pages/LoginForm.tsx
index f21e98343..707c1e688 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -18,15 +18,15 @@ import { TranslatedString } from "@gnu-taler/taler-util";
 import { Notification, useLocalNotification, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { doAutoFocus } from "./PaytoWireTransferForm.js";
-import { Attention } from "../components/Attention.js";
-import { ShowLocalNotification } from "../components/ShowLocalNotification.js";
+import { Attention } from "@gnu-taler/web-util/browser";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 
 /**
diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts 
b/packages/demobank-ui/src/pages/OperationState/index.ts
index b17b0d787..120fd7b45 100644
--- a/packages/demobank-ui/src/pages/OperationState/index.ts
+++ b/packages/demobank-ui/src/pages/OperationState/index.ts
@@ -16,8 +16,8 @@
 
 import { AbsoluteTime, AmountJson, TalerCoreBankErrorsByMethod, TalerError, 
TalerErrorDetail, TranslatedString, WithdrawUriResult } from 
"@gnu-taler/taler-util";
 import { utils } from "@gnu-taler/web-util/browser";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
-import { Loading } from "../../components/Loading.js";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
 import { useComponentState } from "./state.js";
 import { AbortedView, ConfirmedView, FailedView, InvalidPaytoView, 
InvalidReserveView, InvalidWithdrawalView, NeedConfirmationView, ReadyView } 
from "./views.js";
 
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx 
b/packages/demobank-ui/src/pages/OperationState/views.tsx
index e623b0dc2..2c4019de2 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -19,13 +19,13 @@ import { notifyInfo, useLocalNotification, 
useTranslationContext } from "@gnu-ta
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useMemo, useState } from "preact/hooks";
 import { QR } from "../../components/QR.js";
-import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useSettings } from "../../hooks/settings.js";
 import { undefinedIfEmpty } from "../../utils.js";
 import { State } from "./index.js";
-import { ShowLocalNotification } from 
"../../components/ShowLocalNotification.js";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
-import { Attention } from "../../components/Attention.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Attention } from "@gnu-taler/web-util/browser";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
 
 export function InvalidPaytoView({ payto, onClose }: State.InvalidPayto) {
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx 
b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index 31592039f..55eba423c 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -32,7 +32,7 @@ import {
 import { Fragment, Ref, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { mutate } from "swr";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import {
@@ -41,7 +41,7 @@ import {
   withRuntimeErrorHandling
 } from "../utils.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { ShowLocalNotification } from "../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 const logger = new Logger("PaytoWireTransferForm");
 
diff --git a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx 
b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
index d33353180..d441d002e 100644
--- a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
+++ b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
@@ -18,7 +18,7 @@ import { Logger, TalerError } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { Loading } from "../components/Loading.js";
+import { Loading } from "@gnu-taler/web-util/browser";
 import { Transactions } from "../components/Transactions/index.js";
 import { usePublicAccounts } from "../hooks/access.js";
 
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx 
b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index 22bf604f2..e8c1a0e6e 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -29,7 +29,7 @@ import { QR } from "../components/QR.js";
 import { useBankCoreApiContext } from "../context/config.js";
 import { withRuntimeErrorHandling } from "../utils.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { ShowLocalNotification } from "../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 export function QrCodeSection({
   withdrawUri,
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx 
b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index c2eca25e8..e8969afb9 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -20,13 +20,13 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
 import { getRandomPassword, getRandomUsername } from "./rnd.js";
-import { ShowLocalNotification } from "../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 const logger = new Logger("RegistrationPage");
 
diff --git a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx 
b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
index c07802273..43fd39205 100644
--- a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
@@ -2,8 +2,8 @@ import { TalerCorebankApi, TalerError, TranslatedString } from 
"@gnu-taler/taler
 import { notifyInfo, useLocalNotification, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { ErrorLoading } from "../components/ErrorLoading.js";
-import { Loading } from "../components/Loading.js";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useAccountDetails } from "../hooks/access.js";
 import { useBackendState } from "../hooks/backend.js";
@@ -12,7 +12,7 @@ import { LoginForm } from "./LoginForm.js";
 import { ProfileNavigation } from "./ProfileNavigation.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
 import { AccountForm } from "./admin/AccountForm.js";
-import { ShowLocalNotification } from "../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 export function ShowAccountDetails({
   account,
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx 
b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
index d30216f3f..759182997 100644
--- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
@@ -1,14 +1,14 @@
 import { notifyInfo, useLocalNotification, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
 import { doAutoFocus } from "./PaytoWireTransferForm.js";
 import { ProfileNavigation } from "./ProfileNavigation.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { ShowLocalNotification } from "../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 export function UpdateAccountPassword({
   account: accountName,
@@ -63,7 +63,7 @@ export function UpdateAccountPassword({
           })
           case "old-password-invalid-or-not-allowed": return notify({
             type: "error",
-            title: current ? 
+            title: current ?
               i18n.str`This user have no right on to change the password.` :
               i18n.str`This user have no right on to change the password or 
the old password doesn't match.`
           })
@@ -79,7 +79,7 @@ export function UpdateAccountPassword({
 
   return (
     <Fragment>
-          <ShowLocalNotification notification={notification} />
+      <ShowLocalNotification notification={notification} />
       {accountIsTheCurrentUser ?
         <ProfileNavigation current="credentials" /> :
         <h1 class="text-base font-semibold leading-6 text-gray-900">
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx 
b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index e3a713fdd..9a45e6285 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -29,7 +29,7 @@ import {
 import { Fragment, VNode, h } from "preact";
 import { forwardRef } from "preact/compat";
 import { useState } from "preact/hooks";
-import { Attention } from "../components/Attention.js";
+import { Attention } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import { useSettings } from "../hooks/settings.js";
@@ -37,7 +37,7 @@ import { undefinedIfEmpty, withRuntimeErrorHandling } from 
"../utils.js";
 import { OperationState } from "./OperationState/index.js";
 import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { ShowLocalNotification } from "../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 const logger = new Logger("WalletWithdrawForm");
 const RefAmount = forwardRef(InputAmount);
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx 
b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index b548c0d16..f34e8a919 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -31,13 +31,13 @@ import {
 import { Fragment, VNode, h } from "preact";
 import { useMemo, useState } from "preact/hooks";
 import { mutate } from "swr";
-import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useSettings } from "../hooks/settings.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
 import { RenderAmount } from "./PaytoWireTransferForm.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
-import { ShowLocalNotification } from "../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 const logger = new Logger("WithdrawalConfirmationQuestion");
 
diff --git a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx 
b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
index 4620e5456..5ed57a0f7 100644
--- a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
@@ -23,7 +23,7 @@ import {
   useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
-import { Attention } from "../components/Attention.js";
+import { Attention } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useSettings } from "../hooks/settings.js";
 import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx 
b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index bdd8ea585..52e3c63ee 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -23,9 +23,9 @@ import {
 } from "@gnu-taler/taler-util";
 import { notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
-import { Attention } from "../components/Attention.js";
-import { ErrorLoading } from "../components/ErrorLoading.js";
-import { Loading } from "../components/Loading.js";
+import { Attention } from "@gnu-taler/web-util/browser";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
 import { useWithdrawalDetails } from "../hooks/access.js";
 import { QrCodeSection } from "./QrCodeSection.js";
 import { WithdrawalConfirmationQuestion } from 
"./WithdrawalConfirmationQuestion.js";
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx 
b/packages/demobank-ui/src/pages/admin/Account.tsx
index 19189bec4..588d945ba 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -1,8 +1,8 @@
 import { Amounts, TalerError } from "@gnu-taler/taler-util";
 import { notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
-import { Loading } from "../../components/Loading.js";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
 import { useAccountDetails } from "../../hooks/access.js";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { LoginForm } from "../LoginForm.js";
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx 
b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index fa3a28057..7311d826e 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -1,11 +1,11 @@
 import { ComponentChildren, Fragment, VNode, h } from "preact";
-import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { PartialButDefined, RecursivePartial, WithIntermediate, 
undefinedIfEmpty, validateIBAN } from "../../utils.js";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { PaytoString, TalerCorebankApi, buildPayto, parsePaytoUri } from 
"@gnu-taler/taler-util";
 import { doAutoFocus } from "../PaytoWireTransferForm.js";
-import { CopyButton } from "../../components/CopyButton.js";
+import { CopyButton } from "@gnu-taler/web-util/browser";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
 
 const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx 
b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index be5194e6d..2aefde715 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -1,8 +1,8 @@
 import { Amounts, TalerError } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
-import { Loading } from "../../components/Loading.js";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../../context/config.js";
 import { useBusinessAccounts } from "../../hooks/circuit.js";
 import { RenderAmount } from "../PaytoWireTransferForm.js";
diff --git a/packages/demobank-ui/src/pages/admin/AdminHome.tsx 
b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
index a30cae547..9bc2ee571 100644
--- a/packages/demobank-ui/src/pages/admin/AdminHome.tsx
+++ b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
@@ -2,7 +2,7 @@ import { AmountString, Amounts, TalerCorebankApi, TalerError } 
from "@gnu-taler/
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
 import { Transactions } from "../../components/Transactions/index.js";
 import { useLastMonitorInfo } from "../../hooks/circuit.js";
 import { RenderAmount } from "../PaytoWireTransferForm.js";
diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx 
b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
index 3f4364c16..0369a6283 100644
--- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
@@ -3,14 +3,14 @@ import { notifyInfo, useLocalNotification, 
useTranslationContext } from "@gnu-ta
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { mutate } from "swr";
-import { Attention } from "../../components/Attention.js";
+import { Attention } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../../context/config.js";
 import { useBackendState } from "../../hooks/backend.js";
 import { withRuntimeErrorHandling } from "../../utils.js";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { getRandomPassword } from "../rnd.js";
 import { AccountForm, AccountFormData } from "./AccountForm.js";
-import { ShowLocalNotification } from 
"../../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 export function CreateNewAccount({
   onCancel,
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx 
b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index fa9693941..01136fdaf 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -2,10 +2,10 @@ import { Amounts, TalerError, TranslatedString } from 
"@gnu-taler/taler-util";
 import { notifyInfo, useLocalNotification, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { Attention } from "../../components/Attention.js";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
-import { Loading } from "../../components/Loading.js";
-import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { Attention } from "@gnu-taler/web-util/browser";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../../context/config.js";
 import { useAccountDetails } from "../../hooks/access.js";
 import { useBackendState } from "../../hooks/backend.js";
@@ -13,7 +13,7 @@ import { undefinedIfEmpty } from "../../utils.js";
 import { LoginForm } from "../LoginForm.js";
 import { doAutoFocus } from "../PaytoWireTransferForm.js";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { ShowLocalNotification } from 
"../../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 export function RemoveAccount({
   account,
diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx 
b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
index 5c284be24..735d84847 100644
--- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx
+++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
@@ -25,10 +25,10 @@ import {
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { mutate } from "swr";
-import { Attention } from "../../components/Attention.js";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
-import { Loading } from "../../components/Loading.js";
-import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { Attention } from "@gnu-taler/web-util/browser";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../../context/config.js";
 import { useAccountDetails } from "../../hooks/access.js";
 import { useBackendState } from "../../hooks/backend.js";
@@ -43,7 +43,7 @@ import {
 import { LoginForm } from "../LoginForm.js";
 import { InputAmount } from "../PaytoWireTransferForm.js";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { ShowLocalNotification } from 
"../../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 interface Props {
   account: string;
diff --git a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx 
b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
index a8b57b90c..80e585cf5 100644
--- a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
+++ b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
@@ -25,10 +25,10 @@ import { format } from "date-fns";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { mutate } from "swr";
-import { Attention } from "../../components/Attention.js";
-import { ErrorLoading } from "../../components/ErrorLoading.js";
-import { Loading } from "../../components/Loading.js";
-import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { Attention } from "@gnu-taler/web-util/browser";
+import { ErrorLoading } from "@gnu-taler/web-util/browser";
+import { Loading } from "@gnu-taler/web-util/browser";
+import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../../context/config.js";
 import { useBackendState } from "../../hooks/backend.js";
 import {
@@ -39,7 +39,7 @@ import {
   withRuntimeErrorHandling
 } from "../../utils.js";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
-import { ShowLocalNotification } from 
"../../components/ShowLocalNotification.js";
+import { ShowLocalNotification } from "@gnu-taler/web-util/browser";
 
 interface Props {
   id: string;
diff --git a/packages/demobank-ui/tailwind.config.js 
b/packages/demobank-ui/tailwind.config.js
index 01f058b2e..ec51dfbb8 100644
--- a/packages/demobank-ui/tailwind.config.js
+++ b/packages/demobank-ui/tailwind.config.js
@@ -1,6 +1,12 @@
 /** @type {import('tailwindcss').Config} */
 export default {
-  content: ["./src/**/*.{html,tsx}"],
+  content: {
+    relative: true,
+    files: [
+      "./src/**/*.{html,tsx}",
+      "./node_modules/@gnu-taler/web-util/src/**/*.{html,tsx}"
+    ],
+  },
   theme: {
     extend: {},
   },
diff --git a/packages/demobank-ui/src/assets/lang.svg 
b/packages/web-util/src/assets/lang.svg
similarity index 100%
rename from packages/demobank-ui/src/assets/lang.svg
rename to packages/web-util/src/assets/lang.svg
diff --git a/packages/demobank-ui/src/components/Attention.tsx 
b/packages/web-util/src/components/Attention.tsx
similarity index 93%
rename from packages/demobank-ui/src/components/Attention.tsx
rename to packages/web-util/src/components/Attention.tsx
index 57d0a4199..b85230a1b 100644
--- a/packages/demobank-ui/src/components/Attention.tsx
+++ b/packages/web-util/src/components/Attention.tsx
@@ -1,12 +1,11 @@
-import { TranslatedString } from "@gnu-taler/taler-util";
+import { TranslatedString, assertUnreachable } from "@gnu-taler/taler-util";
 import { ComponentChildren, Fragment, VNode, h } from "preact";
-import { assertUnreachable } from "./Routing.js";
 
-interface Props { 
-  type?: "info" | "success" | "warning" | "danger", 
-  onClose?: () => void, 
-  title: TranslatedString, 
-  children?: ComponentChildren ,
+interface Props {
+  type?: "info" | "success" | "warning" | "danger",
+  onClose?: () => void,
+  title: TranslatedString,
+  children?: ComponentChildren,
 }
 export function Attention({ type = "info", title, children, onClose }: Props): 
VNode {
   return <div class={`group attention-${type} mt-2 shadow-lg`}>
diff --git a/packages/demobank-ui/src/components/CopyButton.tsx 
b/packages/web-util/src/components/CopyButton.tsx
similarity index 99%
rename from packages/demobank-ui/src/components/CopyButton.tsx
rename to packages/web-util/src/components/CopyButton.tsx
index ca1ceaa8a..0096da365 100644
--- a/packages/demobank-ui/src/components/CopyButton.tsx
+++ b/packages/web-util/src/components/CopyButton.tsx
@@ -1,8 +1,6 @@
 import { h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 
-
-
 export function CopyIcon(): VNode {
   return (
     <svg xmlns="http://www.w3.org/2000/svg"; fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx 
b/packages/web-util/src/components/ErrorLoading.tsx
similarity index 96%
rename from packages/demobank-ui/src/components/ErrorLoading.tsx
rename to packages/web-util/src/components/ErrorLoading.tsx
index 84e72c5a1..02f2a3282 100644
--- a/packages/demobank-ui/src/components/ErrorLoading.tsx
+++ b/packages/web-util/src/components/ErrorLoading.tsx
@@ -15,11 +15,10 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { TalerError, TalerErrorCode } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { TalerError, TalerErrorCode, assertUnreachable } from 
"@gnu-taler/taler-util";
 import { Fragment, VNode, h } from "preact";
 import { Attention } from "./Attention.js";
-import { assertUnreachable } from "./Routing.js";
+import { useTranslationContext } from "../index.browser.js";
 
 export function ErrorLoading({ error, showDetail }: { error: TalerError, 
showDetail?: boolean }): VNode {
   const { i18n } = useTranslationContext()
diff --git a/packages/demobank-ui/src/components/LangSelector.tsx 
b/packages/web-util/src/components/LangSelector.tsx
similarity index 92%
rename from packages/demobank-ui/src/components/LangSelector.tsx
rename to packages/web-util/src/components/LangSelector.tsx
index 7cf0300df..a8d910129 100644
--- a/packages/demobank-ui/src/components/LangSelector.tsx
+++ b/packages/web-util/src/components/LangSelector.tsx
@@ -21,12 +21,12 @@
 
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { strings as messages } from "../i18n/strings.js";
+// import { strings as messages } from "../i18n/strings.js";
 import langIcon from "../assets/lang.svg";
+import { useTranslationContext } from "../index.browser.js";
 
 type LangsNames = {
-  [P in keyof typeof messages]: string;
+  [P: string]: string;
 };
 
 const names: LangsNames = {
@@ -43,7 +43,7 @@ function getLangName(s: keyof LangsNames | string): string {
   return String(s);
 }
 
-export function LangSelector(): VNode {
+export function LangSelector({ supportedLangs }: { supportedLangs: string[] 
}): VNode {
   const [updatingLang, setUpdatingLang] = useState(false);
   const { lang, changeLanguage } = useTranslationContext();
   const [hidden, setHidden] = useState(true);
@@ -70,7 +70,7 @@ export function LangSelector(): VNode {
             setHidden((h) => !h);
           }}>
           <span class="flex items-center">
-            <img src={langIcon} alt="" class="h-5 w-5 flex-shrink-0 
rounded-full" />
+            <img alt="language" class="h-5 w-5 flex-shrink-0 rounded-full" 
src={langIcon} />
             <span class="ml-3 block truncate">{getLangName(lang)}</span>
           </span>
           <span class="pointer-events-none absolute inset-y-0 right-0 flex 
items-center pr-2">
@@ -82,7 +82,7 @@ export function LangSelector(): VNode {
 
         {!hidden &&
           <ul class="absolute z-10 mt-1 max-h-60 w-full overflow-auto 
rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 
focus:outline-none sm:text-sm" tabIndex={-1} role="listbox" 
aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
-            {Object.keys(messages)
+            {supportedLangs
               .filter((l) => l !== lang)
               .map((lang) => (
                 <li class="text-gray-900 hover:bg-indigo-600 hover:text-white 
cursor-pointer relative select-none py-2 pl-3 pr-9" role="option"
diff --git a/packages/demobank-ui/src/components/Loading.tsx 
b/packages/web-util/src/components/Loading.tsx
similarity index 100%
rename from packages/demobank-ui/src/components/Loading.tsx
rename to packages/web-util/src/components/Loading.tsx
diff --git a/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx 
b/packages/web-util/src/components/ShowInputErrorLabel.tsx
similarity index 100%
copy from packages/demobank-ui/src/components/ShowInputErrorLabel.tsx
copy to packages/web-util/src/components/ShowInputErrorLabel.tsx
diff --git a/packages/demobank-ui/src/components/ShowLocalNotification.tsx 
b/packages/web-util/src/components/ShowLocalNotification.tsx
similarity index 70%
rename from packages/demobank-ui/src/components/ShowLocalNotification.tsx
rename to packages/web-util/src/components/ShowLocalNotification.tsx
index bb62a48f0..cb947e536 100644
--- a/packages/demobank-ui/src/components/ShowLocalNotification.tsx
+++ b/packages/web-util/src/components/ShowLocalNotification.tsx
@@ -1,7 +1,7 @@
-import { Notification } from "@gnu-taler/web-util/browser";
 import { h, Fragment, VNode } from "preact";
 import { Attention } from "./Attention.js";
-import { useSettings } from "../hooks/settings.js";
+import { Notification } from "../index.browser.js";
+// import { useSettings } from "../hooks/settings.js";
 
 export function ShowLocalNotification({ notification }: { notification?: 
Notification }): VNode {
   if (!notification) return <Fragment />
@@ -17,7 +17,7 @@ export function ShowLocalNotification({ notification }: { 
notification?: Notific
                 {notification.message.description}
               </div>
             }
-            <MaybeShowDebugInfo info={notification.message.debug} />
+            {/* <MaybeShowDebugInfo info={notification.message.debug} /> */}
           </Attention>
         </div>
       </div>
@@ -31,13 +31,13 @@ export function ShowLocalNotification({ notification }: { 
notification?: Notific
 }
 
 
-function MaybeShowDebugInfo({ info }: { info: any }): VNode {
-  const [settings] = useSettings()
-  if (settings.showDebugInfo) {
-    return <pre class="whitespace-break-spaces ">
-      {info}
-    </pre>
-  }
-  return <Fragment />
-}
+// function MaybeShowDebugInfo({ info }: { info: any }): VNode {
+//   const [settings] = useSettings()
+//   if (settings.showDebugInfo) {
+//     return <pre class="whitespace-break-spaces ">
+//       {info}
+//     </pre>
+//   }
+//   return <Fragment />
+// }
 
diff --git a/packages/web-util/src/components/index.ts 
b/packages/web-util/src/components/index.ts
index 9441e971d..8344d4a7a 100644
--- a/packages/web-util/src/components/index.ts
+++ b/packages/web-util/src/components/index.ts
@@ -1 +1,8 @@
 export * as utils from "./utils.js";
+export * from "./Attention.js";
+export * from "./CopyButton.js";
+export * from "./ErrorLoading.js";
+export * from "./LangSelector.js";
+export * from "./Loading.js";
+export * from "./ShowInputErrorLabel.js";
+export * from "./ShowLocalNotification.js";
diff --git a/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx 
b/packages/web-util/src/declaration.d.ts
similarity index 63%
rename from packages/demobank-ui/src/components/ShowInputErrorLabel.tsx
rename to packages/web-util/src/declaration.d.ts
index c5840cad9..c8ba3d576 100644
--- a/packages/demobank-ui/src/components/ShowInputErrorLabel.tsx
+++ b/packages/web-util/src/declaration.d.ts
@@ -14,16 +14,22 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Fragment, h, VNode } from "preact";
-
-export function ShowInputErrorLabel({
-  isDirty,
-  message,
-}: {
-  message: string | undefined;
-  isDirty: boolean;
-}): VNode {
-  if (message && isDirty)
-    return <div class="text-base" style={{ color: "red" }}>{message}</div>;
-  return <div class="text-base" style={{ }}> </div>;
+declare module "*.css" {
+  const mapping: Record<string, string>;
+  export default mapping;
+}
+declare module "*.svg" {
+  const content: any;
+  export default content;
+}
+declare module "*.jpeg" {
+  const content: any;
+  export default content;
 }
+declare module "*.png" {
+  const content: any;
+  export default content;
+}
+
+declare const __VERSION__: string;
+declare const __GIT_HASH__: string;

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