/* 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 */ 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 "@gnu-taler/web-util/browser"; 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 { const [settings] = usePreferences(); const [bankState, updateBankState] = useBankState(); const { state: credentials } = useSessionState(); const creds = credentials.status !== "loggedIn" ? undefined : credentials; const { lib: { 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().href, withdrawalOperationId, }); const parsedUri = parseWithdrawUri(uri); if (!parsedUri) { return { status: "invalid-withdrawal", error: undefined, uri, }; } return (): utils.RecursiveState => { 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, }; }; }