diff options
Diffstat (limited to 'packages/demobank-ui/src/pages/OperationState/state.ts')
-rw-r--r-- | packages/demobank-ui/src/pages/OperationState/state.ts | 162 |
1 files changed, 98 insertions, 64 deletions
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts b/packages/demobank-ui/src/pages/OperationState/state.ts index 4be680377..148571ec9 100644 --- a/packages/demobank-ui/src/pages/OperationState/state.ts +++ b/packages/demobank-ui/src/pages/OperationState/state.ts @@ -14,20 +14,26 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util"; +import { Amounts, HttpStatusCode, TalerError, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util"; import { RequestError, notify, notifyError, notifyInfo, useTranslationContext, utils } from "@gnu-taler/web-util/browser"; import { useEffect, useState } from "preact/hooks"; -import { useAccessAPI, useAccessAnonAPI, useWithdrawalDetails } from "../../hooks/access.js"; -import { getInitialBackendBaseURL } from "../../hooks/backend.js"; +import { useBankCoreApiContext } from "../../context/config.js"; +import { useWithdrawalDetails } from "../../hooks/access.js"; +import { useBackendState } from "../../hooks/backend.js"; import { useSettings } from "../../hooks/settings.js"; import { buildRequestErrorMessage } from "../../utils.js"; import { Props, State } from "./index.js"; +import { assertUnreachable } from "../HomePage.js"; +import { mutate } from "swr"; export function useComponentState({ currency, onClose }: Props): utils.RecursiveState<State> { const { i18n } = useTranslationContext(); const [settings, updateSettings] = useSettings() - const { createWithdrawal } = useAccessAPI(); - const { abortWithdrawal, confirmWithdrawal } = useAccessAnonAPI(); + const { state: credentials } = useBackendState() + const creds = credentials.status !== "loggedIn" ? undefined : credentials + const { api } = useBankCoreApiContext() + // const { createWithdrawal } = useAccessAPI(); + // const { abortWithdrawal, confirmWithdrawal } = useAccessAnonAPI(); const [busy, setBusy] = useState<Record<string, undefined>>() const amount = settings.maxWithdrawalAmount @@ -37,27 +43,33 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`) try { - const result = await createWithdrawal({ + if (!creds) return; + const resp = await api.createWithdrawal(creds, { amount: Amounts.stringify(parsedAmount), }); - const uri = parseWithdrawUri(result.data.taler_withdraw_uri); + if (resp.type === "fail") { + switch (resp.case) { + case "insufficient-funds": return notify({ + type: "error", + title: i18n.str`The operation was rejected due to insufficient funds`, + description: resp.detail.hint as TranslatedString, + debug: resp.detail, + }); + default: assertUnreachable(resp.case) + } + } + + const uri = parseWithdrawUri(resp.body.taler_withdraw_uri); if (!uri) { return notifyError( i18n.str`Server responded with an invalid withdraw URI`, - i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`); + i18n.str`Withdraw URI: ${resp.body.taler_withdraw_uri}`); } else { updateSettings("currentWithdrawalOperationId", uri.withdrawalOperationId) } } catch (error) { - if (error instanceof RequestError) { - notify( - buildRequestErrorMessage(i18n, error.cause, { - onClientError: (status) => - status === HttpStatusCode.Forbidden - ? i18n.str`The operation was rejected due to insufficient funds` - : undefined, - }), - ); + if (error instanceof TalerError) { + notify(buildRequestErrorMessage(i18n, error)) } else { notifyError( i18n.str`Operation failed, please report`, @@ -76,8 +88,6 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive } }, [settings.fastWithdrawal, amount]) - const baseUrl = getInitialBackendBaseURL() - if (!withdrawalOperationId) { return { status: "loading", @@ -90,18 +100,24 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive async function doAbort() { try { setBusy({}) - await abortWithdrawal(wid); - onClose(); + const resp = await api.abortWithdrawalById(wid); + setBusy(undefined) + if (resp.type === "ok") { + onClose(); + } else { + switch (resp.case) { + case "previously-confirmed": return notify({ + type: "error", + title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`, + description: resp.detail.hint as TranslatedString, + debug: resp.detail, + }) + default: assertUnreachable(resp.case) + } + } } 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, - }), - ); + if (error instanceof TalerError) { + notify(buildRequestErrorMessage(i18n, error)) } else { notifyError( i18n.str`Operation failed, please report`, @@ -111,28 +127,38 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive ) } } - setBusy(undefined) } async function doConfirm() { try { setBusy({}) - await confirmWithdrawal(wid); - if (!settings.showWithdrawalSuccess) { - notifyInfo(i18n.str`Wire transfer completed!`) + const resp = await api.confirmWithdrawalById(wid); + if (resp.type === "ok") { + mutate(() => true)//clean withdrawal state + if (!settings.showWithdrawalSuccess) { + notifyInfo(i18n.str`Wire transfer completed!`) + } + setBusy(undefined) + } else { + switch (resp.case) { + case "previously-aborted": return notify({ + type: "error", + title: i18n.str`The withdrawal has been aborted previously and can't be confirmed`, + description: resp.detail.hint as TranslatedString, + debug: resp.detail, + }) + case "no-exchange-or-reserve-selected": return notify({ + type: "error", + title: i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`, + description: resp.detail.hint as TranslatedString, + debug: resp.detail, + }) + default: assertUnreachable(resp) + } } } 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, - }), - ); + if (error instanceof TalerError) { + notify(buildRequestErrorMessage(i18n, error)) } else { notifyError( i18n.str`Operation failed, please report`, @@ -142,11 +168,10 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive ) } } - setBusy(undefined) } - const bankIntegrationApiBaseUrl = `${baseUrl}/taler-integration` + const uri = stringifyWithdrawUri({ - bankIntegrationApiBaseUrl, + bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl, withdrawalOperationId, }); const parsedUri = parseWithdrawUri(uri); @@ -161,32 +186,43 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive return (): utils.RecursiveState<State> => { const result = useWithdrawalDetails(withdrawalOperationId); - const shouldCreateNewOperation = !result.ok && !result.loading && result.info.status === HttpStatusCode.NotFound + const shouldCreateNewOperation = result && !(result instanceof TalerError) useEffect(() => { if (shouldCreateNewOperation) { doSilentStart() } }, []) - if (!result.ok) { - if (result.loading) { - return { - status: "loading", - error: undefined - } - } - if (result.info.status === HttpStatusCode.NotFound) { - return { - status: "loading", - error: undefined, - } + if (!result) { + return { + status: "loading", + error: undefined } + } + if (result instanceof TalerError) { return { status: "loading-error", error: result } } - const { data } = result; + + if (result.type === "fail") { + switch (result.case) { + case "not-found": { + return { + status: "aborted", + error: undefined, + onClose: async () => { + updateSettings("currentWithdrawalOperationId", undefined) + onClose() + }, + } + } + default: assertUnreachable(result.case) + } + } + + const { body: data } = result; if (data.aborted) { return { status: "aborted", @@ -247,8 +283,6 @@ export function useComponentState({ currency, onClose }: Props): utils.Recursive } } - - // goToConfirmOperation(withdrawalOperationId) return { status: "need-confirmation", error: undefined, |