diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/cta/Withdraw/state.ts')
-rw-r--r-- | packages/taler-wallet-webextension/src/cta/Withdraw/state.ts | 218 |
1 files changed, 199 insertions, 19 deletions
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts index 849dd5cca..3b138e74d 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts @@ -15,16 +15,210 @@ */ -import { Amounts } from "@gnu-taler/taler-util"; +import { Amounts, parsePaytoUri } from "@gnu-taler/taler-util"; import { TalerError } from "@gnu-taler/taler-wallet-core"; import { useState } from "preact/hooks"; import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js"; import { buildTermsOfServiceState } from "../../utils/index.js"; import * as wxApi from "../../wxApi.js"; -import { Props, State } from "./index.js"; +import { PropsFromURI, PropsFromParams, State } from "./index.js"; -export function useComponentState( - { talerWithdrawUri, cancel }: Props, +export function useComponentStateFromParams( + { amount, cancel }: PropsFromParams, + api: typeof wxApi, +): State { + + const [ageRestricted, setAgeRestricted] = useState(0); + + const exchangeHook = useAsyncAsHook(api.listExchanges); + + const exchangeHookDep = + !exchangeHook || exchangeHook.hasError || !exchangeHook.response + ? undefined + : exchangeHook.response; + + const chosenAmount = Amounts.parseOrThrow(amount); + + // get the first exchange with the currency as the default one + const exchange = exchangeHookDep ? exchangeHookDep.exchanges.find(e => e.currency === chosenAmount.currency) : undefined + /** + * For the exchange selected, bring the status of the terms of service + */ + const terms = useAsyncAsHook(async () => { + if (!exchange) return undefined + + const exchangeTos = await api.getExchangeTos(exchange.exchangeBaseUrl, ["text/xml"]); + + const state = buildTermsOfServiceState(exchangeTos); + + return { state }; + }, [exchangeHookDep]); + + /** + * With the exchange and amount, ask the wallet the information + * about the withdrawal + */ + const amountHook = useAsyncAsHook(async () => { + if (!exchange) return undefined + + const info = await api.getExchangeWithdrawalInfo({ + exchangeBaseUrl: exchange.exchangeBaseUrl, + amount: chosenAmount, + tosAcceptedFormat: ["text/xml"], + }); + + const withdrawAmount = { + raw: Amounts.parseOrThrow(info.withdrawalAmountRaw), + effective: Amounts.parseOrThrow(info.withdrawalAmountEffective), + } + + return { amount: withdrawAmount }; + }, [exchangeHookDep]); + + const [reviewing, setReviewing] = useState<boolean>(false); + const [reviewed, setReviewed] = useState<boolean>(false); + + const [withdrawError, setWithdrawError] = useState<TalerError | undefined>( + undefined, + ); + const [doingWithdraw, setDoingWithdraw] = useState<boolean>(false); + const [withdrawCompleted, setWithdrawCompleted] = useState<boolean>(false); + + if (!exchangeHook) return { status: "loading", error: undefined } + if (exchangeHook.hasError) { + return { + status: "loading-uri", + error: exchangeHook, + }; + } + + if (!exchange) { + return { + status: "loading-exchange", + error: { + hasError: true, + operational: false, + message: "ERROR_NO-DEFAULT-EXCHANGE", + }, + }; + } + + async function doWithdrawAndCheckError(): Promise<void> { + if (!exchange) return; + + try { + setDoingWithdraw(true); + + const response = await wxApi.acceptManualWithdrawal( + exchange.exchangeBaseUrl, + Amounts.stringify(amount), + ); + + setWithdrawCompleted(true); + } catch (e) { + if (e instanceof TalerError) { + setWithdrawError(e); + } + } + setDoingWithdraw(false); + } + + if (!amountHook) { + return { status: "loading", error: undefined } + } + if (amountHook.hasError) { + return { + status: "loading-info", + error: amountHook, + }; + } + if (!amountHook.response) { + return { status: "loading", error: undefined }; + } + if (withdrawCompleted) { + return { status: "completed", error: undefined }; + } + + const withdrawalFee = Amounts.sub( + amountHook.response.amount.raw, + amountHook.response.amount.effective, + ).amount; + const toBeReceived = amountHook.response.amount.effective; + + const { state: termsState } = (!terms + ? undefined + : terms.hasError + ? undefined + : terms.response) || { state: undefined }; + + async function onAccept(accepted: boolean): Promise<void> { + if (!termsState || !exchange) return; + + try { + await api.setExchangeTosAccepted( + exchange.exchangeBaseUrl, + accepted ? termsState.version : undefined, + ); + setReviewed(accepted); + } catch (e) { + if (e instanceof Error) { + //FIXME: uncomment this and display error + // setErrorAccepting(e.message); + } + } + } + + const mustAcceptFirst = + termsState !== undefined && + (termsState.status === "changed" || termsState.status === "new"); + + const ageRestrictionOptions: Record<string, string> | undefined = "6:12:18" + .split(":") + .reduce((p, c) => ({ ...p, [c]: `under ${c}` }), {}); + + if (ageRestrictionOptions) { + ageRestrictionOptions["0"] = "Not restricted"; + } + + //TODO: calculate based on exchange info + const ageRestrictionEnabled = false; + const ageRestriction = ageRestrictionEnabled ? { + list: ageRestrictionOptions, + value: String(ageRestricted), + onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)), + } : undefined; + + return { + status: "success", + error: undefined, + exchangeUrl: exchange.exchangeBaseUrl, + toBeReceived, + withdrawalFee, + chosenAmount, + ageRestriction, + doWithdrawal: { + onClick: + doingWithdraw || (mustAcceptFirst && !reviewed) + ? undefined + : doWithdrawAndCheckError, + error: withdrawError, + }, + tosProps: !termsState + ? undefined + : { + onAccept, + onReview: setReviewing, + reviewed: reviewed, + reviewing: reviewing, + terms: termsState, + }, + mustAcceptFirst, + cancel, + }; +} + +export function useComponentStateFromURI( + { talerWithdrawUri, cancel }: PropsFromURI, api: typeof wxApi, ): State { const [ageRestricted, setAgeRestricted] = useState(0); @@ -50,21 +244,6 @@ export function useComponentState( ? undefined : uriInfoHook.response; - // const { amount, thisExchange } = useMemo(() => { - // if (!uriHookDep) - // return { - // amount: undefined, - // thisExchange: undefined, - // thisCurrencyExchanges: [], - // }; - - // const { uriInfo } = uriHookDep; - - // const amount = uriHookDep ? Amounts.parseOrThrow(uriHookDep.amount) : undefined; - // const thisExchange = uriHookDep?.thisExchange; - - // return { amount, thisExchange }; - // }, [uriHookDep]); /** * For the exchange selected, bring the status of the terms of service @@ -118,6 +297,7 @@ export function useComponentState( } const { amount, thisExchange } = uriInfoHook.response + const chosenAmount = Amounts.parseOrThrow(amount); if (!thisExchange) { |