From 4a781bd0dd8828ce152f6ab2c3f1bbd6b5e826f7 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 9 Jan 2023 20:20:09 -0300 Subject: [PATCH] fix #7153: more error handling if handler do not trap error then fail at compile time, all safe handlers push alert on error errors are typed so they render good information --- .../src/components/AmountField.stories.tsx | 8 +- .../src/components/CurrentAlerts.tsx | 75 ++++++--- .../PendingTransactions.stories.tsx | 8 +- .../src/components/QR.stories.tsx | 4 +- .../ShowFullContractTermPopup.stories.tsx | 10 +- .../components/ShowFullContractTermPopup.tsx | 13 +- .../src/components/TermsOfService/state.ts | 42 +++-- .../src/components/TermsOfService/stories.tsx | 4 +- .../src/context/alert.ts | 98 +++++++++--- .../src/context/devContext.ts | 17 +- .../src/cta/Deposit/state.ts | 5 +- .../src/cta/Deposit/stories.tsx | 4 +- .../src/cta/Deposit/test.ts | 2 +- .../src/cta/InvoiceCreate/state.ts | 56 +++---- .../src/cta/InvoiceCreate/stories.tsx | 9 +- .../src/cta/InvoicePay/index.ts | 1 - .../src/cta/InvoicePay/state.ts | 37 ++--- .../src/cta/InvoicePay/stories.tsx | 4 +- .../src/cta/InvoicePay/views.tsx | 22 +-- .../src/cta/Payment/state.ts | 69 ++++---- .../src/cta/Payment/stories.tsx | 42 +++-- .../src/cta/Payment/test.ts | 42 ++--- .../src/cta/Recovery/state.ts | 7 +- .../src/cta/Recovery/stories.tsx | 2 +- .../src/cta/Refund/state.ts | 7 +- .../src/cta/Refund/stories.tsx | 10 +- .../src/cta/Refund/test.ts | 9 +- .../src/cta/Tip/state.ts | 7 +- .../src/cta/Tip/stories.tsx | 6 +- .../src/cta/Tip/test.ts | 5 +- .../src/cta/TransferCreate/index.ts | 1 - .../src/cta/TransferCreate/state.ts | 46 ++---- .../src/cta/TransferCreate/stories.tsx | 9 +- .../src/cta/TransferCreate/views.tsx | 8 - .../src/cta/TransferPickup/index.ts | 1 - .../src/cta/TransferPickup/state.ts | 33 ++-- .../src/cta/TransferPickup/stories.tsx | 4 +- .../src/cta/TransferPickup/views.tsx | 8 - .../src/cta/Withdraw/state.ts | 9 +- .../src/cta/Withdraw/stories.tsx | 49 ++---- .../src/cta/Withdraw/test.ts | 3 +- .../src/cta/Withdraw/views.tsx | 8 - .../src/hooks/useAsyncAsHook.ts | 6 +- .../src/hooks/useAutoOpenPermissions.ts | 69 ++++---- .../src/hooks/useClipboardPermissions.ts | 69 ++++---- .../src/hooks/useSelectedExchange.ts | 8 +- .../src/hooks/useTalerActionURL.test.ts | 56 ++++--- .../src/hooks/useWalletDevMode.ts | 48 +++--- .../src/mui/handlers.ts | 40 ++++- .../src/popup/Application.tsx | 8 +- .../src/popup/Balance.stories.tsx | 12 +- .../src/popup/BalancePage.tsx | 13 +- .../src/popup/TalerActionFound.stories.tsx | 14 +- .../src/stories.test.ts | 19 ++- .../src/test-utils.ts | 5 +- .../src/wallet/AddBackupProvider/index.ts | 2 +- .../src/wallet/AddBackupProvider/state.ts | 33 ++-- .../src/wallet/AddBackupProvider/stories.tsx | 16 +- .../src/wallet/AddBackupProvider/test.ts | 3 +- .../src/wallet/AddNewActionView.stories.tsx | 4 +- .../src/wallet/Backup.stories.tsx | 10 +- .../src/wallet/BackupPage.tsx | 6 +- .../src/wallet/DepositPage/state.ts | 23 +-- .../src/wallet/DepositPage/stories.tsx | 51 ++---- .../src/wallet/DepositPage/test.ts | 3 +- .../src/wallet/DestinationSelection/state.ts | 31 ++-- .../wallet/DestinationSelection/stories.tsx | 8 +- .../src/wallet/DestinationSelection/test.ts | 3 +- .../src/wallet/DeveloperPage.stories.tsx | 4 +- .../wallet/EmptyComponentExample/stories.tsx | 4 +- .../src/wallet/ExchangeAddConfirm.stories.tsx | 8 +- .../src/wallet/ExchangeAddSetUrl.stories.tsx | 10 +- .../src/wallet/ExchangeSelection/state.ts | 41 ++--- .../src/wallet/ExchangeSelection/stories.tsx | 14 +- .../src/wallet/History.stories.tsx | 72 +++++---- .../src/wallet/History.tsx | 11 +- .../src/wallet/ManageAccount/state.ts | 19 +-- .../src/wallet/ManageAccount/stories.tsx | 13 +- .../src/wallet/Notifications/stories.tsx | 4 +- .../ProviderAddConfirmProvider.stories.tsx | 6 +- .../src/wallet/ProviderAddSetUrl.stories.tsx | 12 +- .../src/wallet/ProviderDetail.stories.tsx | 97 ++++++------ .../src/wallet/ProviderDetailPage.tsx | 6 +- .../src/wallet/QrReader.stories.tsx | 4 +- .../src/wallet/ReserveCreated.stories.tsx | 14 +- .../src/wallet/Settings.stories.tsx | 97 ++++++------ .../src/wallet/Settings.tsx | 24 ++- .../src/wallet/Transaction.stories.tsx | 147 +++++++++--------- .../src/wallet/Transaction.tsx | 6 +- .../src/wallet/Welcome.stories.tsx | 8 +- .../taler-wallet-webextension/src/wxApi.ts | 26 ++-- 91 files changed, 1003 insertions(+), 1008 deletions(-) diff --git a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx index 61c4a7661..f253d1996 100644 --- a/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx +++ b/packages/taler-wallet-webextension/src/components/AmountField.stories.tsx @@ -20,12 +20,10 @@ */ import { AmountJson, Amounts } from "@gnu-taler/taler-util"; -import { styled } from "@linaria/react"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useTranslationContext } from "../context/translation.js"; -import { Grid } from "../mui/Grid.js"; -import { AmountFieldHandler, TextFieldHandler } from "../mui/handlers.js"; +import { AmountFieldHandler, nullFunction, withSafe } from "../mui/handlers.js"; import { AmountField } from "./AmountField.js"; export default { @@ -39,9 +37,9 @@ function RenderAmount(): VNode { const handler: AmountFieldHandler = { value: value ?? Amounts.zeroOfCurrency("USD"), - onInput: async (e) => { + onInput: withSafe(async (e) => { setValue(e); - }, + }, nullFunction), error, }; const { i18n } = useTranslationContext(); diff --git a/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx b/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx index a56c82dee..47863d73e 100644 --- a/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx +++ b/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx @@ -18,7 +18,6 @@ import { ComponentChildren, Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useTranslationContext } from "../../../web-util/src/index.browser.js"; import { - ErrorAlert, Alert as AlertNotification, useAlertContext, } from "../context/alert.js"; @@ -37,41 +36,78 @@ function AlertContext({ context: undefined | object; }): VNode { const [more, setMore] = useState(false); + const [wrap, setWrap] = useState(false); const { i18n } = useTranslationContext(); if (!more) { return (
- setMore(true)}> + setMore(true)} + style={{ cursor: "pointer", textDecoration: "underline" }} + > more info
); } + const errorInfo = JSON.stringify( + context === undefined ? { cause } : { context, cause }, + undefined, + 2, + ); return ( -
-      {JSON.stringify(
-        context === undefined ? { cause } : { context, cause },
-        undefined,
-        2,
-      )}
-    
+ +
+ setWrap(!wrap)} + style={{ cursor: "pointer", textDecoration: "underline" }} + > + wrap text + +    + navigator.clipboard.writeText(errorInfo)} + style={{ cursor: "pointer", textDecoration: "underline" }} + > + copy content + +    + setMore(false)} + style={{ cursor: "pointer", textDecoration: "underline" }} + > + less info + +
+
+        {errorInfo}
+      
+
); } export function ErrorAlertView({ - error: alert, + error, onClose, }: { - error: ErrorAlert; + error: AlertNotification; onClose?: () => Promise; }): VNode { return ( - -
-
{alert.description}
- -
-
+ + + ); } @@ -86,6 +122,9 @@ export function AlertView({
{alert.description}
+ {alert.type === "error" ? ( + + ) : undefined}
); @@ -104,5 +143,5 @@ export function CurrentAlerts(): VNode { } function Wrapper({ children }: { children: ComponentChildren }): VNode { - return
{children}
; + return
{children}
; } diff --git a/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx b/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx index 2155c7aa6..d54dfe8fc 100644 --- a/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx +++ b/packages/taler-wallet-webextension/src/components/PendingTransactions.stories.tsx @@ -24,7 +24,7 @@ import { Transaction, TransactionType, } from "@gnu-taler/taler-util"; -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { PendingTransactionsView as TestedComponent } from "./PendingTransactions.js"; export default { @@ -32,7 +32,7 @@ export default { component: TestedComponent, }; -export const OnePendingTransaction = createExample(TestedComponent, { +export const OnePendingTransaction = tests.createExample(TestedComponent, { transactions: [ { amountEffective: "USD:10", @@ -42,7 +42,7 @@ export const OnePendingTransaction = createExample(TestedComponent, { ], }); -export const ThreePendingTransactions = createExample(TestedComponent, { +export const ThreePendingTransactions = tests.createExample(TestedComponent, { transactions: [ { amountEffective: "USD:10", @@ -62,7 +62,7 @@ export const ThreePendingTransactions = createExample(TestedComponent, { ], }); -export const TenPendingTransactions = createExample(TestedComponent, { +export const TenPendingTransactions = tests.createExample(TestedComponent, { transactions: [ { amountEffective: "USD:10", diff --git a/packages/taler-wallet-webextension/src/components/QR.stories.tsx b/packages/taler-wallet-webextension/src/components/QR.stories.tsx index 83365670e..bdaa842f2 100644 --- a/packages/taler-wallet-webextension/src/components/QR.stories.tsx +++ b/packages/taler-wallet-webextension/src/components/QR.stories.tsx @@ -19,13 +19,13 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { QR } from "./QR.js"; export default { title: "qr", }; -export const Restore = createExample(QR, { +export const Restore = tests.createExample(QR, { text: "taler://restore/6J0RZTJC6AV21WXK87BTE67WTHE9P2QSHF2BZXTP7PDZY2ARYBPG@sync1.demo.taler.net,sync2.demo.taler.net,sync1.demo.taler.net,sync3.demo.taler.net", }); diff --git a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx index 8c94e6e60..ef88d1c28 100644 --- a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx +++ b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx @@ -20,7 +20,7 @@ */ import { WalletContractData } from "@gnu-taler/taler-wallet-core"; -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ErrorView, HiddenView, @@ -86,10 +86,10 @@ const cd: WalletContractData = { deliveryLocation: undefined, }; -export const ShowingSimpleOrder = createExample(ShowView, { +export const ShowingSimpleOrder = tests.createExample(ShowView, { contractTerms: cd, }); -export const Error = createExample(ErrorView, { +export const Error = tests.createExample(ErrorView, { proposalId: "asd", error: { hasError: true, @@ -103,5 +103,5 @@ export const Error = createExample(ErrorView, { // }, }, }); -export const Loading = createExample(LoadingView, {}); -export const Hidden = createExample(HiddenView, {}); +export const Loading = tests.createExample(LoadingView, {}); +export const Hidden = tests.createExample(HiddenView, {}); diff --git a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx index 9871611f2..3e1f1dbe4 100644 --- a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx +++ b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx @@ -24,14 +24,14 @@ import { useState } from "preact/hooks"; import { Loading } from "../components/Loading.js"; import { Modal } from "../components/Modal.js"; import { Time } from "../components/Time.js"; -import { alertFromError } from "../context/alert.js"; +import { alertFromError, useAlertContext } from "../context/alert.js"; import { useBackendContext } from "../context/backend.js"; import { useTranslationContext } from "../context/translation.js"; import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { ButtonHandler } from "../mui/handlers.js"; import { compose, StateViewMap } from "../utils/index.js"; import { Amount } from "./Amount.js"; -import { AlertView } from "./CurrentAlerts.js"; +import { ErrorAlertView } from "./CurrentAlerts.js"; import { Link } from "./styled/index.js"; const ContractTermsTable = styled.table` @@ -102,6 +102,7 @@ interface Props { function useComponentState({ proposalId }: Props): State { const api = useBackendContext(); const [show, setShow] = useState(false); + const { pushAlertOnError } = useAlertContext(); const hook = useAsyncAsHook(async () => { if (!show) return undefined; return await api.wallet.call(WalletApiOperation.GetContractTermsDetails, { @@ -110,10 +111,10 @@ function useComponentState({ proposalId }: Props): State { }, [show]); const hideHandler = { - onClick: async () => setShow(false), + onClick: pushAlertOnError(async () => setShow(false)), }; const showHandler = { - onClick: async () => setShow(true), + onClick: pushAlertOnError(async () => setShow(true)), }; if (!show) { return { @@ -161,8 +162,8 @@ export function ErrorView({ const { i18n } = useTranslationContext(); return ( - (readOnly); const { i18n } = useTranslationContext(); - const { pushAlert } = useAlertContext(); + const { pushAlertOnError } = useAlertContext(); /** * For the exchange selected, bring the status of the terms of service @@ -67,24 +67,20 @@ export function useComponentState({ exchangeUrl, onChange }: Props): State { async function onUpdate(accepted: boolean): Promise { if (!state) return; - try { - if (accepted) { - await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, { - exchangeBaseUrl: exchangeUrl, - etag: state.version, - }); - } else { - // mark as not accepted - await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, { - exchangeBaseUrl: exchangeUrl, - etag: undefined, - }); - } - // setAccepted(accepted); - if (!readOnly) onChange(accepted); //external update - } catch (e) { - pushAlert(alertFromError(i18n.str`Could not accept terms of service`, e)); + if (accepted) { + await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, { + exchangeBaseUrl: exchangeUrl, + etag: state.version, + }); + } else { + // mark as not accepted + await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, { + exchangeBaseUrl: exchangeUrl, + etag: undefined, + }); } + // setAccepted(accepted); + if (!readOnly) onChange(accepted); //external update } const accepted = state.status === "accepted"; @@ -94,20 +90,20 @@ export function useComponentState({ exchangeUrl, onChange }: Props): State { showingTermsOfService: { value: showContent, button: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setShowContent(!showContent); - }, + }), }, }, terms: state, termsAccepted: { value: accepted, button: { - onClick: async () => { + onClick: pushAlertOnError(async () => { const newValue = !accepted; //toggle - onUpdate(newValue); + await onUpdate(newValue); setShowContent(false); - }, + }), }, }, }; diff --git a/packages/taler-wallet-webextension/src/components/TermsOfService/stories.tsx b/packages/taler-wallet-webextension/src/components/TermsOfService/stories.tsx index 2479274cb..9ef1c4298 100644 --- a/packages/taler-wallet-webextension/src/components/TermsOfService/stories.tsx +++ b/packages/taler-wallet-webextension/src/components/TermsOfService/stories.tsx @@ -19,11 +19,11 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; // import { ReadyView } from "./views.js"; export default { title: "TermsOfService", }; -// export const Ready = createExample(ReadyView, {}); +// export const Ready = tests.createExample(ReadyView, {}); diff --git a/packages/taler-wallet-webextension/src/context/alert.ts b/packages/taler-wallet-webextension/src/context/alert.ts index cc98ec1e0..e67d94671 100644 --- a/packages/taler-wallet-webextension/src/context/alert.ts +++ b/packages/taler-wallet-webextension/src/context/alert.ts @@ -19,19 +19,26 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { TranslatedString } from "@gnu-taler/taler-util"; +import { TalerErrorDetail, TranslatedString } from "@gnu-taler/taler-util"; import { ComponentChildren, createContext, h, VNode } from "preact"; import { useContext, useState } from "preact/hooks"; +import { HookError } from "../hooks/useAsyncAsHook.js"; +import { SafeHandler, withSafe } from "../mui/handlers.js"; +import { BackgroundError } from "../wxApi.js"; export type AlertType = "info" | "warning" | "error" | "success"; -export interface Alert { +export interface InfoAlert { message: TranslatedString; description: TranslatedString | VNode; - type: AlertType; + type: "info" | "warning" | "success"; } -export interface ErrorAlert extends Alert { +export type Alert = InfoAlert | ErrorAlert; + +export interface ErrorAlert { + message: TranslatedString; + description: TranslatedString | VNode; type: "error"; context: object; cause: any; @@ -41,10 +48,14 @@ type Type = { alerts: Alert[]; pushAlert: (n: Alert) => void; removeAlert: (n: Alert) => void; + pushAlertOnError: (h: (p: T) => Promise) => SafeHandler; }; const initial: Type = { alerts: [], + pushAlertOnError: () => { + throw Error("alert context not initialized"); + }, pushAlert: () => { null; }, @@ -80,8 +91,17 @@ export const AlertProvider = ({ children }: Props): VNode => { setAlerts((ns: AlertWithDate[]) => ns.filter((n) => n !== alert)); }; + function pushAlertOnError( + handler: (p: T) => Promise, + ): SafeHandler { + return withSafe(handler, (e) => { + const a = alertFromError(e.message as TranslatedString, e); + pushAlert(a); + }); + } + return h(Context.Provider, { - value: { alerts, pushAlert, removeAlert }, + value: { alerts, pushAlert, removeAlert, pushAlertOnError }, children, }); }; @@ -90,29 +110,71 @@ export const useAlertContext = (): Type => useContext(Context); export function alertFromError( message: TranslatedString, - error: unknown, + error: HookError, + ...context: any[] +): ErrorAlert; + +export function alertFromError( + message: TranslatedString, + error: Error, + ...context: any[] +): ErrorAlert; + +export function alertFromError( + message: TranslatedString, + error: TalerErrorDetail, + ...context: any[] +): ErrorAlert; + +export function alertFromError( + message: TranslatedString, + error: HookError | TalerErrorDetail | Error, ...context: any[] ): ErrorAlert { - let description = "" as TranslatedString; + let description: TranslatedString; + let cause: any; - const isObject = typeof error === "object" && - error !== null; - const hasMessage = - isObject && - "message" in error && - typeof error.message === "string"; - - if (hasMessage) { - description = error.message as TranslatedString; + if (typeof error === "object" && error !== null) { + if ("code" in error) { + //TalerErrorDetail + description = (error.hint ?? + `Error code: ${error.code}`) as TranslatedString; + cause = { + details: error, + }; + } else if ("hasError" in error) { + //HookError + description = error.message as TranslatedString; + if (error.type === "taler") { + cause = { + details: error.details, + }; + } + } else { + if (error instanceof BackgroundError) { + description = (error.errorDetail.hint ?? + `Error code: ${error.errorDetail.code}`) as TranslatedString; + cause = { + details: error.errorDetail, + stack: error.stack, + }; + } else { + description = error.message as TranslatedString; + cause = { + stack: error.stack, + }; + } + } } else { - description = `Unknown error: ${String(error)}` as TranslatedString; + description = "" as TranslatedString; + cause = error; } return { type: "error", message, description, - cause: error, + cause, context, }; } diff --git a/packages/taler-wallet-webextension/src/context/devContext.ts b/packages/taler-wallet-webextension/src/context/devContext.ts index 99301df52..e2ad2914b 100644 --- a/packages/taler-wallet-webextension/src/context/devContext.ts +++ b/packages/taler-wallet-webextension/src/context/devContext.ts @@ -22,16 +22,15 @@ import { createContext, h, VNode } from "preact"; import { useContext } from "preact/hooks"; import { useWalletDevMode } from "../hooks/useWalletDevMode.js"; -import { ToggleHandler } from "../mui/handlers.js"; interface Type { devMode: boolean; - devModeToggle: ToggleHandler; + toggle: () => Promise; } const Context = createContext({ devMode: false, - devModeToggle: { - button: {}, + toggle: async () => { + null; }, }); @@ -47,9 +46,8 @@ export const DevContextProviderForTesting = ({ return h(Context.Provider, { value: { devMode: !!value, - devModeToggle: { - value, - button: {}, + toggle: async () => { + null; }, }, children, @@ -58,7 +56,10 @@ export const DevContextProviderForTesting = ({ export const DevContextProvider = ({ children }: { children: any }): VNode => { const devModeToggle = useWalletDevMode(); - const value: Type = { devMode: !!devModeToggle.value, devModeToggle }; + 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" diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts index 4cee7cfd0..3e09597a2 100644 --- a/packages/taler-wallet-webextension/src/cta/Deposit/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Deposit/state.ts @@ -16,7 +16,7 @@ import { Amounts } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -29,6 +29,7 @@ export function useComponentState({ onSuccess, }: Props): State { const api = useBackendContext(); + const { pushAlertOnError } = useAlertContext(); const info = useAsyncAsHook(async () => { if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT"); if (!amountStr) throw Error("ERROR_NO-AMOUNT-FOR-DEPOSIT"); @@ -66,7 +67,7 @@ export function useComponentState({ status: "ready", error: undefined, confirm: { - onClick: doDeposit, + onClick: pushAlertOnError(doDeposit), }, fee: Amounts.sub(deposit.totalDepositCost, deposit.effectiveDepositAmount) .amount, diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx b/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx index 6d1535953..fd3044dcb 100644 --- a/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Deposit/stories.tsx @@ -20,14 +20,14 @@ */ import { Amounts } from "@gnu-taler/taler-util"; -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ReadyView } from "./views.js"; export default { title: "deposit", }; -export const Ready = createExample(ReadyView, { +export const Ready = tests.createExample(ReadyView, { status: "ready", confirm: {}, cost: Amounts.parseOrThrow("EUR:1.2"), diff --git a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts index 031dcffaa..b9fbc3638 100644 --- a/packages/taler-wallet-webextension/src/cta/Deposit/test.ts +++ b/packages/taler-wallet-webextension/src/cta/Deposit/test.ts @@ -55,7 +55,7 @@ describe("Deposit CTA states", () => { if (!error) expect.fail(); // if (!error.hasError) expect.fail(); // if (error.operational) expect.fail(); - expect(error.cause?.message).eq("ERROR_NO-URI-FOR-DEPOSIT"); + expect(error.description).eq("ERROR_NO-URI-FOR-DEPOSIT"); }, ], TestingContext, diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts index 7dcda4c52..ee5375859 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts +++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts @@ -15,15 +15,11 @@ */ /* eslint-disable react-hooks/rules-of-hooks */ -import { - Amounts, - TalerErrorDetail, - TalerProtocolTimestamp, -} from "@gnu-taler/taler-util"; +import { Amounts, TalerProtocolTimestamp } from "@gnu-taler/taler-util"; import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { isFuture, parse } from "date-fns"; import { useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -71,6 +67,7 @@ export function useComponentState({ return () => { const [subject, setSubject] = useState(); const [timestamp, setTimestamp] = useState(); + const { pushAlertOnError } = useAlertContext(); const selectedExchange = useSelectedExchange({ currency: amount.currency, @@ -144,27 +141,20 @@ export function useComponentState({ async function accept(): Promise { if (!subject || !purse_expiration) return; - try { - const resp = await api.wallet.call( - WalletApiOperation.InitiatePeerPullPayment, - { - exchangeBaseUrl: exchange.exchangeBaseUrl, - partialContractTerms: { - amount: Amounts.stringify(amount), - summary: subject, - purse_expiration, - }, - }, - ); - onSuccess(resp.transactionId); - } catch (e) { - if (e instanceof TalerError) { - // setOperationError(e.errorDetail); - } - console.error(e); - throw Error("error trying to accept"); - } + const resp = await api.wallet.call( + WalletApiOperation.InitiatePeerPullPayment, + { + exchangeBaseUrl: exchange.exchangeBaseUrl, + partialContractTerms: { + amount: Amounts.stringify(amount), + summary: subject, + purse_expiration, + }, + }, + ); + + onSuccess(resp.transactionId); } const unableToCreate = !subject || Amounts.isZero(amount) || !purse_expiration; @@ -176,25 +166,25 @@ export function useComponentState({ subject === undefined ? undefined : !subject - ? "Can't be empty" - : undefined, + ? "Can't be empty" + : undefined, value: subject ?? "", - onInput: async (e) => setSubject(e), + onInput: pushAlertOnError(async (e) => setSubject(e)), }, expiration: { error: timestampError, value: timestamp === undefined ? "" : timestamp, - onInput: async (e) => { + onInput: pushAlertOnError(async (e) => { setTimestamp(e); - }, + }), }, doSelectExchange: selectedExchange.doSelect, exchangeUrl: exchange.exchangeBaseUrl, create: { - onClick: unableToCreate ? undefined : accept, + onClick: unableToCreate ? undefined : pushAlertOnError(accept), }, cancel: { - onClick: onClose, + onClick: pushAlertOnError(onClose), }, requestAmount, toBeReceived, diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx index 05b923c9e..4ab4dc8f6 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/stories.tsx @@ -19,14 +19,15 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; +import { nullFunction } from "../../mui/handlers.js"; import { ReadyView } from "./views.js"; export default { title: "invoice create", }; -export const Ready = createExample(ReadyView, { +export const Ready = tests.createExample(ReadyView, { requestAmount: { currency: "ARS", value: 1, @@ -45,9 +46,7 @@ export const Ready = createExample(ReadyView, { exchangeUrl: "https://exchange.taler.ar", subject: { value: "some subject", - onInput: async () => { - null; - }, + onInput: nullFunction, }, create: {}, }); diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts index 82b2c7af5..c8a7eed65 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts +++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts @@ -61,7 +61,6 @@ export namespace State { goToWalletManualWithdraw: (currency: string) => Promise; summary: string | undefined; expiration: AbsoluteTime | undefined; - operationError?: TalerErrorDetail; payStatus: PreparePayResult; } diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts index 9c4a3162e..66c018ddf 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts +++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts @@ -25,10 +25,11 @@ import { } from "@gnu-taler/taler-util"; import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useEffect, useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; +import { withSafe } from "../../mui/handlers.js"; import { Props, State } from "./index.js"; export function useComponentState({ @@ -39,6 +40,7 @@ export function useComponentState({ }: Props): State { const api = useBackendContext(); const { i18n } = useTranslationContext(); + const { pushAlertOnError } = useAlertContext(); const hook = useAsyncAsHook(async () => { const p2p = await api.wallet.call(WalletApiOperation.CheckPeerPullPayment, { talerUri: talerPayPullUri, @@ -54,10 +56,6 @@ export function useComponentState({ ), ); - const [operationError, setOperationError] = useState< - TalerErrorDetail | undefined - >(undefined); - if (!hook) { return { status: "loading", @@ -109,18 +107,17 @@ export function useComponentState({ contractTerms: {} as any, amountRaw: hook.response.p2p.amount, noncePriv: "", - }; + } as any; //FIXME: check this interface with new values const baseResult = { uri: talerPayPullUri, cancel: { - onClick: onClose, + onClick: pushAlertOnError(onClose), }, amount, goToWalletManualWithdraw, summary, expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : undefined, - operationError, }; if (!foundBalance) { @@ -148,21 +145,13 @@ export function useComponentState({ } async function accept(): Promise { - try { - const resp = await api.wallet.call( - WalletApiOperation.AcceptPeerPullPayment, - { - peerPullPaymentIncomingId, - }, - ); - onSuccess(resp.transactionId); - } catch (e) { - if (e instanceof TalerError) { - setOperationError(e.errorDetail); - } - console.error(e); - throw Error("error trying to accept"); - } + const resp = await api.wallet.call( + WalletApiOperation.AcceptPeerPullPayment, + { + peerPullPaymentIncomingId, + }, + ); + onSuccess(resp.transactionId); } return { @@ -172,7 +161,7 @@ export function useComponentState({ payStatus: paymentPossible, balance: foundAmount, accept: { - onClick: accept, + onClick: pushAlertOnError(accept), }, }; } diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx b/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx index 749cd78fc..1dada5a91 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx @@ -20,14 +20,14 @@ */ import { PreparePayResult, PreparePayResultType } from "@gnu-taler/taler-util"; -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ReadyView } from "./views.js"; export default { title: "invoice payment", }; -export const Ready = createExample(ReadyView, { +export const Ready = tests.createExample(ReadyView, { amount: { currency: "ARS", value: 1, diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx b/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx index 6a9ab3cf7..9a748891c 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx +++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx @@ -16,11 +16,10 @@ import { Fragment, h, VNode } from "preact"; import { Amount } from "../../components/Amount.js"; -import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js"; import { LogoHeader } from "../../components/LogoHeader.js"; import { Part } from "../../components/Part.js"; import { PaymentButtons } from "../../components/PaymentButtons.js"; -import { Link, SubTitle, WalletAction } from "../../components/styled/index.js"; +import { SubTitle, WalletAction } from "../../components/styled/index.js"; import { Time } from "../../components/Time.js"; import { useTranslationContext } from "../../context/translation.js"; import { State } from "./index.js"; @@ -29,29 +28,14 @@ export function ReadyView( state: State.Ready | State.NoBalanceForCurrency | State.NoEnoughBalance, ): VNode { const { i18n } = useTranslationContext(); - const { - operationError, - summary, - amount, - expiration, - uri, - status, - balance, - payStatus, - cancel, - } = state; + const { summary, amount, expiration, uri, status, balance, payStatus } = + state; return ( Digital invoice - {operationError && ( - - )}
{summary}} /> } /> diff --git a/packages/taler-wallet-webextension/src/cta/Payment/state.ts b/packages/taler-wallet-webextension/src/cta/Payment/state.ts index 6d7ef6b20..0f1388ea5 100644 --- a/packages/taler-wallet-webextension/src/cta/Payment/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Payment/state.ts @@ -19,11 +19,10 @@ import { ConfirmPayResultType, NotificationType, PreparePayResultType, - TalerErrorCode, } from "@gnu-taler/taler-util"; -import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { useEffect, useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { useEffect } from "preact/hooks"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -36,7 +35,7 @@ export function useComponentState({ goToWalletManualWithdraw, onSuccess, }: Props): State { - const [payErrMsg, setPayErrMsg] = useState(undefined); + const { pushAlertOnError } = useAlertContext(); const api = useBackendContext(); const { i18n } = useTranslationContext(); @@ -142,43 +141,41 @@ export function useComponentState({ } async function doPayment(): Promise { - try { - if (payStatus.status !== "payment-possible") { - throw TalerError.fromUncheckedDetail({ - code: TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR, - hint: `payment is not possible: ${payStatus.status}`, - }); - } - const res = await api.wallet.call(WalletApiOperation.ConfirmPay, { - proposalId: payStatus.proposalId, - }); - // handle confirm pay - if (res.type !== ConfirmPayResultType.Done) { - throw TalerError.fromUncheckedDetail({ - code: TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR, - hint: `could not confirm payment`, - payResult: res, - }); - } - const fu = res.contractTerms.fulfillment_url; - if (fu) { - if (typeof window !== "undefined") { - document.location.href = fu; - } else { - console.log(`should d to ${fu}`); - } - } + // if (payStatus.status !== "payment-possible") { + // throw TalerError.fromUncheckedDetail({ + // code: TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR, + // when: new Date().toISOString(), + // hint: `payment is not possible: ${payStatus.status}`, + // }); + // } + const res = await api.wallet.call(WalletApiOperation.ConfirmPay, { + proposalId: payStatus.proposalId, + }); + // handle confirm pay + if (res.type !== ConfirmPayResultType.Done) { + // throw new BackgroundError("Could not confirm payment", res.lastError) + // // throw TalerError.fromUncheckedDetail({ + // // code: TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR, + // // when: new Date().toISOString(), + // // hint: `could not confirm payment`, + // // payResult: res, + // // }); onSuccess(res.transactionId); - } catch (e) { - if (e instanceof TalerError) { - setPayErrMsg(e); + return; + } + const fu = res.contractTerms.fulfillment_url; + if (fu) { + if (typeof window !== "undefined") { + document.location.href = fu; + } else { + console.log(`should d to ${fu}`); } } + onSuccess(res.transactionId); } const payHandler: ButtonHandler = { - onClick: payErrMsg ? undefined : doPayment, - error: payErrMsg, + onClick: pushAlertOnError(doPayment), }; // (payStatus.status === PreparePayResultType.PaymentPossible) diff --git a/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx b/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx index 28fcd8db7..b63190236 100644 --- a/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx @@ -24,10 +24,11 @@ import { MerchantContractTerms as ContractTerms, PreparePayResultType, } from "@gnu-taler/taler-util"; -import merchantIcon from "../../../static-dev/merchant-icon.jpeg"; -import { createExample } from "../../test-utils.js"; -import { BaseView } from "./views.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import beer from "../../../static-dev/beer.png"; +import merchantIcon from "../../../static-dev/merchant-icon.jpeg"; +import { nullFunction } from "../../mui/handlers.js"; +import { BaseView } from "./views.js"; export default { title: "payment", @@ -35,7 +36,7 @@ export default { argTypes: {}, }; -export const NoBalance = createExample(BaseView, { +export const NoBalance = tests.createExample(BaseView, { status: "no-balance-for-currency", error: undefined, amount: Amounts.parseOrThrow("USD:10"), @@ -44,6 +45,7 @@ export const NoBalance = createExample(BaseView, { uri: "", payStatus: { status: PreparePayResultType.InsufficientBalance, + balanceDetails: {} as any, talerUri: "taler://pay/..", noncePriv: "", proposalId: "96YY92RQZGF3V7TJSPN4SF9549QX7BRF88Q5PYFCSBNQ0YK4RPK0", @@ -61,7 +63,7 @@ export const NoBalance = createExample(BaseView, { }, }); -export const NoEnoughBalance = createExample(BaseView, { +export const NoEnoughBalance = tests.createExample(BaseView, { status: "no-enough-balance", error: undefined, amount: Amounts.parseOrThrow("USD:10"), @@ -74,6 +76,7 @@ export const NoEnoughBalance = createExample(BaseView, { uri: "", payStatus: { status: PreparePayResultType.InsufficientBalance, + balanceDetails: {} as any, talerUri: "taler://pay/..", noncePriv: "", proposalId: "96YY92RQZGF3V7TJSPN4SF9549QX7BRF88Q5PYFCSBNQ0YK4RPK0", @@ -91,7 +94,7 @@ export const NoEnoughBalance = createExample(BaseView, { }, }); -export const EnoughBalanceButRestricted = createExample(BaseView, { +export const EnoughBalanceButRestricted = tests.createExample(BaseView, { status: "no-enough-balance", error: undefined, amount: Amounts.parseOrThrow("USD:10"), @@ -104,6 +107,7 @@ export const EnoughBalanceButRestricted = createExample(BaseView, { uri: "", payStatus: { status: PreparePayResultType.InsufficientBalance, + balanceDetails: {} as any, talerUri: "taler://pay/..", noncePriv: "", proposalId: "96YY92RQZGF3V7TJSPN4SF9549QX7BRF88Q5PYFCSBNQ0YK4RPK0", @@ -121,7 +125,7 @@ export const EnoughBalanceButRestricted = createExample(BaseView, { }, }); -export const PaymentPossible = createExample(BaseView, { +export const PaymentPossible = tests.createExample(BaseView, { status: "ready", error: undefined, amount: Amounts.parseOrThrow("USD:10"), @@ -131,9 +135,7 @@ export const PaymentPossible = createExample(BaseView, { value: 11, }, payHandler: { - onClick: async () => { - null; - }, + onClick: nullFunction, }, uri: "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0", @@ -162,7 +164,7 @@ export const PaymentPossible = createExample(BaseView, { }, }); -export const PaymentPossibleWithFee = createExample(BaseView, { +export const PaymentPossibleWithFee = tests.createExample(BaseView, { status: "ready", error: undefined, amount: Amounts.parseOrThrow("USD:10"), @@ -172,9 +174,7 @@ export const PaymentPossibleWithFee = createExample(BaseView, { value: 11, }, payHandler: { - onClick: async () => { - null; - }, + onClick: nullFunction, }, uri: "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0", @@ -200,7 +200,7 @@ export const PaymentPossibleWithFee = createExample(BaseView, { }, }); -export const TicketWithAProductList = createExample(BaseView, { +export const TicketWithAProductList = tests.createExample(BaseView, { status: "ready", error: undefined, amount: Amounts.parseOrThrow("USD:10"), @@ -210,9 +210,7 @@ export const TicketWithAProductList = createExample(BaseView, { value: 11, }, payHandler: { - onClick: async () => { - null; - }, + onClick: nullFunction, }, uri: "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0", @@ -257,7 +255,7 @@ export const TicketWithAProductList = createExample(BaseView, { }, }); -export const TicketWithShipping = createExample(BaseView, { +export const TicketWithShipping = tests.createExample(BaseView, { status: "ready", error: undefined, amount: Amounts.parseOrThrow("USD:10"), @@ -267,9 +265,7 @@ export const TicketWithShipping = createExample(BaseView, { value: 11, }, payHandler: { - onClick: async () => { - null; - }, + onClick: nullFunction, }, uri: "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0", @@ -309,7 +305,7 @@ export const TicketWithShipping = createExample(BaseView, { }, }); -export const AlreadyConfirmedByOther = createExample(BaseView, { +export const AlreadyConfirmedByOther = tests.createExample(BaseView, { status: "confirmed", error: undefined, amount: Amounts.parseOrThrow("USD:10"), diff --git a/packages/taler-wallet-webextension/src/cta/Payment/test.ts b/packages/taler-wallet-webextension/src/cta/Payment/test.ts index 123e95a87..f53be00c9 100644 --- a/packages/taler-wallet-webextension/src/cta/Payment/test.ts +++ b/packages/taler-wallet-webextension/src/cta/Payment/test.ts @@ -31,7 +31,8 @@ import { import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; import { tests } from "../../../../web-util/src/index.browser.js"; -import { mountHook, nullFunction } from "../../test-utils.js"; +import { ErrorAlert, useAlertContext } from "../../context/alert.js"; +import { nullFunction } from "../../mui/handlers.js"; import { createWalletApiMock } from "../../test-utils.js"; import { useComponentState } from "./state.js"; @@ -385,8 +386,12 @@ describe("Payment CTA states", () => { } as ConfirmPayResult); const hookBehavior = await tests.hookBehaveLikeThis( - useComponentState, - props, + () => { + const state = useComponentState(props); + // const { alerts } = useAlertContext(); + return { ...state, alerts: {} }; + }, + {}, [ ({ status, error }) => { expect(status).equals("loading"); @@ -400,22 +405,21 @@ describe("Payment CTA states", () => { if (state.payHandler.onClick === undefined) expect.fail(); state.payHandler.onClick(); }, - (state) => { - if (state.status !== "ready") expect.fail(); - expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15")); - expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9")); - // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1")); - expect(state.payHandler.onClick).undefined; - if (state.payHandler.error === undefined) expect.fail(); - //FIXME: error message here is bad - expect(state.payHandler.error.errorDetail.hint).eq( - "could not confirm payment", - ); - expect(state.payHandler.error.errorDetail.payResult).deep.equal({ - type: ConfirmPayResultType.Pending, - lastError: { code: 1 }, - }); - }, + // (state) => { + // if (state.status !== "ready") expect.fail(); + // expect(state.balance).deep.equal(Amounts.parseOrThrow("USD:15")); + // expect(state.amount).deep.equal(Amounts.parseOrThrow("USD:9")); + + // // FIXME: check that the error is pushed to the alertContext + // // expect(state.alerts.length).eq(1); + // // const alert = state.alerts[0] + // // if (alert.type !== "error") expect.fail(); + + // // expect(alert.cause.errorDetail.payResult).deep.equal({ + // // type: ConfirmPayResultType.Pending, + // // lastError: { code: 1 }, + // // }); + // }, ], TestingContext, ); diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts index 078e53bf9..9731d3f69 100644 --- a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts @@ -16,7 +16,7 @@ import { parseRecoveryUri } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { Alert } from "../../context/alert.js"; +import { useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { Props, State } from "./index.js"; @@ -27,6 +27,7 @@ export function useComponentState({ onSuccess, }: Props): State { const api = useBackendContext(); + const { pushAlertOnError } = useAlertContext(); const { i18n } = useTranslationContext(); if (!talerRecoveryUri) { return { @@ -67,10 +68,10 @@ export function useComponentState({ status: "ready", accept: { - onClick: recoverBackup, + onClick: pushAlertOnError(recoverBackup), }, cancel: { - onClick: onCancel, + onClick: pushAlertOnError(onCancel), }, error: undefined, }; diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx b/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx index 9243cc015..4f7a14c6d 100644 --- a/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx @@ -20,7 +20,7 @@ */ import { Amounts } from "@gnu-taler/taler-util"; -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ReadyView } from "./views.js"; export default { diff --git a/packages/taler-wallet-webextension/src/cta/Refund/state.ts b/packages/taler-wallet-webextension/src/cta/Refund/state.ts index 5a5073ba3..4c411ec04 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Refund/state.ts @@ -17,7 +17,7 @@ import { Amounts, NotificationType } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useEffect, useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -31,6 +31,7 @@ export function useComponentState({ const api = useBackendContext(); const { i18n } = useTranslationContext(); const [ignored, setIgnored] = useState(false); + const { pushAlertOnError } = useAlertContext(); const info = useAsyncAsHook(async () => { if (!talerRefundUri) throw Error("ERROR_NO-URI-FOR-REFUND"); @@ -108,10 +109,10 @@ export function useComponentState({ ...baseInfo, orderId: info.response.refund.info.orderId, accept: { - onClick: doAccept, + onClick: pushAlertOnError(doAccept), }, ignore: { - onClick: doIgnore, + onClick: pushAlertOnError(doIgnore), }, cancel, }; diff --git a/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx b/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx index 921cf77e6..faaee1104 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx @@ -21,13 +21,13 @@ import { Amounts } from "@gnu-taler/taler-util"; import beer from "../../../static-dev/beer.png"; -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { IgnoredView, InProgressView, ReadyView } from "./views.js"; export default { title: "refund", }; -export const InProgress = createExample(InProgressView, { +export const InProgress = tests.createExample(InProgressView, { status: "in-progress", error: undefined, amount: Amounts.parseOrThrow("USD:1"), @@ -37,7 +37,7 @@ export const InProgress = createExample(InProgressView, { products: undefined, }); -export const Ready = createExample(ReadyView, { +export const Ready = tests.createExample(ReadyView, { status: "ready", error: undefined, accept: {}, @@ -51,7 +51,7 @@ export const Ready = createExample(ReadyView, { orderId: "abcdef", }); -export const WithAProductList = createExample(ReadyView, { +export const WithAProductList = tests.createExample(ReadyView, { status: "ready", error: undefined, accept: {}, @@ -75,7 +75,7 @@ export const WithAProductList = createExample(ReadyView, { orderId: "abcdef", }); -export const Ignored = createExample(IgnoredView, { +export const Ignored = tests.createExample(IgnoredView, { status: "ignored", error: undefined, merchantName: "the merchant", diff --git a/packages/taler-wallet-webextension/src/cta/Refund/test.ts b/packages/taler-wallet-webextension/src/cta/Refund/test.ts index 8c4daa4d2..a07158e1a 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund/test.ts +++ b/packages/taler-wallet-webextension/src/cta/Refund/test.ts @@ -27,11 +27,8 @@ import { import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; import { tests } from "../../../../web-util/src/index.browser.js"; -import { - createWalletApiMock, - mountHook, - nullFunction, -} from "../../test-utils.js"; +import { nullFunction } from "../../mui/handlers.js"; +import { createWalletApiMock } from "../../test-utils.js"; import { useComponentState } from "./state.js"; describe("Refund CTA states", () => { @@ -57,7 +54,7 @@ describe("Refund CTA states", () => { if (!error) expect.fail(); // if (!error.hasError) expect.fail(); // if (error.operational) expect.fail(); - expect(error.cause?.message).eq("ERROR_NO-URI-FOR-REFUND"); + expect(error.description).eq("ERROR_NO-URI-FOR-REFUND"); }, ], TestingContext, diff --git a/packages/taler-wallet-webextension/src/cta/Tip/state.ts b/packages/taler-wallet-webextension/src/cta/Tip/state.ts index 29a9c4c71..3b9abf5a3 100644 --- a/packages/taler-wallet-webextension/src/cta/Tip/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Tip/state.ts @@ -16,7 +16,7 @@ import { Amounts } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -29,6 +29,7 @@ export function useComponentState({ }: Props): State { const api = useBackendContext(); const { i18n } = useTranslationContext(); + const { pushAlertOnError } = useAlertContext(); const tipInfo = useAsyncAsHook(async () => { if (!talerTipUri) throw Error("ERROR_NO-URI-FOR-TIP"); const tip = await api.wallet.call(WalletApiOperation.PrepareTip, { @@ -77,7 +78,7 @@ export function useComponentState({ amount: Amounts.parseOrThrow(tip.tipAmountEffective), error: undefined, cancel: { - onClick: onCancel, + onClick: pushAlertOnError(onCancel), }, }; @@ -92,7 +93,7 @@ export function useComponentState({ status: "ready", ...baseInfo, accept: { - onClick: doAccept, + onClick: pushAlertOnError(doAccept), }, }; } diff --git a/packages/taler-wallet-webextension/src/cta/Tip/stories.tsx b/packages/taler-wallet-webextension/src/cta/Tip/stories.tsx index 86bdd27a9..dd358d9d2 100644 --- a/packages/taler-wallet-webextension/src/cta/Tip/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Tip/stories.tsx @@ -20,14 +20,14 @@ */ import { Amounts } from "@gnu-taler/taler-util"; -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { AcceptedView, ReadyView } from "./views.js"; export default { title: "tip", }; -export const Accepted = createExample(AcceptedView, { +export const Accepted = tests.createExample(AcceptedView, { status: "accepted", error: undefined, amount: Amounts.parseOrThrow("EUR:1"), @@ -35,7 +35,7 @@ export const Accepted = createExample(AcceptedView, { merchantBaseUrl: "", }); -export const Ready = createExample(ReadyView, { +export const Ready = tests.createExample(ReadyView, { status: "ready", error: undefined, amount: Amounts.parseOrThrow("EUR:1"), diff --git a/packages/taler-wallet-webextension/src/cta/Tip/test.ts b/packages/taler-wallet-webextension/src/cta/Tip/test.ts index 2cc95f424..44a6f9b0b 100644 --- a/packages/taler-wallet-webextension/src/cta/Tip/test.ts +++ b/packages/taler-wallet-webextension/src/cta/Tip/test.ts @@ -23,7 +23,8 @@ import { Amounts } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; import { tests } from "../../../../web-util/src/index.browser.js"; -import { createWalletApiMock, nullFunction } from "../../test-utils.js"; +import { nullFunction } from "../../mui/handlers.js"; +import { createWalletApiMock } from "../../test-utils.js"; import { Props } from "./index.js"; import { useComponentState } from "./state.js"; @@ -48,7 +49,7 @@ describe("Tip CTA states", () => { ({ status, error }) => { expect(status).equals("error"); if (!error) expect.fail(); - expect(error.cause?.message).eq("ERROR_NO-URI-FOR-TIP"); + expect(error.description).eq("ERROR_NO-URI-FOR-TIP"); }, ], TestingContext, diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts index b191b4efa..654b03b7f 100644 --- a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts +++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts @@ -54,7 +54,6 @@ export namespace State { subject: TextFieldHandler; expiration: TextFieldHandler; error: undefined; - operationError?: TalerErrorDetail; } } diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts index ecea53848..6574d6ba1 100644 --- a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts +++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts @@ -22,7 +22,7 @@ import { import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { isFuture, parse } from "date-fns"; import { useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -34,16 +34,13 @@ export function useComponentState({ onSuccess, }: Props): State { const api = useBackendContext(); + const { pushAlertOnError } = useAlertContext(); const amount = Amounts.parseOrThrow(amountStr); const { i18n } = useTranslationContext(); const [subject, setSubject] = useState(); const [timestamp, setTimestamp] = useState(); - const [operationError, setOperationError] = useState< - TalerErrorDetail | undefined - >(undefined); - const hook = useAsyncAsHook(async () => { const resp = await api.wallet.call( WalletApiOperation.PreparePeerPushPayment, @@ -104,25 +101,17 @@ export function useComponentState({ async function accept(): Promise { if (!subject || !purse_expiration) return; - try { - const resp = await api.wallet.call( - WalletApiOperation.InitiatePeerPushPayment, - { - partialContractTerms: { - summary: subject, - amount: amountStr, - purse_expiration, - }, + const resp = await api.wallet.call( + WalletApiOperation.InitiatePeerPushPayment, + { + partialContractTerms: { + summary: subject, + amount: amountStr, + purse_expiration, }, - ); - onSuccess(resp.transactionId); - } catch (e) { - if (e instanceof TalerError) { - setOperationError(e.errorDetail); - } - console.error(e); - throw Error("error trying to accept"); - } + }, + ); + onSuccess(resp.transactionId); } const unableToCreate = @@ -131,7 +120,7 @@ export function useComponentState({ return { status: "ready", cancel: { - onClick: onClose, + onClick: pushAlertOnError(onClose), }, subject: { error: @@ -141,21 +130,20 @@ export function useComponentState({ ? "Can't be empty" : undefined, value: subject ?? "", - onInput: async (e) => setSubject(e), + onInput: pushAlertOnError(async (e) => setSubject(e)), }, expiration: { error: timestampError, value: timestamp === undefined ? "" : timestamp, - onInput: async (e) => { + onInput: pushAlertOnError(async (e) => { setTimestamp(e); - }, + }), }, create: { - onClick: unableToCreate ? undefined : accept, + onClick: unableToCreate ? undefined : pushAlertOnError(accept), }, debitAmount, toBeReceived, error: undefined, - operationError, }; } diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx b/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx index d0650f562..57409bde5 100644 --- a/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/stories.tsx @@ -19,14 +19,15 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; +import { nullFunction } from "../../mui/handlers.js"; import { ReadyView } from "./views.js"; export default { title: "transfer create", }; -export const Ready = createExample(ReadyView, { +export const Ready = tests.createExample(ReadyView, { debitAmount: { currency: "ARS", value: 1, @@ -44,8 +45,6 @@ export const Ready = createExample(ReadyView, { }, subject: { value: "the subject", - onInput: async () => { - null; - }, + onInput: nullFunction, }, }); diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx index cee61b3b8..373af8f74 100644 --- a/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx +++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/views.tsx @@ -32,8 +32,6 @@ export function ReadyView({ toBeReceived, debitAmount, create, - operationError, - cancel, }: State.Ready): VNode { const { i18n } = useTranslationContext(); @@ -65,12 +63,6 @@ export function ReadyView({ Digital cash transfer - {operationError && ( - - )}

{ return await api.wallet.call(WalletApiOperation.CheckPeerPushPayment, { talerUri: talerPayPushUri, }); }, []); - const [operationError, setOperationError] = useState< - TalerErrorDetail | undefined - >(undefined); if (!hook) { return { @@ -74,34 +72,25 @@ export function useComponentState({ contractTerms?.purse_expiration; async function accept(): Promise { - try { - const resp = await api.wallet.call( - WalletApiOperation.AcceptPeerPushPayment, - { - peerPushPaymentIncomingId, - }, - ); - onSuccess(resp.transactionId); - } catch (e) { - if (e instanceof TalerError) { - setOperationError(e.errorDetail); - } - console.error(e); - throw Error("error trying to accept"); - } + const resp = await api.wallet.call( + WalletApiOperation.AcceptPeerPushPayment, + { + peerPushPaymentIncomingId, + }, + ); + onSuccess(resp.transactionId); } return { status: "ready", amount: Amounts.parseOrThrow(amount), error: undefined, accept: { - onClick: accept, + onClick: pushAlertOnError(accept), }, summary, expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : undefined, cancel: { - onClick: onClose, + onClick: pushAlertOnError(onClose), }, - operationError, }; } diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/stories.tsx b/packages/taler-wallet-webextension/src/cta/TransferPickup/stories.tsx index 250e99ae1..48f006127 100644 --- a/packages/taler-wallet-webextension/src/cta/TransferPickup/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/stories.tsx @@ -19,14 +19,14 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ReadyView } from "./views.js"; export default { title: "transfer pickup", }; -export const Ready = createExample(ReadyView, { +export const Ready = tests.createExample(ReadyView, { amount: { currency: "ARS", value: 1, diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx b/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx index d2402db3a..25f5cdf52 100644 --- a/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx +++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/views.tsx @@ -30,8 +30,6 @@ export function ReadyView({ summary, expiration, amount, - cancel, - operationError, }: State.Ready): VNode { const { i18n } = useTranslationContext(); return ( @@ -40,12 +38,6 @@ export function ReadyView({ Digital cash transfer - {operationError && ( - - )}

{summary}} /> } /> diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts index 18c467aae..5f149064c 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts @@ -23,7 +23,7 @@ import { } from "@gnu-taler/taler-util"; import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -205,6 +205,7 @@ function exchangeSelectionState( return () => { const { i18n } = useTranslationContext(); + const { pushAlertOnError } = useAlertContext(); const [ageRestricted, setAgeRestricted] = useState(0); const currentExchange = selectedExchange.selected; const tosNeedToBeAccepted = @@ -299,7 +300,9 @@ function exchangeSelectionState( ? { list: ageRestrictionOptions, value: String(ageRestricted), - onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)), + onChange: pushAlertOnError(async (v: string) => + setAgeRestricted(parseInt(v, 10)), + ), } : undefined; @@ -317,7 +320,7 @@ function exchangeSelectionState( onClick: doingWithdraw || tosNeedToBeAccepted ? undefined - : doWithdrawAndCheckError, + : pushAlertOnError(doWithdrawAndCheckError), error: withdrawError, }, onTosUpdate, diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx index a8031223b..cde03dd8f 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx @@ -20,7 +20,8 @@ */ import { ExchangeListItem } from "@gnu-taler/taler-util"; -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; +import { nullFunction } from "../../mui/handlers.js"; // import { TermsState } from "../../utils/index.js"; import { SuccessView } from "./views.js"; @@ -28,28 +29,6 @@ export default { title: "withdraw", }; -const exchangeList = { - "exchange.demo.taler.net": "http://exchange.demo.taler.net (USD)", - "exchange.test.taler.net": "http://exchange.test.taler.net (KUDOS)", -}; - -const nullHandler = { - onClick: async (): Promise => { - null; - }, -}; - -// const normalTosState = { -// terms: { -// status: "accepted", -// version: "", -// } as TermsState, -// onAccept: () => null, -// onReview: () => null, -// reviewed: false, -// reviewing: false, -// }; - const ageRestrictionOptions: Record = "6:12:18" .split(":") .reduce((p, c) => ({ ...p, [c]: `under ${c}` }), {}); @@ -61,7 +40,7 @@ const ageRestrictionSelectField = { value: "0", }; -export const TermsOfServiceNotYetLoaded = createExample(SuccessView, { +export const TermsOfServiceNotYetLoaded = tests.createExample(SuccessView, { error: undefined, status: "success", chosenAmount: { @@ -69,7 +48,7 @@ export const TermsOfServiceNotYetLoaded = createExample(SuccessView, { value: 2, fraction: 10000000, }, - doWithdrawal: nullHandler, + doWithdrawal: { onClick: nullFunction }, currentExchange: { exchangeBaseUrl: "https://exchange.demo.taler.net", tos: {}, @@ -87,7 +66,7 @@ export const TermsOfServiceNotYetLoaded = createExample(SuccessView, { }, }); -export const WithSomeFee = createExample(SuccessView, { +export const WithSomeFee = tests.createExample(SuccessView, { error: undefined, status: "success", chosenAmount: { @@ -95,7 +74,7 @@ export const WithSomeFee = createExample(SuccessView, { value: 2, fraction: 10000000, }, - doWithdrawal: nullHandler, + doWithdrawal: { onClick: nullFunction }, currentExchange: { exchangeBaseUrl: "https://exchange.demo.taler.net", tos: {}, @@ -113,7 +92,7 @@ export const WithSomeFee = createExample(SuccessView, { doSelectExchange: {}, }); -export const WithoutFee = createExample(SuccessView, { +export const WithoutFee = tests.createExample(SuccessView, { error: undefined, status: "success", chosenAmount: { @@ -121,7 +100,7 @@ export const WithoutFee = createExample(SuccessView, { value: 2, fraction: 0, }, - doWithdrawal: nullHandler, + doWithdrawal: { onClick: nullFunction }, currentExchange: { exchangeBaseUrl: "https://exchange.demo.taler.net", tos: {}, @@ -139,7 +118,7 @@ export const WithoutFee = createExample(SuccessView, { }, }); -export const EditExchangeUntouched = createExample(SuccessView, { +export const EditExchangeUntouched = tests.createExample(SuccessView, { error: undefined, status: "success", chosenAmount: { @@ -147,7 +126,7 @@ export const EditExchangeUntouched = createExample(SuccessView, { value: 2, fraction: 10000000, }, - doWithdrawal: nullHandler, + doWithdrawal: { onClick: nullFunction }, currentExchange: { exchangeBaseUrl: "https://exchange.demo.taler.net", tos: {}, @@ -165,7 +144,7 @@ export const EditExchangeUntouched = createExample(SuccessView, { }, }); -export const EditExchangeModified = createExample(SuccessView, { +export const EditExchangeModified = tests.createExample(SuccessView, { error: undefined, status: "success", chosenAmount: { @@ -173,7 +152,7 @@ export const EditExchangeModified = createExample(SuccessView, { value: 2, fraction: 10000000, }, - doWithdrawal: nullHandler, + doWithdrawal: { onClick: nullFunction }, currentExchange: { exchangeBaseUrl: "https://exchange.demo.taler.net", tos: {}, @@ -191,7 +170,7 @@ export const EditExchangeModified = createExample(SuccessView, { }, }); -export const WithAgeRestriction = createExample(SuccessView, { +export const WithAgeRestriction = tests.createExample(SuccessView, { error: undefined, status: "success", ageRestriction: ageRestrictionSelectField, @@ -201,7 +180,7 @@ export const WithAgeRestriction = createExample(SuccessView, { fraction: 10000000, }, doSelectExchange: {}, - doWithdrawal: nullHandler, + doWithdrawal: { onClick: nullFunction }, currentExchange: { exchangeBaseUrl: "https://exchange.demo.taler.net", tos: {}, diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts index 2caa50dca..5a6200844 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts @@ -28,7 +28,6 @@ import { import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; import { tests } from "../../../../web-util/src/index.browser.js"; -import { mountHook } from "../../test-utils.js"; import { createWalletApiMock } from "../../test-utils.js"; import { useComponentStateFromURI } from "./state.js"; @@ -88,7 +87,7 @@ describe("Withdraw CTA states", () => { if (!error) expect.fail(); // if (!error.hasError) expect.fail(); // if (error.operational) expect.fail(); - expect(error.cause?.message).eq("ERROR_NO-URI-FOR-WITHDRAWAL"); + expect(error.description).eq("ERROR_NO-URI-FOR-WITHDRAWAL"); }, ], TestingContext, diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx index cf87b35bb..1cc87547e 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx @@ -18,7 +18,6 @@ import { ExchangeTosStatus } from "@gnu-taler/taler-util"; import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { Amount } from "../../components/Amount.js"; -import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js"; import { Part } from "../../components/Part.js"; import { QR } from "../../components/QR.js"; import { SelectList } from "../../components/SelectList.js"; @@ -36,13 +35,6 @@ export function SuccessView(state: State.Success): VNode { state.currentExchange.tosStatus === ExchangeTosStatus.Accepted; return ( - {state.doWithdrawal.error && ( - - )} -
{ hasError: false; @@ -74,12 +74,12 @@ export function useAsyncAsHook( message: e.message, details: e.errorDetail, }); - } else if (e instanceof WalletError) { + } else if (e instanceof BackgroundError) { setHookResponse({ hasError: true, type: "taler", message: e.message, - details: e.errorDetail.errorDetail, + details: e.errorDetail, }); } else if (e instanceof Error) { setHookResponse({ diff --git a/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts index cf2fd880e..e0a34f690 100644 --- a/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts +++ b/packages/taler-wallet-webextension/src/hooks/useAutoOpenPermissions.ts @@ -14,23 +14,41 @@ GNU Taler; see the file COPYING. If not, see */ -import { TalerError } from "@gnu-taler/taler-wallet-core"; import { useEffect, useState } from "preact/hooks"; +import { useAlertContext } from "../context/alert.js"; import { useBackendContext } from "../context/backend.js"; import { ToggleHandler } from "../mui/handlers.js"; import { platform } from "../platform/foreground.js"; export function useAutoOpenPermissions(): ToggleHandler { const api = useBackendContext(); + const { pushAlertOnError } = useAlertContext(); const [enabled, setEnabled] = useState(false); - const [error, setError] = useState(); - const toggle = async (): Promise => { - return handleAutoOpenPerm(enabled, setEnabled, api.background).catch( - (e) => { - setError(TalerError.fromException(e)); - }, - ); - }; + + async function handleAutoOpenPerm(): Promise { + if (!enabled) { + // We set permissions here, since apparently FF wants this to be done + // as the result of an input event ... + let granted: boolean; + try { + granted = await platform.getPermissionsApi().requestHostPermissions(); + } catch (lastError) { + setEnabled(false); + throw lastError; + } + const res = await api.background.call("toggleHeaderListener", granted); + setEnabled(res.newValue); + } else { + try { + await api.background + .call("toggleHeaderListener", false) + .then((r) => setEnabled(r.newValue)); + } catch (e) { + console.log(e); + } + } + return; + } useEffect(() => { async function getValue(): Promise { @@ -42,40 +60,11 @@ export function useAutoOpenPermissions(): ToggleHandler { } getValue(); }, []); + return { value: enabled, button: { - onClick: toggle, - error, + onClick: pushAlertOnError(handleAutoOpenPerm), }, }; } - -async function handleAutoOpenPerm( - isEnabled: boolean, - onChange: (value: boolean) => void, - background: ReturnType["background"], -): Promise { - if (!isEnabled) { - // We set permissions here, since apparently FF wants this to be done - // as the result of an input event ... - let granted: boolean; - try { - granted = await platform.getPermissionsApi().requestHostPermissions(); - } catch (lastError) { - onChange(false); - throw lastError; - } - const res = await background.call("toggleHeaderListener", granted); - onChange(res.newValue); - } else { - try { - await background - .call("toggleHeaderListener", false) - .then((r) => onChange(r.newValue)); - } catch (e) { - console.log(e); - } - } - return; -} diff --git a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts index 0f035d0f2..25757f473 100644 --- a/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts +++ b/packages/taler-wallet-webextension/src/hooks/useClipboardPermissions.ts @@ -14,24 +14,42 @@ GNU Taler; see the file COPYING. If not, see */ -import { TalerError } from "@gnu-taler/taler-wallet-core"; import { useEffect, useState } from "preact/hooks"; +import { useAlertContext } from "../context/alert.js"; import { useBackendContext } from "../context/backend.js"; import { ToggleHandler } from "../mui/handlers.js"; import { platform } from "../platform/foreground.js"; export function useClipboardPermissions(): ToggleHandler { const [enabled, setEnabled] = useState(false); - const [error, setError] = useState(); const api = useBackendContext(); + const { pushAlertOnError } = useAlertContext(); - const toggle = async (): Promise => { - return handleClipboardPerm(enabled, setEnabled, api.background).catch( - (e) => { - setError(TalerError.fromException(e)); - }, - ); - }; + async function handleClipboardPerm(): Promise { + if (!enabled) { + // We set permissions here, since apparently FF wants this to be done + // as the result of an input event ... + let granted: boolean; + try { + granted = await platform + .getPermissionsApi() + .requestClipboardPermissions(); + } catch (lastError) { + setEnabled(false); + throw lastError; + } + setEnabled(granted); + } else { + try { + await api.background + .call("toggleHeaderListener", false) + .then((r) => setEnabled(r.newValue)); + } catch (e) { + console.log(e); + } + } + return; + } useEffect(() => { async function getValue(): Promise { @@ -47,38 +65,7 @@ export function useClipboardPermissions(): ToggleHandler { return { value: enabled, button: { - onClick: toggle, - error, + onClick: pushAlertOnError(handleClipboardPerm), }, }; } - -async function handleClipboardPerm( - isEnabled: boolean, - onChange: (value: boolean) => void, - background: ReturnType["background"], -): Promise { - if (!isEnabled) { - // We set permissions here, since apparently FF wants this to be done - // as the result of an input event ... - let granted: boolean; - try { - granted = await platform - .getPermissionsApi() - .requestClipboardPermissions(); - } catch (lastError) { - onChange(false); - throw lastError; - } - onChange(granted); - } else { - try { - await background - .call("toggleHeaderListener", false) - .then((r) => onChange(r.newValue)); - } catch (e) { - console.log(e); - } - } - return; -} diff --git a/packages/taler-wallet-webextension/src/hooks/useSelectedExchange.ts b/packages/taler-wallet-webextension/src/hooks/useSelectedExchange.ts index c04dcce84..6ceae2d47 100644 --- a/packages/taler-wallet-webextension/src/hooks/useSelectedExchange.ts +++ b/packages/taler-wallet-webextension/src/hooks/useSelectedExchange.ts @@ -16,6 +16,7 @@ import { ExchangeListItem } from "@gnu-taler/taler-util"; import { useState } from "preact/hooks"; +import { useAlertContext } from "../context/alert.js"; import { ButtonHandler } from "../mui/handlers.js"; type State = State.Ready | State.NoExchange | State.Selecting; @@ -59,6 +60,7 @@ export function useSelectedExchange({ const [selectedExchange, setSelectedExchange] = useState( undefined, ); + const { pushAlertOnError } = useAlertContext(); if (!list.length) { return { @@ -105,7 +107,7 @@ export function useSelectedExchange({ return { status: "ready", doSelect: { - onClick: async () => setIsSelecting(true), + onClick: pushAlertOnError(async () => setIsSelecting(true)), }, selected: found, }; @@ -118,7 +120,7 @@ export function useSelectedExchange({ return { status: "ready", doSelect: { - onClick: async () => setIsSelecting(true), + onClick: pushAlertOnError(async () => setIsSelecting(true)), }, selected: found, }; @@ -127,7 +129,7 @@ export function useSelectedExchange({ return { status: "ready", doSelect: { - onClick: async () => setIsSelecting(true), + onClick: pushAlertOnError(async () => setIsSelecting(true)), }, selected: listCurrency[0], }; diff --git a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts index 8aabb8adf..e70f7f9be 100644 --- a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts +++ b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts @@ -13,11 +13,11 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ -import { useTalerActionURL } from "./useTalerActionURL.js"; -import { mountHook } from "../test-utils.js"; -import { IoCProviderForTesting } from "../context/iocContext.js"; -import { h, VNode } from "preact"; import { expect } from "chai"; +import { h, VNode } from "preact"; +import { IoCProviderForTesting } from "../context/iocContext.js"; +import { useTalerActionURL } from "./useTalerActionURL.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; describe("useTalerActionURL hook", () => { it("should be set url to undefined when dismiss", async () => { @@ -31,32 +31,28 @@ describe("useTalerActionURL hook", () => { }); }; - const { pullLastResultOrThrow, waitForStateUpdate, assertNoPendingUpdate } = - mountHook(useTalerActionURL, ctx); + const hookBehavior = await tests.hookBehaveLikeThis( + useTalerActionURL, + {}, + [ + ([url]) => { + expect(url).undefined; + }, + ([url, setDismissed]) => { + expect(url).deep.equals({ + location: "clipboard", + uri: "qwe", + }); + setDismissed(true); + }, + ([url]) => { + if (url !== undefined) throw Error("invalid"); + expect(url).undefined; + }, + ], + ctx, + ); - { - const [url] = pullLastResultOrThrow(); - expect(url).undefined; - } - - expect(await waitForStateUpdate()).true; - - { - const [url, setDismissed] = pullLastResultOrThrow(); - expect(url).deep.equals({ - location: "clipboard", - uri: "qwe", - }); - setDismissed(true); - } - - expect(await waitForStateUpdate()).true; - - { - const [url] = pullLastResultOrThrow(); - if (url !== undefined) throw Error("invalid"); - expect(url).undefined; - } - await assertNoPendingUpdate(); + expect(hookBehavior).deep.equal({ result: "ok" }); }); }); diff --git a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts b/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts index 6ae55da61..db7effe96 100644 --- a/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts +++ b/packages/taler-wallet-webextension/src/hooks/useWalletDevMode.ts @@ -14,21 +14,28 @@ GNU Taler; see the file COPYING. If not, see */ -import { useState, useEffect } from "preact/hooks"; -import { ToggleHandler } from "../mui/handlers.js"; -import { TalerError, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { useEffect, useState } from "preact/hooks"; import { useBackendContext } from "../context/backend.js"; -export function useWalletDevMode(): ToggleHandler { - const [enabled, setEnabled] = useState(undefined); - const [error, setError] = useState(); - const api = useBackendContext(); +type Result = { + value: boolean | undefined; + toggle: () => Promise; +}; - const toggle = async (): Promise => { - return handleOpen(enabled, setEnabled, api).catch((e) => { - setError(TalerError.fromException(e)); +export function useWalletDevMode(): Result { + const [enabled, setEnabled] = useState(undefined); + const api = useBackendContext(); + // const { pushAlertOnError } = useAlertContext(); + + async function handleOpen(): Promise { + const nextValue = !enabled; + await api.wallet.call(WalletApiOperation.SetDevMode, { + devModeEnabled: nextValue, }); - }; + setEnabled(nextValue); + return; + } useEffect(() => { async function getValue(): Promise { @@ -37,24 +44,9 @@ export function useWalletDevMode(): ToggleHandler { } getValue(); }, []); + return { value: enabled, - button: { - onClick: enabled === undefined ? undefined : toggle, - error, - }, + toggle: handleOpen, }; } - -async function handleOpen( - currentValue: undefined | boolean, - onChange: (value: boolean) => void, - api: ReturnType, -): Promise { - const nextValue = !currentValue; - await api.wallet.call(WalletApiOperation.SetDevMode, { - devModeEnabled: nextValue, - }); - onChange(nextValue); - return; -} diff --git a/packages/taler-wallet-webextension/src/mui/handlers.ts b/packages/taler-wallet-webextension/src/mui/handlers.ts index 655fceef9..61786742f 100644 --- a/packages/taler-wallet-webextension/src/mui/handlers.ts +++ b/packages/taler-wallet-webextension/src/mui/handlers.ts @@ -14,23 +14,51 @@ GNU Taler; see the file COPYING. If not, see */ import { AmountJson } from "@gnu-taler/taler-util"; -import { TalerError } from "@gnu-taler/taler-wallet-core"; export interface TextFieldHandler { - onInput?: (value: string) => Promise; + onInput?: SafeHandler; value: string; error?: string; } export interface AmountFieldHandler { - onInput?: (value: AmountJson) => Promise; + onInput?: SafeHandler; value: AmountJson; error?: string; } +declare const __safe_handler: unique symbol; +export type SafeHandler = { + (req: Req): Promise; + (): Promise; + [__safe_handler]: true; +}; + +export function withSafe( + handler: (p: T) => Promise, + onError: (e: Error) => void, +): SafeHandler { + const sh = async function (p: T): Promise { + try { + await handler(p); + } catch (e) { + if (e instanceof Error) { + onError(e); + } else { + onError(new Error(String(e))); + } + } + }; + return sh as SafeHandler; +} + +export const nullFunction = async function (): Promise { + //do nothing +} as SafeHandler; + export interface ButtonHandler { - onClick?: () => Promise; - error?: TalerError; + onClick?: SafeHandler; + // error?: TalerError; } export interface ToggleHandler { @@ -39,7 +67,7 @@ export interface ToggleHandler { } export interface SelectFieldHandler { - onChange?: (value: string) => Promise; + onChange?: SafeHandler; error?: string; value: string; isDirty?: boolean; diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx b/packages/taler-wallet-webextension/src/popup/Application.tsx index 13ce71974..c9f98c0fb 100644 --- a/packages/taler-wallet-webextension/src/popup/Application.tsx +++ b/packages/taler-wallet-webextension/src/popup/Application.tsx @@ -23,10 +23,10 @@ import { createHashHistory } from "history"; import { ComponentChildren, Fragment, h, VNode } from "preact"; import Router, { route, Route } from "preact-router"; -import { Match } from "preact-router/match"; 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 { @@ -34,7 +34,7 @@ import { useTranslationContext, } from "../context/translation.js"; import { useTalerActionURL } from "../hooks/useTalerActionURL.js"; -import { PopupNavBarOptions, Pages, PopupNavBar } from "../NavigationBar.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"; @@ -219,7 +219,9 @@ function PopupTemplate({ ) : undefined} - {children} + + {children} + ); } diff --git a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx index 8f3762c29..0fe9e7b49 100644 --- a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx @@ -19,19 +19,19 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { BalanceView as TestedComponent } from "./BalancePage.js"; export default { title: "balance", }; -export const EmptyBalance = createExample(TestedComponent, { +export const EmptyBalance = tests.createExample(TestedComponent, { balances: [], goToWalletManualWithdraw: {}, }); -export const SomeCoins = createExample(TestedComponent, { +export const SomeCoins = tests.createExample(TestedComponent, { balances: [ { available: "USD:10.5", @@ -45,7 +45,7 @@ export const SomeCoins = createExample(TestedComponent, { goToWalletManualWithdraw: {}, }); -export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, { +export const SomeCoinsInTreeCurrencies = tests.createExample(TestedComponent, { balances: [ { available: "EUR:1", @@ -73,7 +73,7 @@ export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, { addAction: {}, }); -export const NoCoinsInTreeCurrencies = createExample(TestedComponent, { +export const NoCoinsInTreeCurrencies = tests.createExample(TestedComponent, { balances: [ { available: "EUR:3", @@ -101,7 +101,7 @@ export const NoCoinsInTreeCurrencies = createExample(TestedComponent, { addAction: {}, }); -export const SomeCoinsInFiveCurrencies = createExample(TestedComponent, { +export const SomeCoinsInFiveCurrencies = tests.createExample(TestedComponent, { balances: [ { available: "USD:0", diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx index 96f0f6dd9..87767d008 100644 --- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx @@ -22,7 +22,11 @@ import { BalanceTable } from "../components/BalanceTable.js"; import { ErrorAlertView } from "../components/CurrentAlerts.js"; import { Loading } from "../components/Loading.js"; import { MultiActionButton } from "../components/MultiActionButton.js"; -import { alertFromError, ErrorAlert } from "../context/alert.js"; +import { + alertFromError, + ErrorAlert, + useAlertContext, +} from "../context/alert.js"; import { useBackendContext } from "../context/backend.js"; import { useTranslationContext } from "../context/translation.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; @@ -75,6 +79,7 @@ function useComponentState({ }: Props): State { const api = useBackendContext(); const { i18n } = useTranslationContext(); + const { pushAlertOnError } = useAlertContext(); const [addingAction, setAddingAction] = useState(false); const state = useAsyncAsHook(() => api.wallet.call(WalletApiOperation.GetBalances, {}), @@ -104,7 +109,7 @@ function useComponentState({ status: "action", error: undefined, cancel: { - onClick: async () => setAddingAction(false), + onClick: pushAlertOnError(async () => setAddingAction(false)), }, }; } @@ -113,10 +118,10 @@ function useComponentState({ error: undefined, balances: state.response.balances, addAction: { - onClick: async () => setAddingAction(true), + onClick: pushAlertOnError(async () => setAddingAction(true)), }, goToWalletManualWithdraw: { - onClick: goToWalletManualWithdraw, + onClick: pushAlertOnError(goToWalletManualWithdraw), }, goToWalletDeposit, goToWalletHistory, diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx index 00293a690..e928cb538 100644 --- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx @@ -19,33 +19,33 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { TalerActionFound as TestedComponent } from "./TalerActionFound.js"; export default { title: "TalerActionFound", }; -export const PayAction = createExample(TestedComponent, { +export const PayAction = tests.createExample(TestedComponent, { url: "taler://pay/something", }); -export const WithdrawalAction = createExample(TestedComponent, { +export const WithdrawalAction = tests.createExample(TestedComponent, { url: "taler://withdraw/something", }); -export const TipAction = createExample(TestedComponent, { +export const TipAction = tests.createExample(TestedComponent, { url: "taler://tip/something", }); -export const NotifyAction = createExample(TestedComponent, { +export const NotifyAction = tests.createExample(TestedComponent, { url: "taler://notify-reserve/something", }); -export const RefundAction = createExample(TestedComponent, { +export const RefundAction = tests.createExample(TestedComponent, { url: "taler://refund/something", }); -export const InvalidAction = createExample(TestedComponent, { +export const InvalidAction = tests.createExample(TestedComponent, { url: "taler://something/asd", }); diff --git a/packages/taler-wallet-webextension/src/stories.test.ts b/packages/taler-wallet-webextension/src/stories.test.ts index 47061282d..f02aa5cba 100644 --- a/packages/taler-wallet-webextension/src/stories.test.ts +++ b/packages/taler-wallet-webextension/src/stories.test.ts @@ -20,15 +20,17 @@ */ import { setupI18n } from "@gnu-taler/taler-util"; import { parseGroupImport } from "@gnu-taler/web-util/lib/index.browser"; -import { setupPlatform } from "./platform/foreground.js"; import chromeAPI from "./platform/chrome.js"; -import { renderNodeOrBrowser } from "./test-utils.js"; +import { setupPlatform } from "./platform/foreground.js"; import * as components from "./components/index.stories.js"; import * as cta from "./cta/index.stories.js"; import * as mui from "./mui/index.stories.js"; import * as popup from "./popup/index.stories.js"; import * as wallet from "./wallet/index.stories.js"; +import { renderNodeOrBrowser } from "./test-utils.js"; +import { h, VNode } from "preact"; +import { AlertProvider } from "./context/alert.js"; setupI18n("en", { en: {} }); setupPlatform(chromeAPI); @@ -41,10 +43,15 @@ describe("All the examples:", () => { describe(`Component ${component.name}:`, () => { component.examples.forEach((example) => { it(`should render example: ${example.name}`, () => { - renderNodeOrBrowser( - example.render.component, - example.render.props, - ); + function C(): VNode { + const B = h(example.render.component, example.render.props); + //FIXME: + //some components push the alter in the UI function + //that's not correct, should be moved into the sate function + // until then, we ran the tests with the alert provider + return h(AlertProvider, { children: B }, B); + } + renderNodeOrBrowser(C, {}); }); }); }); diff --git a/packages/taler-wallet-webextension/src/test-utils.ts b/packages/taler-wallet-webextension/src/test-utils.ts index 7e7ddd88d..085055a7e 100644 --- a/packages/taler-wallet-webextension/src/test-utils.ts +++ b/packages/taler-wallet-webextension/src/test-utils.ts @@ -31,8 +31,10 @@ import { VNode, } from "preact"; import { render as renderToString } from "preact-render-to-string"; +import { AlertProvider } from "./context/alert.js"; import { BackendProvider } from "./context/backend.js"; import { TranslationProvider } from "./context/translation.js"; +import { nullFunction } from "./mui/handlers.js"; import { BackgroundApiClient, wxApi } from "./wxApi.js"; // When doing tests we want the requestAnimationFrame to be as fast as possible. @@ -218,7 +220,7 @@ export function mountHook( }; } -export const nullFunction: any = () => null; +// export const nullFunction: any = () => null; interface MockHandler { addWalletCallResponse( @@ -365,6 +367,7 @@ export function createWalletApiMock(): { children: ComponentChildren; }): VNode { let children = _cs; + children = create(AlertProvider, { children }, children); children = create(TranslationProvider, { children }, children); return create( BackendProvider, diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts index 4ec4c0ffe..e0b79e060 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/index.ts @@ -53,7 +53,7 @@ export namespace State { export interface ConfirmProvider { status: "confirm-provider"; - error: undefined | TalerErrorDetail; + error: undefined; url: string; provider: SyncTermsOfServiceResponse; tos: ToggleHandler; diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts index 1b30ed0cd..cf35abac7 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/state.ts @@ -14,16 +14,13 @@ GNU Taler; see the file COPYING. If not, see */ -import { - canonicalizeBaseUrl, - Codec, - TalerErrorDetail, -} from "@gnu-taler/taler-util"; +import { canonicalizeBaseUrl, Codec } from "@gnu-taler/taler-util"; import { codecForSyncTermsOfServiceResponse, WalletApiOperation, } from "@gnu-taler/taler-wallet-core"; import { useEffect, useState } from "preact/hooks"; +import { useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { assertUnreachable } from "../../utils/index.js"; import { Props, State } from "./index.js"; @@ -152,17 +149,15 @@ export function useComponentState({ const [url, setHost] = useState(); const [name, setName] = useState(); const [tos, setTos] = useState(false); + const { pushAlertOnError } = useAlertContext(); const urlState = useUrlState( url, "config", codecForSyncTermsOfServiceResponse(), ); - const [operationError, setOperationError] = useState< - TalerErrorDetail | undefined - >(); const [showConfirm, setShowConfirm] = useState(false); - async function addBackupProvider() { + async function addBackupProvider(): Promise { if (!url || !name) return; const resp = await api.wallet.call(WalletApiOperation.AddBackupProvider, { @@ -178,8 +173,6 @@ export function useComponentState({ } else { return onComplete(url); } - case "error": - return setOperationError(resp.error); case "ok": return onComplete(url); default: @@ -190,18 +183,18 @@ export function useComponentState({ if (showConfirm && urlState && urlState.status === "ok") { return { status: "confirm-provider", - error: operationError, + error: undefined, onAccept: { - onClick: !tos ? undefined : addBackupProvider, + onClick: !tos ? undefined : pushAlertOnError(addBackupProvider), }, onCancel: { - onClick: onBack, + onClick: pushAlertOnError(onBack), }, provider: urlState.result, tos: { value: tos, button: { - onClick: async () => setTos(!tos), + onClick: pushAlertOnError(async () => setTos(!tos)), }, }, url: url ?? "", @@ -213,25 +206,25 @@ export function useComponentState({ error: undefined, name: { value: name || "", - onInput: async (e) => setName(e), + onInput: pushAlertOnError(async (e) => setName(e)), error: name === undefined ? undefined : !name ? "Can't be empty" : undefined, }, onCancel: { - onClick: onBack, + onClick: pushAlertOnError(onBack), }, onConfirm: { onClick: !urlState || urlState.status !== "ok" || !name ? undefined - : async () => { + : pushAlertOnError(async () => { setShowConfirm(true); - }, + }), }, urlOk: urlState?.status === "ok", url: { value: url || "", - onInput: async (e) => setHost(e), + onInput: pushAlertOnError(async (e) => setHost(e)), error: errorString(urlState), }, }; diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx index 887ad235e..9d1656ec6 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/stories.tsx @@ -19,14 +19,14 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ConfirmProviderView, SelectProviderView } from "./views.js"; export default { title: "add backup provider", }; -export const DemoService = createExample(ConfirmProviderView, { +export const DemoService = tests.createExample(ConfirmProviderView, { url: "https://sync.demo.taler.net/", provider: { annual_fee: "KUDOS:0.1", @@ -40,7 +40,7 @@ export const DemoService = createExample(ConfirmProviderView, { onCancel: {}, }); -export const FreeService = createExample(ConfirmProviderView, { +export const FreeService = tests.createExample(ConfirmProviderView, { url: "https://sync.taler:9667/", provider: { annual_fee: "ARS:0", @@ -54,14 +54,14 @@ export const FreeService = createExample(ConfirmProviderView, { onCancel: {}, }); -export const Initial = createExample(SelectProviderView, { +export const Initial = tests.createExample(SelectProviderView, { url: { value: "" }, name: { value: "" }, onCancel: {}, onConfirm: {}, }); -export const WithValue = createExample(SelectProviderView, { +export const WithValue = tests.createExample(SelectProviderView, { url: { value: "sync.demo.taler.net", }, @@ -72,7 +72,7 @@ export const WithValue = createExample(SelectProviderView, { onConfirm: {}, }); -export const WithConnectionError = createExample(SelectProviderView, { +export const WithConnectionError = tests.createExample(SelectProviderView, { url: { value: "sync.demo.taler.net", error: "Network error", @@ -84,7 +84,7 @@ export const WithConnectionError = createExample(SelectProviderView, { onConfirm: {}, }); -export const WithClientError = createExample(SelectProviderView, { +export const WithClientError = tests.createExample(SelectProviderView, { url: { value: "sync.demo.taler.net", error: "URL may not be right: (404) Not Found", @@ -96,7 +96,7 @@ export const WithClientError = createExample(SelectProviderView, { onConfirm: {}, }); -export const WithServerError = createExample(SelectProviderView, { +export const WithServerError = tests.createExample(SelectProviderView, { url: { value: "sync.demo.taler.net", error: "Try another server: (500) Internal Server Error", diff --git a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts index 3241a3ab0..a939c9268 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts +++ b/packages/taler-wallet-webextension/src/wallet/AddBackupProvider/test.ts @@ -21,7 +21,8 @@ import { expect } from "chai"; import { tests } from "../../../../web-util/src/index.browser.js"; -import { createWalletApiMock, nullFunction } from "../../test-utils.js"; +import { nullFunction } from "../../mui/handlers.js"; +import { createWalletApiMock } from "../../test-utils.js"; import { Props } from "./index.js"; import { useComponentState } from "./state.js"; diff --git a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx index f5db3825d..8c45ae050 100644 --- a/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/AddNewActionView.stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { AddNewActionView as TestedComponent } from "./AddNewActionView.js"; export default { @@ -30,4 +30,4 @@ export default { }, }; -export const Initial = createExample(TestedComponent, {}); +export const Initial = tests.createExample(TestedComponent, {}); diff --git a/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx index c3a1ea5d6..8ae1a76ce 100644 --- a/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx @@ -25,14 +25,14 @@ import { BackupView as TestedComponent, ShowRecoveryInfo, } from "./BackupPage.js"; -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { TalerProtocolTimestamp } from "@gnu-taler/taler-util"; export default { title: "backup", }; -export const LotOfProviders = createExample(TestedComponent, { +export const LotOfProviders = tests.createExample(TestedComponent, { providers: [ { active: true, @@ -164,7 +164,7 @@ export const LotOfProviders = createExample(TestedComponent, { ], }); -export const OneProvider = createExample(TestedComponent, { +export const OneProvider = tests.createExample(TestedComponent, { providers: [ { active: true, @@ -190,10 +190,10 @@ export const OneProvider = createExample(TestedComponent, { ], }); -export const Empty = createExample(TestedComponent, { +export const Empty = tests.createExample(TestedComponent, { providers: [], }); -export const Recovery = createExample(ShowRecoveryInfo, { +export const Recovery = tests.createExample(ShowRecoveryInfo, { info: "taler://recovery/ASLDKJASLKDJASD", }); diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx index 48c9c9cb1..c3abb570b 100644 --- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx @@ -29,7 +29,7 @@ import { } from "date-fns"; import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; -import { AlertView } from "../components/CurrentAlerts.js"; +import { ErrorAlertView } from "../components/CurrentAlerts.js"; import { Loading } from "../components/Loading.js"; import { QR } from "../components/QR.js"; import { @@ -118,8 +118,8 @@ export function BackupPage({ onAddProvider }: Props): VNode { } if (status.hasError) { return ( - { + onClick: pushAlertOnError(async () => { setAddingAccount(true); - }, + }), }, }; } @@ -221,27 +222,27 @@ export function useComponentState({ currency, amount: { value: amount, - onInput: updateAmount, + onInput: pushAlertOnError(updateAmount), error: amountError, }, onAddAccount: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setAddingAccount(true); - }, + }), }, account: { list: accountMap, value: stringifyPaytoUri(currentAccount), - onChange: updateAccountFromList, + onChange: pushAlertOnError(updateAccountFromList), }, currentAccount, cancelHandler: { - onClick: async () => { + onClick: pushAlertOnError(async () => { onCancel(currency); - }, + }), }, depositHandler: { - onClick: unableToDeposit ? undefined : doSend, + onClick: unableToDeposit ? undefined : pushAlertOnError(doSend), }, totalFee, totalToDeposit, @@ -263,7 +264,7 @@ async function getFeeForAmount( }); } -export function labelForAccountType(id: string) { +export function labelForAccountType(id: string): string { switch (id) { case "": return "Choose one"; diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx index b4d1060eb..99f08477f 100644 --- a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx @@ -19,26 +19,21 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { Amounts, DepositGroupFees } from "@gnu-taler/taler-util"; -import { createExample } from "../../test-utils.js"; -import { labelForAccountType } from "./state.js"; +import { Amounts } from "@gnu-taler/taler-util"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; +import { nullFunction } from "../../mui/handlers.js"; import { ReadyView } from "./views.js"; export default { title: "deposit", }; -// const ac = parsePaytoUri("payto://iban/ES8877998399652238")!; -// const accountMap = createLabelsForBankAccount([ac]); - -export const WithNoAccountForIBAN = createExample(ReadyView, { +export const WithNoAccountForIBAN = tests.createExample(ReadyView, { status: "ready", account: { list: {}, value: "", - onChange: async () => { - null; - }, + onChange: nullFunction, }, currentAccount: { isKnown: true, @@ -49,31 +44,25 @@ export const WithNoAccountForIBAN = createExample(ReadyView, { }, currency: "USD", amount: { - onInput: async () => { - null; - }, + onInput: nullFunction, value: Amounts.parseOrThrow("USD:10"), }, onAddAccount: {}, cancelHandler: {}, depositHandler: { - onClick: async () => { - return; - }, + onClick: nullFunction, }, totalFee: Amounts.zeroOfCurrency("USD"), totalToDeposit: Amounts.parseOrThrow("USD:10"), // onCalculateFee: alwaysReturnFeeToOne, }); -export const WithIBANAccountTypeSelected = createExample(ReadyView, { +export const WithIBANAccountTypeSelected = tests.createExample(ReadyView, { status: "ready", account: { list: { asdlkajsdlk: "asdlkajsdlk", qwerqwer: "qwerqwer" }, value: "asdlkajsdlk", - onChange: async () => { - null; - }, + onChange: nullFunction, }, currentAccount: { isKnown: true, @@ -84,31 +73,25 @@ export const WithIBANAccountTypeSelected = createExample(ReadyView, { }, currency: "USD", amount: { - onInput: async () => { - null; - }, + onInput: nullFunction, value: Amounts.parseOrThrow("USD:10"), }, onAddAccount: {}, cancelHandler: {}, depositHandler: { - onClick: async () => { - return; - }, + onClick: nullFunction, }, totalFee: Amounts.zeroOfCurrency("USD"), totalToDeposit: Amounts.parseOrThrow("USD:10"), // onCalculateFee: alwaysReturnFeeToOne, }); -export const NewBitcoinAccountTypeSelected = createExample(ReadyView, { +export const NewBitcoinAccountTypeSelected = tests.createExample(ReadyView, { status: "ready", account: { list: {}, value: "asdlkajsdlk", - onChange: async () => { - null; - }, + onChange: nullFunction, }, currentAccount: { isKnown: true, @@ -120,16 +103,12 @@ export const NewBitcoinAccountTypeSelected = createExample(ReadyView, { onAddAccount: {}, currency: "USD", amount: { - onInput: async () => { - null; - }, + onInput: nullFunction, value: Amounts.parseOrThrow("USD:10"), }, cancelHandler: {}, depositHandler: { - onClick: async () => { - return; - }, + onClick: nullFunction, }, totalFee: Amounts.zeroOfCurrency("USD"), totalToDeposit: Amounts.parseOrThrow("USD:10"), diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts index b222709a7..0054ab5af 100644 --- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts +++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts @@ -28,7 +28,8 @@ import { import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; import { tests } from "../../../../web-util/src/index.browser.js"; -import { createWalletApiMock, nullFunction } from "../../test-utils.js"; +import { nullFunction } from "../../mui/handlers.js"; +import { createWalletApiMock } from "../../test-utils.js"; import { useComponentState } from "./state.js"; diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts index 1fe324c5a..d5015ae1d 100644 --- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts +++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/state.ts @@ -17,7 +17,7 @@ import { Amounts } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -26,6 +26,7 @@ import { Contact, Props, State } from "./index.js"; export function useComponentState(props: Props): RecursiveState { const api = useBackendContext(); + const { pushAlertOnError } = useAlertContext(); const parsedInitialAmount = !props.amount ? undefined : Amounts.parse(props.amount); @@ -108,26 +109,26 @@ export function useComponentState(props: Props): RecursiveState { error: undefined, previous, selectCurrency: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setAmount(undefined); - }, + }), }, goToBank: { onClick: invalid ? undefined - : async () => { + : pushAlertOnError(async () => { props.goToWalletBankDeposit(currencyAndAmount); - }, + }), }, goToWallet: { onClick: invalid ? undefined - : async () => { + : pushAlertOnError(async () => { props.goToWalletWalletSend(currencyAndAmount); - }, + }), }, amountHandler: { - onInput: async (s) => setAmount(s), + onInput: pushAlertOnError(async (s) => setAmount(s)), value: amount, }, type: props.type, @@ -138,26 +139,26 @@ export function useComponentState(props: Props): RecursiveState { error: undefined, previous, selectCurrency: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setAmount(undefined); - }, + }), }, goToBank: { onClick: invalid ? undefined - : async () => { + : pushAlertOnError(async () => { props.goToWalletManualWithdraw(currencyAndAmount); - }, + }), }, goToWallet: { onClick: invalid ? undefined - : async () => { + : pushAlertOnError(async () => { props.goToWalletWalletInvoice(currencyAndAmount); - }, + }), }, amountHandler: { - onInput: async (s) => setAmount(s), + onInput: pushAlertOnError(async (s) => setAmount(s)), value: amount, }, type: props.type, diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx index ffec8ba36..111f47776 100644 --- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/stories.tsx @@ -19,14 +19,14 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ReadyView, SelectCurrencyView } from "./views.js"; export default { title: "destination", }; -export const GetCash = createExample(ReadyView, { +export const GetCash = tests.createExample(ReadyView, { amountHandler: { value: { currency: "EUR", @@ -40,7 +40,7 @@ export const GetCash = createExample(ReadyView, { selectCurrency: {}, type: "get", }); -export const SendCash = createExample(ReadyView, { +export const SendCash = tests.createExample(ReadyView, { amountHandler: { value: { currency: "EUR", @@ -55,7 +55,7 @@ export const SendCash = createExample(ReadyView, { type: "send", }); -export const SelectCurrency = createExample(SelectCurrencyView, { +export const SelectCurrency = tests.createExample(SelectCurrencyView, { currencies: { "": "Select a currency", USD: "USD", diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts index cc511ce65..b079ef0e8 100644 --- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts +++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/test.ts @@ -28,7 +28,8 @@ import { import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { expect } from "chai"; import { tests } from "../../../../web-util/src/index.browser.js"; -import { createWalletApiMock, nullFunction } from "../../test-utils.js"; +import { nullFunction } from "../../mui/handlers.js"; +import { createWalletApiMock } from "../../test-utils.js"; import { useComponentState } from "./state.js"; const exchangeArs: ExchangeListItem = { diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.stories.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.stories.tsx index d9a5a8fd7..d4ccb60e6 100644 --- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.stories.tsx @@ -20,7 +20,7 @@ */ import { PendingTaskType } from "@gnu-taler/taler-wallet-core"; -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { View as TestedComponent } from "./DeveloperPage.js"; export default { @@ -31,7 +31,7 @@ export default { }, }; -export const AllOff = createExample(TestedComponent, { +export const AllOff = tests.createExample(TestedComponent, { onDownloadDatabase: async () => "this is the content of the database", operations: [ { diff --git a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/stories.tsx b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/stories.tsx index 696e424c4..e157e6e6f 100644 --- a/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/EmptyComponentExample/stories.tsx @@ -19,11 +19,11 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ReadyView } from "./views.js"; export default { title: "example", }; -export const Ready = createExample(ReadyView, {}); +export const Ready = tests.createExample(ReadyView, {}); diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx index 8fbecfc4c..216e50385 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeAddConfirm.stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ExchangeAddConfirmPage as TestedComponent } from "./ExchangeAddConfirm.js"; export default { @@ -32,14 +32,14 @@ export default { }, }; -export const TermsNotFound = createExample(TestedComponent, { +export const TermsNotFound = tests.createExample(TestedComponent, { url: "https://exchange.demo.taler.net/", }); -export const NewTerms = createExample(TestedComponent, { +export const NewTerms = tests.createExample(TestedComponent, { url: "https://exchange.demo.taler.net/", }); -export const TermsChanged = createExample(TestedComponent, { +export const TermsChanged = tests.createExample(TestedComponent, { url: "https://exchange.demo.taler.net/", }); diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx index cd86ad8c6..914ca8ae4 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeAddSetUrl.stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { queryToSlashKeys } from "../utils/index.js"; import { ExchangeSetUrlPage as TestedComponent } from "./ExchangeSetUrl.js"; @@ -27,17 +27,17 @@ export default { title: "exchange add set url", }; -export const ExpectedUSD = createExample(TestedComponent, { +export const ExpectedUSD = tests.createExample(TestedComponent, { expectedCurrency: "USD", onVerify: queryToSlashKeys, }); -export const ExpectedKUDOS = createExample(TestedComponent, { +export const ExpectedKUDOS = tests.createExample(TestedComponent, { expectedCurrency: "KUDOS", onVerify: queryToSlashKeys, }); -export const InitialState = createExample(TestedComponent, { +export const InitialState = tests.createExample(TestedComponent, { onVerify: queryToSlashKeys, }); @@ -55,7 +55,7 @@ const knownExchanges = [ }, ]; -export const WithDemoAsKnownExchange = createExample(TestedComponent, { +export const WithDemoAsKnownExchange = tests.createExample(TestedComponent, { onVerify: async (url) => { const found = knownExchanges.findIndex((e) => e.exchangeBaseUrl === url) !== -1; diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts index cfb32cbbb..7ad11e67c 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts @@ -20,7 +20,7 @@ import { WalletApiOperation, } from "@gnu-taler/taler-wallet-core"; import { useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -33,6 +33,7 @@ export function useComponentState({ currentExchange, }: Props): State { const api = useBackendContext(); + const { pushAlertOnError } = useAlertContext(); const { i18n } = useTranslationContext(); const initialValue = exchanges.findIndex( (e) => e.exchangeBaseUrl === currentExchange, @@ -115,7 +116,7 @@ export function useComponentState({ status: "showing-privacy", error: undefined, onClose: { - onClick: async () => setShowingPrivacy(undefined), + onClick: pushAlertOnError(async () => setShowingPrivacy(undefined)), }, exchangeUrl: showingPrivacy, }; @@ -125,7 +126,7 @@ export function useComponentState({ status: "showing-tos", error: undefined, onClose: { - onClick: async () => setShowingTos(undefined), + onClick: pushAlertOnError(async () => setShowingTos(undefined)), }, exchangeUrl: showingTos, }; @@ -138,24 +139,24 @@ export function useComponentState({ exchanges: { list: exchangeMap, value: value, - onChange: async (v) => { + onChange: pushAlertOnError(async (v) => { setValue(v); - }, + }), }, error: undefined, onClose: { - onClick: onCancel, + onClick: pushAlertOnError(onCancel), }, selected, onShowPrivacy: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setShowingPrivacy(selected.exchangeBaseUrl); - }, + }), }, onShowTerms: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setShowingTos(selected.exchangeBaseUrl); - }, + }), }, }; } @@ -215,30 +216,30 @@ export function useComponentState({ exchanges: { list: exchangeMap, value: value, - onChange: async (v) => { + onChange: pushAlertOnError(async (v) => { setValue(v); - }, + }), }, error: undefined, onReset: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setValue(String(initialValue)); - }, + }), }, onSelect: { - onClick: async () => { + onClick: pushAlertOnError(async () => { onSelection(selected.exchangeBaseUrl); - }, + }), }, onShowPrivacy: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setShowingPrivacy(selected.exchangeBaseUrl); - }, + }), }, onShowTerms: { - onClick: async () => { + onClick: pushAlertOnError(async () => { setShowingTos(selected.exchangeBaseUrl); - }, + }), }, selected, coinOperationTimeline, diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx index 05765b50a..a65f85c6a 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx @@ -19,14 +19,14 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ComparingView, ReadyView } from "./views.js"; export default { title: "select exchange", }; -export const Bitcoin1 = createExample(ReadyView, { +export const Bitcoin1 = tests.createExample(ReadyView, { exchanges: { list: { "0": "https://exchange.taler.ar" }, value: "0", @@ -43,7 +43,7 @@ export const Bitcoin1 = createExample(ReadyView, { onShowTerms: {}, onClose: {}, }); -export const Bitcoin2 = createExample(ReadyView, { +export const Bitcoin2 = tests.createExample(ReadyView, { exchanges: { list: { "https://exchange.taler.ar": "https://exchange.taler.ar", @@ -64,7 +64,7 @@ export const Bitcoin2 = createExample(ReadyView, { onClose: {}, }); -export const Kudos1 = createExample(ReadyView, { +export const Kudos1 = tests.createExample(ReadyView, { exchanges: { list: { "https://exchange-kudos.taler.ar": "https://exchange-kudos.taler.ar", @@ -83,7 +83,7 @@ export const Kudos1 = createExample(ReadyView, { onShowTerms: {}, onClose: {}, }); -export const Kudos2 = createExample(ReadyView, { +export const Kudos2 = tests.createExample(ReadyView, { exchanges: { list: { "https://exchange-kudos.taler.ar": "https://exchange-kudos.taler.ar", @@ -103,7 +103,7 @@ export const Kudos2 = createExample(ReadyView, { onShowTerms: {}, onClose: {}, }); -export const ComparingBitcoin = createExample(ComparingView, { +export const ComparingBitcoin = tests.createExample(ComparingView, { exchanges: { list: { "http://exchange": "http://exchange" }, value: "http://exchange", @@ -131,7 +131,7 @@ export const ComparingBitcoin = createExample(ComparingView, { missingWireTYpe: [], wireFeeTimeline: {}, }); -export const ComparingKudos = createExample(ComparingView, { +export const ComparingKudos = tests.createExample(ComparingView, { exchanges: { list: { "http://exchange": "http://exchange" }, value: "http://exchange", diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx index 1674ac135..13f4c8230 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx @@ -37,7 +37,7 @@ import { WithdrawalType, } from "@gnu-taler/taler-util"; import { HistoryView as TestedComponent } from "./History.js"; -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; export default { title: "balance", @@ -160,25 +160,28 @@ const exampleData = { } as TransactionPeerPullDebit, }; -export const NoBalance = createExample(TestedComponent, { +export const NoBalance = tests.createExample(TestedComponent, { transactions: [], balances: [], }); -export const SomeBalanceWithNoTransactions = createExample(TestedComponent, { - transactions: [], - balances: [ - { - available: "TESTKUDOS:10", - pendingIncoming: "TESTKUDOS:0", - pendingOutgoing: "TESTKUDOS:0", - hasPendingTransactions: false, - requiresUserInput: false, - }, - ], -}); +export const SomeBalanceWithNoTransactions = tests.createExample( + TestedComponent, + { + transactions: [], + balances: [ + { + available: "TESTKUDOS:10", + pendingIncoming: "TESTKUDOS:0", + pendingOutgoing: "TESTKUDOS:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + ], + }, +); -export const OneSimpleTransaction = createExample(TestedComponent, { +export const OneSimpleTransaction = tests.createExample(TestedComponent, { transactions: [exampleData.withdraw], balances: [ { @@ -191,20 +194,23 @@ export const OneSimpleTransaction = createExample(TestedComponent, { ], }); -export const TwoTransactionsAndZeroBalance = createExample(TestedComponent, { - transactions: [exampleData.withdraw, exampleData.deposit], - balances: [ - { - available: "USD:0", - pendingIncoming: "USD:0", - pendingOutgoing: "USD:0", - hasPendingTransactions: false, - requiresUserInput: false, - }, - ], -}); +export const TwoTransactionsAndZeroBalance = tests.createExample( + TestedComponent, + { + transactions: [exampleData.withdraw, exampleData.deposit], + balances: [ + { + available: "USD:0", + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + ], + }, +); -export const OneTransactionPending = createExample(TestedComponent, { +export const OneTransactionPending = tests.createExample(TestedComponent, { transactions: [ { ...exampleData.withdraw, @@ -222,7 +228,7 @@ export const OneTransactionPending = createExample(TestedComponent, { ], }); -export const SomeTransactions = createExample(TestedComponent, { +export const SomeTransactions = tests.createExample(TestedComponent, { transactions: [ exampleData.withdraw, exampleData.payment, @@ -251,7 +257,7 @@ export const SomeTransactions = createExample(TestedComponent, { ], }); -export const SomeTransactionsWithTwoCurrencies = createExample( +export const SomeTransactionsWithTwoCurrencies = tests.createExample( TestedComponent, { transactions: [ @@ -283,7 +289,7 @@ export const SomeTransactionsWithTwoCurrencies = createExample( }, ); -export const FiveOfficialCurrencies = createExample(TestedComponent, { +export const FiveOfficialCurrencies = tests.createExample(TestedComponent, { transactions: [exampleData.withdraw], balances: [ { @@ -324,7 +330,7 @@ export const FiveOfficialCurrencies = createExample(TestedComponent, { ], }); -export const FiveOfficialCurrenciesWithHighValue = createExample( +export const FiveOfficialCurrenciesWithHighValue = tests.createExample( TestedComponent, { transactions: [exampleData.withdraw], @@ -368,7 +374,7 @@ export const FiveOfficialCurrenciesWithHighValue = createExample( }, ); -export const PeerToPeer = createExample(TestedComponent, { +export const PeerToPeer = tests.createExample(TestedComponent, { transactions: [ exampleData.pull_credit, exampleData.pull_debit, diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx index 143d3adbb..1d51f835a 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.tsx @@ -23,7 +23,7 @@ import { import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; -import { AlertView } from "../components/CurrentAlerts.js"; +import { ErrorAlertView } from "../components/CurrentAlerts.js"; import { Loading } from "../components/Loading.js"; import { CenteredBoldText, @@ -33,7 +33,7 @@ import { } from "../components/styled/index.js"; import { Time } from "../components/Time.js"; import { TransactionItem } from "../components/TransactionItem.js"; -import { alertFromError } from "../context/alert.js"; +import { alertFromError, useAlertContext } from "../context/alert.js"; import { useBackendContext } from "../context/backend.js"; import { useTranslationContext } from "../context/translation.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; @@ -72,8 +72,8 @@ export function HistoryPage({ if (state.hasError) { return ( - b.available.split(":")[0]); + const { pushAlertOnError } = useAlertContext(); const defaultCurrencyIndex = currencies.findIndex( (c) => c === defaultCurrency, @@ -145,7 +146,7 @@ export function HistoryView({ return ( ); diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts index 176a8d100..f7383d483 100644 --- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts +++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/state.ts @@ -21,7 +21,7 @@ import { } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useState } from "preact/hooks"; -import { alertFromError } from "../../context/alert.js"; +import { alertFromError, useAlertContext } from "../../context/alert.js"; import { useBackendContext } from "../../context/backend.js"; import { useTranslationContext } from "../../context/translation.js"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; @@ -33,6 +33,7 @@ export function useComponentState({ onCancel, }: Props): State { const api = useBackendContext(); + const { pushAlertOnError } = useAlertContext(); const { i18n } = useTranslationContext(); const hook = useAsyncAsHook(() => api.wallet.call(WalletApiOperation.ListKnownBankAccounts, { currency }), @@ -109,30 +110,30 @@ export function useComponentState({ accountType: { list: accountType, value: type, - onChange: async (v) => { + onChange: pushAlertOnError(async (v) => { setType(v); - }, + }), }, alias: { value: alias, - onInput: async (v) => { + onInput: pushAlertOnError(async (v) => { setAlias(v); - }, + }), }, uri: { value: payto, error: paytoUriError, - onInput: async (v) => { + onInput: pushAlertOnError(async (v) => { setPayto(v); - }, + }), }, accountByType, deleteAccount, onAccountAdded: { - onClick: unableToAdd ? undefined : addAccount, + onClick: unableToAdd ? undefined : pushAlertOnError(addAccount), }, onCancel: { - onClick: async () => onCancel(), + onClick: pushAlertOnError(async () => onCancel()), }, }; } diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx b/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx index ca6db8be9..e20d4e0e8 100644 --- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/stories.tsx @@ -19,18 +19,15 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; +import { nullFunction } from "../../mui/handlers.js"; import { ReadyView } from "./views.js"; export default { title: "manage account", }; -const nullFunction = async () => { - null; -}; - -export const JustTwoBitcoinAccounts = createExample(ReadyView, { +export const JustTwoBitcoinAccounts = tests.createExample(ReadyView, { status: "ready", currency: "ARS", accountType: { @@ -84,7 +81,7 @@ export const JustTwoBitcoinAccounts = createExample(ReadyView, { onCancel: {}, }); -export const WithAllTypeOfAccounts = createExample(ReadyView, { +export const WithAllTypeOfAccounts = tests.createExample(ReadyView, { status: "ready", currency: "ARS", accountType: { @@ -165,7 +162,7 @@ export const WithAllTypeOfAccounts = createExample(ReadyView, { onCancel: {}, }); -export const AddingIbanAccount = createExample(ReadyView, { +export const AddingIbanAccount = tests.createExample(ReadyView, { status: "ready", currency: "ARS", accountType: { diff --git a/packages/taler-wallet-webextension/src/wallet/Notifications/stories.tsx b/packages/taler-wallet-webextension/src/wallet/Notifications/stories.tsx index c4da99909..bce4a8f41 100644 --- a/packages/taler-wallet-webextension/src/wallet/Notifications/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Notifications/stories.tsx @@ -20,14 +20,14 @@ */ import { AbsoluteTime, AttentionType } from "@gnu-taler/taler-util"; -import { createExample } from "../../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ReadyView } from "./views.js"; export default { title: "notifications", }; -export const Ready = createExample(ReadyView, { +export const Ready = tests.createExample(ReadyView, { list: [ { when: AbsoluteTime.now(), diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddConfirmProvider.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderAddConfirmProvider.stories.tsx index 9ca397302..55f80a397 100644 --- a/packages/taler-wallet-webextension/src/wallet/ProviderAddConfirmProvider.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddConfirmProvider.stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ConfirmProviderView as TestedComponent } from "./ProviderAddPage.js"; export default { @@ -32,7 +32,7 @@ export default { }, }; -export const DemoService = createExample(TestedComponent, { +export const DemoService = tests.createExample(TestedComponent, { url: "https://sync.demo.taler.net/", provider: { annual_fee: "KUDOS:0.1", @@ -41,7 +41,7 @@ export const DemoService = createExample(TestedComponent, { }, }); -export const FreeService = createExample(TestedComponent, { +export const FreeService = tests.createExample(TestedComponent, { url: "https://sync.taler:9667/", provider: { annual_fee: "ARS:0", diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddSetUrl.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderAddSetUrl.stories.tsx index a5528c36b..b4f2533bd 100644 --- a/packages/taler-wallet-webextension/src/wallet/ProviderAddSetUrl.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddSetUrl.stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { SetUrlView as TestedComponent } from "./ProviderAddPage.js"; export default { @@ -32,20 +32,20 @@ export default { }, }; -export const Initial = createExample(TestedComponent, {}); +export const Initial = tests.createExample(TestedComponent, {}); -export const WithValue = createExample(TestedComponent, { +export const WithValue = tests.createExample(TestedComponent, { initialValue: "sync.demo.taler.net", }); -export const WithConnectionError = createExample(TestedComponent, { +export const WithConnectionError = tests.createExample(TestedComponent, { withError: "Network error", }); -export const WithClientError = createExample(TestedComponent, { +export const WithClientError = tests.createExample(TestedComponent, { withError: "URL may not be right: (404) Not Found", }); -export const WithServerError = createExample(TestedComponent, { +export const WithServerError = tests.createExample(TestedComponent, { withError: "Try another server: (500) Internal Server Error", }); diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx index 98c68e6bd..08f26438f 100644 --- a/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetail.stories.tsx @@ -21,7 +21,7 @@ import { TalerProtocolTimestamp } from "@gnu-taler/taler-util"; import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core"; -import { createExample } from "../test-utils.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; import { ProviderView as TestedComponent } from "./ProviderDetailPage.js"; export default { @@ -34,7 +34,7 @@ export default { }, }; -export const Active = createExample(TestedComponent, { +export const Active = tests.createExample(TestedComponent, { info: { active: true, name: "sync.demo", @@ -58,7 +58,7 @@ export const Active = createExample(TestedComponent, { }, }); -export const ActiveErrorSync = createExample(TestedComponent, { +export const ActiveErrorSync = tests.createExample(TestedComponent, { info: { active: true, name: "sync.demo", @@ -79,6 +79,7 @@ export const ActiveErrorSync = createExample(TestedComponent, { lastError: { code: 2002, details: "details", + when: new Date().toISOString(), hint: "error hint from the server", message: "message", }, @@ -90,34 +91,37 @@ export const ActiveErrorSync = createExample(TestedComponent, { }, }); -export const ActiveBackupProblemUnreadable = createExample(TestedComponent, { - info: { - active: true, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.taler:9967/", - lastSuccessfulBackupTimestamp: - TalerProtocolTimestamp.fromSeconds(1625063925), - paymentProposalIds: [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", - ], - paymentStatus: { - type: ProviderPaymentType.Paid, - paidUntil: { - t_ms: 1656599921000, +export const ActiveBackupProblemUnreadable = tests.createExample( + TestedComponent, + { + info: { + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.taler:9967/", + lastSuccessfulBackupTimestamp: + TalerProtocolTimestamp.fromSeconds(1625063925), + paymentProposalIds: [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", + ], + paymentStatus: { + type: ProviderPaymentType.Paid, + paidUntil: { + t_ms: 1656599921000, + }, + }, + backupProblem: { + type: "backup-unreadable", + }, + terms: { + annualFee: "EUR:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", }, }, - backupProblem: { - type: "backup-unreadable", - }, - terms: { - annualFee: "EUR:1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, }, -}); +); -export const ActiveBackupProblemDevice = createExample(TestedComponent, { +export const ActiveBackupProblemDevice = tests.createExample(TestedComponent, { info: { active: true, name: "sync.demo", @@ -149,7 +153,7 @@ export const ActiveBackupProblemDevice = createExample(TestedComponent, { }, }); -export const InactiveUnpaid = createExample(TestedComponent, { +export const InactiveUnpaid = tests.createExample(TestedComponent, { info: { active: false, name: "sync.demo", @@ -166,25 +170,28 @@ export const InactiveUnpaid = createExample(TestedComponent, { }, }); -export const InactiveInsufficientBalance = createExample(TestedComponent, { - info: { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.InsufficientBalance, - amount: "EUR:123", - }, - terms: { - annualFee: "EUR:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", +export const InactiveInsufficientBalance = tests.createExample( + TestedComponent, + { + info: { + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.InsufficientBalance, + amount: "EUR:123", + }, + terms: { + annualFee: "EUR:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, }, }, -}); +); -export const InactivePending = createExample(TestedComponent, { +export const InactivePending = tests.createExample(TestedComponent, { info: { active: false, name: "sync.demo", @@ -202,7 +209,7 @@ export const InactivePending = createExample(TestedComponent, { }, }); -export const ActiveTermsChanged = createExample(TestedComponent, { +export const ActiveTermsChanged = tests.createExample(TestedComponent, { info: { active: true, name: "sync.demo", diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx index 9b72c0fae..789465a87 100644 --- a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx @@ -23,7 +23,7 @@ import { WalletApiOperation, } from "@gnu-taler/taler-wallet-core"; import { Fragment, h, VNode } from "preact"; -import { AlertView } from "../components/CurrentAlerts.js"; +import { ErrorAlertView } from "../components/CurrentAlerts.js"; import { ErrorMessage } from "../components/ErrorMessage.js"; import { Loading } from "../components/Loading.js"; import { PaymentStatus, SmallLightText } from "../components/styled/index.js"; @@ -66,8 +66,8 @@ export function ProviderDetailPage({ } if (state.hasError) { return ( - Promise.resolve(), - knownExchanges: [ - { - currency: "USD", - exchangeBaseUrl: "http://exchange1.taler", - tos: { - currentVersion: "1", - acceptedVersion: "1", - content: "content of tos", - contentType: "text/plain", +export const WithExchangeInDifferentState = tests.createExample( + TestedComponent, + { + deviceName: "this-is-the-device-name", + devModeToggle: { value: false, button: {} }, + autoOpenToggle: { value: false, button: {} }, + clipboardToggle: { value: false, button: {} }, + setDeviceName: () => Promise.resolve(), + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "http://exchange1.taler", + tos: { + currentVersion: "1", + acceptedVersion: "1", + content: "content of tos", + contentType: "text/plain", + }, + paytoUris: [ + "payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator", + ], }, - paytoUris: ["payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator"], - }, - { - currency: "USD", - exchangeBaseUrl: "http://exchange2.taler", - tos: { - currentVersion: "2", - acceptedVersion: "1", - content: "content of tos", - contentType: "text/plain", + { + currency: "USD", + exchangeBaseUrl: "http://exchange2.taler", + tos: { + currentVersion: "2", + acceptedVersion: "1", + content: "content of tos", + contentType: "text/plain", + }, + paytoUris: [ + "payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator", + ], + } as any, //TODO: complete with auditors, wireInfo and denominations + { + currency: "USD", + exchangeBaseUrl: "http://exchange3.taler", + tos: { + currentVersion: "1", + content: "content of tos", + contentType: "text/plain", + }, + paytoUris: [ + "payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator", + ], }, - paytoUris: ["payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator"], - } as any, //TODO: complete with auditors, wireInfo and denominations - { - currency: "USD", - exchangeBaseUrl: "http://exchange3.taler", - tos: { - currentVersion: "1", - content: "content of tos", - contentType: "text/plain", - }, - paytoUris: ["payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator"], - }, - ], - ...version, -}); + ], + ...version, + }, +); diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx index ed1bc838a..d65f3a095 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx @@ -22,7 +22,6 @@ import { import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { Fragment, h, VNode } from "preact"; import { Checkbox } from "../components/Checkbox.js"; -import { ErrorTalerOperation } from "../components/ErrorTalerOperation.js"; import { JustInDevMode } from "../components/JustInDevMode.js"; import { Part } from "../components/Part.js"; import { SelectList } from "../components/SelectList.js"; @@ -34,6 +33,7 @@ import { SuccessText, WarningText, } 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 "../context/translation.js"; @@ -50,8 +50,9 @@ const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined; export function SettingsPage(): VNode { const autoOpenToggle = useAutoOpenPermissions(); const clipboardToggle = useClipboardPermissions(); - const { devModeToggle } = useDevContext(); + const { devMode, toggle } = useDevContext(); const { name, update } = useBackupDeviceName(); + const { pushAlertOnError } = useAlertContext(); const webex = platform.getWalletWebExVersion(); const api = useBackendContext(); @@ -72,7 +73,12 @@ export function SettingsPage(): VNode { setDeviceName={update} autoOpenToggle={autoOpenToggle} clipboardToggle={clipboardToggle} - devModeToggle={devModeToggle} + devModeToggle={{ + value: devMode, + button: { + onClick: pushAlertOnError(toggle), + }, + }} webexVersion={{ version: webex.version, hash: GIT_HASH, @@ -109,18 +115,6 @@ export function SettingsView({ return (
- {autoOpenToggle.button.error && ( - - )} - {/* {clipboardToggle.button.error && ( - - )} */} Navigator diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx index 868d3b0e6..bc941c9af 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx @@ -39,11 +39,13 @@ import { WithdrawalType, } from "@gnu-taler/taler-util"; import { DevContextProviderForTesting } from "../context/devContext.js"; -import { - createExample, - createExampleWithCustomContext as createExampleInCustomContext, -} from "../test-utils.js"; +// import { +// createExample, +// createExampleWithCustomContext as createExampleInCustomContext, +// } from "../test-utils.js"; import { TransactionView as TestedComponent } from "./Transaction.js"; +import { tests } from "@gnu-taler/web-util/lib/index.browser"; +import beer from "../../static-dev/beer.png"; export default { title: "transaction details", @@ -214,24 +216,28 @@ const transactionError = { hint: "The payment is too late, the offer has expired.", }, }, + when: new Date().toISOString(), hint: "Error: WALLET_UNEXPECTED_REQUEST_ERROR", message: "Unexpected error code in response", }; -export const Withdraw = createExample(TestedComponent, { +export const Withdraw = tests.createExample(TestedComponent, { transaction: exampleData.withdraw, }); -export const WithdrawFiveMinutesAgo = createExample(TestedComponent, () => ({ - transaction: { - ...exampleData.withdraw, - timestamp: TalerProtocolTimestamp.fromSeconds( - new Date().getTime() / 1000 - 60 * 5, - ), - }, -})); +export const WithdrawFiveMinutesAgo = tests.createExample( + TestedComponent, + () => ({ + transaction: { + ...exampleData.withdraw, + timestamp: TalerProtocolTimestamp.fromSeconds( + new Date().getTime() / 1000 - 60 * 5, + ), + }, + }), +); -export const WithdrawFiveMinutesAgoAndPending = createExample( +export const WithdrawFiveMinutesAgoAndPending = tests.createExample( TestedComponent, () => ({ transaction: { @@ -244,38 +250,41 @@ export const WithdrawFiveMinutesAgoAndPending = createExample( }), ); -export const WithdrawError = createExample(TestedComponent, { +export const WithdrawError = tests.createExample(TestedComponent, { transaction: { ...exampleData.withdraw, error: transactionError, }, }); -export const WithdrawErrorInDevMode = createExampleInCustomContext( +// export const WithdrawErrorInDevMode = tests.createExampleInCustomContext( +// TestedComponent, +// { +// transaction: { +// ...exampleData.withdraw, +// error: transactionError, +// }, +// }, +// DevContextProviderForTesting, +// { value: true }, +// ); + +export const WithdrawPendingManual = tests.createExample( TestedComponent, - { + () => ({ transaction: { ...exampleData.withdraw, - error: transactionError, + withdrawalDetails: { + type: WithdrawalType.ManualTransfer, + exchangePaytoUris: ["payto://iban/ES8877998399652238"], + reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", + } as WithdrawalDetails, + pending: true, }, - }, - DevContextProviderForTesting, - { value: true }, + }), ); -export const WithdrawPendingManual = createExample(TestedComponent, () => ({ - transaction: { - ...exampleData.withdraw, - withdrawalDetails: { - type: WithdrawalType.ManualTransfer, - exchangePaytoUris: ["payto://iban/ES8877998399652238"], - reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", - } as WithdrawalDetails, - pending: true, - }, -})); - -export const WithdrawPendingTalerBankUnconfirmed = createExample( +export const WithdrawPendingTalerBankUnconfirmed = tests.createExample( TestedComponent, { transaction: { @@ -291,7 +300,7 @@ export const WithdrawPendingTalerBankUnconfirmed = createExample( }, ); -export const WithdrawPendingTalerBankConfirmed = createExample( +export const WithdrawPendingTalerBankConfirmed = tests.createExample( TestedComponent, { transaction: { @@ -306,18 +315,18 @@ export const WithdrawPendingTalerBankConfirmed = createExample( }, ); -export const Payment = createExample(TestedComponent, { +export const Payment = tests.createExample(TestedComponent, { transaction: exampleData.payment, }); -export const PaymentError = createExample(TestedComponent, { +export const PaymentError = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, error: transactionError, }, }); -export const PaymentWithRefund = createExample(TestedComponent, { +export const PaymentWithRefund = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:12", @@ -334,7 +343,7 @@ export const PaymentWithRefund = createExample(TestedComponent, { }, }); -export const PaymentWithDeliveryDate = createExample(TestedComponent, { +export const PaymentWithDeliveryDate = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:12", @@ -347,7 +356,7 @@ export const PaymentWithDeliveryDate = createExample(TestedComponent, { }, }); -export const PaymentWithDeliveryAddr = createExample(TestedComponent, { +export const PaymentWithDeliveryAddr = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:12", @@ -363,7 +372,7 @@ export const PaymentWithDeliveryAddr = createExample(TestedComponent, { }, }); -export const PaymentWithDeliveryFull = createExample(TestedComponent, { +export const PaymentWithDeliveryFull = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:12", @@ -382,7 +391,7 @@ export const PaymentWithDeliveryFull = createExample(TestedComponent, { }, }); -export const PaymentWithRefundPending = createExample(TestedComponent, { +export const PaymentWithRefundPending = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:12", @@ -392,7 +401,7 @@ export const PaymentWithRefundPending = createExample(TestedComponent, { }, }); -export const PaymentWithFeeAndRefund = createExample(TestedComponent, { +export const PaymentWithFeeAndRefund = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:11", @@ -401,7 +410,7 @@ export const PaymentWithFeeAndRefund = createExample(TestedComponent, { }, }); -export const PaymentWithFeeAndRefundFee = createExample(TestedComponent, { +export const PaymentWithFeeAndRefundFee = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:11", @@ -410,20 +419,18 @@ export const PaymentWithFeeAndRefundFee = createExample(TestedComponent, { }, }); -export const PaymentWithoutFee = createExample(TestedComponent, { +export const PaymentWithoutFee = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, amountRaw: "KUDOS:12", }, }); -export const PaymentPending = createExample(TestedComponent, { +export const PaymentPending = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, pending: true }, }); -import beer from "../../static-dev/beer.png"; - -export const PaymentWithProducts = createExample(TestedComponent, { +export const PaymentWithProducts = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, info: { @@ -460,7 +467,7 @@ export const PaymentWithProducts = createExample(TestedComponent, { } as TransactionPayment, }); -export const PaymentWithLongSummary = createExample(TestedComponent, { +export const PaymentWithLongSummary = tests.createExample(TestedComponent, { transaction: { ...exampleData.payment, info: { @@ -484,16 +491,16 @@ export const PaymentWithLongSummary = createExample(TestedComponent, { } as TransactionPayment, }); -export const Deposit = createExample(TestedComponent, { +export const Deposit = tests.createExample(TestedComponent, { transaction: exampleData.deposit, }); -export const DepositTalerBank = createExample(TestedComponent, { +export const DepositTalerBank = tests.createExample(TestedComponent, { transaction: { ...exampleData.deposit, targetPaytoUri: "payto://x-taler-bank/bank.demo.taler.net/Exchange", }, }); -export const DepositBitcoin = createExample(TestedComponent, { +export const DepositBitcoin = tests.createExample(TestedComponent, { transaction: { ...exampleData.deposit, amountRaw: "BITCOINBTC:0.0000011", @@ -502,88 +509,88 @@ export const DepositBitcoin = createExample(TestedComponent, { "payto://bitcoin/bcrt1q6ps8qs6v8tkqrnru4xqqqa6rfwcx5ufpdfqht4?amount=BTC:0.1&subject=0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00", }, }); -export const DepositIBAN = createExample(TestedComponent, { +export const DepositIBAN = tests.createExample(TestedComponent, { transaction: { ...exampleData.deposit, targetPaytoUri: "payto://iban/ES8877998399652238", }, }); -export const DepositError = createExample(TestedComponent, { +export const DepositError = tests.createExample(TestedComponent, { transaction: { ...exampleData.deposit, error: transactionError, }, }); -export const DepositPending = createExample(TestedComponent, { +export const DepositPending = tests.createExample(TestedComponent, { transaction: { ...exampleData.deposit, pending: true }, }); -export const Refresh = createExample(TestedComponent, { +export const Refresh = tests.createExample(TestedComponent, { transaction: exampleData.refresh, }); -export const RefreshError = createExample(TestedComponent, { +export const RefreshError = tests.createExample(TestedComponent, { transaction: { ...exampleData.refresh, error: transactionError, }, }); -export const Tip = createExample(TestedComponent, { +export const Tip = tests.createExample(TestedComponent, { transaction: exampleData.tip, }); -export const TipError = createExample(TestedComponent, { +export const TipError = tests.createExample(TestedComponent, { transaction: { ...exampleData.tip, error: transactionError, }, }); -export const TipPending = createExample(TestedComponent, { +export const TipPending = tests.createExample(TestedComponent, { transaction: { ...exampleData.tip, pending: true }, }); -export const Refund = createExample(TestedComponent, { +export const Refund = tests.createExample(TestedComponent, { transaction: exampleData.refund, }); -export const RefundError = createExample(TestedComponent, { +export const RefundError = tests.createExample(TestedComponent, { transaction: { ...exampleData.refund, error: transactionError, }, }); -export const RefundPending = createExample(TestedComponent, { +export const RefundPending = tests.createExample(TestedComponent, { transaction: { ...exampleData.refund, pending: true }, }); -export const InvoiceCreditComplete = createExample(TestedComponent, { +export const InvoiceCreditComplete = tests.createExample(TestedComponent, { transaction: { ...exampleData.pull_credit }, }); -export const InvoiceCreditIncomplete = createExample(TestedComponent, { +export const InvoiceCreditIncomplete = tests.createExample(TestedComponent, { transaction: { ...exampleData.pull_credit, pending: true, }, }); -export const InvoiceDebit = createExample(TestedComponent, { +export const InvoiceDebit = tests.createExample(TestedComponent, { transaction: { ...exampleData.pull_debit }, }); -export const TransferCredit = createExample(TestedComponent, { +export const TransferCredit = tests.createExample(TestedComponent, { transaction: { ...exampleData.push_credit }, }); -export const TransferDebitComplete = createExample(TestedComponent, { +export const TransferDebitComplete = tests.createExample(TestedComponent, { transaction: { ...exampleData.push_debit }, }); -export const TransferDebitIncomplete = createExample(TestedComponent, { +export const TransferDebitIncomplete = tests.createExample(TestedComponent, { transaction: { ...exampleData.push_debit, pending: true, diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 542694490..f22f3b4ee 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -44,7 +44,7 @@ import emptyImg from "../../static/img/empty.png"; import { Amount } from "../components/Amount.js"; import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType.js"; import { CopyButton } from "../components/CopyButton.js"; -import { AlertView, ErrorAlertView } from "../components/CurrentAlerts.js"; +import { ErrorAlertView } from "../components/CurrentAlerts.js"; import { Loading } from "../components/Loading.js"; import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js"; import { QR } from "../components/QR.js"; @@ -99,8 +99,8 @@ export function TransactionPage({ if (state.hasError) { return ( - ; } -export class WalletError extends Error { - public errorDetail: TalerError; - - constructor(op: string, e: TalerError) { - super(`Wallet operation "${op}" failed: ${e.message}`); - this.errorDetail = e; - // Object.setPrototypeOf(this, WalletError.prototype); - } -} export class BackgroundError extends Error { public errorDetail: TalerErrorDetail; - constructor(op: string, e: TalerErrorDetail) { - super(`Background operation "${op}" failed: ${e.message}`); + constructor(title: string, e: TalerErrorDetail) { + super(title); this.errorDetail = e; - // Object.setPrototypeOf(this, BackgroundError.prototype); } } @@ -135,13 +125,17 @@ class BackgroundApiClientImpl implements BackgroundApiClient { if (error instanceof Error) { throw new BackgroundError(operation, { code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR, + when: new Date().toISOString(), }); } throw error; } logger.info("got response", response); if (response.type === "error") { - throw new BackgroundError(operation, response.error); + throw new BackgroundError( + `Background operation "${operation}" failed`, + response.error, + ); } return response.result as any; } @@ -169,8 +163,10 @@ class WalletApiClientImpl implements WalletCoreApiClient { } logger.info("got response", response); if (response.type === "error") { - const error = TalerError.fromUncheckedDetail(response.error); - throw new WalletError(operation, error); + throw new BackgroundError( + `Wallet operation "${operation}" failed`, + response.error, + ); } return response.result as any; }