taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit df796e8d916778d843bc3c89de13a48ad68e4ddb
parent 15bd10b0d363f3d8b0ac6f633c6047f4e0d78ad1
Author: Sebastian <sebasjm@gmail.com>
Date:   Mon, 25 Nov 2024 12:25:15 -0300

prevent caching in withdrawal info

Diffstat:
Mpackages/bank-ui/src/Routing.tsx | 5-----
Mpackages/bank-ui/src/hooks/account.ts | 105++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mpackages/bank-ui/src/pages/PaymentOptions.tsx | 84++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mpackages/bank-ui/src/pages/PaytoWireTransferForm.tsx | 5+++--
Mpackages/bank-ui/src/pages/WalletWithdrawForm.tsx | 62++++++++++++++++++++++++++++++--------------------------------
5 files changed, 134 insertions(+), 127 deletions(-)

diff --git a/packages/bank-ui/src/Routing.tsx b/packages/bank-ui/src/Routing.tsx @@ -96,11 +96,6 @@ export function Routing(): VNode { timeLeftBeforeExpiration.d_ms - refreshWindow.d_ms, 0, ); - console.log({ - remain, - left: timeLeftBeforeExpiration.d_ms, - safe: refreshWindow.d_ms, - }); const timeoutId = setTimeout(async () => { const result = await authenticator( refreshSession.user, diff --git a/packages/bank-ui/src/hooks/account.ts b/packages/bank-ui/src/hooks/account.ts @@ -21,7 +21,7 @@ import { TalerHttpError, WithdrawalOperationStatus, } from "@gnu-taler/taler-util"; -import { useEffect, useState } from "preact/hooks"; +import { useCallback, useEffect, useMemo, useState } from "preact/hooks"; import { useSessionState } from "./session.js"; // FIX default import https://github.com/microsoft/TypeScript/issues/49189 @@ -72,63 +72,79 @@ export function revalidateWithdrawalDetails() { ); } +function useAsyncWithRetry<Res>( + fetcher: (() => Promise<Res>) | undefined, + retry?: (res: Res | undefined, err?: TalerHttpError | undefined) => boolean, +): { result: Res | undefined; error: TalerHttpError | undefined } { + const [result, setResult] = useState<Res>(); + const [error, setError] = useState<TalerHttpError>(); + const [retryCounter, setRetryCounter] = useState(0); + + useEffect(() => { + let unloaded = false; + if (fetcher) { + fetcher() + .then((resp) => { + if (unloaded) return; + setResult(resp); + }) + .catch((error: TalerHttpError) => { + if (unloaded) return; + setError(error); + }); + } + + return () => { + unloaded = true; + }; + }, [fetcher, retryCounter]); + + // retry on result or error + useEffect(() => { + if (retry && retry(result, error)) { + setRetryCounter((c) => c + 1); + } + }, [result, error]); + + return { result, error }; +} + export function useWithdrawalDetails(wid: string | undefined) { const { lib: { bank: api }, } = useBankCoreApiContext(); - const [latestStatus, setLatestStatus] = useState<{ - name: WithdrawalOperationStatus; - cacheBreaker: number; - }>(); - async function fetcher([wid, old_state]: [ - string, - WithdrawalOperationStatus | undefined, - ]) { - return await api.getWithdrawalById( - wid, - old_state === undefined ? undefined : { old_state, timeoutMs: 15000 }, - ); - } + const [latestStatus, setLatestStatus] = useState<WithdrawalOperationStatus>(); - const { data, error } = useSWR< - TalerCoreBankResultByMethod<"getWithdrawalById">, - TalerHttpError - >( - wid === undefined + const fetcher = useMemo(() => { + return wid === undefined ? undefined - : [ - wid, - latestStatus?.name, - latestStatus?.cacheBreaker, - "getWithdrawalById", - ], - fetcher, - { - refreshInterval: 3000, - refreshWhenHidden: false, - revalidateOnFocus: false, - revalidateOnReconnect: false, - refreshWhenOffline: false, - errorRetryCount: 0, - errorRetryInterval: 1, - shouldRetryOnError: false, - revalidateIfStale: true, - keepPreviousData: true, - }, - ); + : () => { + return api.getWithdrawalById( + wid, + latestStatus === undefined + ? undefined + : { old_state: latestStatus, timeoutMs: 5000 }, + ); + }; + }, [wid, latestStatus]); + + const { result: data, error } = useAsyncWithRetry(fetcher, (r, err) => { + // retry if error + let retry = err !== undefined; + // retry if still pending + retry = r !== undefined && r.type === "ok" && r.body.status === "pending"; + return retry; + }); const currentStatus = data !== undefined && data.type === "ok" ? data.body.status : undefined; useEffect(() => { - if (currentStatus !== undefined && currentStatus !== latestStatus?.name) { + if (currentStatus !== undefined && currentStatus !== latestStatus) { // withdrawal has a new state, save the current // and make the query again - setLatestStatus({ - name: currentStatus, - cacheBreaker: Date.now(), - }); + setLatestStatus(currentStatus); } }, [currentStatus]); @@ -319,6 +335,7 @@ export function useTransactions(account: string, initial?: number) { revalidateIfStale: false, revalidateOnFocus: false, revalidateOnReconnect: false, + shouldRetryOnError: true, }); if (error) return error; if (data === undefined) return undefined; diff --git a/packages/bank-ui/src/pages/PaymentOptions.tsx b/packages/bank-ui/src/pages/PaymentOptions.tsx @@ -14,57 +14,53 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { AmountJson, TalerError } from "@gnu-taler/taler-util"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; +import { AmountJson } from "@gnu-taler/taler-util"; +import { RouteDefinition, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { useEffect } from "preact/hooks"; -import { useWithdrawalDetails } from "../hooks/account.js"; import { useBankState } from "../hooks/bank-state.js"; -import { useSessionState } from "../hooks/session.js"; -import { RouteDefinition } from "@gnu-taler/web-util/browser"; import { PaytoWireTransferForm } from "./PaytoWireTransferForm.js"; import { WalletWithdrawForm } from "./WalletWithdrawForm.js"; const TALER_SCREEN_ID = 105; -function ShowOperationPendingTag({ - woid, - onOperationAlreadyCompleted, -}: { - woid: string; - onOperationAlreadyCompleted?: () => void; -}): VNode { - const { i18n } = useTranslationContext(); - const { state: credentials } = useSessionState(); - const result = useWithdrawalDetails(woid); - const loading = !result; - const error = - !loading && (result instanceof TalerError || result.type === "fail"); - const pending = - !loading && - !error && - result.body.status === "selected" && - // (result.body.status === "pending" || result.body.status === "selected") && - credentials.status === "loggedIn" && - credentials.username === result.body.username; +// function ShowOperationPendingTag({ +// woid, +// onOperationAlreadyCompleted, +// }: { +// woid: string; +// onOperationAlreadyCompleted?: () => void; +// }): VNode { +// const { i18n } = useTranslationContext(); +// const { state: credentials } = useSessionState(); +// const result = useWithdrawalDetails(woid); +// const loading = !result; +// const error = +// !loading && (result instanceof TalerError || result.type === "fail"); +// const pending = +// !loading && +// !error && +// result.body.status === "selected" && +// // (result.body.status === "pending" || result.body.status === "selected") && +// credentials.status === "loggedIn" && +// credentials.username === result.body.username; - if (error || !pending) { - return <Fragment />; - } +// if (error || !pending) { +// return <Fragment />; +// } - return ( - <span class="flex items-center gap-x-1.5 w-fit rounded-md bg-yellow-100 px-2 py-1 text-xs font-medium text-yellow-700 whitespace-pre"> - <svg - class="h-1.5 w-1.5 fill-yellow-500" - viewBox="0 0 6 6" - aria-hidden="true" - > - <circle cx="3" cy="3" r="3" /> - </svg> - <i18n.Translate>Pending operation</i18n.Translate> - </span> - ); -} +// return ( +// <span class="flex items-center gap-x-1.5 w-fit rounded-md bg-yellow-100 px-2 py-1 text-xs font-medium text-yellow-700 whitespace-pre"> +// <svg +// class="h-1.5 w-1.5 fill-yellow-500" +// viewBox="0 0 6 6" +// aria-hidden="true" +// > +// <circle cx="3" cy="3" r="3" /> +// </svg> +// <i18n.Translate>Pending operation</i18n.Translate> +// </span> +// ); +// } /** * Let the user choose a payment option, @@ -150,7 +146,7 @@ export function PaymentOptions({ extension </i18n.Translate> </div> - {!!bankState.currentWithdrawalOperationId && ( + {/* {!!bankState.currentWithdrawalOperationId && ( <ShowOperationPendingTag woid={bankState.currentWithdrawalOperationId} onOperationAlreadyCompleted={() => { @@ -160,7 +156,7 @@ export function PaymentOptions({ ); }} /> - )} + )} */} </div> </label> </a> diff --git a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx @@ -543,8 +543,9 @@ export function PaytoWireTransferForm({ <b style={{ color: "red" }}> *</b> </label> <div class="mt-2"> - <input - type="text" + <textarea + type="textarea" + rows={3} class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" name="subject" id="subject" diff --git a/packages/bank-ui/src/pages/WalletWithdrawForm.tsx b/packages/bank-ui/src/pages/WalletWithdrawForm.tsx @@ -20,27 +20,27 @@ import { Amounts, HttpStatusCode, TalerCorebankApi, - TalerError, TranslatedString, assertUnreachable, - parseWithdrawUri, + parseWithdrawUri } from "@gnu-taler/taler-util"; import { Attention, LocalNotificationBanner, - notifyError, + RouteDefinition, ShowInputErrorLabel, + notifyError, + useBankCoreApiContext, useLocalNotification, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; import { forwardRef } from "preact/compat"; -import { useEffect, useState } from "preact/hooks"; -import { useBankCoreApiContext } from "@gnu-taler/web-util/browser"; -import { useSessionState } from "../hooks/session.js"; +import { useState } from "preact/hooks"; +import { useSettingsContext } from "../context/settings.js"; import { useBankState } from "../hooks/bank-state.js"; import { usePreferences } from "../hooks/preferences.js"; -import { RouteDefinition } from "@gnu-taler/web-util/browser"; +import { useSessionState } from "../hooks/session.js"; import { undefinedIfEmpty } from "../utils.js"; import { OperationState } from "./OperationState/index.js"; import { @@ -48,8 +48,6 @@ import { RenderAmount, doAutoFocus, } from "./PaytoWireTransferForm.js"; -import { useSettingsContext } from "../context/settings.js"; -import { useWithdrawalDetails } from "../hooks/account.js"; const TALER_SCREEN_ID = 112; @@ -129,31 +127,31 @@ function OldWithdrawalForm({ `${settings.defaultSuggestedAmount ?? 1}`, ); const [notification, notify, handleError] = useLocalNotification(); - const result = useWithdrawalDetails(bankState.currentWithdrawalOperationId); - const loading = !result; - const error = - !loading && (result instanceof TalerError || result.type === "fail"); - const pending = !loading && !error && result.body.status === "pending"; + // const result = useWithdrawalDetails(bankState.currentWithdrawalOperationId); + // const loading = !result; + // const error = + // !loading && (result instanceof TalerError || result.type === "fail"); + // const pending = !loading && !error && result.body.status === "pending"; - if (pending) { - // FIXME: doing the preventDefault is not optimal + // if (pending) { + // // FIXME: doing the preventDefault is not optimal - // const suri = stringifyWithdrawUri({ - // bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl, - // withdrawalOperationId: bankState.currentWithdrawalOperationId, - // }); - // const uri = parseWithdrawUri(suri)! - return ( - <ThereIsAnOperationWarning - onClose={() => { - updateBankState("currentWithdrawalOperationId", undefined); - }} - routeOperationDetails={routeOperationDetails} - wopid={bankState.currentWithdrawalOperationId!} - focus - /> - ); - } + // // const suri = stringifyWithdrawUri({ + // // bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl, + // // withdrawalOperationId: bankState.currentWithdrawalOperationId, + // // }); + // // const uri = parseWithdrawUri(suri)! + // return ( + // <ThereIsAnOperationWarning + // onClose={() => { + // updateBankState("currentWithdrawalOperationId", undefined); + // }} + // routeOperationDetails={routeOperationDetails} + // wopid={bankState.currentWithdrawalOperationId!} + // focus + // /> + // ); + // } const trimmedAmountStr = amountStr?.trim();