diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src')
28 files changed, 650 insertions, 686 deletions
| diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx index d6b22ba28..806646a02 100644 --- a/packages/taler-wallet-webextension/src/NavigationBar.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -26,7 +26,7 @@   */  import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";  import { Fragment, h, VNode } from "preact"; -import { JustInDevMode } from "./components/JustInDevMode.js"; +import { EnabledBySettings } from "./components/EnabledBySettings.js";  import {    NavigationHeader,    NavigationHeaderHolder, @@ -183,11 +183,11 @@ export function PopupNavBar({ path }: { path?: PopupNavBarOptions }): VNode {        <a href={Pages.balance} class={path === "balance" ? "active" : ""}>          <i18n.Translate>Balance</i18n.Translate>        </a> -      <JustInDevMode> +      <EnabledBySettings name="backup">          <a href={Pages.backup} class={path === "backup" ? "active" : ""}>            <i18n.Translate>Backup</i18n.Translate>          </a> -      </JustInDevMode> +      </EnabledBySettings>        <div style={{ display: "flex", paddingTop: 4, justifyContent: "right" }}>          {attentionCount > 0 ? (            <a href={Pages.notifications}> @@ -238,11 +238,11 @@ export function WalletNavBar({ path }: { path?: WalletNavBarOptions }): VNode {          <a href={Pages.balance} class={path === "balance" ? "active" : ""}>            <i18n.Translate>Balance</i18n.Translate>          </a> -        <JustInDevMode> +        <EnabledBySettings name="backup">            <a href={Pages.backup} class={path === "backup" ? "active" : ""}>              <i18n.Translate>Backup</i18n.Translate>            </a> -        </JustInDevMode> +        </EnabledBySettings>          {attentionCount > 0 ? (            <a href={Pages.notifications}> @@ -252,11 +252,11 @@ export function WalletNavBar({ path }: { path?: WalletNavBarOptions }): VNode {            <Fragment />          )} -        <JustInDevMode> +        <EnabledBySettings name="advanceMode">            <a href={Pages.dev} class={path === "dev" ? "active" : ""}> -            <i18n.Translate>Dev</i18n.Translate> +            <i18n.Translate>Dev tools</i18n.Translate>            </a> -        </JustInDevMode> +        </EnabledBySettings>          <div            style={{ display: "flex", paddingTop: 4, justifyContent: "right" }} diff --git a/packages/taler-wallet-webextension/src/background.dev.ts b/packages/taler-wallet-webextension/src/background.dev.ts index 9ed0e1b8f..96cf63409 100644 --- a/packages/taler-wallet-webextension/src/background.dev.ts +++ b/packages/taler-wallet-webextension/src/background.dev.ts @@ -27,7 +27,6 @@ import { platform, setupPlatform } from "./platform/background.js";  import devAPI from "./platform/dev.js";  import { wxMain } from "./wxBackend.js"; -console.log("Wallet setup for Dev API");  setupPlatform(devAPI);  async function start() { diff --git a/packages/taler-wallet-webextension/src/background.ts b/packages/taler-wallet-webextension/src/background.ts index 0d5a186d2..7df66eff8 100644 --- a/packages/taler-wallet-webextension/src/background.ts +++ b/packages/taler-wallet-webextension/src/background.ts @@ -35,10 +35,8 @@ const isFirefox =  // FIXME: create different entry point for any platform instead of  // switching in runtime  if (isFirefox) { -  console.log("Wallet setup for Firefox API");    setupPlatform(firefoxAPI);  } else { -  console.log("Wallet setup for Chrome API");    setupPlatform(chromeAPI);  } diff --git a/packages/taler-wallet-webextension/src/components/JustInDevMode.tsx b/packages/taler-wallet-webextension/src/components/EnabledBySettings.tsx index a29bea791..6f666d301 100644 --- a/packages/taler-wallet-webextension/src/components/JustInDevMode.tsx +++ b/packages/taler-wallet-webextension/src/components/EnabledBySettings.tsx @@ -14,14 +14,25 @@   GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>   */  import { ComponentChildren, Fragment, h, VNode } from "preact"; -import { useDevContext } from "../context/devContext.js"; +import { useSettings } from "../hooks/useSettings.js"; +import { Settings } from "../platform/api.js"; -export function JustInDevMode({ +export function EnabledBySettings<K extends keyof Settings>({    children, +  value, +  name,  }: { +  name: K; +  value?: Settings[K];    children: ComponentChildren;  }): VNode { -  const { devMode } = useDevContext(); -  if (!devMode) return <Fragment />; +  const [settings] = useSettings(); +  if (value === undefined) { +    if (!settings[name]) return <Fragment />; +    return <Fragment>{children}</Fragment>; +  } +  if (settings[name] !== value) { +    return <Fragment />; +  }    return <Fragment>{children}</Fragment>;  } diff --git a/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx b/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx index f8203f38a..2979c28e5 100644 --- a/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx +++ b/packages/taler-wallet-webextension/src/components/ErrorTalerOperation.tsx @@ -17,8 +17,8 @@ import { TalerErrorDetail, TranslatedString } from "@gnu-taler/taler-util";  import { Fragment, h, VNode } from "preact";  import { useState } from "preact/hooks";  import arrowDown from "../svg/chevron-down.svg"; -import { useDevContext } from "../context/devContext.js";  import { ErrorBox } from "./styled/index.js"; +import { EnabledBySettings } from "./EnabledBySettings.js";  export function ErrorTalerOperation({    title, @@ -27,7 +27,6 @@ export function ErrorTalerOperation({    title?: TranslatedString;    error?: TalerErrorDetail;  }): VNode | null { -  const { devMode } = useDevContext();    const [showErrorDetail, setShowErrorDetail] = useState(false);    if (!title || !error) return null; @@ -62,11 +61,11 @@ export function ErrorTalerOperation({                <b>{error.hint}</b> {!errorHint ? "" : `: ${errorHint}`}{" "}              </div>            </div> -          {devMode && ( +          <EnabledBySettings name="showJsonOnError">              <div style={{ textAlign: "left", overflowX: "auto" }}>                <pre>{JSON.stringify(error, undefined, 2)}</pre>              </div> -          )} +          </EnabledBySettings>          </Fragment>        )}      </ErrorBox> diff --git a/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts b/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts index 391932574..fdca78ee5 100644 --- a/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts +++ b/packages/taler-wallet-webextension/src/components/TermsOfService/utils.ts @@ -14,7 +14,11 @@   GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>   */ -import { ExchangeTosStatus, GetExchangeTosResult } from "@gnu-taler/taler-util"; +import { +  ExchangeTosStatus, +  GetExchangeTosResult, +  Logger, +} from "@gnu-taler/taler-util";  export function buildTermsOfServiceState(    tos: GetExchangeTosResult, @@ -27,6 +31,8 @@ export function buildTermsOfServiceState(    return { content, status: tos.tosStatus, version: tos.currentEtag };  } +const logger = new Logger("termsofservice"); +  function parseTermsOfServiceContent(    type: string,    text: string, @@ -36,28 +42,28 @@ function parseTermsOfServiceContent(        const document = new DOMParser().parseFromString(text, "text/xml");        return { type: "xml", document };      } catch (e) { -      console.log(e); +      logger.error("error parsing xml", e);      }    } else if (type === "text/html") {      try {        const href = new URL(text);        return { type: "html", href };      } catch (e) { -      console.log(e); +      logger.error("error parsing url", e);      }    } else if (type === "text/json") {      try {        const data = JSON.parse(text);        return { type: "json", data };      } catch (e) { -      console.log(e); +      logger.error("error parsing json", e);      }    } else if (type === "text/pdf") {      try {        const location = new URL(text);        return { type: "pdf", location };      } catch (e) { -      console.log(e); +      logger.error("error parsing url", e);      }    }    const content = text; diff --git a/packages/taler-wallet-webextension/src/context/devContext.ts b/packages/taler-wallet-webextension/src/context/devContext.ts deleted file mode 100644 index e2ad2914b..000000000 --- a/packages/taler-wallet-webextension/src/context/devContext.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -/** - * - * @author Sebastian Javier Marchano (sebasjm) - */ - -import { createContext, h, VNode } from "preact"; -import { useContext } from "preact/hooks"; -import { useWalletDevMode } from "../hooks/useWalletDevMode.js"; - -interface Type { -  devMode: boolean; -  toggle: () => Promise<void>; -} -const Context = createContext<Type>({ -  devMode: false, -  toggle: async () => { -    null; -  }, -}); - -export const useDevContext = (): Type => useContext(Context); - -export const DevContextProviderForTesting = ({ -  value, -  children, -}: { -  value?: boolean; -  children: any; -}): VNode => { -  return h(Context.Provider, { -    value: { -      devMode: !!value, -      toggle: async () => { -        null; -      }, -    }, -    children, -  }); -}; - -export const DevContextProvider = ({ children }: { children: any }): VNode => { -  const devModeToggle = useWalletDevMode(); -  const value: Type = { -    devMode: !!devModeToggle.value, -    toggle: devModeToggle.toggle, -  }; -  //support for function as children, useful for getting the value right away -  children = -    children.length === 1 && typeof children === "function" -      ? children(value) -      : children; - -  return h(Context.Provider, { value, children }); -}; diff --git a/packages/taler-wallet-webextension/src/cta/Payment/state.ts b/packages/taler-wallet-webextension/src/cta/Payment/state.ts index b3d2e6e81..5cd4686ce 100644 --- a/packages/taler-wallet-webextension/src/cta/Payment/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Payment/state.ts @@ -167,8 +167,6 @@ export function useComponentState({      if (fu) {        if (typeof window !== "undefined") {          document.location.href = fu; -      } else { -        console.log(`should d to ${fu}`);        }      }      onSuccess(res.transactionId); diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx index 42e51014f..bd265750f 100644 --- a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx +++ b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/views.tsx @@ -30,7 +30,6 @@ export function ReadyView({  }: State.FillTemplate): VNode {    const { i18n } = useTranslationContext(); -  console.log("is summary", !!summary);    return (      <Fragment>        <section style={{ textAlign: "left" }}> diff --git a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts index 3255c90e5..35b7148cc 100644 --- a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts +++ b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts @@ -51,7 +51,6 @@ export function useClipboardPermissions(): ToggleHandler {        //     .call("toggleHeaderListener", false)        //     .then((r) => setEnabled(r.newValue));        // } catch (e) { -      //   console.log(e);        // }      }      return; diff --git a/packages/taler-wallet-webextension/src/hooks/useSettings.ts b/packages/taler-wallet-webextension/src/hooks/useSettings.ts index 040fee424..1fa2b0539 100644 --- a/packages/taler-wallet-webextension/src/hooks/useSettings.ts +++ b/packages/taler-wallet-webextension/src/hooks/useSettings.ts @@ -36,7 +36,6 @@ export function useSettings(): [    function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {      const newValue = { ...parsed, [k]: v };      const json = JSON.stringify(newValue); -    console.log(json);      update(json);    }    return [parsed, updateField]; diff --git a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts b/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts deleted file mode 100644 index db7effe96..000000000 --- a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE.  See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/> - */ - -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { useEffect, useState } from "preact/hooks"; -import { useBackendContext } from "../context/backend.js"; - -type Result = { -  value: boolean | undefined; -  toggle: () => Promise<void>; -}; - -export function useWalletDevMode(): Result { -  const [enabled, setEnabled] = useState<undefined | boolean>(undefined); -  const api = useBackendContext(); -  // const { pushAlertOnError } = useAlertContext(); - -  async function handleOpen(): Promise<void> { -    const nextValue = !enabled; -    await api.wallet.call(WalletApiOperation.SetDevMode, { -      devModeEnabled: nextValue, -    }); -    setEnabled(nextValue); -    return; -  } - -  useEffect(() => { -    async function getValue(): Promise<void> { -      const res = await api.wallet.call(WalletApiOperation.GetVersion, {}); -      setEnabled(res.devMode); -    } -    getValue(); -  }, []); - -  return { -    value: enabled, -    toggle: handleOpen, -  }; -} diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts index f87500c4f..f8fa0a848 100644 --- a/packages/taler-wallet-webextension/src/platform/api.ts +++ b/packages/taler-wallet-webextension/src/platform/api.ts @@ -15,7 +15,11 @@   */  import { CoreApiResponse, NotificationType } from "@gnu-taler/taler-util"; -import { WalletOperations } from "@gnu-taler/taler-wallet-core"; +import { +  WalletConfig, +  WalletConfigParameter, +  WalletOperations, +} from "@gnu-taler/taler-wallet-core";  import { BackgroundOperations } from "../wxApi.js";  import {    ExtensionOperations, @@ -87,12 +91,35 @@ export interface WalletWebExVersion {    version: string;  } -export interface Settings { +type F = WalletConfig["features"]; +type kf = keyof F; +type WebexWalletConfig = { +  [P in keyof F as `wallet${Capitalize<P>}`]: F[P]; +}; + +export interface Settings extends WebexWalletConfig {    injectTalerSupport: boolean; +  advanceMode: boolean; +  backup: boolean; +  langSelector: boolean; +  showJsonOnError: boolean; +  extendedAccountTypes: boolean; +  //WORKAROUND +  //Able to delete tx in dev mode +  //FIXME: remove this when DD37 is implemented +  deleteActiveTransactions: boolean;  }  export const defaultSettings: Settings = {    injectTalerSupport: false, +  advanceMode: false, +  backup: false, +  langSelector: false, +  deleteActiveTransactions: false, +  showJsonOnError: false, +  extendedAccountTypes: false, +  walletAllowHttp: false, +  walletBatchWithdrawal: false,  };  /** diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts b/packages/taler-wallet-webextension/src/platform/firefox.ts index 01848e1ab..9f666e7ae 100644 --- a/packages/taler-wallet-webextension/src/platform/firefox.ts +++ b/packages/taler-wallet-webextension/src/platform/firefox.ts @@ -45,7 +45,7 @@ function isFirefox(): boolean {  }  function addPermissionsListener(callback: (p: Permissions) => void): void { -  console.log("addPermissionListener is not supported for Firefox"); +  throw Error("addPermissionListener is not supported for Firefox");  }  function getPermissionsApi(): CrossBrowserPermissionsApi { diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx b/packages/taler-wallet-webextension/src/popup/Application.tsx index c03360486..e0cb840aa 100644 --- a/packages/taler-wallet-webextension/src/popup/Application.tsx +++ b/packages/taler-wallet-webextension/src/popup/Application.tsx @@ -20,6 +20,10 @@   * @author sebasjm   */ +import { +  TranslationProvider, +  useTranslationContext, +} from "@gnu-taler/web-util/lib/index.browser";  import { createHashHistory } from "history";  import { ComponentChildren, Fragment, h, VNode } from "preact";  import { route, Route, Router } from "preact-router"; @@ -27,29 +31,22 @@ import { useEffect, useState } from "preact/hooks";  import PendingTransactions from "../components/PendingTransactions.js";  import { PopupBox } from "../components/styled/index.js";  import { AlertProvider } from "../context/alert.js"; -import { DevContextProvider } from "../context/devContext.js";  import { IoCProviderForRuntime } from "../context/iocContext.js"; -import { -  TranslationProvider, -  useTranslationContext, -} from "@gnu-taler/web-util/lib/index.browser";  import { useTalerActionURL } from "../hooks/useTalerActionURL.js"; +import { strings } from "../i18n/strings.js";  import { Pages, PopupNavBar, PopupNavBarOptions } from "../NavigationBar.js";  import { platform } from "../platform/foreground.js";  import { BackupPage } from "../wallet/BackupPage.js";  import { ProviderDetailPage } from "../wallet/ProviderDetailPage.js";  import { BalancePage } from "./BalancePage.js";  import { TalerActionFound } from "./TalerActionFound.js"; -import { strings } from "../i18n/strings.js";  export function Application(): VNode {    return (      <TranslationProvider source={strings}> -      <DevContextProvider> -        <IoCProviderForRuntime> -          <ApplicationView /> -        </IoCProviderForRuntime> -      </DevContextProvider> +      <IoCProviderForRuntime> +        <ApplicationView /> +      </IoCProviderForRuntime>      </TranslationProvider>    );  } diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx index 6b42e5809..f0bc81399 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.dev.tsx @@ -27,7 +27,6 @@ import { setupPlatform } from "./platform/foreground.js";  import devAPI from "./platform/dev.js";  import { Application } from "./popup/Application.js"; -console.log("Wallet setup for Dev API");  setupPlatform(devAPI);  function main(): void { diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index 22aba6c9d..08915ea96 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -32,10 +32,8 @@ import { Application } from "./popup/Application.js";  //switching in runtime  const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined";  if (isFirefox) { -  console.log("Wallet setup for Firefox API");    setupPlatform(firefoxAPI);  } else { -  console.log("Wallet setup for Chrome API");    setupPlatform(chromeAPI);  } diff --git a/packages/taler-wallet-webextension/src/test-utils.ts b/packages/taler-wallet-webextension/src/test-utils.ts index d85d992b1..1d2538703 100644 --- a/packages/taler-wallet-webextension/src/test-utils.ts +++ b/packages/taler-wallet-webextension/src/test-utils.ts @@ -41,7 +41,6 @@ import { strings } from "./i18n/strings.js";  // When doing tests we want the requestAnimationFrame to be as fast as possible.  // without this option the RAF will timeout after 100ms making the tests slower  options.requestAnimationFrame = (fn: () => void) => { -  // console.log("RAF called")    return fn();  }; diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx b/packages/taler-wallet-webextension/src/wallet/Application.tsx index ff8cf0314..5c75b734b 100644 --- a/packages/taler-wallet-webextension/src/wallet/Application.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx @@ -20,15 +20,21 @@   * @author sebasjm   */ +import { TranslatedString } from "@gnu-taler/taler-util";  import { -  TalerUriAction, -  TranslatedString, -  parseTalerUri, -} from "@gnu-taler/taler-util"; +  TranslationProvider, +  useTranslationContext, +} from "@gnu-taler/web-util/lib/index.browser";  import { createHashHistory } from "history"; -import { ComponentChildren, Fragment, h, VNode } from "preact"; -import { route, Route, Router } from "preact-router"; +import { ComponentChildren, Fragment, VNode, h } from "preact"; +import { Route, Router, route } from "preact-router";  import { useEffect } from "preact/hooks"; +import { +  Pages, +  WalletNavBar, +  WalletNavBarOptions, +  getPathnameForTalerURI, +} from "../NavigationBar.js";  import { AlertView, CurrentAlerts } from "../components/CurrentAlerts.js";  import { LogoHeader } from "../components/LogoHeader.js";  import PendingTransactions from "../components/PendingTransactions.js"; @@ -39,12 +45,7 @@ import {    WalletBox,  } from "../components/styled/index.js";  import { AlertProvider } from "../context/alert.js"; -import { DevContextProvider } from "../context/devContext.js";  import { IoCProviderForRuntime } from "../context/iocContext.js"; -import { -  TranslationProvider, -  useTranslationContext, -} from "@gnu-taler/web-util/lib/index.browser";  import { DepositPage as DepositPageCTA } from "../cta/Deposit/index.js";  import { InvoiceCreatePage } from "../cta/InvoiceCreate/index.js";  import { InvoicePayPage } from "../cta/InvoicePay/index.js"; @@ -59,12 +60,7 @@ import {    WithdrawPageFromParams,    WithdrawPageFromURI,  } from "../cta/Withdraw/index.js"; -import { -  Pages, -  WalletNavBar, -  WalletNavBarOptions, -  getPathnameForTalerURI, -} from "../NavigationBar.js"; +import { strings } from "../i18n/strings.js";  import { platform } from "../platform/foreground.js";  import CloseIcon from "../svg/close_24px.svg";  import { AddBackupProviderPage } from "./AddBackupProvider/index.js"; @@ -80,7 +76,6 @@ import { QrReaderPage } from "./QrReader.js";  import { SettingsPage } from "./Settings.js";  import { TransactionPage } from "./Transaction.js";  import { WelcomePage } from "./Welcome.js"; -import { strings } from "../i18n/strings.js";  export function Application(): VNode {    const { i18n } = useTranslationContext(); @@ -91,433 +86,421 @@ export function Application(): VNode {    }    return (      <TranslationProvider source={strings}> -      <DevContextProvider> -        <IoCProviderForRuntime> -          <Router history={hash_history}> -            <Route -              path={Pages.welcome} -              component={() => ( -                <WalletTemplate> -                  <WelcomePage /> -                </WalletTemplate> -              )} -            /> +      <IoCProviderForRuntime> +        <Router history={hash_history}> +          <Route +            path={Pages.welcome} +            component={() => ( +              <WalletTemplate> +                <WelcomePage /> +              </WalletTemplate> +            )} +          /> -            <Route -              path={Pages.qr} -              component={() => ( -                <WalletTemplate goToTransaction={redirectToTxInfo}> -                  <QrReaderPage -                    onDetected={(talerActionUrl: string) => { -                      platform.openWalletURIFromPopup(talerActionUrl); -                    }} -                  /> -                </WalletTemplate> -              )} -            /> +          <Route +            path={Pages.qr} +            component={() => ( +              <WalletTemplate goToTransaction={redirectToTxInfo}> +                <QrReaderPage +                  onDetected={(talerActionUrl: string) => { +                    platform.openWalletURIFromPopup(talerActionUrl); +                  }} +                /> +              </WalletTemplate> +            )} +          /> -            <Route -              path={Pages.settings} -              component={() => ( -                <WalletTemplate goToTransaction={redirectToTxInfo}> -                  <SettingsPage /> -                </WalletTemplate> -              )} -            /> -            <Route -              path={Pages.notifications} -              component={() => ( -                <WalletTemplate> -                  <NotificationsPage /> -                </WalletTemplate> -              )} -            /> -            {/** -             * SETTINGS -             */} -            <Route -              path={Pages.settingsExchangeAdd.pattern} -              component={() => ( -                <WalletTemplate> -                  <ExchangeAddPage onBack={() => redirectTo(Pages.balance)} /> -                </WalletTemplate> -              )} -            /> +          <Route +            path={Pages.settings} +            component={() => ( +              <WalletTemplate goToTransaction={redirectToTxInfo}> +                <SettingsPage /> +              </WalletTemplate> +            )} +          /> +          <Route +            path={Pages.notifications} +            component={() => ( +              <WalletTemplate> +                <NotificationsPage /> +              </WalletTemplate> +            )} +          /> +          {/** +           * SETTINGS +           */} +          <Route +            path={Pages.settingsExchangeAdd.pattern} +            component={() => ( +              <WalletTemplate> +                <ExchangeAddPage onBack={() => redirectTo(Pages.balance)} /> +              </WalletTemplate> +            )} +          /> -            <Route -              path={Pages.balanceHistory.pattern} -              component={({ currency }: { currency?: string }) => ( -                <WalletTemplate -                  path="balance" -                  goToTransaction={redirectToTxInfo} -                > -                  <HistoryPage -                    currency={currency} -                    goToWalletDeposit={(currency: string) => -                      redirectTo(Pages.sendCash({ amount: `${currency}:0` })) -                    } -                    goToWalletManualWithdraw={(currency?: string) => -                      redirectTo( -                        Pages.receiveCash({ -                          amount: !currency ? undefined : `${currency}:0`, -                        }), -                      ) -                    } -                  /> -                </WalletTemplate> -              )} -            /> -            <Route -              path={Pages.sendCash.pattern} -              component={({ amount }: { amount?: string }) => ( -                <WalletTemplate path="balance"> -                  <DestinationSelectionPage -                    type="send" -                    amount={amount} -                    goToWalletBankDeposit={(amount: string) => -                      redirectTo(Pages.balanceDeposit({ amount })) -                    } -                    goToWalletWalletSend={(amount: string) => -                      redirectTo(Pages.ctaTransferCreate({ amount })) -                    } -                  /> -                </WalletTemplate> -              )} -            /> -            <Route -              path={Pages.receiveCash.pattern} -              component={({ amount }: { amount?: string }) => ( -                <WalletTemplate path="balance"> -                  <DestinationSelectionPage -                    type="get" -                    amount={amount} -                    goToWalletManualWithdraw={(amount?: string) => -                      redirectTo(Pages.ctaWithdrawManual({ amount })) -                    } -                    goToWalletWalletInvoice={(amount?: string) => -                      redirectTo(Pages.ctaInvoiceCreate({ amount })) -                    } -                  /> -                </WalletTemplate> -              )} -            /> +          <Route +            path={Pages.balanceHistory.pattern} +            component={({ currency }: { currency?: string }) => ( +              <WalletTemplate path="balance" goToTransaction={redirectToTxInfo}> +                <HistoryPage +                  currency={currency} +                  goToWalletDeposit={(currency: string) => +                    redirectTo(Pages.sendCash({ amount: `${currency}:0` })) +                  } +                  goToWalletManualWithdraw={(currency?: string) => +                    redirectTo( +                      Pages.receiveCash({ +                        amount: !currency ? undefined : `${currency}:0`, +                      }), +                    ) +                  } +                /> +              </WalletTemplate> +            )} +          /> +          <Route +            path={Pages.sendCash.pattern} +            component={({ amount }: { amount?: string }) => ( +              <WalletTemplate path="balance"> +                <DestinationSelectionPage +                  type="send" +                  amount={amount} +                  goToWalletBankDeposit={(amount: string) => +                    redirectTo(Pages.balanceDeposit({ amount })) +                  } +                  goToWalletWalletSend={(amount: string) => +                    redirectTo(Pages.ctaTransferCreate({ amount })) +                  } +                /> +              </WalletTemplate> +            )} +          /> +          <Route +            path={Pages.receiveCash.pattern} +            component={({ amount }: { amount?: string }) => ( +              <WalletTemplate path="balance"> +                <DestinationSelectionPage +                  type="get" +                  amount={amount} +                  goToWalletManualWithdraw={(amount?: string) => +                    redirectTo(Pages.ctaWithdrawManual({ amount })) +                  } +                  goToWalletWalletInvoice={(amount?: string) => +                    redirectTo(Pages.ctaInvoiceCreate({ amount })) +                  } +                /> +              </WalletTemplate> +            )} +          /> -            <Route -              path={Pages.balanceTransaction.pattern} -              component={({ tid }: { tid: string }) => ( -                <WalletTemplate path="balance"> -                  <TransactionPage -                    tid={tid} -                    goToWalletHistory={(currency?: string) => -                      redirectTo(Pages.balanceHistory({ currency })) -                    } -                  /> -                </WalletTemplate> -              )} -            /> +          <Route +            path={Pages.balanceTransaction.pattern} +            component={({ tid }: { tid: string }) => ( +              <WalletTemplate path="balance"> +                <TransactionPage +                  tid={tid} +                  goToWalletHistory={(currency?: string) => +                    redirectTo(Pages.balanceHistory({ currency })) +                  } +                /> +              </WalletTemplate> +            )} +          /> -            <Route -              path={Pages.balanceDeposit.pattern} -              component={({ amount }: { amount: string }) => ( -                <WalletTemplate path="balance"> -                  <DepositPage -                    amount={amount} -                    onCancel={(currency: string) => { -                      redirectTo(Pages.balanceHistory({ currency })); -                    }} -                    onSuccess={(currency: string) => { -                      redirectTo(Pages.balanceHistory({ currency })); -                    }} -                  /> -                </WalletTemplate> -              )} -            /> +          <Route +            path={Pages.balanceDeposit.pattern} +            component={({ amount }: { amount: string }) => ( +              <WalletTemplate path="balance"> +                <DepositPage +                  amount={amount} +                  onCancel={(currency: string) => { +                    redirectTo(Pages.balanceHistory({ currency })); +                  }} +                  onSuccess={(currency: string) => { +                    redirectTo(Pages.balanceHistory({ currency })); +                  }} +                /> +              </WalletTemplate> +            )} +          /> -            <Route -              path={Pages.backup} -              component={() => ( -                <WalletTemplate -                  path="backup" -                  goToTransaction={redirectToTxInfo} -                > -                  <BackupPage -                    onAddProvider={() => redirectTo(Pages.backupProviderAdd)} -                  /> -                </WalletTemplate> -              )} -            /> -            <Route -              path={Pages.backupProviderDetail.pattern} -              component={({ pid }: { pid: string }) => ( -                <WalletTemplate> -                  <ProviderDetailPage -                    pid={pid} -                    onPayProvider={(uri: string) => -                      redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`) -                    } -                    onWithdraw={(amount: string) => -                      redirectTo(Pages.receiveCash({ amount })) -                    } -                    onBack={() => redirectTo(Pages.backup)} -                  /> -                </WalletTemplate> -              )} -            /> -            <Route -              path={Pages.backupProviderAdd} -              component={() => ( -                <WalletTemplate> -                  <AddBackupProviderPage -                    onPaymentRequired={(uri: string) => -                      redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`) -                    } -                    onComplete={(pid: string) => -                      redirectTo(Pages.backupProviderDetail({ pid })) -                    } -                    onBack={() => redirectTo(Pages.backup)} -                  /> -                </WalletTemplate> -              )} -            /> +          <Route +            path={Pages.backup} +            component={() => ( +              <WalletTemplate path="backup" goToTransaction={redirectToTxInfo}> +                <BackupPage +                  onAddProvider={() => redirectTo(Pages.backupProviderAdd)} +                /> +              </WalletTemplate> +            )} +          /> +          <Route +            path={Pages.backupProviderDetail.pattern} +            component={({ pid }: { pid: string }) => ( +              <WalletTemplate> +                <ProviderDetailPage +                  pid={pid} +                  onPayProvider={(uri: string) => +                    redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`) +                  } +                  onWithdraw={(amount: string) => +                    redirectTo(Pages.receiveCash({ amount })) +                  } +                  onBack={() => redirectTo(Pages.backup)} +                /> +              </WalletTemplate> +            )} +          /> +          <Route +            path={Pages.backupProviderAdd} +            component={() => ( +              <WalletTemplate> +                <AddBackupProviderPage +                  onPaymentRequired={(uri: string) => +                    redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`) +                  } +                  onComplete={(pid: string) => +                    redirectTo(Pages.backupProviderDetail({ pid })) +                  } +                  onBack={() => redirectTo(Pages.backup)} +                /> +              </WalletTemplate> +            )} +          /> -            {/** -             * DEV -             */} -            <Route -              path={Pages.dev} -              component={() => ( -                <WalletTemplate path="dev" goToTransaction={redirectToTxInfo}> -                  <DeveloperPage /> -                </WalletTemplate> -              )} -            /> +          {/** +           * DEV +           */} +          <Route +            path={Pages.dev} +            component={() => ( +              <WalletTemplate path="dev" goToTransaction={redirectToTxInfo}> +                <DeveloperPage /> +              </WalletTemplate> +            )} +          /> -            {/** -             * CALL TO ACTION -             */} -            <Route -              path={Pages.defaultCta.pattern} -              component={({ uri }: { uri: string }) => { -                const path = getPathnameForTalerURI(uri); -                if (!path) { -                  return ( -                    <CallToActionTemplate title={i18n.str`Taler URI handler`}> -                      <AlertView -                        alert={{ -                          type: "warning", -                          message: i18n.str`Could not found a handler for the Taler URI`, -                          description: i18n.str`The uri read in the path parameter is not valid: "${uri}"`, -                        }} -                      /> -                    </CallToActionTemplate> -                  ); -                } -                return <Redirect to={path} />; -              }} -            /> -            <Route -              path={Pages.ctaPay} -              component={({ talerUri }: { talerUri: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash payment`}> -                  <PaymentPage -                    talerPayUri={decodeURIComponent(talerUri)} -                    goToWalletManualWithdraw={(amount?: string) => -                      redirectTo(Pages.receiveCash({ amount })) -                    } -                    cancel={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaPayTemplate} -              component={({ talerUri }: { talerUri: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash payment`}> -                  <PaymentTemplatePage -                    talerTemplateUri={decodeURIComponent(talerUri)} -                    goToWalletManualWithdraw={(amount?: string) => -                      redirectTo(Pages.receiveCash({ amount })) -                    } -                    cancel={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaRefund} -              component={({ talerUri }: { talerUri: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash refund`}> -                  <RefundPage -                    talerRefundUri={decodeURIComponent(talerUri)} -                    cancel={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaTips} -              component={({ talerUri }: { talerUri: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash tip`}> -                  <TipPage -                    talerTipUri={decodeURIComponent(talerUri)} -                    onCancel={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaWithdraw} -              component={({ talerUri }: { talerUri: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash withdrawal`}> -                  <WithdrawPageFromURI -                    talerWithdrawUri={decodeURIComponent(talerUri)} -                    cancel={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaWithdrawManual.pattern} -              component={({ amount }: { amount: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash withdrawal`}> -                  <WithdrawPageFromParams -                    amount={amount} -                    cancel={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaDeposit} -              component={({ -                amount, -                talerUri, -              }: { -                amount: string; -                talerUri: string; -              }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash deposit`}> -                  <DepositPageCTA -                    amountStr={amount} -                    talerDepositUri={decodeURIComponent(talerUri)} -                    cancel={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaInvoiceCreate.pattern} -              component={({ amount }: { amount: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash invoice`}> -                  <InvoiceCreatePage -                    amount={amount} -                    onClose={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaTransferCreate.pattern} -              component={({ amount }: { amount: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash transfer`}> -                  <TransferCreatePage -                    amount={amount} -                    onClose={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaInvoicePay} -              component={({ talerUri }: { talerUri: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash invoice`}> -                  <InvoicePayPage -                    talerPayPullUri={decodeURIComponent(talerUri)} -                    goToWalletManualWithdraw={(amount?: string) => -                      redirectTo(Pages.receiveCash({ amount })) -                    } -                    onClose={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaTransferPickup} -              component={({ talerUri }: { talerUri: string }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash transfer`}> -                  <TransferPickupPage -                    talerPayPushUri={decodeURIComponent(talerUri)} -                    onClose={() => redirectTo(Pages.balance)} -                    onSuccess={(tid: string) => -                      redirectTo(Pages.balanceTransaction({ tid })) -                    } -                  /> -                </CallToActionTemplate> -              )} -            /> -            <Route -              path={Pages.ctaRecovery} -              component={({ -                talerRecoveryUri, -              }: { -                talerRecoveryUri: string; -              }) => ( -                <CallToActionTemplate title={i18n.str`Digital cash recovery`}> -                  <RecoveryPage -                    talerRecoveryUri={decodeURIComponent(talerRecoveryUri)} -                    onCancel={() => redirectTo(Pages.balance)} -                    onSuccess={() => redirectTo(Pages.backup)} -                  /> -                </CallToActionTemplate> -              )} -            /> +          {/** +           * CALL TO ACTION +           */} +          <Route +            path={Pages.defaultCta.pattern} +            component={({ uri }: { uri: string }) => { +              const path = getPathnameForTalerURI(uri); +              if (!path) { +                return ( +                  <CallToActionTemplate title={i18n.str`Taler URI handler`}> +                    <AlertView +                      alert={{ +                        type: "warning", +                        message: i18n.str`Could not found a handler for the Taler URI`, +                        description: i18n.str`The uri read in the path parameter is not valid: "${uri}"`, +                      }} +                    /> +                  </CallToActionTemplate> +                ); +              } +              return <Redirect to={path} />; +            }} +          /> +          <Route +            path={Pages.ctaPay} +            component={({ talerUri }: { talerUri: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash payment`}> +                <PaymentPage +                  talerPayUri={decodeURIComponent(talerUri)} +                  goToWalletManualWithdraw={(amount?: string) => +                    redirectTo(Pages.receiveCash({ amount })) +                  } +                  cancel={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaPayTemplate} +            component={({ talerUri }: { talerUri: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash payment`}> +                <PaymentTemplatePage +                  talerTemplateUri={decodeURIComponent(talerUri)} +                  goToWalletManualWithdraw={(amount?: string) => +                    redirectTo(Pages.receiveCash({ amount })) +                  } +                  cancel={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaRefund} +            component={({ talerUri }: { talerUri: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash refund`}> +                <RefundPage +                  talerRefundUri={decodeURIComponent(talerUri)} +                  cancel={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaTips} +            component={({ talerUri }: { talerUri: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash tip`}> +                <TipPage +                  talerTipUri={decodeURIComponent(talerUri)} +                  onCancel={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaWithdraw} +            component={({ talerUri }: { talerUri: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash withdrawal`}> +                <WithdrawPageFromURI +                  talerWithdrawUri={decodeURIComponent(talerUri)} +                  cancel={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaWithdrawManual.pattern} +            component={({ amount }: { amount: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash withdrawal`}> +                <WithdrawPageFromParams +                  amount={amount} +                  cancel={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaDeposit} +            component={({ +              amount, +              talerUri, +            }: { +              amount: string; +              talerUri: string; +            }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash deposit`}> +                <DepositPageCTA +                  amountStr={amount} +                  talerDepositUri={decodeURIComponent(talerUri)} +                  cancel={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaInvoiceCreate.pattern} +            component={({ amount }: { amount: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash invoice`}> +                <InvoiceCreatePage +                  amount={amount} +                  onClose={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaTransferCreate.pattern} +            component={({ amount }: { amount: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash transfer`}> +                <TransferCreatePage +                  amount={amount} +                  onClose={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaInvoicePay} +            component={({ talerUri }: { talerUri: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash invoice`}> +                <InvoicePayPage +                  talerPayPullUri={decodeURIComponent(talerUri)} +                  goToWalletManualWithdraw={(amount?: string) => +                    redirectTo(Pages.receiveCash({ amount })) +                  } +                  onClose={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaTransferPickup} +            component={({ talerUri }: { talerUri: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash transfer`}> +                <TransferPickupPage +                  talerPayPushUri={decodeURIComponent(talerUri)} +                  onClose={() => redirectTo(Pages.balance)} +                  onSuccess={(tid: string) => +                    redirectTo(Pages.balanceTransaction({ tid })) +                  } +                /> +              </CallToActionTemplate> +            )} +          /> +          <Route +            path={Pages.ctaRecovery} +            component={({ talerRecoveryUri }: { talerRecoveryUri: string }) => ( +              <CallToActionTemplate title={i18n.str`Digital cash recovery`}> +                <RecoveryPage +                  talerRecoveryUri={decodeURIComponent(talerRecoveryUri)} +                  onCancel={() => redirectTo(Pages.balance)} +                  onSuccess={() => redirectTo(Pages.backup)} +                /> +              </CallToActionTemplate> +            )} +          /> -            {/** -             * NOT FOUND -             * all redirects should be at the end -             */} -            <Route -              path={Pages.balance} -              component={() => <Redirect to={Pages.balanceHistory({})} />} -            /> +          {/** +           * NOT FOUND +           * all redirects should be at the end +           */} +          <Route +            path={Pages.balance} +            component={() => <Redirect to={Pages.balanceHistory({})} />} +          /> -            <Route -              default -              component={() => <Redirect to={Pages.balanceHistory({})} />} -            /> -          </Router> -        </IoCProviderForRuntime> -      </DevContextProvider> +          <Route +            default +            component={() => <Redirect to={Pages.balanceHistory({})} />} +          /> +        </Router> +      </IoCProviderForRuntime>      </TranslationProvider>    );  } diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx index 7f239f33d..8416e17e9 100644 --- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx @@ -17,7 +17,7 @@  import { styled } from "@linaria/react";  import { Fragment, h, VNode } from "preact";  import { AmountField } from "../../components/AmountField.js"; -import { JustInDevMode } from "../../components/JustInDevMode.js"; +import { EnabledBySettings } from "../../components/EnabledBySettings.js";  import { SelectList } from "../../components/SelectList.js";  import {    Input, @@ -300,11 +300,11 @@ export function ReadySendView({            required            handler={amountHandler}          /> -        <JustInDevMode> +        <EnabledBySettings name="advanceMode">            <Button onClick={sendAll.onClick}>              <i18n.Translate>Send all</i18n.Translate>            </Button> -        </JustInDevMode> +        </EnabledBySettings>        </Grid>        <Grid container spacing={1} columns={1}> diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts index 1f8ab4883..641b47cd7 100644 --- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts +++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts @@ -23,10 +23,10 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";  import { useState } from "preact/hooks";  import { alertFromError, useAlertContext } from "../../context/alert.js";  import { useBackendContext } from "../../context/backend.js"; -import { useDevContext } from "../../context/devContext.js";  import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";  import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";  import { AccountByType, Props, State } from "./index.js"; +import { useSettings } from "../../hooks/useSettings.js";  export function useComponentState({    currency, @@ -39,12 +39,11 @@ export function useComponentState({    const hook = useAsyncAsHook(() =>      api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }),    ); -  const { devMode } = useDevContext();    const accountType: Record<string, string> = {      iban: "IBAN", -    // "x-taler-bank": "Taler Bank",    }; -  if (devMode) { +  const [settings] = useSettings(); +  if (settings.extendedAccountTypes) {      accountType["bitcoin"] = "Bitcoin";      accountType["x-taler-bank"] = "Taler Bank";    } diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx index 53bc577d4..89d92b82c 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.stories.tsx @@ -47,27 +47,27 @@ const version = {  export const AllOff = tests.createExample(TestedComponent, {    deviceName: "this-is-the-device-name", -  devModeToggle: { value: false, button: {} }, +  advanceToggle: { value: false, button: {} },    autoOpenToggle: { value: false, button: {} }, -  clipboardToggle: { value: false, button: {} }, +  langToggle: { value: false, button: {} },    setDeviceName: () => Promise.resolve(),    ...version,  });  export const OneChecked = tests.createExample(TestedComponent, {    deviceName: "this-is-the-device-name", -  devModeToggle: { value: false, button: {} }, +  advanceToggle: { value: false, button: {} },    autoOpenToggle: { value: false, button: {} }, -  clipboardToggle: { value: false, button: {} }, +  langToggle: { value: false, button: {} },    setDeviceName: () => Promise.resolve(),    ...version,  });  export const WithOneExchange = tests.createExample(TestedComponent, {    deviceName: "this-is-the-device-name", -  devModeToggle: { value: false, button: {} }, +  advanceToggle: { value: false, button: {} },    autoOpenToggle: { value: false, button: {} }, -  clipboardToggle: { value: false, button: {} }, +  langToggle: { value: false, button: {} },    setDeviceName: () => Promise.resolve(),    knownExchanges: [      { @@ -89,9 +89,9 @@ export const WithExchangeInDifferentState = tests.createExample(    TestedComponent,    {      deviceName: "this-is-the-device-name", -    devModeToggle: { value: false, button: {} }, +    advanceToggle: { value: false, button: {} },      autoOpenToggle: { value: false, button: {} }, -    clipboardToggle: { 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 ae3a6e688..62a6c55ff 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx @@ -17,12 +17,15 @@  import {    ExchangeListItem,    ExchangeTosStatus, +  TranslatedString,    WalletCoreVersion,  } from "@gnu-taler/taler-util";  import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { Fragment, h, VNode } from "preact"; +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { Fragment, VNode, h } from "preact"; +import { Pages } from "../NavigationBar.js";  import { Checkbox } from "../components/Checkbox.js"; -import { JustInDevMode } from "../components/JustInDevMode.js"; +import { EnabledBySettings } from "../components/EnabledBySettings.js";  import { Part } from "../components/Part.js";  import { SelectList } from "../components/SelectList.js";  import { @@ -35,25 +38,20 @@ import {  } from "../components/styled/index.js";  import { useAlertContext } from "../context/alert.js";  import { useBackendContext } from "../context/backend.js"; -import { useDevContext } from "../context/devContext.js"; -import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";  import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; -import { useSettings } from "../hooks/useSettings.js";  import { useBackupDeviceName } from "../hooks/useBackupDeviceName.js";  import { useClipboardPermissions } from "../hooks/useClipboardPermissions.js"; +import { useSettings } from "../hooks/useSettings.js";  import { ToggleHandler } from "../mui/handlers.js"; -import { Pages } from "../NavigationBar.js";  import { platform } from "../platform/foreground.js"; +import { Settings } from "../platform/api.js";  const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;  export function SettingsPage(): VNode {    const [settings, updateSettings] = useSettings(); -  const clipboardToggle = useClipboardPermissions(); -  const { devMode, toggle } = useDevContext();    const { safely } = useAlertContext();    const { name, update } = useBackupDeviceName(); -  const { pushAlertOnError } = useAlertContext();    const webex = platform.getWalletWebExVersion();    const api = useBackendContext(); @@ -76,16 +74,24 @@ export function SettingsPage(): VNode {          value: settings.injectTalerSupport,          button: {            onClick: safely("update support injection", async () => { -            console.log("click", settings.injectTalerSupport);              updateSettings("injectTalerSupport", !settings.injectTalerSupport);            }),          },        }} -      clipboardToggle={clipboardToggle} -      devModeToggle={{ -        value: devMode, +      advanceToggle={{ +        value: settings.advanceMode,          button: { -          onClick: pushAlertOnError(toggle), +          onClick: safely("update advance mode", async () => { +            updateSettings("advanceMode", !settings.advanceMode); +          }), +        }, +      }} +      langToggle={{ +        value: settings.langSelector, +        button: { +          onClick: safely("update lang selector", async () => { +            updateSettings("langSelector", !settings.langSelector); +          }),          },        }}        webexVersion={{ @@ -101,8 +107,8 @@ export interface ViewProps {    deviceName: string;    setDeviceName: (s: string) => Promise<void>;    autoOpenToggle: ToggleHandler; -  clipboardToggle: ToggleHandler; -  devModeToggle: ToggleHandler; +  advanceToggle: ToggleHandler; +  langToggle: ToggleHandler;    knownExchanges: Array<ExchangeListItem>;    coreVersion: WalletCoreVersion | undefined;    webexVersion: { @@ -114,8 +120,8 @@ export interface ViewProps {  export function SettingsView({    knownExchanges,    autoOpenToggle, -  clipboardToggle, -  devModeToggle, +  advanceToggle, +  langToggle,    coreVersion,    webexVersion,  }: ViewProps): VNode { @@ -204,45 +210,19 @@ export function SettingsView({            </LinkPrimary>          </div> -        <JustInDevMode> -          <SubTitle> -            <i18n.Translate>Display</i18n.Translate> -          </SubTitle> -          <Input> -            <SelectList -              label={<i18n.Translate>Current Language</i18n.Translate>} -              list={supportedLang} -              name="lang" -              value={lang} -              onChange={(v) => changeLanguage(v)} -            /> -          </Input> -        </JustInDevMode> -        <SubTitle> -          <i18n.Translate>Version</i18n.Translate> -        </SubTitle> -        {coreVersion && ( -          <Part -            title={i18n.str`Wallet Core`} -            text={ -              <span> -                {coreVersion.version}{" "} -                <JustInDevMode>{coreVersion.hash}</JustInDevMode> -              </span> -            } -          /> -        )}          <Part            title={i18n.str`Web Extension`}            text={              <span>                {webexVersion.version}{" "} -              <JustInDevMode>{webexVersion.hash}</JustInDevMode> +              <EnabledBySettings name="advanceMode"> +                {webexVersion.hash} +              </EnabledBySettings>              </span>            }          />          {coreVersion && ( -          <JustInDevMode> +          <EnabledBySettings name="advanceMode">              <Part                title={i18n.str`Exchange compatibility`}                text={<span>{coreVersion.exchange}</span>} @@ -255,18 +235,42 @@ export function SettingsView({                title={i18n.str`Bank compatibility`}                text={<span>{coreVersion.bank}</span>}              /> -          </JustInDevMode> +          </EnabledBySettings>          )}          <SubTitle> -          <i18n.Translate>Troubleshooting</i18n.Translate> +          <i18n.Translate>Advance mode</i18n.Translate>          </SubTitle>          <Checkbox -          label={i18n.str`Developer mode`} +          label={i18n.str`Enable advance mode`}            name="devMode" -          description={i18n.str`More options and information useful for debugging`} -          enabled={devModeToggle.value!} -          onToggle={devModeToggle.button.onClick!} +          description={i18n.str`Show more information and options in the UI`} +          enabled={advanceToggle.value!} +          onToggle={advanceToggle.button.onClick!} +        /> +        <EnabledBySettings name="advanceMode"> +          <AdvanceSettings /> +        </EnabledBySettings> +        <Checkbox +          label={i18n.str`Lang selector`} +          name="langSelector" +          description={i18n.str`Allows to manually change the language of the UI. Otherwise it will be automatically selected by your browser configuration.`} +          enabled={langToggle.value!} +          onToggle={langToggle.button.onClick!}          /> +        <EnabledBySettings name="langSelector"> +          <SubTitle> +            <i18n.Translate>Display</i18n.Translate> +          </SubTitle> +          <Input> +            <SelectList +              label={<i18n.Translate>Current Language</i18n.Translate>} +              list={supportedLang} +              name="lang" +              value={lang} +              onChange={(v) => changeLanguage(v)} +            /> +          </Input> +        </EnabledBySettings>          <SubTitle>            <i18n.Translate>Navigator</i18n.Translate>          </SubTitle> @@ -283,6 +287,77 @@ export function SettingsView({            enabled={autoOpenToggle.value!}            onToggle={autoOpenToggle.button.onClick!}          /> +        <SubTitle> +          <i18n.Translate>Version</i18n.Translate> +        </SubTitle> +        {coreVersion && ( +          <Part +            title={i18n.str`Wallet Core`} +            text={ +              <span> +                {coreVersion.version}{" "} +                <EnabledBySettings name="advanceMode"> +                  {coreVersion.hash} +                </EnabledBySettings> +              </span> +            } +          /> +        )} +      </section> +    </Fragment> +  ); +} + +type Info = { label: TranslatedString; description: TranslatedString }; +type Options = { +  [k in keyof Settings]?: Info; +}; +function AdvanceSettings(): VNode { +  const [settings, updateSettings] = useSettings(); +  const { i18n } = useTranslationContext(); +  const o: Options = { +    backup: { +      label: i18n.str`Show backup feature`, +      description: i18n.str`Backup integration still in beta.`, +    }, +    deleteActiveTransactions: { +      label: i18n.str`Show delete active transaction`, +      description: i18n.str`Deleting active transaction is not safe and you may loose your coins.`, +    }, +    extendedAccountTypes: { +      label: i18n.str`Show more account types on deposit`, +      description: i18n.str`Extends the UI to more payment target types.`, +    }, +    showJsonOnError: { +      label: i18n.str`Show JSON on error`, +      description: i18n.str`Print more information about the error. Useful for debugging.`, +    }, +    walletAllowHttp: { +      label: i18n.str`Allow HTTP connections`, +      description: i18n.str`Using HTTP connection may be faster but unsafe (wallet restart required)`, +    }, +    walletBatchWithdrawal: { +      label: i18n.str`Allow batch withdrawals`, +      description: i18n.str`Using the batch withdrawal API allows faster withdrawals (wallet restart required)`, +    }, +  }; +  return ( +    <Fragment> +      <section> +        {Object.entries(o).map(([name, { label, description }]) => { +          const settingsName = name as keyof Settings; +          return ( +            <Checkbox +              label={label} +              name={name} +              description={description} +              enabled={settings[settingsName]} +              onToggle={async () => { +                updateSettings(settingsName, !settings[settingsName]); +              }} +            /> +          ); +        })}        </section>      </Fragment>    ); diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 1ef0f0b79..9bcae8997 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -34,6 +34,7 @@ import {    WithdrawalType,  } from "@gnu-taler/taler-util";  import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";  import { styled } from "@linaria/react";  import { differenceInSeconds, isPast } from "date-fns";  import { ComponentChildren, Fragment, h, VNode } from "preact"; @@ -43,7 +44,6 @@ import { Amount } from "../components/Amount.js";  import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType.js";  import { CopyButton } from "../components/CopyButton.js";  import { AlertView, ErrorAlertView } from "../components/CurrentAlerts.js"; -import { JustInDevMode } from "../components/JustInDevMode.js";  import { Loading } from "../components/Loading.js";  import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js";  import { QR } from "../components/QR.js"; @@ -61,12 +61,12 @@ import {  import { Time } from "../components/Time.js";  import { alertFromError, useAlertContext } from "../context/alert.js";  import { useBackendContext } from "../context/backend.js"; -import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";  import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";  import { Button } from "../mui/Button.js";  import { SafeHandler } from "../mui/handlers.js";  import { Pages } from "../NavigationBar.js";  import { assertUnreachable } from "../utils/index.js"; +import { EnabledBySettings } from "../components/EnabledBySettings.js";  interface Props {    tid: string; @@ -359,18 +359,15 @@ function TransactionTemplate({                  <i18n.Translate>Cancel</i18n.Translate>                </Button>              ) : ( -              //WORKAROUND -              //Able to delete tx in dev mode -              //FIXME: remove this when DD37 is implemented -              <JustInDevMode> +              <EnabledBySettings name="deleteActiveTransactions">                  <Button                    variant="contained"                    color="error"                    onClick={doCheckBeforeForget as SafeHandler<void>}                  > -                  <i18n.Translate>Forget</i18n.Translate> +                  <i18n.Translate>Delete</i18n.Translate>                  </Button> -              </JustInDevMode> +              </EnabledBySettings>              )            ) : (              <Button @@ -378,7 +375,7 @@ function TransactionTemplate({                color="error"                onClick={doCheckBeforeForget as SafeHandler<void>}              > -              <i18n.Translate>Forget</i18n.Translate> +              <i18n.Translate>Delete</i18n.Translate>              </Button>            )}          </div> diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx index c2906373c..60a5970e4 100644 --- a/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.dev.tsx @@ -27,7 +27,6 @@ import { setupPlatform } from "./platform/foreground.js";  import devAPI from "./platform/dev.js";  import { Application } from "./wallet/Application.js"; -console.log("Wallet setup for Dev API");  setupPlatform(devAPI);  function main(): void { diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx index 84822b8e6..1bd42796b 100644 --- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx @@ -33,10 +33,8 @@ const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined";  //FIXME: create different entry point for any platform instead of  //switching in runtime  if (isFirefox) { -  console.log("Wallet setup for Firefox API");    setupPlatform(firefoxAPI);  } else { -  console.log("Wallet setup for Chrome API");    setupPlatform(chromeAPI);  } diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index d15528699..ce1dac14f 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -156,7 +156,7 @@ class WalletApiClientImpl implements WalletCoreApiClient {        };        response = await platform.sendMessageToBackground(message);      } catch (e) { -      console.log("Error calling backend"); +      logger.error("Error calling backend", e);        throw new Error(`Error contacting backend: ${e}`);      }      if (response.type === "error") { diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts index bf91e8521..8acc41247 100644 --- a/packages/taler-wallet-webextension/src/wxBackend.ts +++ b/packages/taler-wallet-webextension/src/wxBackend.ts @@ -319,12 +319,19 @@ async function reinitWallet(): Promise<void> {      timer = new SetTimeoutTimerAPI();    } +  const settings = await platform.getSettingsFromStorage();    logger.info("Setting up wallet");    const wallet = await Wallet.create(      currentDatabase,      httpLib,      timer,      cryptoWorker, +    { +      features: { +        allowHttp: settings.walletAllowHttp, +        batchWithdrawal: settings.walletBatchWithdrawal, +      }, +    },    );    try {      await wallet.handleCoreApiRequest("initWallet", "native-init", {}); | 
