gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 03/04: wip DD39: removed webRequest permission and c


From: gnunet
Subject: [taler-wallet-core] 03/04: wip DD39: removed webRequest permission and changes made into demobank
Date: Mon, 05 Feb 2024 13:18:45 +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 b71d6f2b11342bd22197289ad3872d8a341686b5
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Sun Feb 4 12:04:27 2024 -0300

    wip DD39: removed webRequest permission and changes made into demobank
---
 packages/demobank-ui/src/Routing.tsx               | 153 ++++++-----
 packages/demobank-ui/src/components/app.tsx        |   8 +-
 packages/demobank-ui/src/context/navigation.ts     |  80 ++++++
 .../demobank-ui/src/context/wallet-integration.ts  |  90 +++++++
 packages/demobank-ui/src/index.html                |  42 +--
 .../demobank-ui/src/pages/OperationState/views.tsx |  16 +-
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |  49 +++-
 .../demobank-ui/src/pages/ProfileNavigation.tsx    |  10 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |  19 +-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |  31 ++-
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |  46 ++--
 .../src/pages/business/CreateCashout.tsx           |   2 +-
 packages/demobank-ui/src/route.ts                  | 124 +--------
 .../taler-wallet-webextension/manifest-v2.json     |   1 -
 .../taler-wallet-webextension/manifest-v3.json     |   1 -
 .../taler-wallet-webextension/src/platform/api.ts  |  35 +--
 .../src/platform/chrome.ts                         | 300 ++-------------------
 .../taler-wallet-webextension/src/platform/dev.ts  |   7 -
 .../src/platform/firefox.ts                        |  11 -
 .../src/taler-wallet-interaction-loader.ts         | 121 ++++++---
 .../src/taler-wallet-interaction-support.ts        |  26 +-
 .../src/wallet/DeveloperPage.tsx                   | 205 +++++++-------
 .../src/wallet/Settings.stories.tsx                |   4 -
 .../src/wallet/Settings.tsx                        |  23 --
 packages/taler-wallet-webextension/src/wxApi.ts    |  10 +-
 .../taler-wallet-webextension/src/wxBackend.ts     |  81 +-----
 26 files changed, 660 insertions(+), 835 deletions(-)

diff --git a/packages/demobank-ui/src/Routing.tsx 
b/packages/demobank-ui/src/Routing.tsx
index e73493d60..442a276a0 100644
--- a/packages/demobank-ui/src/Routing.tsx
+++ b/packages/demobank-ui/src/Routing.tsx
@@ -48,6 +48,8 @@ import { RemoveAccount } from 
"./pages/admin/RemoveAccount.js";
 import { CreateCashout } from "./pages/business/CreateCashout.js";
 import { ShowCashoutDetails } from "./pages/business/ShowCashoutDetails.js";
 import { RouteParamsType, urlPattern, useCurrentLocation } from "./route.js";
+import { useNavigationContext } from "./context/navigation.js";
+import { useEffect } from "preact/hooks";
 
 export function Routing(): VNode {
   const backend = useBackendState();
@@ -89,12 +91,18 @@ function PublicRounting({
 }): VNode {
   const settings = useSettingsContext();
   const { i18n } = useTranslationContext();
-  const [loc, routeTo] = useCurrentLocation(publicPages);
+  const location = useCurrentLocation(publicPages);
+  const { navigateTo } = useNavigationContext()
   const { api } = useBankCoreApiContext();
   const [notification, notify, handleError] = useLocalNotification();
 
-  if (loc === undefined) {
-    routeTo("login", {});
+  useEffect(() => {
+    if (location === undefined) {
+      navigateTo(privatePages.home.url({}))
+    }
+  }, [location])
+
+  if (location === undefined) {
     return <Fragment />;
   }
 
@@ -132,7 +140,7 @@ function PublicRounting({
     });
   }
 
-  switch (loc.name) {
+  switch (location.name) {
     case "login": {
       return (
         <Fragment>
@@ -148,17 +156,17 @@ function PublicRounting({
       return <PublicHistoriesPage />;
     }
     case "operationDetails": {
-      const { wopid } = loc.values as RouteParamsType<
-        typeof loc.parent,
-        typeof loc.name
+      const { wopid } = location.values as RouteParamsType<
+        typeof location.parent,
+        typeof location.name
       >;
 
       return (
         <WithdrawalOperationPage
           operationId={wopid}
-          onOperationAborted={() => routeTo("login", {})}
+          onOperationAborted={() => navigateTo(publicPages.login.url({}))}
           routeClose={publicPages.login}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onAuthorizationRequired={() => 
navigateTo(publicPages.solveSecondFactor.url({}))}
         />
       );
     }
@@ -176,18 +184,17 @@ function PublicRounting({
     case "solveSecondFactor": {
       return (
         <SolveChallengePage
-          onChallengeCompleted={() => routeTo("login", {})}
+          onChallengeCompleted={() => navigateTo(publicPages.login.url({}))}
           routeClose={publicPages.login}
         />
       );
     }
     default:
-      assertUnreachable(loc.name);
+      assertUnreachable(location.name);
   }
 }
 
 export const privatePages = {
-  home: urlPattern(/\/account/, () => "#/account"),
   homeChargeWallet: urlPattern(
     /\/account\/charge-wallet/,
     () => "#/account/charge-wallet",
@@ -196,6 +203,7 @@ export const privatePages = {
     /\/account\/wire-transfer/,
     () => "#/account/wire-transfer",
   ),
+  home: urlPattern(/\/account/, () => "#/account"),
   solveSecondFactor: urlPattern(/\/2fa/, () => "#/2fa"),
   cashoutCreate: urlPattern(/\/new-cashout/, () => "#/new-cashout"),
   cashoutDetails: urlPattern<{ cid: string }>(
@@ -233,7 +241,7 @@ export const privatePages = {
     ({ account }) => `#/profile/${account}/cashouts`,
   ),
   operationDetails: urlPattern<{ wopid: string }>(
-    /\/operation\/(?<wopid>[a-zA-Z0-9]+)/,
+    /\/operation\/(?<wopid>[a-zA-Z0-9-]+)/,
     ({ wopid }) => `#/operation/${wopid}`,
   ),
 };
@@ -245,33 +253,38 @@ function PrivateRouting({
   username: string;
   isAdmin: boolean;
 }): VNode {
-  const [loc, routeTo] = useCurrentLocation(privatePages);
+  const { navigateTo } = useNavigationContext()
+  const location = useCurrentLocation(privatePages);
+  useEffect(() => {
+    if (location === undefined) {
+      navigateTo(privatePages.home.url({}))
+    }
+  }, [location])
 
-  if (loc === undefined) {
-    routeTo("home", {});
+  if (location === undefined) {
     return <Fragment />;
   }
 
-  switch (loc.name) {
+  switch (location.name) {
     case "operationDetails": {
-      const { wopid } = loc.values as RouteParamsType<
-        typeof loc.parent,
-        typeof loc.name
+      const { wopid } = location.values as RouteParamsType<
+        typeof location.parent,
+        typeof location.name
       >;
 
       return (
         <WithdrawalOperationPage
           operationId={wopid}
-          onOperationAborted={() => routeTo("home", {})}
+          onOperationAborted={() => navigateTo(privatePages.home.url({}))}
           routeClose={privatePages.home}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
         />
       );
     }
     case "solveSecondFactor": {
       return (
         <SolveChallengePage
-          onChallengeCompleted={() => routeTo("home", {})}
+          onChallengeCompleted={() => navigateTo(privatePages.home.url({}))}
           routeClose={privatePages.home}
         />
       );
@@ -286,64 +299,64 @@ function PrivateRouting({
       return (
         <CreateNewAccount
           routeCancel={privatePages.home}
-          onCreateSuccess={() => routeTo("home", {})}
+          onCreateSuccess={() => navigateTo(privatePages.home.url({}))}
         />
       );
     }
     case "accountDetails": {
-      const { account } = loc.values as RouteParamsType<
-        typeof loc.parent,
-        typeof loc.name
+      const { account } = location.values as RouteParamsType<
+        typeof location.parent,
+        typeof location.name
       >;
       return (
         <ShowAccountDetails
           account={account}
-          onUpdateSuccess={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeClose={privatePages.home}
         />
       );
     }
     case "accountChangePassword": {
-      const { account } = loc.values as RouteParamsType<
-        typeof loc.parent,
-        typeof loc.name
+      const { account } = location.values as RouteParamsType<
+        typeof location.parent,
+        typeof location.name
       >;
       return (
         <UpdateAccountPassword
           focus
           account={account}
-          onUpdateSuccess={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeClose={privatePages.home}
         />
       );
     }
     case "accountDelete": {
-      const { account } = loc.values as RouteParamsType<
-        typeof loc.parent,
-        typeof loc.name
+      const { account } = location.values as RouteParamsType<
+        typeof location.parent,
+        typeof location.name
       >;
       return (
         <RemoveAccount
           account={account}
-          onUpdateSuccess={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeCancel={privatePages.home}
         />
       );
     }
     case "accountCashouts": {
-      const { account } = loc.values as RouteParamsType<
-        typeof loc.parent,
-        typeof loc.name
+      const { account } = location.values as RouteParamsType<
+        typeof location.parent,
+        typeof location.name
       >;
       return (
         <CashoutListForAccount
           account={account}
           routeCashoutDetails={privatePages.cashoutDetails}
           routeClose={privatePages.home}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
         />
       );
     }
@@ -351,8 +364,8 @@ function PrivateRouting({
       return (
         <RemoveAccount
           account={username}
-          onUpdateSuccess={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeCancel={privatePages.home}
         />
       );
@@ -361,8 +374,8 @@ function PrivateRouting({
       return (
         <ShowAccountDetails
           account={username}
-          onUpdateSuccess={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeClose={privatePages.home}
         />
       );
@@ -372,8 +385,8 @@ function PrivateRouting({
         <UpdateAccountPassword
           focus
           account={username}
-          onUpdateSuccess={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onUpdateSuccess={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeClose={privatePages.home}
         />
       );
@@ -383,7 +396,7 @@ function PrivateRouting({
         <CashoutListForAccount
           account={username}
           routeCashoutDetails={privatePages.cashoutDetails}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeClose={privatePages.home}
         />
       );
@@ -392,7 +405,7 @@ function PrivateRouting({
       if (isAdmin) {
         return (
           <AdminHome
-            onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+            onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
             routeCreate={privatePages.accountCreate}
             routeRemoveAccount={privatePages.accountDelete}
             routeShowAccount={privatePages.accountDetails}
@@ -408,9 +421,9 @@ function PrivateRouting({
           routeChargeWallet={privatePages.homeChargeWallet}
           routeWireTransfer={privatePages.homeWireTransfer}
           routeClose={privatePages.home}
-          onClose={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
-          onOperationCreated={(wopid) => routeTo("operationDetails", { wopid 
})}
+          onClose={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
+          onOperationCreated={(wopid) => 
navigateTo(privatePages.operationDetails.url({ wopid }))}
         />
       );
     }
@@ -418,15 +431,15 @@ function PrivateRouting({
       return (
         <CreateCashout
           account={username}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeClose={privatePages.home}
         />
       );
     }
     case "cashoutDetails": {
-      const { cid } = loc.values as RouteParamsType<
-        typeof loc.parent,
-        typeof loc.name
+      const { cid } = location.values as RouteParamsType<
+        typeof location.parent,
+        typeof location.name
       >;
       return (
         <ShowCashoutDetails
@@ -436,16 +449,16 @@ function PrivateRouting({
       );
     }
     case "wireTranserCreate": {
-      const { destination } = loc.values as RouteParamsType<
-        typeof loc.parent,
-        typeof loc.name
+      const { destination } = location.values as RouteParamsType<
+        typeof location.parent,
+        typeof location.name
       >;
       return (
         <WireTransfer
           toAccount={destination}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
           routeCancel={privatePages.home}
-          onSuccess={() => routeTo("home", {})}
+          onSuccess={() => navigateTo(privatePages.home.url({}))}
         />
       );
     }
@@ -457,9 +470,9 @@ function PrivateRouting({
           routeChargeWallet={privatePages.homeChargeWallet}
           routeWireTransfer={privatePages.homeWireTransfer}
           routeClose={privatePages.home}
-          onClose={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
-          onOperationCreated={(wopid) => routeTo("operationDetails", { wopid 
})}
+          onClose={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
+          onOperationCreated={(wopid) => 
navigateTo(privatePages.operationDetails.url({ wopid }))}
         />
       );
     }
@@ -471,13 +484,13 @@ function PrivateRouting({
           routeChargeWallet={privatePages.homeChargeWallet}
           routeWireTransfer={privatePages.homeWireTransfer}
           routeClose={privatePages.home}
-          onClose={() => routeTo("home", {})}
-          onAuthorizationRequired={() => routeTo("solveSecondFactor", {})}
-          onOperationCreated={(wopid) => routeTo("operationDetails", { wopid 
})}
+          onClose={() => navigateTo(privatePages.home.url({}))}
+          onAuthorizationRequired={() => 
navigateTo(privatePages.solveSecondFactor.url({}))}
+          onOperationCreated={(wopid) => 
navigateTo(privatePages.operationDetails.url({ wopid }))}
         />
       );
     }
     default:
-      assertUnreachable(loc.name);
+      assertUnreachable(location.name);
   }
 }
diff --git a/packages/demobank-ui/src/components/app.tsx 
b/packages/demobank-ui/src/components/app.tsx
index 31013388b..97778e6d7 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -30,6 +30,8 @@ import { SettingsProvider } from "../context/settings.js";
 import { strings } from "../i18n/strings.js";
 import { BankFrame } from "../pages/BankFrame.js";
 import { BankUiSettings, fetchSettings } from "../settings.js";
+import { TalerWalletIntegrationBrowserProvider } from 
"../context/wallet-integration.js";
+import { BrowserHashNavigationProvider } from "../context/navigation.js";
 const WITH_LOCAL_STORAGE_CACHE = false;
 
 const App: FunctionalComponent = () => {
@@ -78,7 +80,11 @@ const App: FunctionalComponent = () => {
                 keepPreviousData: true,
               }}
             >
-              <Routing />
+              <TalerWalletIntegrationBrowserProvider>
+                <BrowserHashNavigationProvider>
+                  <Routing />
+                </BrowserHashNavigationProvider>
+              </TalerWalletIntegrationBrowserProvider>
             </SWRConfig>
           </BankCoreApiProvider>
         </BackendStateProvider>
diff --git a/packages/demobank-ui/src/context/navigation.ts 
b/packages/demobank-ui/src/context/navigation.ts
new file mode 100644
index 000000000..fc1460c02
--- /dev/null
+++ b/packages/demobank-ui/src/context/navigation.ts
@@ -0,0 +1,80 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 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, createContext, h, VNode } from "preact";
+import { useContext, useEffect, useState } from "preact/hooks";
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+export type Type = {
+  path: string;
+  params: Record<string, string>;
+  navigateTo: (path: string) => void;
+  // addNavigationListener: (listener: (path: string, params: Record<string, 
string>) => void) => (() => void);
+};
+
+// @ts-expect-error shold not be used without provider
+const Context = createContext<Type>(undefined);
+
+export const useNavigationContext = (): Type => useContext(Context);
+
+function getPathAndParamsFromWindow() {
+  const path = typeof window !== "undefined" ? 
window.location.hash.substring(1) : "/";
+  const params: Record<string, string> = {}
+  if (typeof window !== "undefined") {
+    for (const [key, value] of new URLSearchParams(window.location.search)) {
+      params[key] = value;
+    }
+  }
+  return { path, params }
+}
+
+const { path: initialPath, params: initialParams } = 
getPathAndParamsFromWindow()
+
+// there is a posibility that if the browser does a redirection
+// (which doesn't go through navigatTo function) and that exectued
+// too early (before addEventListener runs) it won't be taking
+// into account
+const PopStateEventType = "popstate";
+
+export const BrowserHashNavigationProvider = ({ children }: { children: 
ComponentChildren }): VNode => {
+  const [{ path, params }, setState] = useState({ path: initialPath, params: 
initialParams })
+  if (typeof window === "undefined") {
+    throw Error("Can't use BrowserHashNavigationProvider if there is no window 
object")
+  }
+  function navigateTo(path: string) {
+    const { params } = getPathAndParamsFromWindow()
+    setState({ path, params })
+    window.location.href = path
+  }
+
+  useEffect(() => {
+    function eventListener() {
+      setState(getPathAndParamsFromWindow())
+    }
+    window.addEventListener(PopStateEventType, eventListener);
+    return () => {
+      window.removeEventListener(PopStateEventType, eventListener)
+    }
+  }, [])
+  return h(Context.Provider, {
+    value: { path, params, navigateTo },
+    children,
+  });
+};
diff --git a/packages/demobank-ui/src/context/wallet-integration.ts 
b/packages/demobank-ui/src/context/wallet-integration.ts
new file mode 100644
index 000000000..47bdc90ec
--- /dev/null
+++ b/packages/demobank-ui/src/context/wallet-integration.ts
@@ -0,0 +1,90 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022-2024 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 {
+  stringifyTalerUri,
+  TalerUri
+} from "@gnu-taler/taler-util";
+import {
+  ComponentChildren,
+  createContext,
+  h,
+  VNode
+} from "preact";
+import { useContext } from "preact/hooks";
+
+/**
+ * https://docs.taler.net/design-documents/039-taler-browser-integration.html
+ * 
+ * @param uri 
+ */
+function createHeadMetaTag(uri: TalerUri, onNotFound?: () => void) {
+
+  const meta = document.createElement("meta");
+  meta.setAttribute("name", "taler-uri");
+  meta.setAttribute("content", stringifyTalerUri(uri));
+
+  document.head.appendChild(meta);
+
+  let walletFound = false
+  window.addEventListener("beforeunload", () => {
+    walletFound = true
+  })
+  setTimeout(() => {
+    if (!walletFound && onNotFound) {
+      onNotFound()
+    }
+  }, 10)//very short timeout
+}
+interface Type {
+  /**
+   * Tell the active wallet that an action is found
+   * 
+   * @param uri 
+   * @returns 
+   */
+  publishTalerAction: (uri: TalerUri, onNotFound?: () => void) => void;
+}
+
+// @ts-expect-error default value to undefined, should it be another thing?
+const Context = createContext<Type>(undefined);
+
+export const useTalerWalletIntegrationAPI = (): Type => useContext(Context);
+
+export const TalerWalletIntegrationBrowserProvider = ({ children }: { 
children: ComponentChildren }): VNode => {
+  const value: Type = {
+    publishTalerAction: createHeadMetaTag
+  };
+  return h(Context.Provider, {
+    value,
+    children,
+  });
+};
+
+
+export const TalerWalletIntegrationTestingProvider = ({
+  children,
+  value,
+}: {
+  children: ComponentChildren;
+  value: Type;
+}): VNode => {
+
+  return h(Context.Provider, {
+    value,
+    children,
+  });
+};
diff --git a/packages/demobank-ui/src/index.html 
b/packages/demobank-ui/src/index.html
index 720b678a3..6e0638e3f 100644
--- a/packages/demobank-ui/src/index.html
+++ b/packages/demobank-ui/src/index.html
@@ -17,25 +17,25 @@
 -->
 <!doctype html>
 <html lang="en" class="h-full bg-gray-100">
-  <head>
-    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
-    <meta charset="utf-8" />
-    <meta name="viewport" content="width=device-width,initial-scale=1" />
-    <meta name="taler-support" content="uri" />
-    <meta name="mobile-web-app-capable" content="yes" />
-    <meta name="apple-mobile-web-app-capable" content="yes" />
-    <link
-      rel="icon"
-      
href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/
 [...]
-    />
-    <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
-    <title>Bank</title>
-    <!-- Entry point for the bank SPA. -->
-    <script type="module" src="index.js"></script>
-    <link rel="stylesheet" href="index.css" />
-  </head>
 
-  <body class="h-full">
-    <div id="app"></div>
-  </body>
-</html>
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+  <meta charset="utf-8" />
+  <meta name="viewport" content="width=device-width,initial-scale=1" />
+  <meta name="taler-support" content="uri,api" />
+  <meta name="mobile-web-app-capable" content="yes" />
+  <meta name="apple-mobile-web-app-capable" content="yes" />
+  <link rel="icon"
+    
href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn
 [...]
+  <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
+  <title>Bank</title>
+  <!-- Entry point for the bank SPA. -->
+  <script type="module" src="index.js"></script>
+  <link rel="stylesheet" href="index.css" />
+</head>
+
+<body class="h-full">
+  <div id="app"></div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx 
b/packages/demobank-ui/src/pages/OperationState/views.tsx
index ac3724eb8..4d193505e 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -36,6 +36,7 @@ import { useBankState } from "../../hooks/bank-state.js";
 import { usePreferences } from "../../hooks/preferences.js";
 import { ShouldBeSameUser } from "../WithdrawalConfirmationQuestion.js";
 import { State } from "./index.js";
+import { useTalerWalletIntegrationAPI } from 
"../../context/wallet-integration.js";
 
 export function InvalidPaytoView({ payto }: State.InvalidPayto) {
   return <div>Payto from server is not valid &quot;{payto}&quot;</div>;
@@ -328,23 +329,12 @@ export function ReadyView({
   onAbort: doAbort,
 }: State.Ready): VNode<Record<string, never>> {
   const { i18n } = useTranslationContext();
+  const walletInegrationApi = useTalerWalletIntegrationAPI()
   const [notification, notify, errorHandler] = useLocalNotification();
 
   const talerWithdrawUri = stringifyWithdrawUri(uri);
   useEffect(() => {
-    // Taler Wallet WebExtension is listening to headers response and tab 
updates.
-    // In the SPA there is no header response with the Taler URI so
-    // this hack manually triggers the tab update after the QR is in the DOM.
-    // WebExtension will be using
-    // 
https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
-    document.title = `${document.title} ${uri.withdrawalOperationId}`;
-    const meta = document.createElement("meta");
-    meta.setAttribute("name", "taler-uri");
-    meta.setAttribute("content", talerWithdrawUri);
-    document.head.insertBefore(
-      meta,
-      document.head.children.length ? document.head.children[0] : null,
-    );
+    walletInegrationApi.publishTalerAction(uri)
   }, []);
 
   async function onAbort() {
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx 
b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 53086d4cc..51a6a17a9 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -14,13 +14,43 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AmountJson } from "@gnu-taler/taler-util";
-import { VNode, h } from "preact";
+import { AmountJson, TalerError } from "@gnu-taler/taler-util";
+import { Fragment, VNode, h } from "preact";
 import { useBankState } from "../hooks/bank-state.js";
 import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js";
 import { WalletWithdrawForm } from "./WalletWithdrawForm.js";
 import { RouteDefinition } from "../route.js";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { useWithdrawalDetails } from "../hooks/access.js";
+import { useEffect } from "preact/hooks";
+
+function ShowOperationPendingTag({ woid, onOperationAlreadyCompleted }: { 
woid: string, onOperationAlreadyCompleted?: () => void }): VNode {
+  const { i18n } = useTranslationContext();
+  const result = useWithdrawalDetails(woid);
+  const error = !result || result instanceof TalerError || result.type === 
"fail"
+  const completed = !error && (result.body.status === "aborted" || 
result.body.status === "confirmed")
+  useEffect(() => {
+    if (completed && onOperationAlreadyCompleted) {
+      onOperationAlreadyCompleted()
+    }
+  }, [completed])
+
+  if (error || completed) {
+    return <Fragment />;
+  }
+
+  return <span class="flex items-center gap-x-1.5 w-fit rounded-md 
bg-green-100 px-2 py-1 text-xs font-medium text-green-700 whitespace-pre">
+    <svg
+      class="h-1.5 w-1.5 fill-green-500"
+      viewBox="0 0 6 6"
+      aria-hidden="true"
+    >
+      <circle cx="3" cy="3" r="3" />
+    </svg>
+    <i18n.Translate>operation ready</i18n.Translate>
+  </span>
+
+}
 
 /**
  * Let the user choose a payment option,
@@ -46,7 +76,7 @@ export function PaymentOptions({
   routeWireTransfer: RouteDefinition<Record<string, never>>;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const [bankState] = useBankState();
+  const [bankState, updateBankState] = useBankState();
 
   return (
     <div class="mt-4">
@@ -98,16 +128,9 @@ export function PaymentOptions({
                   </i18n.Translate>
                 </div>
                 {!!bankState.currentWithdrawalOperationId && (
-                  <span class="flex items-center gap-x-1.5 w-fit rounded-md 
bg-green-100 px-2 py-1 text-xs font-medium text-green-700 whitespace-pre">
-                    <svg
-                      class="h-1.5 w-1.5 fill-green-500"
-                      viewBox="0 0 6 6"
-                      aria-hidden="true"
-                    >
-                      <circle cx="3" cy="3" r="3" />
-                    </svg>
-                    <i18n.Translate>operation ready</i18n.Translate>
-                  </span>
+                  <ShowOperationPendingTag 
woid={bankState.currentWithdrawalOperationId} onOperationAlreadyCompleted={() 
=> {
+                    updateBankState("currentWithdrawalOperationId", undefined)
+                  }} />
                 )}
               </div>
             </label>
diff --git a/packages/demobank-ui/src/pages/ProfileNavigation.tsx 
b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
index a6615d578..02f30d8e8 100644
--- a/packages/demobank-ui/src/pages/ProfileNavigation.tsx
+++ b/packages/demobank-ui/src/pages/ProfileNavigation.tsx
@@ -19,6 +19,7 @@ import { privatePages } from "../Routing.js";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
 import { assertUnreachable } from "@gnu-taler/taler-util";
+import { useNavigationContext } from "../context/navigation.js";
 
 export function ProfileNavigation({
   current,
@@ -32,6 +33,7 @@ export function ProfileNavigation({
     credentials.status !== "loggedIn"
       ? false
       : !credentials.isUserAdministrator;
+  const { navigateTo } = useNavigationContext()
   return (
     <div>
       <div class="sm:hidden">
@@ -46,19 +48,19 @@ export function ProfileNavigation({
             const op = e.currentTarget.value as typeof current;
             switch (op) {
               case "details": {
-                window.location.href = privatePages.myAccountDetails.url({});
+                navigateTo(privatePages.myAccountDetails.url({}));
                 return;
               }
               case "delete": {
-                window.location.href = privatePages.myAccountDelete.url({});
+                navigateTo(privatePages.myAccountDelete.url({}));
                 return;
               }
               case "credentials": {
-                window.location.href = privatePages.myAccountPassword.url({});
+                navigateTo(privatePages.myAccountPassword.url({}));
                 return;
               }
               case "cashouts": {
-                window.location.href = privatePages.myAccountCashouts.url({});
+                navigateTo(privatePages.myAccountCashouts.url({}));
                 return;
               }
               default:
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx 
b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index f21134aa1..037849804 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -19,7 +19,7 @@ import {
   HttpStatusCode,
   stringifyWithdrawUri,
   TranslatedString,
-  WithdrawUriResult,
+  WithdrawUriResult
 } from "@gnu-taler/taler-util";
 import {
   LocalNotificationBanner,
@@ -30,6 +30,7 @@ import { Fragment, h, VNode } from "preact";
 import { useEffect } from "preact/hooks";
 import { QR } from "../components/QR.js";
 import { useBankCoreApiContext } from "../context/config.js";
+import { useTalerWalletIntegrationAPI } from 
"../context/wallet-integration.js";
 import { useBackendState } from "../hooks/backend.js";
 
 export function QrCodeSection({
@@ -40,25 +41,15 @@ export function QrCodeSection({
   onAborted: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
+  const walletInegrationApi = useTalerWalletIntegrationAPI()
   const talerWithdrawUri = stringifyWithdrawUri(withdrawUri);
   const { state: credentials } = useBackendState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials;
 
   useEffect(() => {
-    // Taler Wallet WebExtension is listening to headers response and tab 
updates.
-    // In the SPA there is no header response with the Taler URI so
-    // this hack manually triggers the tab update after the QR is in the DOM.
-    // WebExtension will be using
-    // 
https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated
-    document.title = `${document.title} ${withdrawUri.withdrawalOperationId}`;
-    const meta = document.createElement("meta");
-    meta.setAttribute("name", "taler-uri");
-    meta.setAttribute("content", talerWithdrawUri);
-    document.head.insertBefore(
-      meta,
-      document.head.children.length ? document.head.children[0] : null,
-    );
+    walletInegrationApi.publishTalerAction(withdrawUri)
   }, []);
+
   const [notification, notify, handleError] = useLocalNotification();
 
   const { api } = useBankCoreApiContext();
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx 
b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index 1e48b818a..9f7f46c4f 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -21,6 +21,7 @@ import {
   TranslatedString,
   assertUnreachable,
   parseWithdrawUri,
+  stringifyWithdrawUri,
 } from "@gnu-taler/taler-util";
 import {
   Attention,
@@ -41,6 +42,8 @@ import { RouteDefinition } from "../route.js";
 import { undefinedIfEmpty } from "../utils.js";
 import { OperationState } from "./OperationState/index.js";
 import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js";
+import { useTalerWalletIntegrationAPI } from 
"../context/wallet-integration.js";
+import { useNavigationContext } from "../context/navigation.js";
 
 const RefAmount = forwardRef(InputAmount);
 
@@ -57,18 +60,32 @@ function OldWithdrawalForm({
 }): VNode {
   const { i18n } = useTranslationContext();
   const [settings] = usePreferences();
+
+  // const walletInegrationApi = useTalerWalletIntegrationAPI()
+  // const { navigateTo } = useNavigationContext();
+
   const [bankState, updateBankState] = useBankState();
+  const { api } = useBankCoreApiContext();
 
   const { state: credentials } = useBackendState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials;
 
-  const { api } = useBankCoreApiContext();
   const [amountStr, setAmountStr] = useState<string | undefined>(
     `${settings.maxWithdrawalAmount}`,
   );
   const [notification, notify, handleError] = useLocalNotification();
 
   if (bankState.currentWithdrawalOperationId) {
+    // FIXME: doing the preventDefault is not optimal
+
+    // const suri = stringifyWithdrawUri({
+    //   bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
+    //   withdrawalOperationId: bankState.currentWithdrawalOperationId,
+    // });
+    // const uri = parseWithdrawUri(suri)!
+    const url = privatePages.operationDetails.url({
+      wopid: bankState.currentWithdrawalOperationId,
+    })
     return (
       <Attention type="warning" title={i18n.str`There is an operation 
already`}>
         <span ref={focus ? doAutoFocus : undefined} />
@@ -77,9 +94,13 @@ function OldWithdrawalForm({
         </i18n.Translate>{" "}
         <a
           class="font-semibold text-yellow-700 hover:text-yellow-600"
-          href={privatePages.operationDetails.url({
-            wopid: bankState.currentWithdrawalOperationId,
-          })}
+          href={url}
+        // onClick={(e) => {
+        //   e.preventDefault()
+        //   walletInegrationApi.publishTalerAction(uri, () => {
+        //     navigateTo(url)
+        //   })
+        // }}
         >
           <i18n.Translate>this page</i18n.Translate>
         </a>
@@ -324,7 +345,7 @@ export function WalletWithdrawForm({
             onAuthorizationRequired={onAuthorizationRequired}
             routeClose={routeCancel}
             onAbort={onOperationAborted}
-            // route={routeCancel}
+          // route={routeCancel}
           />
         )}
       </div>
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx 
b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 3cf552f39..03f6556af 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -75,29 +75,39 @@ export function WithdrawalQRCode({
 
   if (data.status === "aborted") {
     return (
-      <section id="main" class="content">
-        <h1 class="nav">{i18n.str`Operation aborted`}</h1>
-        <section>
-          <p>
-            <i18n.Translate>
-              The wire transfer to the Taler Exchange operator's account was
-              aborted, your balance was not affected.
-            </i18n.Translate>
-          </p>
-          <p>
-            <i18n.Translate>
-              You can close this page now or continue to the account page.
-            </i18n.Translate>
-          </p>
+      <div class="relative ml-auto mr-auto transform overflow-hidden 
rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 
sm:w-full sm:max-w-sm sm:p-6">
+        <div>
+          <div class="mx-auto flex h-12 w-12 items-center justify-center 
rounded-full bg-yellow-100">
+            <svg class="h-5 w-5 text-yellow-400" viewBox="0 0 20 20" 
fill="currentColor" aria-hidden="true">
+              <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 
3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 
0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 
01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" 
clip-rule="evenodd" />
+            </svg>
+          </div>
+          <div class="mt-3 text-center sm:mt-5">
+            <h3
+              class="text-base font-semibold leading-6 text-gray-900"
+              id="modal-title"
+            >
+              <i18n.Translate>Operation aborted</i18n.Translate>
+            </h3>
+            <div class="mt-2">
+              <p class="text-sm text-gray-500">
+                <i18n.Translate>
+                  The wire transfer to the Taler Exchange operator's account 
was
+                  aborted from somewhere else, your balance was not affected.
+                </i18n.Translate>
+              </p>
+            </div>
+          </div>
+        </div>
+        <div class="mt-5 sm:mt-6">
           <a
             href={routeClose.url({})}
-            class="pure-button pure-button-primary"
-            style={{ float: "right" }}
+            class="inline-flex w-full justify-center rounded-md bg-indigo-600 
px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-indigo-600"
           >
             <i18n.Translate>Continue</i18n.Translate>
           </a>
-        </section>
-      </section>
+        </div>
+      </div>
     );
   }
 
diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx 
b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
index 75e0a9b84..46da5c847 100644
--- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx
+++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
@@ -316,7 +316,7 @@ export function CreateCashout({
   const cashoutDisabled =
     config.supported_tan_channels.length < 1 ||
     !resultAccount.body.cashout_payto_uri;
-  console.log("disab", cashoutDisabled);
+
   const cashoutAccount = !resultAccount.body.cashout_payto_uri
     ? undefined
     : parsePaytoUri(resultAccount.body.cashout_payto_uri);
diff --git a/packages/demobank-ui/src/route.ts 
b/packages/demobank-ui/src/route.ts
index 72b405791..912ba274d 100644
--- a/packages/demobank-ui/src/route.ts
+++ b/packages/demobank-ui/src/route.ts
@@ -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 { useEffect, useState } from "preact/hooks";
+import { useNavigationContext } from "./context/navigation.js";
 
 export function urlPattern<
   T extends Record<string, string> = Record<string, never>,
@@ -24,29 +24,6 @@ export function urlPattern<
   };
 }
 
-// export function Router({
-//   pageList,
-//   onNotFound,
-// }: {
-//   pageList: Array<PageEntry<unknown>>;
-//   onNotFound: () => VNode;
-// }): VNode {
-//   const current = useCurrentLocation<unknown>(pageList);
-//   if (current !== undefined) {
-//     const d = current.page.url
-//     if (typeof current.page.url === "string") {
-//       const p = current.page.url
-//       return create(current.page.view, {});
-//     }
-//     const p = current.page.url
-//     return create(current.page.view, current.values);
-//   }
-//   return onNotFound();
-// }
-// type PagesMap<T extends object> = {
-//   [name in keyof T]: PageDefinition<any>;
-// };
-
 export type RouteDefinition<T> = {
   pattern: RegExp;
   url: (p: T) => string;
@@ -72,17 +49,9 @@ export type RouteParamsType<
 type Location<E, T extends RouteMap<E>, NAME extends keyof T> = {
   parent: T;
   name: NAME;
-  // mapped values from params and url
   values: RouteParamsType<T, NAME>;
 };
 
-const STARTUP_SPA_LOCATION =
-  typeof window !== "undefined" ? window.location.hash.substring(1) : "/";
-const STARTUP_SPA_PARAMS =
-  typeof window !== "undefined"
-    ? new URLSearchParams(window.location.search)
-    : new URLSearchParams();
-
 /**
  * Search path in the pageList
  * get the values from the path found
@@ -91,11 +60,11 @@ const STARTUP_SPA_PARAMS =
  * @param path
  * @param params
  */
-function doSync<DEF, RM extends RouteMap<DEF>, ROUTES extends keyof RM>(
+function findMatch<DEF, RM extends RouteMap<DEF>, ROUTES extends keyof RM>(
   pagesMap: RM,
   pageList: Array<ROUTES>,
   path: string,
-  params: URLSearchParams,
+  params: Record<string, string>,
 ): Location<DEF, RM, ROUTES> | undefined {
   for (let idx = 0; idx < pageList.length; idx++) {
     const name = pageList[idx];
@@ -103,9 +72,10 @@ function doSync<DEF, RM extends RouteMap<DEF>, ROUTES 
extends keyof RM>(
     if (found !== null) {
       const values =
         found.groups === undefined ? {} : structuredClone(found.groups);
-      params.forEach((v, k) => {
-        values[k] = v;
-      });
+
+      Object.entries(params).forEach(([key, value]) => {
+        values[key] = value
+      })
 
       // @ts-expect-error values is a map string which is equivalent to the 
RouteParamsType
       return { name, parent: pagesMap, values };
@@ -114,87 +84,13 @@ function doSync<DEF, RM extends RouteMap<DEF>, ROUTES 
extends keyof RM>(
   return undefined;
 }
 
-const PopStateEventType = "popstate";
-
 export function useCurrentLocation<
   DEF,
   RM extends RouteMap<DEF>,
   ROUTES extends keyof RM,
 >(pagesMap: RM) {
   const pageList = Object.keys(pagesMap) as Array<ROUTES>;
-  const [currentLocation, setCurrentLocation] = useState<
-    Location<DEF, RM, ROUTES> | undefined
-  >(doSync(pagesMap, pageList, STARTUP_SPA_LOCATION, STARTUP_SPA_PARAMS));
-  useEffect(() => {
-    window.addEventListener(PopStateEventType, () => {
-      const path = window.location.hash.substring(1);
-      console.log("event", path);
-      const l = doSync(
-        pagesMap,
-        pageList,
-        path,
-        new URLSearchParams(window.location.search),
-      );
-      setCurrentLocation(l);
-    });
-  }, []);
-  function routeTo<N extends ROUTES>(
-    n: N,
-    values: RouteParamsType<RM, N>,
-  ): void {
-    setCurrentLocation({
-      parent: pagesMap,
-      name: n,
-      values,
-    });
-  }
-  return [currentLocation, routeTo] as const;
-}
-
-// function doestUrlMatchToRoute(
-//   url: string,
-//   route: string,
-// ): undefined | Record<string, string> {
-//   const paramsPattern = /(?:\?([^#]*))?$/;
+  const { path, params } = useNavigationContext()
 
-//   const urlSeg = url.replace(paramsPattern, "").split("/");
-//   const routeSeg = route.split("/");
-//   let max = Math.max(urlSeg.length, routeSeg.length);
-
-//   const result: Record<string, string> = {};
-//   for (let i = 0; i < max; i++) {
-//     if (routeSeg[i] && routeSeg[i].charAt(0) === ":") {
-//       const param = routeSeg[i].replace(/(^:|[+*?]+$)/g, "");
-
-//       const flags = (routeSeg[i].match(/[+*?]+$/) || EMPTY)[0] || "";
-//       const plus = ~flags.indexOf("+");
-//       const star = ~flags.indexOf("*");
-//       const val = urlSeg[i] || "";
-
-//       if (!val && !star && (flags.indexOf("?") < 0 || plus)) {
-//         return undefined;
-//       }
-//       result[param] = decodeURIComponent(val);
-//       if (plus || star) {
-//         result[param] = urlSeg.slice(i).map(decodeURIComponent).join("/");
-//         break;
-//       }
-//     } else if (routeSeg[i] !== urlSeg[i]) {
-//       return undefined;
-//     }
-//   }
-
-//   const params = url.match(paramsPattern);
-//   if (params && params[1]) {
-//     const paramList = params[1].split("&");
-//     for (let i = 0; i < paramList.length; i++) {
-//       const idx = paramList[i].indexOf("=");
-//       const name = paramList[i].substring(0, idx);
-//       const value = paramList[i].substring(idx + 1);
-//       result[decodeURIComponent(name)] = decodeURIComponent(value);
-//     }
-//   }
-
-//   return result;
-// }
-// const EMPTY: Record<string, string> = {};
+  return findMatch(pagesMap, pageList, path, params);
+}
diff --git a/packages/taler-wallet-webextension/manifest-v2.json 
b/packages/taler-wallet-webextension/manifest-v2.json
index 3475cd8aa..6f2096b05 100644
--- a/packages/taler-wallet-webextension/manifest-v2.json
+++ b/packages/taler-wallet-webextension/manifest-v2.json
@@ -18,7 +18,6 @@
   "permissions": [
     "unlimitedStorage",
     "storage",
-    "webRequest",
     "<all_urls>",
     "activeTab"
   ],
diff --git a/packages/taler-wallet-webextension/manifest-v3.json 
b/packages/taler-wallet-webextension/manifest-v3.json
index d6a303ed6..65a75824b 100644
--- a/packages/taler-wallet-webextension/manifest-v3.json
+++ b/packages/taler-wallet-webextension/manifest-v3.json
@@ -17,7 +17,6 @@
     "storage",
     "activeTab",
     "scripting",
-    "webRequest",
     "declarativeContent",
     "alarms"
   ],
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts 
b/packages/taler-wallet-webextension/src/platform/api.ts
index a2b26441b..c7d297db9 100644
--- a/packages/taler-wallet-webextension/src/platform/api.ts
+++ b/packages/taler-wallet-webextension/src/platform/api.ts
@@ -46,20 +46,32 @@ export interface Permissions {
  * Compatibility API that works on multiple browsers.
  */
 export interface CrossBrowserPermissionsApi {
-  containsHostPermissions(): Promise<boolean>;
-  requestHostPermissions(): Promise<boolean>;
-  removeHostPermissions(): Promise<boolean>;
 
   containsClipboardPermissions(): Promise<boolean>;
   requestClipboardPermissions(): Promise<boolean>;
   removeClipboardPermissions(): Promise<boolean>;
 
-  addPermissionsListener(
-    callback: (p: Permissions, lastError?: string) => void,
-  ): void;
 }
 
-export type MessageFromBackend = WalletNotification;
+export enum ExtensionNotificationType {
+  SettingsChange = "settings-change",
+}
+
+export interface SettingsChangeNotification {
+  type: ExtensionNotificationType.SettingsChange;
+
+  currentValue: Settings;
+}
+
+export type ExtensionNotification = SettingsChangeNotification
+
+export type MessageFromBackend = {
+  type: "wallet",
+  notification: WalletNotification
+} | {
+  type: "web-extension",
+  notification: ExtensionNotification
+};
 
 export type MessageFromFrontend<
   Op extends BackgroundOperations | WalletOperations | ExtensionOperations,
@@ -110,7 +122,7 @@ export interface Settings extends WebexWalletConfig {
 }
 
 export const defaultSettings: Settings = {
-  injectTalerSupport: true,
+  injectTalerSupport: false,
   autoOpen: true,
   advanceMode: false,
   backup: false,
@@ -207,13 +219,6 @@ export interface BackgroundPlatformAPI {
     ) => Promise<MessageResponse>,
   ): void;
 
-  /**
-   * Use by the wallet backend to activate the listener of HTTP request
-   */
-  registerTalerHeaderListener(): void;
-
-  containsTalerHeaderListener(): boolean;
-
 }
 export interface ForegroundPlatformAPI {
   /**
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts 
b/packages/taler-wallet-webextension/src/platform/chrome.ts
index 18d282342..d791a560f 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -28,6 +28,7 @@ import { BackgroundOperations } from "../wxApi.js";
 import {
   BackgroundPlatformAPI,
   CrossBrowserPermissionsApi,
+  ExtensionNotificationType,
   ForegroundPlatformAPI,
   MessageFromBackend,
   MessageFromFrontend,
@@ -60,27 +61,31 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
   useServiceWorkerAsBackgroundProcess,
   keepAlive,
   listenNetworkConnectionState,
-  registerTalerHeaderListener,
-  containsTalerHeaderListener,
 };
 
 export default api;
 
 const logger = new Logger("chrome.ts");
 
-async function getSettingsFromStorage(): Promise<Settings> {
-  const data = await chrome.storage.local.get("wallet-settings");
-  if (!data) return defaultSettings;
-  const settings = data["wallet-settings"];
-  if (!settings) return defaultSettings;
+
+const WALLET_STORAGE_KEY = "wallet-settings";
+
+function jsonParseOrDefault(unparsed: any, def: any) {
+  if (!unparsed) return def
   try {
-    const parsed = JSON.parse(settings);
-    return parsed;
+    return JSON.parse(unparsed);
   } catch (e) {
-    return defaultSettings;
+    return def;
   }
 }
 
+async function getSettingsFromStorage(): Promise<Settings> {
+  const data = await chrome.storage.local.get(WALLET_STORAGE_KEY);
+  if (!data) return defaultSettings;
+  const settings = data[WALLET_STORAGE_KEY];
+  return jsonParseOrDefault(settings, defaultSettings)
+}
+
 function keepAlive(callback: any): void {
   if (extensionIsManifestV3()) {
     chrome.alarms.create("wallet-worker", { periodInMinutes: 1 });
@@ -140,21 +145,8 @@ export function removeClipboardPermissions(): 
Promise<boolean> {
   });
 }
 
-function addPermissionsListener(
-  callback: (p: Permissions, lastError?: string) => void,
-): void {
-  chrome.permissions.onAdded.addListener((perm: Permissions) => {
-    const lastError = chrome.runtime.lastError?.message;
-    callback(perm, lastError);
-  });
-}
-
 function getPermissionsApi(): CrossBrowserPermissionsApi {
   return {
-    containsHostPermissions,
-    requestHostPermissions,
-    removeHostPermissions,
-    addPermissionsListener,
     requestClipboardPermissions,
     removeClipboardPermissions,
     containsClipboardPermissions,
@@ -363,6 +355,18 @@ function registerAllIncomingConnections(): void {
       logger.error("error trying to save incoming connection", e);
     }
   });
+  chrome.storage.onChanged.addListener((event) => {
+    if (event[WALLET_STORAGE_KEY]) {
+      sendMessageToAllChannels({
+        type: "web-extension",
+        notification: {
+          type: ExtensionNotificationType.SettingsChange,
+          currentValue: jsonParseOrDefault(event[WALLET_STORAGE_KEY], 
defaultSettings)
+        }
+      })
+    }
+  })
+
 }
 
 function listenToAllChannels(
@@ -723,253 +727,3 @@ function listenNetworkConnectionState(
   };
 }
 
-type HeaderListenerFunc = (
-  details: chrome.webRequest.WebResponseHeadersDetails,
-) => void;
-let currentHeaderListener: HeaderListenerFunc | undefined = undefined;
-
-// type TabListenerFunc = (tabId: number, info: chrome.tabs.TabChangeInfo) => 
void;
-// let currentTabListener: TabListenerFunc | undefined = undefined;
-
-
-function containsTalerHeaderListener(): boolean {
-  return (
-    currentHeaderListener !== undefined
-    // || currentTabListener !== undefined
-  );
-}
-
-function headerListener(
-  details: chrome.webRequest.WebResponseHeadersDetails,
-): chrome.webRequest.BlockingResponse | undefined {
-  logger.trace("header listener run", details.statusCode, 
chrome.runtime.lastError)
-  if (chrome.runtime.lastError) {
-    logger.error(JSON.stringify(chrome.runtime.lastError));
-    return;
-  }
-
-  if (
-    details.statusCode === 402 ||
-    details.statusCode === 202 ||
-    details.statusCode === 200
-  ) {
-    const values = (details.responseHeaders || [])
-      .filter((h) => h.name.toLowerCase() === "taler")
-      .map((h) => h.value)
-      .filter((value): value is string => !!value);
-
-    const talerUri = values.length > 0 ? values[0] : undefined
-    if (talerUri) {
-      logger.info(
-        `Found a Taler URI in a response header for the request ${details.url} 
from tab ${details.tabId}: ${talerUri}`,
-      );
-      parseTalerUriAndRedirect(details.tabId, talerUri);
-      return;
-    }
-  }
-  return details;
-}
-function parseTalerUriAndRedirect(tabId: number, maybeTalerUri: string): void {
-  const talerUri = maybeTalerUri.startsWith("ext+")
-    ? maybeTalerUri.substring(4)
-    : maybeTalerUri;
-  const uri = parseTalerUri(talerUri);
-  if (!uri) {
-    logger.warn(
-      `Response with HTTP 402 the Taler header but could not classify 
${talerUri}`,
-    );
-    return;
-  }
-  redirectTabToWalletPage(
-    tabId,
-    `/taler-uri/${encodeURIComponent(talerUri)}`,
-  );
-}
-
-/**
- * Not needed anymore since SPA use taler support
- */
-
-// async function tabListener(
-//   tabId: number,
-//   info: chrome.tabs.TabChangeInfo,
-// ): Promise<void> {
-//   if (tabId < 0) return;
-//   const tabLocationHasBeenUpdated = info.status === "complete";
-//   const tabTitleHasBeenUpdated = info.title !== undefined;
-//   if (tabLocationHasBeenUpdated || tabTitleHasBeenUpdated) {
-//     const uri = await findTalerUriInTab(tabId);
-//     if (!uri) return;
-//     logger.info(`Found a Taler URI in the tab ${tabId}`);
-//     parseTalerUriAndRedirect(tabId, uri);
-//   }
-// }
-
-/**
- * unused, declarative redirect is not good enough
- *
- */
-// async function registerDeclarativeRedirect() {
-//   await chrome.declarativeNetRequest.updateDynamicRules({
-//     removeRuleIds: [1],
-//     addRules: [
-//       {
-//         id: 1,
-//         priority: 1,
-//         condition: {
-//           urlFilter: "https://developer.chrome.com/docs/extensions/mv2/";,
-//           regexFilter: ".*taler_uri=([^&]*).*",
-//           // isUrlFilterCaseSensitive: false,
-//           // requestMethods: 
[chrome.declarativeNetRequest.RequestMethod.GET]
-//           // resourceTypes: 
[chrome.declarativeNetRequest.ResourceType.MAIN_FRAME],
-//         },
-//         action: {
-//           type: chrome.declarativeNetRequest.RuleActionType.REDIRECT,
-//           redirect: {
-//             regexSubstitution: 
`chrome-extension://${chrome.runtime.id}/static/wallet.html?action=\\1`,
-//           },
-//         },
-//       },
-//     ],
-//   });
-// }
-
-function registerTalerHeaderListener(): void {
-  logger.info("setting up header listener");
-
-  const prevHeaderListener = currentHeaderListener;
-  // const prevTabListener = currentTabListener;
-
-  if (
-    prevHeaderListener &&
-    chrome?.webRequest?.onHeadersReceived?.hasListener(prevHeaderListener)
-  ) {
-    return;
-    // console.log("removming on header listener")
-    // chrome.webRequest.onHeadersReceived.removeListener(prevHeaderListener);
-    // chrome.webRequest.onCompleted.removeListener(prevHeaderListener);
-    // chrome.webRequest.onResponseStarted.removeListener(prevHeaderListener);
-    // chrome.webRequest.onErrorOccurred.removeListener(prevHeaderListener);
-  }
-
-  // if (
-  //   prevTabListener &&
-  //   chrome?.tabs?.onUpdated?.hasListener(prevTabListener)
-  // ) {
-  //   console.log("removming on tab listener")
-  //   chrome.tabs.onUpdated.removeListener(prevTabListener);
-  // }
-
-  console.log("headers on, disabled:", chrome?.webRequest?.onHeadersReceived 
=== undefined)
-  if (chrome?.webRequest) {
-    if (extensionIsManifestV3()) {
-      chrome.webRequest.onHeadersReceived.addListener(headerListener,
-        { urls: ["<all_urls>"] },
-        ["responseHeaders"]
-      );
-    } else {
-      chrome.webRequest.onHeadersReceived.addListener(headerListener,
-        { urls: ["<all_urls>"] },
-        ["responseHeaders"]
-      );
-    }
-    // chrome.webRequest.onCompleted.addListener(headerListener,
-    //   { urls: ["<all_urls>"] },
-    //   ["responseHeaders", "extraHeaders"]
-    // );
-    // chrome.webRequest.onResponseStarted.addListener(headerListener,
-    //   { urls: ["<all_urls>"] },
-    //   ["responseHeaders", "extraHeaders"]
-    // );
-    // chrome.webRequest.onErrorOccurred.addListener(headerListener,
-    //   { urls: ["<all_urls>"] },
-    //   ["extraHeaders"]
-    // );
-    currentHeaderListener = headerListener;
-  }
-
-  // const tabsEvent: chrome.tabs.TabUpdatedEvent | undefined =
-  //   chrome?.tabs?.onUpdated;
-  // if (tabsEvent) {
-  //   tabsEvent.addListener(tabListener);
-  //   currentTabListener = tabListener;
-  // }
-
-  //notify the browser about this change, this operation is expensive
-  chrome?.webRequest?.handlerBehaviorChanged(() => {
-    if (chrome.runtime.lastError) {
-      logger.error(JSON.stringify(chrome.runtime.lastError));
-    }
-  });
-}
-
-const hostPermissions = {
-  permissions: ["webRequest"],
-  origins: ["http://*/*";, "https://*/*";],
-};
-
-export function containsHostPermissions(): Promise<boolean> {
-  return new Promise((res, rej) => {
-    chrome.permissions.contains(hostPermissions, (resp) => {
-      const le = chrome.runtime.lastError?.message;
-      if (le) {
-        rej(le);
-      }
-      res(resp);
-    });
-  });
-}
-
-export async function requestHostPermissions(): Promise<boolean> {
-  return new Promise((res, rej) => {
-    chrome.permissions.request(hostPermissions, (resp) => {
-      const le = chrome.runtime.lastError?.message;
-      if (le) {
-        rej(le);
-      }
-      res(resp);
-    });
-  });
-}
-
-export async function removeHostPermissions(): Promise<boolean> {
-  //if there is a handler already, remove it
-  if (
-    currentHeaderListener &&
-    chrome?.webRequest?.onHeadersReceived?.hasListener(currentHeaderListener)
-  ) {
-    chrome.webRequest.onHeadersReceived.removeListener(currentHeaderListener);
-  }
-  // if (
-  //   currentTabListener &&
-  //   chrome?.tabs?.onUpdated?.hasListener(currentTabListener)
-  // ) {
-  //   chrome.tabs.onUpdated.removeListener(currentTabListener);
-  // }
-
-  currentHeaderListener = undefined;
-  // currentTabListener = undefined;
-
-  //notify the browser about this change, this operation is expensive
-  if ("webRequest" in chrome) {
-    chrome.webRequest.handlerBehaviorChanged(() => {
-      if (chrome.runtime.lastError) {
-        logger.error(JSON.stringify(chrome.runtime.lastError));
-      }
-    });
-  }
-
-  if (extensionIsManifestV3()) {
-    // Trying to remove host permissions with manifest >= v3 throws an error
-    return true;
-  }
-  return new Promise((res, rej) => {
-    chrome.permissions.remove(hostPermissions, (resp) => {
-      const le = chrome.runtime.lastError?.message;
-      if (le) {
-        rej(le);
-      }
-      res(resp);
-    });
-  });
-}
\ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts 
b/packages/taler-wallet-webextension/src/platform/dev.ts
index 51744e318..2993c88bc 100644
--- a/packages/taler-wallet-webextension/src/platform/dev.ts
+++ b/packages/taler-wallet-webextension/src/platform/dev.ts
@@ -37,18 +37,11 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
   listenNetworkConnectionState,
   openNewURLFromPopup: () => undefined,
   getPermissionsApi: () => ({
-    addPermissionsListener: () => undefined,
-    containsHostPermissions: async () => true,
-    removeHostPermissions: async () => false,
-    requestHostPermissions: async () => false,
     containsClipboardPermissions: async () => true,
     removeClipboardPermissions: async () => false,
     requestClipboardPermissions: async () => false,
   }),
 
-  // registerDeclarativeRedirect: () => false,
-  registerTalerHeaderListener: () => false,
-  containsTalerHeaderListener: () => false,
   getWalletWebExVersion: () => ({
     version: "none",
   }),
diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts 
b/packages/taler-wallet-webextension/src/platform/firefox.ts
index 0bbe805cf..3d67423fd 100644
--- a/packages/taler-wallet-webextension/src/platform/firefox.ts
+++ b/packages/taler-wallet-webextension/src/platform/firefox.ts
@@ -26,9 +26,6 @@ import chromePlatform, {
   containsClipboardPermissions as chromeClipContains,
   removeClipboardPermissions as chromeClipRemove,
   requestClipboardPermissions as chromeClipRequest,
-  containsHostPermissions as chromeHostContains,
-  requestHostPermissions as chromeHostRequest,
-  removeHostPermissions as chromeHostRemove,
 } from "./chrome.js";
 
 const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
@@ -47,16 +44,8 @@ function isFirefox(): boolean {
   return true;
 }
 
-function addPermissionsListener(callback: (p: Permissions) => void): void {
-  // throw Error("addPermissionListener is not supported for Firefox");
-}
-
 function getPermissionsApi(): CrossBrowserPermissionsApi {
   return {
-    addPermissionsListener,
-    containsHostPermissions: chromeHostContains,
-    requestHostPermissions: chromeHostRequest,
-    removeHostPermissions: chromeHostRemove,
     containsClipboardPermissions: chromeClipContains,
     removeClipboardPermissions: chromeClipRemove,
     requestClipboardPermissions: chromeClipRequest,
diff --git 
a/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts 
b/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
index d1b1dc374..6cc4eb2b4 100644
--- a/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
+++ b/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
@@ -15,6 +15,7 @@
  */
 
 import { CoreApiResponse, TalerError, TalerErrorCode } from 
"@gnu-taler/taler-util";
+import type { MessageFromBackend } from "./platform/api.js";
 
 /**
  * This will modify all the pages that the user load when navigating with Web 
Extension enabled
@@ -46,6 +47,9 @@ const suffixIsNotXMLorPDF =
 const rootElementIsHTML =
   document.documentElement.nodeName &&
   document.documentElement.nodeName.toLowerCase() === "html";
+// const pageAcceptsTalerSupport = document.head.querySelector(
+//   "meta[name=taler-support]",
+// );
 
 
 
@@ -67,6 +71,7 @@ function convertURIToWebExtensionPath(uri: string) {
 const shouldNotInject =
   !documentDocTypeIsHTML ||
   !suffixIsNotXMLorPDF ||
+  // !pageAcceptsTalerSupport ||
   !rootElementIsHTML;
 
 const logger = {
@@ -93,16 +98,22 @@ function redirectToTalerActionHandler(element: 
HTMLMetaElement) {
     return;
   }
 
-  location.href = convertURIToWebExtensionPath(uri)
+  const walletPage = convertURIToWebExtensionPath(uri)
+  window.location.replace(walletPage)
 }
 
-function injectTalerSupportScript(head: HTMLHeadElement) {
+function injectTalerSupportScript(head: HTMLHeadElement, trusted: boolean) {
   const meta = head.querySelector("meta[name=taler-support]")
+  if (!meta) return;
+  const content = meta.getAttribute("content");
+  if (!content) return;
+  const features = content.split(",")
 
-  const debugEnabled = meta?.getAttribute("debug") === "true";
+  const debugEnabled = meta.getAttribute("debug") === "true";
+  const hijackEnabled = features.indexOf("uri") !== -1
+  const talerApiEnabled = features.indexOf("api") !== -1 && trusted
 
   const scriptTag = document.createElement("script");
-
   scriptTag.setAttribute("async", "false");
   const url = new URL(
     chrome.runtime.getURL("/dist/taler-wallet-interaction-support.js"),
@@ -111,6 +122,12 @@ function injectTalerSupportScript(head: HTMLHeadElement) {
   if (debugEnabled) {
     url.searchParams.set("debug", "true");
   }
+  if (talerApiEnabled) {
+    url.searchParams.set("api", "true");
+  }
+  if (hijackEnabled) {
+    url.searchParams.set("hijack", "true");
+  }
   scriptTag.src = url.href;
 
   try {
@@ -123,12 +140,14 @@ function injectTalerSupportScript(head: HTMLHeadElement) {
 
 
 export interface ExtensionOperations {
-  isInjectionEnabled: {
+  isAutoOpenEnabled: {
     request: void;
     response: boolean;
   };
-  isAutoOpenEnabled: {
-    request: void;
+  isDomainTrusted: {
+    request: {
+      domain: string;
+    };
     response: boolean;
   };
 }
@@ -200,48 +219,82 @@ async function sendMessageToBackground<Op extends keyof 
ExtensionOperations>(
   });
 }
 
+let notificationPort: chrome.runtime.Port | undefined;
+function listenToWalletBackground(listener: (m: any) => void): () => void {
+  if (notificationPort === undefined) {
+    notificationPort = chrome.runtime.connect({ name: "notifications" });
+  }
+  notificationPort.onMessage.addListener(listener);
+  function removeListener(): void {
+    if (notificationPort !== undefined) {
+      notificationPort.onMessage.removeListener(listener);
+    }
+  }
+  return removeListener;
+}
+
+const loaderSettings = {
+  isAutoOpenEnabled: false,
+}
+
 function start(
-  onTalerMetaTagFound: (listener:(el: HTMLMetaElement)=>void) => void,
-  onHeadReady: (listener:(el: HTMLHeadElement)=>void) => void
+  onTalerMetaTagFound: (listener: (el: HTMLMetaElement) => void) => void,
+  onHeadReady: (listener: (el: HTMLHeadElement) => void) => void
 ) {
-  // do not run everywhere, this is just expected to run on html
-  // sites
+  // do not run everywhere, this is just expected to run on site
+  // that are aware of taler
   if (shouldNotInject) return;
 
-  const isAutoOpenEnabled_promise = callBackground("isAutoOpenEnabled", 
undefined)
-  const isInjectionEnabled_promise = callBackground("isInjectionEnabled", 
undefined)
+  callBackground("isAutoOpenEnabled", undefined).then(result => {
+    loaderSettings.isAutoOpenEnabled = result
+  })
+  const isDomainTrusted_promise = callBackground("isDomainTrusted", {
+    domain: window.location.origin
+  })
 
-  onTalerMetaTagFound(async (el)=> {
-    const enabled = await isAutoOpenEnabled_promise;
-    if (!enabled) return;
+  onTalerMetaTagFound(async (el) => {
+    if (!loaderSettings.isAutoOpenEnabled) return;
     redirectToTalerActionHandler(el)
   })
 
   onHeadReady(async (el) => {
-    const enabled = await isInjectionEnabled_promise;
-    if (!enabled) return;
-    injectTalerSupportScript(el)
+    const trusted = await isDomainTrusted_promise
+    injectTalerSupportScript(el, trusted)
+  })
+
+  listenToWalletBackground((e: MessageFromBackend) => {
+    if (e.type === "web-extension" && e.notification.type === 
"settings-change") {
+      const settings = e.notification.currentValue
+      loaderSettings.isAutoOpenEnabled = settings.autoOpen
+    }
+    console.log("loader ->", e)
   })
 
 }
 
+function isCorrectMetaElement(el: HTMLMetaElement): boolean {
+  const name = el.getAttribute("name")
+  if (!name) return false;
+  if (name !== "taler-uri") return false;
+  const uri = el.getAttribute("content");
+  if (!uri) return false;
+  return true
+}
+
 /**
  * Tries to find taler meta tag ASAP and report
  * @param notify 
  * @returns 
  */
-function onTalerMetaTag(notify: (el: HTMLMetaElement) => void) {
+function notifyWhenTalerUriIsFound(notify: (el: HTMLMetaElement) => void) {
   if (document.head) {
     const element = document.head.querySelector("meta[name=taler-uri]")
     if (!element) return;
     if (!(element instanceof HTMLMetaElement)) return;
-    const name = element.getAttribute("name")
-    if (!name) return;
-    if (name !== "taler-uri") return;
-    const uri = element.getAttribute("content");
-    if (!uri) return;
 
-    notify(element)
+    if (isCorrectMetaElement(element)) {
+      notify(element)
+    }
     return;
   }
   const obs = new MutationObserver(async function (mutations) {
@@ -250,13 +303,10 @@ function onTalerMetaTag(notify: (el: HTMLMetaElement) => 
void) {
         if (mut.type === "childList") {
           mut.addedNodes.forEach((added) => {
             if (added instanceof HTMLMetaElement) {
-              const name = added.getAttribute("name")
-              if (!name) return;
-              if (name !== "taler-uri") return;
-              const uri = added.getAttribute("content");
-              if (!uri) return;
-              notify(added)
-              obs.disconnect()
+              if (isCorrectMetaElement(added)) {
+                notify(added)
+                obs.disconnect()
+              }
             }
           });
         }
@@ -279,7 +329,7 @@ function onTalerMetaTag(notify: (el: HTMLMetaElement) => 
void) {
  * @param notify 
  * @returns 
  */
-function onHeaderReady(notify: (el: HTMLHeadElement) => void) {
+function notifyWhenHeadIsFound(notify: (el: HTMLHeadElement) => void) {
   if (document.head) {
     notify(document.head)
     return;
@@ -290,7 +340,6 @@ function onHeaderReady(notify: (el: HTMLHeadElement) => 
void) {
         if (mut.type === "childList") {
           mut.addedNodes.forEach((added) => {
             if (added instanceof HTMLHeadElement) {
-
               notify(added)
               obs.disconnect()
             }
@@ -309,4 +358,4 @@ function onHeaderReady(notify: (el: HTMLHeadElement) => 
void) {
   })
 }
 
-start(onTalerMetaTag, onHeaderReady);
+start(notifyWhenTalerUriIsFound, notifyWhenHeadIsFound);
diff --git 
a/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts 
b/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts
index 993c12703..8b15380f9 100644
--- a/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts
+++ b/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts
@@ -47,7 +47,7 @@
   const shouldNotRun =
     !documentDocTypeIsHTML ||
     !suffixIsNotXMLorPDF ||
-    // !pageAcceptsTalerSupport || FIXME: removing this before release for 
testing
+    !pageAcceptsTalerSupport ||
     !rootElementIsHTML;
 
   interface Info {
@@ -154,32 +154,38 @@
 
   function start() {
     if (shouldNotRun) return;
-    // FIXME: we can remove this if the script caller send information we need
     if (!(document.currentScript instanceof HTMLScriptElement)) return;
 
     const url = new URL(document.currentScript.src);
     const { protocol, searchParams, hostname } = url;
     const extensionId = searchParams.get("id") ?? "";
     const debugEnabled = searchParams.get("debug") === "true";
-    if (debugEnabled) {
-      logger.debug = logger.info;
-    }
+    const apiEnabled = searchParams.get("api") === "true";
+    const hijackEnabled = searchParams.get("hijack") === "true";
 
     const info: Info = Object.freeze({
       extensionId,
       protocol,
       hostname,
     });
+
+    if (debugEnabled) {
+      logger.debug = logger.info;
+    }
+
     const taler: TalerSupport = {
       info,
       __internal: buildApi(info),
     };
 
-    //@ts-ignore
-    window.taler = taler;
+    if (apiEnabled) {
+      //@ts-ignore
+      window.taler = taler;
+    }
 
-    //default behavior: register on install
-    taler.__internal.registerProtocolHandler();
+    if (hijackEnabled) {
+      taler.__internal.registerProtocolHandler();
+    }
   }
 
   // utils functions
@@ -189,6 +195,6 @@
     );
   }
 
-  return start
+  start();
 })()
 
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index faa64e07d..d12ae864b 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -40,6 +40,9 @@ import { Button } from "../mui/Button.js";
 import { Grid } from "../mui/Grid.js";
 import { Paper } from "../mui/Paper.js";
 import { TextField } from "../mui/TextField.js";
+import { Checkbox } from "../components/Checkbox.js";
+import { useSettings } from "../hooks/useSettings.js";
+import { useAlertContext } from "../context/alert.js";
 
 export function DeveloperPage(): VNode {
   const listenAllEvents = Array.from<NotificationType>({ length: 1 });
@@ -132,6 +135,8 @@ export function View({ operations, coins, 
onDownloadDatabase }: Props): VNode {
       dump: JSON.parse(str),
     });
   }
+  const [settings, updateSettings] = useSettings();
+  const { safely } = useAlertContext();
 
   const hook = useAsyncAsHook(() =>
     api.wallet.call(WalletApiOperation.ListExchanges, {}),
@@ -252,26 +257,6 @@ export function View({ operations, coins, 
onDownloadDatabase }: Props): VNode {
             <i18n.Translate>export database</i18n.Translate>
           </Button>
         </Grid>
-        <Grid item>
-          <Button
-            variant="contained"
-            onClick={async () => {
-              api.background.call("toggleHeaderListener", true);
-            }}
-          >
-            <i18n.Translate>enable header listener</i18n.Translate>
-          </Button>
-        </Grid>
-        <Grid item>
-          <Button
-            variant="contained"
-            onClick={async () => {
-              api.background.call("toggleHeaderListener", false);
-            }}
-          >
-            <i18n.Translate>disable header listener</i18n.Translate>
-          </Button>
-        </Grid>
         <Grid item>
           <Button
             variant="contained"
@@ -360,6 +345,22 @@ export function View({ operations, coins, 
onDownloadDatabase }: Props): VNode {
           </Button>
         </Grid>{" "}
       </Grid>
+      <Checkbox
+        label={i18n.str`Inject Taler support in all pages`}
+        name="inject"
+        description={
+          <i18n.Translate>
+            Enabling this option will make `window.taler` be available
+            in all sites
+          </i18n.Translate>
+        }
+        enabled={settings.injectTalerSupport!}
+        onToggle={safely("update support injection", async () => {
+          updateSettings("injectTalerSupport", !settings.injectTalerSupport);
+        })}
+      />
+
+
       <Paper style={{ padding: 10, margin: 10 }}>
         <h3>Logging</h3>
         <div>
@@ -396,92 +397,98 @@ export function View({ operations, coins, 
onDownloadDatabase }: Props): VNode {
           Set log level
         </Button>
       </Paper>
-      {downloadedDatabase && (
-        <div>
-          <i18n.Translate>
-            Database exported at{" "}
-            <Time
-              timestamp={AbsoluteTime.fromMilliseconds(
-                downloadedDatabase.time.getTime(),
-              )}
-              format="yyyy/MM/dd HH:mm:ss"
-            />{" "}
-            <a
-              href={`data:text/plain;charset=utf-8;base64,${toBase64(
-                downloadedDatabase.content,
-              )}`}
-              download={`taler-wallet-database-${format(
-                downloadedDatabase.time,
-                "yyyy/MM/dd_HH:mm",
-              )}.json`}
-            >
-              <i18n.Translate>click here</i18n.Translate>
-            </a>{" "}
-            to download
-          </i18n.Translate>
-        </div>
-      )}
+      {
+        downloadedDatabase && (
+          <div>
+            <i18n.Translate>
+              Database exported at{" "}
+              <Time
+                timestamp={AbsoluteTime.fromMilliseconds(
+                  downloadedDatabase.time.getTime(),
+                )}
+                format="yyyy/MM/dd HH:mm:ss"
+              />{" "}
+              <a
+                href={`data:text/plain;charset=utf-8;base64,${toBase64(
+                  downloadedDatabase.content,
+                )}`}
+                download={`taler-wallet-database-${format(
+                  downloadedDatabase.time,
+                  "yyyy/MM/dd_HH:mm",
+                )}.json`}
+              >
+                <i18n.Translate>click here</i18n.Translate>
+              </a>{" "}
+              to download
+            </i18n.Translate>
+          </div>
+        )
+      }
       <br />
       <p>
         <i18n.Translate>Coins</i18n.Translate>:
       </p>
-      {Object.keys(money_by_exchange).map((ex, idx) => {
-        const allcoins = money_by_exchange[ex];
-        allcoins.sort((a, b) => {
-          if (b.denom_value !== a.denom_value) {
-            return b.denom_value - a.denom_value;
-          }
-          return b.denom_fraction - a.denom_fraction;
-        });
+      {
+        Object.keys(money_by_exchange).map((ex, idx) => {
+          const allcoins = money_by_exchange[ex];
+          allcoins.sort((a, b) => {
+            if (b.denom_value !== a.denom_value) {
+              return b.denom_value - a.denom_value;
+            }
+            return b.denom_fraction - a.denom_fraction;
+          });
 
-        const coins = allcoins.reduce(
-          (prev, cur) => {
-            if (cur.status === CoinStatus.Fresh) prev.usable.push(cur);
-            if (cur.status === CoinStatus.Dormant) prev.spent.push(cur);
-            return prev;
-          },
-          {
-            spent: [],
-            usable: [],
-          } as SplitedCoinInfo,
-        );
+          const coins = allcoins.reduce(
+            (prev, cur) => {
+              if (cur.status === CoinStatus.Fresh) prev.usable.push(cur);
+              if (cur.status === CoinStatus.Dormant) prev.spent.push(cur);
+              return prev;
+            },
+            {
+              spent: [],
+              usable: [],
+            } as SplitedCoinInfo,
+          );
 
-        return (
-          <ShowAllCoins
-            key={idx}
-            coins={coins}
-            ex={ex}
-            currencies={currencies}
-          />
-        );
-      })}
+          return (
+            <ShowAllCoins
+              key={idx}
+              coins={coins}
+              ex={ex}
+              currencies={currencies}
+            />
+          );
+        })
+      }
       <br />
-      {operations && operations.length > 0 && (
-        <Fragment>
-          <p>
-            <i18n.Translate>Pending operations</i18n.Translate>
-          </p>
-          <dl>
-            {operations.reverse().map((o) => {
-              return (
-                <NotifyUpdateFadeOut key={hashObjectId(o)}>
-                  <dt>
-                    {o.type}{" "}
-                    <Time
-                      timestamp={o.timestampDue}
-                      format="yy/MM/dd HH:mm:ss"
-                    />
-                  </dt>
-                  <dd>
-                    <pre>{JSON.stringify(o, undefined, 2)}</pre>
-                  </dd>
-                </NotifyUpdateFadeOut>
-              );
-            })}
-          </dl>
-        </Fragment>
-      )}
-    </div>
+      {
+        operations && operations.length > 0 && (
+          <Fragment>
+            <p>
+              <i18n.Translate>Pending operations</i18n.Translate>
+            </p>
+            <dl>
+              {operations.reverse().map((o) => {
+                return (
+                  <NotifyUpdateFadeOut key={hashObjectId(o)}>
+                    <dt>
+                      {o.type}{" "}
+                      <Time
+                        timestamp={o.timestampDue}
+                        format="yy/MM/dd HH:mm:ss"
+                      />
+                    </dt>
+                    <dd>
+                      <pre>{JSON.stringify(o, undefined, 2)}</pre>
+                    </dd>
+                  </NotifyUpdateFadeOut>
+                );
+              })}
+            </dl>
+          </Fragment>
+        )
+      }
+    </div >
   );
 }
 
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
index 86c420b91..a5d6972de 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx
@@ -55,7 +55,6 @@ export const AllOff = tests.createExample(TestedComponent, {
   deviceName: "this-is-the-device-name",
   advanceToggle: { value: false, button: {} },
   autoOpenToggle: { value: false, button: {} },
-  injectTalerToggle: { value: false, button: {} },
   langToggle: { value: false, button: {} },
   setDeviceName: () => Promise.resolve(),
   ...version,
@@ -65,7 +64,6 @@ export const OneChecked = 
tests.createExample(TestedComponent, {
   deviceName: "this-is-the-device-name",
   advanceToggle: { value: false, button: {} },
   autoOpenToggle: { value: false, button: {} },
-  injectTalerToggle: { value: false, button: {} },
   langToggle: { value: false, button: {} },
   setDeviceName: () => Promise.resolve(),
   ...version,
@@ -75,7 +73,6 @@ export const WithOneExchange = 
tests.createExample(TestedComponent, {
   deviceName: "this-is-the-device-name",
   advanceToggle: { value: false, button: {} },
   autoOpenToggle: { value: false, button: {} },
-  injectTalerToggle: { value: false, button: {} },
   langToggle: { value: false, button: {} },
   setDeviceName: () => Promise.resolve(),
   knownExchanges: [
@@ -100,7 +97,6 @@ export const WithExchangeInDifferentState = 
tests.createExample(
     deviceName: "this-is-the-device-name",
     advanceToggle: { value: false, button: {} },
     autoOpenToggle: { value: false, button: {} },
-    injectTalerToggle: { value: false, button: {} },
     langToggle: { value: false, button: {} },
     setDeviceName: () => Promise.resolve(),
     knownExchanges: [
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx 
b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
index b27413a96..e25629148 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
@@ -80,14 +80,6 @@ export function SettingsPage(): VNode {
           }),
         },
       }}
-      injectTalerToggle={{
-        value: settings.injectTalerSupport,
-        button: {
-          onClick: safely("update support injection", async () => {
-            updateSettings("injectTalerSupport", !settings.injectTalerSupport);
-          }),
-        },
-      }}
       advanceToggle={{
         value: settings.advanceMode,
         button: {
@@ -117,7 +109,6 @@ export interface ViewProps {
   deviceName: string;
   setDeviceName: (s: string) => Promise<void>;
   autoOpenToggle: ToggleHandler;
-  injectTalerToggle: ToggleHandler;
   advanceToggle: ToggleHandler;
   langToggle: ToggleHandler;
   knownExchanges: Array<ExchangeListItem>;
@@ -131,7 +122,6 @@ export interface ViewProps {
 export function SettingsView({
   knownExchanges,
   autoOpenToggle,
-  injectTalerToggle,
   advanceToggle,
   langToggle,
   coreVersion,
@@ -275,19 +265,6 @@ export function SettingsView({
         <SubTitle>
           <i18n.Translate>Navigator</i18n.Translate>
         </SubTitle>
-        <Checkbox
-          label={i18n.str`Inject Taler support in all pages`}
-          name="inject"
-          description={
-            <i18n.Translate>
-              Disabling this option will make some web application not able to
-              trigger the wallet when clicking links but you will be able to
-              open the wallet using the keyboard shortcut
-            </i18n.Translate>
-          }
-          enabled={injectTalerToggle.value!}
-          onToggle={injectTalerToggle.button.onClick!}
-        />
         <Checkbox
           label={i18n.str`Automatically open wallet`}
           name="autoOpen"
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts 
b/packages/taler-wallet-webextension/src/wxApi.ts
index 8fb8211ae..d989c9662 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -83,14 +83,6 @@ export interface BackgroundOperations {
     };
     response: void;
   };
-  containsHeaderListener: {
-    request: void;
-    response: ExtendedPermissionsResponse;
-  };
-  toggleHeaderListener: {
-    request: boolean;
-    response: ExtendedPermissionsResponse;
-  };
 }
 
 export interface BackgroundApiClient {
@@ -194,7 +186,7 @@ function onUpdateNotification(
       return;
     };
   const onNewMessage = (message: MessageFromBackend): void => {
-    const shouldNotify = messageTypes.includes(message.type);
+    const shouldNotify = message.type === "wallet" && 
messageTypes.includes(message.notification.type);
     if (shouldNotify) {
       doCallback();
     }
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts 
b/packages/taler-wallet-webextension/src/wxBackend.ts
index 1ecd66f05..95d31c519 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -122,18 +122,18 @@ async function sum(ns: Array<number>): Promise<number> {
 }
 
 const extensionHandlers: ExtensionHandlerType = {
-  isInjectionEnabled,
   isAutoOpenEnabled,
+  isDomainTrusted,
 };
 
-async function isInjectionEnabled(): Promise<boolean> {
+async function isAutoOpenEnabled(): Promise<boolean> {
   const settings = await platform.getSettingsFromStorage();
-  return settings.injectTalerSupport === true;
+  return settings.autoOpen === true;
 }
 
-async function isAutoOpenEnabled(): Promise<boolean> {
+async function isDomainTrusted(): Promise<boolean> {
   const settings = await platform.getSettingsFromStorage();
-  return settings.autoOpen === true;
+  return settings.injectTalerSupport === true;
 }
 
 const backendHandlers: BackendHandlerType = {
@@ -142,14 +142,8 @@ const backendHandlers: BackendHandlerType = {
   resetDb,
   runGarbageCollector,
   setLoggingLevel,
-  containsHeaderListener,
-  toggleHeaderListener,
 };
 
-async function containsHeaderListener(): Promise<ExtendedPermissionsResponse> {
-  const result = platform.containsTalerHeaderListener();
-  return { newValue: result };
-}
 
 async function setLoggingLevel({
   tag,
@@ -309,8 +303,10 @@ async function reinitWallet(): Promise<void> {
     return;
   }
   wallet.addNotificationListener((message) => {
-    logger.info("wallet -> ui", message);
-    platform.sendMessageToAllChannels(message);
+    platform.sendMessageToAllChannels({
+      type: "wallet",
+      notification: message
+    });
   });
 
   platform.keepAlive(() => {
@@ -360,65 +356,6 @@ export async function wxMain(): Promise<void> {
     console.error(e);
   }
 
-  // platform.registerDeclarativeRedirect();
-  // if (false) {
-  /**
-   * this is not working reliable on chrome, just
-   * intercepts queries after the user clicks the popups
-   * which doesn't make sense, keeping it to make more tests 
-   */
-
-  logger.trace("check taler header listener");
-  const enabled = platform.containsTalerHeaderListener()
-  if (!enabled) {
-    logger.info("header listener on")
-    const perm = await platform.getPermissionsApi().containsHostPermissions()
-    if (perm) {
-      logger.info("header listener allowed")
-      try {
-        platform.registerTalerHeaderListener();
-      } catch (e) {
-        logger.error("could not register header listener", e);
-      }
-    } else {
-      logger.info("header listener requested")
-      await platform.getPermissionsApi().requestHostPermissions()
-    }
-  }
-
-  // On platforms that support it, also listen to external
-  // modification of permissions.
-  platform.getPermissionsApi().addPermissionsListener((perm, lastError) => {
-    logger.info(`permission added: ${perm}`,)
-    if (lastError) {
-      logger.error(
-        `there was a problem trying to get permission ${perm}`,
-        lastError,
-      );
-      return;
-    }
-    platform.registerTalerHeaderListener();
-  });
-
-  // }
 }
 
 
-async function toggleHeaderListener(
-  newVal: boolean,
-): Promise<ExtendedPermissionsResponse> {
-  logger.trace("new extended permissions value", newVal);
-  if (newVal) {
-    try {
-      platform.registerTalerHeaderListener();
-      return { newValue: true };
-    } catch (e) {
-      logger.error("FAIL to toggle", e)
-    }
-    return { newValue: false }
-  }
-
-  const rem = await platform.getPermissionsApi().removeHostPermissions();
-  logger.trace("permissions removed:", rem);
-  return { newValue: false };
-}

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