summaryrefslogtreecommitdiff
path: root/packages/bank-ui/src/pages/OperationState/state.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/bank-ui/src/pages/OperationState/state.ts')
-rw-r--r--packages/bank-ui/src/pages/OperationState/state.ts234
1 files changed, 234 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..19c097d18
--- /dev/null
+++ b/packages/bank-ui/src/pages/OperationState/state.ts
@@ -0,0 +1,234 @@
+/*
+ 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 "@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<State> {
+ 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<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,
+ };
+ };
+}