taler-typescript-core

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

commit aba933b1d11ce531a76db78d8ff34e88f8a54950
parent ae440c305aa634896507dde8d8871fb169bc523e
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Thu, 26 Mar 2026 16:12:52 -0300

fix #11306

Diffstat:
Mpackages/merchant-backoffice-ui/src/hooks/order.ts | 97++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx | 18++++++++----------
Mpackages/web-util/src/hooks/useAsync.ts | 39++++++++++++++++++++-------------------
3 files changed, 85 insertions(+), 69 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/hooks/order.ts b/packages/merchant-backoffice-ui/src/hooks/order.ts @@ -22,11 +22,16 @@ import { ListOrdersRequestParams, TalerError, TalerHttpError, - TalerMerchantManagementResultByMethod + TalerMerchantManagementResultByMethod, } from "@gnu-taler/taler-util"; -import { buildPaginatedResult, LONG_POLL_DELAY, useLongPolling } from "@gnu-taler/web-util/browser"; +import { + buildPaginatedResult, + LONG_POLL_DELAY, + useLongPolling, +} from "@gnu-taler/web-util/browser"; import _useSWR, { mutate, SWRHook } from "swr"; import { useSessionContext } from "../context/session.js"; +import { useRef } from "preact/hooks"; const useSWR = _useSWR as unknown as SWRHook; @@ -56,7 +61,7 @@ export function useOrderDetails(oderId: string) { export function useOrderDetailsWithLongPoll(orderId: string) { const { state, lib } = useSessionContext(); - const token = state.token + const token = state.token; async function fetcher([dId, token]: [string, AccessToken]) { return await lib.instance.getOrderDetails(token, dId); @@ -71,20 +76,20 @@ export function useOrderDetailsWithLongPoll(orderId: string) { data, (result) => { if (!result || result.type === "fail") return undefined; - return result.body + return result.body; }, async (latestStatus) => { const r = await lib.instance.getOrderDetails(token!, orderId, { longpoll: { etag: latestStatus.etag!, - timeout: LONG_POLL_DELAY - } + timeout: LONG_POLL_DELAY, + }, }); - mutate(r, { revalidate: false }) - return r + mutate(r, { revalidate: false }); + return r; }, [orderId], - { minTime: LONG_POLL_DELAY } + { minTime: LONG_POLL_DELAY }, ); if (error) return error; @@ -108,56 +113,68 @@ export function revalidateInstanceOrders() { } export function useInstanceOrders( args?: InstanceOrderFilter, - updatePosition: (d: string | undefined) => void = () => { }, + updatePosition: (d: string | undefined) => void = () => {}, ) { const { state, lib } = useSessionContext(); - const token = state.token! - const params: ListOrdersRequestParams = { + const token = state.token!; + + const cacheKey = [ + args?.position, + args?.paid, + args?.refunded, + args?.wired, + args?.date, + "listOrders", + ]; + + console.log({ cacheKey: cacheKey.join(",") }); + + async function fetcher([position, paid, refunded, wired, date]: any) { + return await lib.instance.listOrders(token, { limit: PAGINATED_LIST_REQUEST, - offset: args?.position, + offset: position, order: "dec", - paid: args?.paid, - refunded: args?.refunded, - wired: args?.wired, - date: args?.date, - } - async function fetcher([]) { - return await lib.instance.listOrders(token, params); + paid: paid, + refunded: refunded, + wired: wired, + date: date, + }); } - const cacheKey = [ - args?.position, - args?.paid, - args?.refunded, - args?.wired, - args?.date, - "listOrders", - ]; - const { data, error } = useSWR< TalerMerchantManagementResultByMethod<"listOrders">, TalerHttpError - >( - cacheKey, - fetcher, - ); + >(cacheKey, fetcher); const result = useLongPolling( data, (result) => { if (!result || result.type === "fail") return undefined; - return result.body + return result.body; }, - async (latestStatus) => { - const r = await lib.instance.listOrders(token, {...params, timeout: LONG_POLL_DELAY}); - mutate(r, { revalidate: false }) - return r + async (latestStatus, args) => { + console.log(args) + const params: ListOrdersRequestParams = { + limit: PAGINATED_LIST_REQUEST, + offset: args[0], + order: "dec", + paid: args[1], + refunded: args[2], + wired: args[3], + date: args[4], + }; + // console.log("PARAMS:", params) + const r = await lib.instance.listOrders(token, { + ...params, + timeout: LONG_POLL_DELAY, + }); + mutate(r, { revalidate: false }); + return r; }, cacheKey, - { minTime: LONG_POLL_DELAY } + { minTime: LONG_POLL_DELAY }, ); - if (error || result instanceof TalerError) return error; if (result === undefined) return undefined; if (result.type !== "ok") return result; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx @@ -32,29 +32,27 @@ import { useLocalNotificationBetter, useTranslationContext, } from "@gnu-taler/web-util/browser"; +import { format } from "date-fns"; import { VNode, h } from "preact"; import { useState } from "preact/hooks"; import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js"; import { Loading } from "../../../../components/exception/loading.js"; import { JumpToElementById } from "../../../../components/form/JumpToElementById.js"; +import { DatePicker } from "../../../../components/picker/DatePicker.js"; +import { Tooltip } from "../../../../components/Tooltip.js"; import { useSessionContext } from "../../../../context/session.js"; import { - InstanceOrderFilter, useInstanceOrders, - useOrderDetails, + useOrderDetails } from "../../../../hooks/order.js"; -import { LoginPage } from "../../../login/index.js"; -import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js"; -import { ListPage } from "./ListPage.js"; -import { RefundModal } from "./Table.js"; -import { InputDate } from "../../../../components/form/InputDate.js"; import { dateFormatForPreferences, usePreference, } from "../../../../hooks/preference.js"; -import { format } from "date-fns"; -import { DatePicker } from "../../../../components/picker/DatePicker.js"; -import { Tooltip } from "../../../../components/Tooltip.js"; +import { LoginPage } from "../../../login/index.js"; +import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js"; +import { ListPage } from "./ListPage.js"; +import { RefundModal } from "./Table.js"; const TALER_SCREEN_ID = 46; diff --git a/packages/web-util/src/hooks/useAsync.ts b/packages/web-util/src/hooks/useAsync.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ import { opUnknownFailure, TalerError } from "@gnu-taler/taler-util"; -import { useEffect, useState } from "preact/hooks"; +import { useCallback, useEffect, useState } from "preact/hooks"; /** * convert the async function into preact hook @@ -43,7 +43,7 @@ export function useAsync<Res>( if (error instanceof TalerError) { setError(error); } else { - setError(TalerError.fromException(error)) + setError(TalerError.fromException(error)); } }); } @@ -52,17 +52,16 @@ export function useAsync<Res>( }; }, deps); - if (error) return (error); + if (error) return error; if (!data) return undefined; return data; } export const LONG_POLL_DELAY = 15000; - // FIXME: the problem with this compared with useSWR is that -// if the hook is called from more than one place then -// you will have multiple request. This needs to be merged, maybe based on a +// if the hook is called from more than one place then +// you will have multiple request. This needs to be merged, maybe based on a // key /** * First start with `initial` value, if initial is undefined then finish. @@ -71,12 +70,12 @@ export const LONG_POLL_DELAY = 15000; * If the result is undefined then finish. * Otherwise: * Verify if the call is going to fast, if so slow down. - * + * * Call `retryFn` as the long poll function. * The result will be the next `initial` value. * - * - * + * + * * @param fetcher fetcher should be a memoized function * @param retry * @param deps @@ -85,7 +84,7 @@ export const LONG_POLL_DELAY = 15000; export function useLongPolling<Res, Rt>( initial: Res, shouldRetryFn: (res: Res, count: number) => Rt | undefined, - retryFn: (last: Rt) => Promise<Res>, + retryFn: (last: Rt, deps: Array<any>) => Promise<Res>, deps: Array<any> = [], opts: { minTime?: number } = {}, ) { @@ -105,6 +104,17 @@ export function useLongPolling<Res, Rt>( const body = result ?? initial; + const doRetry = useCallback((rt: Rt) => { + return function doRetryImpl() { + // call again + setRetry((lt) => ({ + // count: lt.count + 1, + fn: () => retryFn(rt, deps), + startMs: new Date().getTime(), + })); + }; + }, deps); + useEffect(() => { if (body === undefined || body instanceof TalerError) return; const _body = body; @@ -112,15 +122,6 @@ export function useLongPolling<Res, Rt>( const rt = shouldRetryFn(_body, retry.startMs ?? 0); if (rt === undefined) return; - function doRetry(rt: Rt) { - // call again - setRetry((lt) => ({ - // count: lt.count + 1, - fn: () => retryFn(rt), - startMs: new Date().getTime(), - })); - } - if (retry.startMs === undefined) { doRetry(rt); } else {