diff --git a/packages/demobank-ui/src/components/Routing.tsx b/packages/demobank-ui/src/components/Routing.tsx index e1fd93737..90d2d4c48 100644 --- a/packages/demobank-ui/src/components/Routing.tsx +++ b/packages/demobank-ui/src/components/Routing.tsx @@ -45,6 +45,20 @@ export function Routing(): VNode { /> )} /> + ( + { + route("/account"); + }} + // onLoadNotOk={() => { + // route("/account"); + // }} + /> + )} + /> ( @@ -64,10 +78,6 @@ export function Routing(): VNode { return ( - ( @@ -76,9 +86,6 @@ export function Routing(): VNode { onContinue={() => { route("/account"); }} - // onLoadNotOk={() => { - // route("/account"); - // }} /> )} /> @@ -108,9 +115,9 @@ export function Routing(): VNode { } else { return { - // route(`/operation/${wopid}`); - // }} + goToConfirmOperation={(wopid) => { + route(`/operation/${wopid}`); + }} goToBusinessAccount={() => { route("/business"); }} diff --git a/packages/demobank-ui/src/hooks/settings.ts b/packages/demobank-ui/src/hooks/settings.ts index c2fd93a0c..5f004c6d4 100644 --- a/packages/demobank-ui/src/hooks/settings.ts +++ b/packages/demobank-ui/src/hooks/settings.ts @@ -30,6 +30,7 @@ interface Settings { currentWithdrawalOperationId: string | undefined; showWithdrawalSuccess: boolean; showDemoDescription: boolean; + showInstallWallet: boolean; maxWithdrawalAmount: number; fastWithdrawal: boolean; } @@ -39,6 +40,7 @@ export const codecForSettings = (): Codec => .property("currentWithdrawalOperationId", codecOptional(codecForString())) .property("showWithdrawalSuccess", (codecForBoolean())) .property("showDemoDescription", (codecForBoolean())) + .property("showInstallWallet", (codecForBoolean())) .property("fastWithdrawal", (codecForBoolean())) .property("maxWithdrawalAmount", codecForNumber()) .build("Settings"); @@ -47,6 +49,7 @@ const defaultSettings: Settings = { currentWithdrawalOperationId: undefined, showWithdrawalSuccess: true, showDemoDescription: true, + showInstallWallet: true, maxWithdrawalAmount: 25, fastWithdrawal: false, }; diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts b/packages/demobank-ui/src/pages/AccountPage/index.ts index 128a6d30f..81eeb4a03 100644 --- a/packages/demobank-ui/src/pages/AccountPage/index.ts +++ b/packages/demobank-ui/src/pages/AccountPage/index.ts @@ -29,6 +29,7 @@ export interface Props { error: HttpResponsePaginated, ) => VNode; goToBusinessAccount: () => void; + goToConfirmOperation: (id:string) => void; } export type State = State.Loading | State.LoadingError | State.Ready | State.InvalidIban | State.UserNotFound; @@ -54,6 +55,7 @@ export namespace State { account: string, limit: AmountJson, goToBusinessAccount: () => void; + goToConfirmOperation: (id:string) => void; } export interface InvalidIban { diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts b/packages/demobank-ui/src/pages/AccountPage/state.ts index a57e19901..1a1475c0d 100644 --- a/packages/demobank-ui/src/pages/AccountPage/state.ts +++ b/packages/demobank-ui/src/pages/AccountPage/state.ts @@ -20,7 +20,7 @@ import { useBackendContext } from "../../context/backend.js"; import { useAccountDetails } from "../../hooks/access.js"; import { Props, State } from "./index.js"; -export function useComponentState({ account, goToBusinessAccount }: Props): State { +export function useComponentState({ account, goToBusinessAccount, goToConfirmOperation }: Props): State { const result = useAccountDetails(account); const backend = useBackendContext(); const { i18n } = useTranslationContext(); @@ -75,6 +75,7 @@ export function useComponentState({ account, goToBusinessAccount }: Props): Stat return { status: "ready", goToBusinessAccount, + goToConfirmOperation, error: undefined, account, limit, diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx b/packages/demobank-ui/src/pages/AccountPage/views.tsx index 0187989af..23a815bd8 100644 --- a/packages/demobank-ui/src/pages/AccountPage/views.tsx +++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx @@ -123,7 +123,7 @@ function ShowDemoInfo():VNode { } -export function ReadyView({ account, limit, goToBusinessAccount }: State.Ready): VNode<{}> { +export function ReadyView({ account, limit, goToBusinessAccount, goToConfirmOperation }: State.Ready): VNode<{}> { const { i18n } = useTranslationContext(); return @@ -131,7 +131,7 @@ export function ReadyView({ account, limit, goToBusinessAccount }: State.Ready): - + ; } diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx index d1c94135b..5bfaa63ec 100644 --- a/packages/demobank-ui/src/pages/BankFrame.tsx +++ b/packages/demobank-ui/src/pages/BankFrame.tsx @@ -206,6 +206,21 @@ export function BankFrame({ +
  • +
    + + + Show install wallet first + + + +
    +
  • diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx index 2acfc9b57..8d5e1f3b9 100644 --- a/packages/demobank-ui/src/pages/HomePage.tsx +++ b/packages/demobank-ui/src/pages/HomePage.tsx @@ -36,6 +36,7 @@ import { useSettings } from "../hooks/settings.js"; import { AccountPage } from "./AccountPage/index.js"; import { LoginForm } from "./LoginForm.js"; import { WithdrawalQRCode } from "./WithdrawalQRCode.js"; +import { route } from "preact-router"; const logger = new Logger("AccountPage"); @@ -52,25 +53,20 @@ const logger = new Logger("AccountPage"); export function HomePage({ onRegister, account, - // onPendingOperationFound, + goToConfirmOperation, goToBusinessAccount, }: { account: string, - // onPendingOperationFound: (id: string) => void; onRegister: () => void; goToBusinessAccount: () => void; + goToConfirmOperation: (id:string) => void; }): VNode { - const [settings] = useSettings(); const { i18n } = useTranslationContext(); - // if (settings.currentWithdrawalOperationId) { - // onPendingOperationFound(settings.currentWithdrawalOperationId); - // return ; - // } - return ( @@ -102,12 +98,13 @@ export function WithdrawalOperationPage({ ); return ; } - + return ( { updateSettings("currentWithdrawalOperationId", undefined) + onContinue() }} /> ); @@ -178,7 +175,7 @@ export function handleNotOkResult( assertUnreachable(result); } } - + route("/") return
    error
    ; } return
    ; diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts b/packages/demobank-ui/src/pages/OperationState/index.ts index 254fcba5f..32302f272 100644 --- a/packages/demobank-ui/src/pages/OperationState/index.ts +++ b/packages/demobank-ui/src/pages/OperationState/index.ts @@ -26,6 +26,7 @@ import { ErrorLoading } from "../../components/ErrorLoading.js"; export interface Props { currency: string; onClose: () => void; + goToConfirmOperation: (id: string) => void; } export type State = State.Loading | @@ -57,26 +58,33 @@ export namespace State { error: undefined; uri: WithdrawUriResult, onClose: () => void; + onAbort: () => void; } export interface InvalidPayto { status: "invalid-payto", error: undefined; payto: string | null; + onClose: () => void; } export interface InvalidWithdrawal { status: "invalid-withdrawal", error: undefined; + onClose: () => void; uri: string, } export interface InvalidReserve { status: "invalid-reserve", error: undefined; + onClose: () => void; reserve: string | null; } export interface NeedConfirmation { status: "need-confirmation", + onAbort: () => void; + onConfirm: () => void; error: undefined; + busy: boolean, } export interface Aborted { status: "aborted", @@ -111,7 +119,7 @@ const viewMapping: utils.StateViewMap = { ready: ReadyView, }; -export const AccountPage = utils.compose( +export const OperationState = utils.compose( (p: Props) => useComponentState(p), viewMapping, ); diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts index 6fb7bb28f..ae03ed529 100644 --- a/packages/demobank-ui/src/pages/OperationState/state.ts +++ b/packages/demobank-ui/src/pages/OperationState/state.ts @@ -15,21 +15,24 @@ */ import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util"; -import { ErrorType, RequestError, notify, notifyError, useTranslationContext, utils } from "@gnu-taler/web-util/browser"; +import { ErrorType, RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser"; import { useBackendContext } from "../../context/backend.js"; -import { useAccessAPI, useAccountDetails, useWithdrawalDetails } from "../../hooks/access.js"; +import { useAccessAPI, useAccessAnonAPI, useAccountDetails, useWithdrawalDetails } from "../../hooks/access.js"; import { Props, State } from "./index.js"; import { useSettings } from "../../hooks/settings.js"; -import { buildRequestErrorMessage } from "../../utils.js"; -import { useEffect } from "preact/hooks"; +import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js"; +import { useEffect, useMemo, useState } from "preact/hooks"; import { getInitialBackendBaseURL } from "../../hooks/backend.js"; -export function useComponentState({ currency, onClose }: Props): utils.RecursiveState { +export function useComponentState({ currency, onClose,goToConfirmOperation }: Props): utils.RecursiveState { const { i18n } = useTranslationContext(); const [settings, updateSettings] = useSettings() const { createWithdrawal } = useAccessAPI(); + const { abortWithdrawal, confirmWithdrawal } = useAccessAnonAPI(); + const [busy, setBusy] = useState>() const amount = settings.maxWithdrawalAmount + async function doSilentStart() { //FIXME: if amount is not enough use balance const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`) @@ -67,12 +70,14 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive } } + const withdrawalOperationId = settings.currentWithdrawalOperationId useEffect(() => { - doSilentStart() + if (withdrawalOperationId === undefined) { + doSilentStart() + } }, [settings.fastWithdrawal, amount]) const baseUrl = getInitialBackendBaseURL() - const withdrawalOperationId = settings.currentWithdrawalOperationId if (!withdrawalOperationId) { return { @@ -81,6 +86,63 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive } } + const wid = withdrawalOperationId + + async function doAbort() { + try { + setBusy({}) + await abortWithdrawal(wid); + onClose(); + } catch (error) { + if (error instanceof RequestError) { + notify( + buildRequestErrorMessage(i18n, error.cause, { + onClientError: (status) => + status === HttpStatusCode.Conflict + ? i18n.str`The reserve operation has been confirmed previously and can't be aborted` + : undefined, + }), + ); + } else { + notifyError( + i18n.str`Operation failed, please report`, + (error instanceof Error + ? error.message + : JSON.stringify(error)) as TranslatedString + ) + } + } + setBusy(undefined) + } + + async function doConfirm() { + try { + setBusy({}) + await confirmWithdrawal(wid); + notifyInfo(i18n.str`Wire transfer completed!`) + } catch (error) { + if (error instanceof RequestError) { + notify( + buildRequestErrorMessage(i18n, error.cause, { + onClientError: (status) => + status === HttpStatusCode.Conflict + ? i18n.str`The withdrawal has been aborted previously and can't be confirmed` + : status === HttpStatusCode.UnprocessableEntity + ? i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before` + : undefined, + }), + ); + } else { + notifyError( + i18n.str`Operation failed, please report`, + (error instanceof Error + ? error.message + : JSON.stringify(error)) as TranslatedString + ) + } + } + setBusy(undefined) + } const bankIntegrationApiBaseUrl = `${baseUrl}/integration-api` const uri = stringifyWithdrawUri({ bankIntegrationApiBaseUrl, @@ -92,11 +154,13 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive status: "invalid-withdrawal", error: undefined, uri, + onClose, } } return (): utils.RecursiveState => { const result = useWithdrawalDetails(withdrawalOperationId); + if (!result.ok) { if (result.loading) { return { @@ -119,10 +183,17 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive } if (data.confirmation_done) { + if (!settings.showWithdrawalSuccess) { + updateSettings("currentWithdrawalOperationId", undefined) + onClose() + } return { status: "confirmed", error: undefined, - onClose, + onClose: async () => { + updateSettings("currentWithdrawalOperationId", undefined) + onClose() + }, } } @@ -131,7 +202,12 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive status: "ready", error: undefined, uri: parsedUri, - onClose + onClose: async () => { + await doAbort() + updateSettings("currentWithdrawalOperationId", undefined) + onClose() + }, + onAbort: doAbort, } } @@ -139,7 +215,8 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive return { status: "invalid-reserve", error: undefined, - reserve: data.selected_reserve_pub + reserve: data.selected_reserve_pub, + onClose, } } @@ -149,13 +226,23 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive return { status: "invalid-payto", error: undefined, - payto: data.selected_exchange_account + payto: data.selected_exchange_account, + onClose, } } + + // goToConfirmOperation(withdrawalOperationId) return { status: "need-confirmation", error: undefined, + onAbort: async () => { + await doAbort() + updateSettings("currentWithdrawalOperationId", undefined) + onClose() + }, + busy: !!busy, + onConfirm: doConfirm } } diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx index db25eaf61..17f1d8457 100644 --- a/packages/demobank-ui/src/pages/OperationState/views.tsx +++ b/packages/demobank-ui/src/pages/OperationState/views.tsx @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { Amounts, stringifyPaytoUri } from "@gnu-taler/taler-util"; +import { Amounts, stringifyPaytoUri, stringifyWithdrawUri } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { Transactions } from "../../components/Transactions/index.js"; @@ -24,42 +24,375 @@ import { CopyButton } from "../../components/CopyButton.js"; import { bankUiSettings } from "../../settings.js"; import { useBusinessAccountDetails } from "../../hooks/circuit.js"; import { useSettings } from "../../hooks/settings.js"; +import { useEffect, useMemo, useState } from "preact/hooks"; +import { undefinedIfEmpty } from "../../utils.js"; +import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js"; +import { QR } from "../../components/QR.js"; -export function InvalidPaytoView({ error }: State.InvalidPayto) { +export function InvalidPaytoView({ payto, onClose }: State.InvalidPayto) { return ( -
    Payto from server is not valid "{error.data.paytoUri}"
    +
    Payto from server is not valid "{payto}"
    ); } -export function InvalidWithdrawalView({ error }: State.InvalidWithdrawal) { +export function InvalidWithdrawalView({ uri, onClose }: State.InvalidWithdrawal) { return ( -
    Payto from server is not valid "{error.data.paytoUri}"
    +
    Withdrawal uri from server is not valid "{uri}"
    ); } -export function InvalidReserveView({ error }: State.InvalidReserve) { +export function InvalidReserveView({ reserve, onClose }: State.InvalidReserve) { return ( -
    Payto from server is not valid "{error.data.paytoUri}"
    +
    Reserve from server is not valid "{reserve}"
    ); } -export function NeedConfirmationView({ error }: State.NeedConfirmation) { +export function NeedConfirmationView({ error, onAbort, onConfirm, busy }: State.NeedConfirmation) { + const { i18n } = useTranslationContext() + + const captchaNumbers = useMemo(() => { + return { + a: Math.floor(Math.random() * 10), + b: Math.floor(Math.random() * 10), + }; + }, []); + const [captchaAnswer, setCaptchaAnswer] = useState(); + const answer = parseInt(captchaAnswer ?? "", 10); + const errors = undefinedIfEmpty({ + answer: !captchaAnswer + ? i18n.str`Answer the question before continue` + : Number.isNaN(answer) + ? i18n.str`The answer should be a number` + : answer !== captchaNumbers.a + captchaNumbers.b + ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.` + : undefined, + }) ?? (busy ? {} as Record : undefined); + return ( -
    Payto from server is not valid "{error.data.paytoUri}"
    +
    +
    +

    + Confirm the withdrawal operation +

    +
    +
    + + + + + + + +
    +
    +
    + +
    { + e.preventDefault() + }} + > +
    + +
    +
    + { + setCaptchaAnswer(e.currentTarget.value) + }} + /> +
    + +
    +
    +
    + + +
    + +
    +
    +
    + {/*
    +
    +

    Wire transfer details

    +
    +
    +
    + {((): VNode => { + switch (details.account.targetType) { + case "iban": { + const p = details.account as PaytoUriIBAN + const name = p.params["receiver-name"] + return +
    +
    Exchange account
    +
    {p.iban}
    +
    + {name && +
    +
    Exchange name
    +
    {p.params["receiver-name"]}
    +
    + } +
    + } + case "x-taler-bank": { + const p = details.account as PaytoUriTalerBank + const name = p.params["receiver-name"] + return +
    +
    Exchange account
    +
    {p.account}
    +
    + {name && +
    +
    Exchange name
    +
    {p.params["receiver-name"]}
    +
    + } +
    + } + default: + return
    +
    Exchange account
    +
    {details.account.targetPath}
    +
    + + } + })()} +
    +
    Withdrawal identification
    +
    {details.reserve}
    +
    +
    +
    Amount
    +
    To be added
    + // {/* Amounts.stringifyValue(details.amount) +
    +
    +
    +
    */} + +
    +
    +
    + ); } -export function AbortedView({ error }: State.Aborted) { +export function AbortedView({ error, onClose }: State.Aborted) { return ( -
    Payto from server is not valid "{error.data.paytoUri}"
    +
    aborted
    ); } -export function ConfirmedView({ error }: State.Confirmed) { +export function ConfirmedView({ error, onClose }: State.Confirmed) { + const { i18n } = useTranslationContext(); + const [settings, updateSettings] = useSettings() return ( -
    Payto from server is not valid "{error.data.paytoUri}"
    + + +
    + +
    + +
    +
    + +
    +

    + + The wire transfer to the Taler exchange bank's account is completed, now the + exchange will send the requested amount into your GNU Taler wallet. + +

    +
    +
    +
    +
    +
    + + + Do not show this again + + + +
    +
    +
    + +
    +
    + ); } -export function ReadyView({ account, limit, goToBusinessAccount }: State.Ready): VNode<{}> { +export function ReadyView({ uri, onClose }: State.Ready): VNode<{}> { const { i18n } = useTranslationContext(); - return
    + useEffect(() => { + //Taler Wallet WebExtension is listening to headers response and tab updates. + //In the SPA there is no header response with the Taler URI so + //this hack manually triggers the tab update after the QR is in the DOM. + // WebExtension will be using + // https://developer.chrome.com/docs/extensions/reference/tabs/#event-onUpdated + document.title = `${document.title} ${uri.withdrawalOperationId}`; + }, []); + const talerWithdrawUri = stringifyWithdrawUri(uri); + const [show, setShow] = useState(false) + return + +
    +
    +

    + On this device +

    +
    +
    +

    + If you are using a desktop browser you can open the popup now or click the link if you have the "Inject Taler support" option enabled. +

    +
    + +
    +
    +
    +
    +
    +

    + On a mobile phone +

    +
    +
    +

    + Scan the QR code with your mobile device. +

    +
    +
    + +
    +
    + {show && +
    + +
    + } +
    +
    + +
    + +
    +
    } diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx index 573f8c769..2830f5c1e 100644 --- a/packages/demobank-ui/src/pages/PaymentOptions.tsx +++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx @@ -26,12 +26,11 @@ import { useSettings } from "../hooks/settings.js"; * Let the user choose a payment option, * then specify the details trigger the action. */ -export function PaymentOptions({ limit }: { limit: AmountJson }): VNode { +export function PaymentOptions({ limit, goToConfirmOperation }: { limit: AmountJson, goToConfirmOperation: (id: string) => void }): VNode { const { i18n } = useTranslationContext(); - const [settings, updateSettings] = useSettings(); + const [settings] = useSettings(); const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>(); - // const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>(undefined); return (
    @@ -56,6 +55,14 @@ export function PaymentOptions({ limit }: { limit: AmountJson }): VNode { Withdraw digital money into your mobile wallet or browser extension + {!!settings.currentWithdrawalOperationId && + + + Operation in progress + + }
    -
    -

    - If you have a Taler wallet installed in this device -

    - -
    -

    - You will see the details of the operation in your wallet including the fees (if applies). - If you still one you can install it from here. -

    -
    -
    -
    - -
    -
    -
    - -
    -
    -

    - Or if you have the wallet in another device -

    -
    - Scan the QR below to start the withdrawal -
    -
    - -
    -
    -
    - -
    -
    - - - ); -} - diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx index 08f706919..8dbdd9da6 100644 --- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx +++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx @@ -36,33 +36,52 @@ import { useAccessAPI } from "../hooks/access.js"; import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js"; import { Amount } from "./PaytoWireTransferForm.js"; import { useSettings } from "../hooks/settings.js"; -import { WithdrawalOperationState } from "./WithdrawalQRCode.js"; -import { Loading } from "../components/Loading.js"; +import { OperationState } from "./OperationState/index.js"; const logger = new Logger("WalletWithdrawForm"); const RefAmount = forwardRef(Amount); -export function WalletWithdrawForm({ - focus, - limit, - onSuccess, - onCancel, -}: { + +function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: { limit: AmountJson; focus?: boolean; - onSuccess: (operationId: string) => void; + goToConfirmOperation: (operationId: string) => void; onCancel: () => void; }): VNode { const { i18n } = useTranslationContext(); - const { createWithdrawal } = useAccessAPI(); const [settings, updateSettings] = useSettings() + const { createWithdrawal } = useAccessAPI(); const [amountStr, setAmountStr] = useState(`${settings.maxWithdrawalAmount}`); const ref = useRef(null); useEffect(() => { if (focus) ref.current?.focus(); }, [focus]); + if (!!settings.currentWithdrawalOperationId) { + return
    +
    +
    + +
    +
    +

    + There is an operation already +

    +
    +

    + + To complete or cancel the operation click here + +

    +
    +
    +
    +
    + } + const trimmedAmountStr = amountStr?.trim(); const parsedAmount = trimmedAmountStr @@ -92,7 +111,8 @@ export function WalletWithdrawForm({ i18n.str`Server responded with an invalid withdraw URI`, i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`); } else { - onSuccess(uri.withdrawalOperationId); + updateSettings("currentWithdrawalOperationId", uri.withdrawalOperationId) + goToConfirmOperation(uri.withdrawalOperationId); } } catch (error) { if (error instanceof RequestError) { @@ -115,113 +135,168 @@ export function WalletWithdrawForm({ } } + return
    { + e.preventDefault() + }} + > +
    +
    +
    + + { + setAmountStr(v); + }} + error={errors?.amount} + ref={ref} + /> +
    +
    + + + + + + +
    + +
    +
    +
    + + +
    + +
    +} + + +export function WalletWithdrawForm({ + focus, + limit, + onCancel, + goToConfirmOperation, +}: { + limit: AmountJson; + focus?: boolean; + goToConfirmOperation: (operationId: string) => void; + onCancel: () => void; +}): VNode { + const { i18n } = useTranslationContext(); + const [settings, updateSettings] = useSettings() + return (

    Prepare your wallet

    - Upon starting you will receive the money in your digital wallet, if you don't have one please install one from here. -

    -

    - After using your wallet you will be redirected here to confirm or cancel the operation. + After using your wallet you will confirm or cancel the operation.

    - {!settings.fastWithdrawal ? -
    { - e.preventDefault() - }} - > -
    -
    -
    - - { - setAmountStr(v); - }} - error={errors?.amount} - ref={ref} - /> -
    -
    - - - - - -
    -
    -
    - - -
    +
    } - - : settings.currentWithdrawalOperationId === undefined ? - : - { - onCancel() - }} + {!settings.fastWithdrawal ? + - } + : + + } +
    ); } diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx index 9976babdb..25c571e28 100644 --- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx @@ -18,23 +18,17 @@ import { Amounts, HttpStatusCode, Logger, - TranslatedString, WithdrawUriResult, - parsePaytoUri, - parseWithdrawUri, - stringifyWithdrawUri, + parsePaytoUri } from "@gnu-taler/taler-util"; -import { ErrorType, RequestError, notify, notifyError, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { ErrorType, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { Loading } from "../components/Loading.js"; -import { useAccessAPI, useWithdrawalDetails } from "../hooks/access.js"; +import { useWithdrawalDetails } from "../hooks/access.js"; import { useSettings } from "../hooks/settings.js"; import { handleNotOkResult } from "./HomePage.js"; -import { QrCodeSection, QrCodeSectionSimpler } from "./QrCodeSection.js"; +import { QrCodeSection } from "./QrCodeSection.js"; import { WithdrawalConfirmationQuestion } from "./WithdrawalConfirmationQuestion.js"; -import { useEffect, useState } from "preact/hooks"; -import { buildRequestErrorMessage } from "../utils.js"; -import { getInitialBackendBaseURL } from "../hooks/backend.js"; const logger = new Logger("WithdrawalQRCode"); @@ -54,18 +48,11 @@ export function WithdrawalQRCode({ const [settings, updateSettings] = useSettings(); const { i18n } = useTranslationContext(); const result = useWithdrawalDetails(withdrawUri.withdrawalOperationId); + if (!result.ok) { if (result.loading) { return ; } - if ( - result.type === ErrorType.CLIENT && - result.status === HttpStatusCode.NotFound - ) { - onClose() - return
    operation not found
    ; - } - // onLoadNotOk(); return handleNotOkResult(i18n)(result); } const { data } = result; @@ -127,22 +114,6 @@ export function WithdrawalQRCode({
    -
    -
    - - - Do not show this again - - - -
    -