diff --git a/packages/demobank-ui/src/hooks/backend.ts b/packages/demobank-ui/src/hooks/backend.ts index 4b60d1b6c..c05ab33e9 100644 --- a/packages/demobank-ui/src/hooks/backend.ts +++ b/packages/demobank-ui/src/hooks/backend.ts @@ -85,18 +85,26 @@ export function getInitialBackendBaseURL(): string { typeof localStorage !== "undefined" ? localStorage.getItem("bank-base-url") : undefined; + let result: string; if (!overrideUrl) { //normal path if (!bankUiSettings.backendBaseURL) { console.error( "ERROR: backendBaseURL was overridden by a setting file and missing. Setting value to 'window.origin'", ); - return canonicalizeBaseUrl(window.origin); + result = window.origin } - return canonicalizeBaseUrl(bankUiSettings.backendBaseURL); + result = bankUiSettings.backendBaseURL; + } else { + // testing/development path + result = overrideUrl + } + try { + return canonicalizeBaseUrl(result) + } catch (e) { + //fall back + return canonicalizeBaseUrl(window.origin) } - // testing/development path - return canonicalizeBaseUrl(overrideUrl); } export const defaultState: BackendState = { diff --git a/packages/demobank-ui/src/hooks/settings.ts b/packages/demobank-ui/src/hooks/settings.ts index 46b31bf2a..43e803726 100644 --- a/packages/demobank-ui/src/hooks/settings.ts +++ b/packages/demobank-ui/src/hooks/settings.ts @@ -15,8 +15,12 @@ */ import { + AmountString, Codec, buildCodecForObject, + codecForAmountString, + codecForBoolean, + codecForNumber, codecForString, codecOptional, } from "@gnu-taler/taler-util"; @@ -24,15 +28,21 @@ import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser"; interface Settings { currentWithdrawalOperationId: string | undefined; + showWithdrawalSuccess: boolean; + maxWithdrawalAmount: number; } export const codecForSettings = (): Codec => buildCodecForObject() .property("currentWithdrawalOperationId", codecOptional(codecForString())) + .property("showWithdrawalSuccess", (codecForBoolean())) + .property("maxWithdrawalAmount", codecForNumber()) .build("Settings"); const defaultSettings: Settings = { currentWithdrawalOperationId: undefined, + showWithdrawalSuccess: true, + maxWithdrawalAmount: 25 }; const DEMOBANK_SETTINGS_KEY = buildStorageKey( diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx index d234845a0..e682085ae 100644 --- a/packages/demobank-ui/src/pages/BankFrame.tsx +++ b/packages/demobank-ui/src/pages/BankFrame.tsx @@ -183,8 +183,28 @@ export function BankFrame({ {/* */} +
  • +
    + + + Show withdrawal confirmation + + + +
    +
  • + + +
  • Sites @@ -343,14 +363,14 @@ function StatusBanner(): VNode { switch (n.message.type) { case "error": return
    -
    -
    - -
    -
    -

    {n.message.title}

    +
    +
    + +
    +
    +

    {n.message.title}

    -

    +

    +
    + {n.message.description && +
    + {n.message.description} +
    + }
    - {n.message.description && -
    - {n.message.description} -
    - } -
    case "info": return
    diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx index e00daf278..e82e46eb2 100644 --- a/packages/demobank-ui/src/pages/HomePage.tsx +++ b/packages/demobank-ui/src/pages/HomePage.tsx @@ -95,8 +95,9 @@ export function WithdrawalOperationPage({ }): VNode { //FIXME: libeufin sandbox should return show to create the integration api endpoint //or return withdrawal uri from response + const baseUrl = getInitialBackendBaseURL() const uri = stringifyWithdrawUri({ - bankIntegrationApiBaseUrl: `${getInitialBackendBaseURL()}/integration-api`, + bankIntegrationApiBaseUrl: `${baseUrl}/integration-api`, withdrawalOperationId: operationId, }); const parsedUri = parseWithdrawUri(uri); @@ -155,7 +156,7 @@ export function handleNotOkResult( } case ErrorType.SERVER: { notify({ - type: "error", + type: "error", title: i18n.str`Server returned with error`, description: result.payload.error.description as TranslatedString, debug: JSON.stringify(result.payload), @@ -164,7 +165,7 @@ export function handleNotOkResult( } case ErrorType.UNREADABLE: { notify({ - type:"error", + type: "error", title: i18n.str`Unexpected error.`, description: i18n.str`Response from ${result.info?.url} is unreadable, http status: ${result.status}`, debug: JSON.stringify(result), @@ -173,7 +174,7 @@ export function handleNotOkResult( } case ErrorType.UNEXPECTED: { notify({ - type:"error", + type: "error", title: i18n.str`Unexpected error.`, description: i18n.str`Diagnostic from ${result.info?.url} is "${result.message}"`, debug: JSON.stringify(result), diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx index 28f00169d..80e7a78ac 100644 --- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx @@ -20,24 +20,23 @@ import { HttpStatusCode, Logger, PaytoUri, - PaytoUriGeneric, PaytoUriIBAN, PaytoUriTalerBank, TranslatedString, - WithdrawUriResult, + WithdrawUriResult } from "@gnu-taler/taler-util"; import { RequestError, notify, notifyError, + notifyInfo, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useMemo, useState } from "preact/hooks"; +import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; import { useAccessAnonAPI } from "../hooks/access.js"; import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js"; -import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; -import { Amount } from "./PaytoWireTransferForm.js"; const logger = new Logger("WithdrawalConfirmationQuestion"); @@ -71,6 +70,7 @@ export function WithdrawalConfirmationQuestion({ const { confirmWithdrawal, abortWithdrawal } = useAccessAnonAPI(); const [captchaAnswer, setCaptchaAnswer] = useState(); const answer = parseInt(captchaAnswer ?? "", 10); + const [busy, setBusy] = useState>() const errors = undefinedIfEmpty({ answer: !captchaAnswer ? i18n.str`Answer the question before continue` @@ -79,13 +79,15 @@ export function WithdrawalConfirmationQuestion({ : answer !== captchaNumbers.a + captchaNumbers.b ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.` : undefined, - }); + }) ?? busy; async function doTransfer() { try { + setBusy({}) await confirmWithdrawal( withdrawUri.withdrawalOperationId, ); + notifyInfo(i18n.str`Wire transfer completed!`) } catch (error) { if (error instanceof RequestError) { notify( @@ -107,10 +109,12 @@ export function WithdrawalConfirmationQuestion({ ) } } + setBusy(undefined) } async function doCancel() { try { + setBusy({}) await abortWithdrawal(withdrawUri.withdrawalOperationId); onAborted(); } catch (error) { @@ -132,6 +136,7 @@ export function WithdrawalConfirmationQuestion({ ) } } + setBusy(undefined) } return ( @@ -142,68 +147,6 @@ export function WithdrawalConfirmationQuestion({ Confirm the withdrawal operation
    -
    -
    -
    -

    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
    -
    {Amounts.stringifyValue(details.amount)}
    -
    -
    -
    -
    - -
    - +
    @@ -323,6 +262,68 @@ export function WithdrawalConfirmationQuestion({
    +
    +
    +
    +

    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
    +
    {Amounts.stringifyValue(details.amount)}
    +
    +
    +
    +
    + +
    diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx index 3b983c2d4..b48e3b1dc 100644 --- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx +++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx @@ -62,9 +62,10 @@ export function WithdrawalQRCode({ result.type === ErrorType.CLIENT && result.status === HttpStatusCode.NotFound ) { + clearCurrentWithdrawal() return
    operation not found
    ; } - onLoadNotOk(); + // onLoadNotOk(); return handleNotOkResult(i18n)(result); } const { data } = result; @@ -85,12 +86,12 @@ export function WithdrawalQRCode({

    { e.preventDefault(); clearCurrentWithdrawal() onContinue() - }}> + }}> {i18n.str`Continue`} @@ -99,35 +100,82 @@ export function WithdrawalQRCode({ } if (data.confirmation_done) { - return
    -

    {i18n.str`Operation completed`}

    - -
    -

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

    -

    - - You can close this page now or continue to the account page. - -

    -
    - - { - e.preventDefault(); - clearCurrentWithdrawal() - onContinue() - }}> - {i18n.str`Continue`} - + if (!settings.showWithdrawalSuccess) { + clearCurrentWithdrawal() + onContinue() + } + return
    +
    +
    +
    -
    -
    +
    + +
    +

    + + 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. + +

    +
    +
    +

    + + You can close this page now or continue to the account page. + +

    +
    +
    + +
    +
    + + + Do not show this again + + + +
    +
    +
    + +
    + + + } + if (!data.selection_done) { + return ( + { + notifyInfo(i18n.str`Operation canceled`); + clearCurrentWithdrawal() + onContinue() + }} + /> + ); + } + if (!data.selected_reserve_pub) { return
    the exchange is selcted but no reserve pub @@ -138,23 +186,10 @@ export function WithdrawalQRCode({ if (!account) { return
    - the exchange is selcted but no account + the exchange is selcted but no account
    } - if (!data.selection_done) { - return ( - { - notifyInfo(i18n.str`Operation canceled`); - clearCurrentWithdrawal() - onContinue() - }} - /> - ); - } - return ( ); } \ No newline at end of file