/*
This file is part of GNU Taler
(C) 2022 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, FailCasesByMethod, TalerCoreBankErrorsByMethod, TalerError, TalerErrorDetail, TranslatedString, parsePaytoUri, parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
import { notify, notifyError, notifyInfo, useTranslationContext, 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/access.js";
import { useBackendState } from "../../hooks/backend.js";
import { usePreferences } from "../../hooks/preferences.js";
import { assertUnreachable } from "../WithdrawalOperationPage.js";
import { Props, State } from "./index.js";
export function useComponentState({ currency, onClose }: Props): utils.RecursiveState {
const [settings, updateSettings] = usePreferences()
const { state: credentials } = useBackendState()
const creds = credentials.status !== "loggedIn" ? undefined : credentials
const { api } = useBankCoreApiContext()
const [busy, setBusy] = useState>()
const [failure, setFailure] = useState | 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 api.createWithdrawal(creds, {
amount: Amounts.stringify(parsedAmount),
});
if (resp.type === "fail") {
setFailure(resp)
return;
}
updateSettings("currentWithdrawalOperationId", resp.body.withdrawal_id)
}
const withdrawalOperationId = settings.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 api.abortWithdrawalById(creds, wid);
if (resp.type === "ok") {
updateSettings("currentWithdrawalOperationId", undefined)
onClose();
} else {
return resp;
}
}
async function doConfirm(): Promise | undefined> {
if (!creds) return;
setBusy({})
const resp = await api.confirmWithdrawalById(creds, wid);
setBusy(undefined)
if (resp.type === "ok") {
mutate(() => true)//clean withdrawal state
} else {
return resp;
}
}
const uri = stringifyWithdrawUri({
bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
withdrawalOperationId,
});
const parsedUri = parseWithdrawUri(uri);
if (!parsedUri) {
return {
status: "invalid-withdrawal",
error: undefined,
uri,
onClose,
}
}
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 "invalid-id":
case "not-found": {
return {
status: "aborted",
error: undefined,
onClose: async () => {
updateSettings("currentWithdrawalOperationId", undefined)
onClose()
},
}
}
default: assertUnreachable(result)
}
}
const { body: data } = result;
if (data.status === "aborted") {
return {
status: "aborted",
error: undefined,
onClose: async () => {
updateSettings("currentWithdrawalOperationId", undefined)
onClose()
},
}
}
if (data.status === "confirmed") {
if (!settings.showWithdrawalSuccess) {
updateSettings("currentWithdrawalOperationId", undefined)
onClose()
}
return {
status: "confirmed",
error: undefined,
onClose: async () => {
updateSettings("currentWithdrawalOperationId", undefined)
onClose()
},
}
}
if (data.status === "pending") {
return {
status: "ready",
error: undefined,
uri: parsedUri,
onClose: !creds ? (async () => {
onClose();
return undefined
}) : doAbort,
}
}
if (!data.selected_reserve_pub) {
return {
status: "invalid-reserve",
error: undefined,
reserve: data.selected_reserve_pub,
onClose,
}
}
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,
onClose,
}
}
return {
status: "need-confirmation",
error: undefined,
account: data.username,
onAbort: !creds ? undefined : doAbort,
busy: !!busy,
onConfirm: !creds ? undefined : doConfirm
}
}
}