diff options
Diffstat (limited to 'packages/bank-ui/src/pages/OperationState/state.ts')
-rw-r--r-- | packages/bank-ui/src/pages/OperationState/state.ts | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/packages/bank-ui/src/pages/OperationState/state.ts b/packages/bank-ui/src/pages/OperationState/state.ts new file mode 100644 index 000000000..5baf2d51c --- /dev/null +++ b/packages/bank-ui/src/pages/OperationState/state.ts @@ -0,0 +1,232 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import { + Amounts, + HttpStatusCode, + TalerCoreBankErrorsByMethod, + TalerError, + assertUnreachable, + parsePaytoUri, + parseWithdrawUri, + stringifyWithdrawUri, +} from "@gnu-taler/taler-util"; +import { utils } from "@gnu-taler/web-util/browser"; +import { useEffect, useState } from "preact/hooks"; +import { mutate } from "swr"; +import { useBankCoreApiContext } from "../../context/config.js"; +import { useWithdrawalDetails } from "../../hooks/account.js"; +import { useSessionState } from "../../hooks/session.js"; +import { useBankState } from "../../hooks/bank-state.js"; +import { usePreferences } from "../../hooks/preferences.js"; +import { Props, State } from "./index.js"; + +export function useComponentState({ + currency, + routeClose, + onAbort, + routeHere, + onAuthorizationRequired, +}: Props): utils.RecursiveState<State> { + const [settings] = usePreferences(); + const [bankState, updateBankState] = useBankState(); + const { state: credentials } = useSessionState(); + const creds = credentials.status !== "loggedIn" ? undefined : credentials; + const { bank } = useBankCoreApiContext(); + + const [failure, setFailure] = useState< + TalerCoreBankErrorsByMethod<"createWithdrawal"> | undefined + >(); + const amount = settings.maxWithdrawalAmount; + + async function doSilentStart() { + // FIXME: if amount is not enough use balance + const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`); + if (!creds) return; + const resp = await bank.createWithdrawal(creds, { + amount: Amounts.stringify(parsedAmount), + }); + if (resp.type === "fail") { + setFailure(resp); + return; + } + updateBankState("currentWithdrawalOperationId", resp.body.withdrawal_id); + } + + const withdrawalOperationId = bankState.currentWithdrawalOperationId; + useEffect(() => { + if (withdrawalOperationId === undefined) { + doSilentStart(); + } + }, [settings.fastWithdrawal, amount]); + + if (failure) { + return { + status: "failed", + error: failure, + }; + } + + if (!withdrawalOperationId) { + return { + status: "loading", + error: undefined, + }; + } + + const wid = withdrawalOperationId; + + async function doAbort() { + if (!creds) return; + const resp = await bank.abortWithdrawalById(creds, wid); + if (resp.type === "ok") { + // updateBankState("currentWithdrawalOperationId", undefined) + onAbort(); + } else { + return resp; + } + } + + async function doConfirm(): Promise< + TalerCoreBankErrorsByMethod<"confirmWithdrawalById"> | undefined + > { + if (!creds) return; + const resp = await bank.confirmWithdrawalById(creds, wid); + if (resp.type === "ok") { + mutate(() => true); //clean withdrawal state + } else { + return resp; + } + } + + const uri = stringifyWithdrawUri({ + bankIntegrationApiBaseUrl: bank.getIntegrationAPI(), + withdrawalOperationId, + }); + const parsedUri = parseWithdrawUri(uri); + if (!parsedUri) { + return { + status: "invalid-withdrawal", + error: undefined, + uri, + }; + } + + return (): utils.RecursiveState<State> => { + const result = useWithdrawalDetails(withdrawalOperationId); + const shouldCreateNewOperation = result && !(result instanceof TalerError); + + useEffect(() => { + if (shouldCreateNewOperation) { + doSilentStart(); + } + }, []); + if (!result) { + return { + status: "loading", + error: undefined, + }; + } + if (result instanceof TalerError) { + return { + status: "loading-error", + error: result, + }; + } + + if (result.type === "fail") { + switch (result.case) { + case HttpStatusCode.BadRequest: + case HttpStatusCode.NotFound: { + return { + status: "aborted", + error: undefined, + routeClose, + }; + } + default: + assertUnreachable(result); + } + } + + const { body: data } = result; + if (data.status === "aborted") { + return { + status: "aborted", + error: undefined, + routeClose, + }; + } + + if (data.status === "confirmed") { + if (!settings.showWithdrawalSuccess) { + updateBankState("currentWithdrawalOperationId", undefined); + // onClose() + } + return { + status: "confirmed", + error: undefined, + routeClose, + }; + } + + if (data.status === "pending") { + return { + status: "ready", + error: undefined, + uri: parsedUri, + routeClose, + onAbort: !creds + ? async () => { + onAbort(); + return undefined; + } + : doAbort, + }; + } + + if (!data.selected_reserve_pub) { + return { + status: "invalid-reserve", + error: undefined, + reserve: data.selected_reserve_pub, + }; + } + + const account = !data.selected_exchange_account + ? undefined + : parsePaytoUri(data.selected_exchange_account); + + if (!account) { + return { + status: "invalid-payto", + error: undefined, + payto: data.selected_exchange_account, + }; + } + + return { + status: "need-confirmation", + error: undefined, + routeHere, + onAuthorizationRequired, + account: data.username, + id: withdrawalOperationId, + onAbort: !creds ? undefined : doAbort, + onConfirm: !creds ? undefined : doConfirm, + }; + }; +} |