commit df796e8d916778d843bc3c89de13a48ad68e4ddb
parent 15bd10b0d363f3d8b0ac6f633c6047f4e0d78ad1
Author: Sebastian <sebasjm@gmail.com>
Date: Mon, 25 Nov 2024 12:25:15 -0300
prevent caching in withdrawal info
Diffstat:
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();