From f01839b28bc508c11ffe486c50b4f415f84e6212 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Wed, 14 Oct 2026 16:36:36 +0300 Subject: [PATCH] fix: respect query eligibility in screen sync (WealthLedger/frontend#54) --- .../model/screen-sync.test.tsx | 31 +++++++++++++++++++ .../tbank-connection/model/screen-sync.ts | 17 +++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/features/tbank-connection/model/screen-sync.test.tsx b/src/features/tbank-connection/model/screen-sync.test.tsx index 28b35d0..d30379b 100644 --- a/src/features/tbank-connection/model/screen-sync.test.tsx +++ b/src/features/tbank-connection/model/screen-sync.test.tsx @@ -78,4 +78,35 @@ describe("read-only screen synchronization", () => { await act(async () => result.current.refresh()); await waitFor(() => expect(gets).toBe(2)); }); + + it("does not manually refetch disabled resources", async () => { + vi.useFakeTimers(); + const refetch = vi.fn(() => Promise.resolve()); + + const resource = { + isEnabled: false, + isError: true, + isFetching: false, + queryKey: [], + refetch, + }; + + try { + const { result, unmount } = renderHook(() => + useScreenSync({ screen: "accounts" }, [resource]), + ); + + await act(async () => { + await result.current.refresh(); + await result.current.retry(); + }); + await act(() => vi.advanceTimersByTime(1_800_000)); + + expect(refetch).not.toHaveBeenCalled(); + unmount(); + } + finally { + vi.useRealTimers(); + } + }); }); diff --git a/src/features/tbank-connection/model/screen-sync.ts b/src/features/tbank-connection/model/screen-sync.ts index fda4122..5fa43f5 100644 --- a/src/features/tbank-connection/model/screen-sync.ts +++ b/src/features/tbank-connection/model/screen-sync.ts @@ -5,6 +5,7 @@ import { useEffect, useRef } from "react"; import type { InvestmentResourceMeta } from "@/shared/api"; type ScreenResource = { + isEnabled: boolean; isError: boolean; isFetching: boolean; meta?: InvestmentResourceMeta; @@ -27,23 +28,31 @@ function useScreenSync(_scope: ScreenScope, resources: ScreenResource[]) { useEffect(() => { const timer = setInterval(() => { - for (const resource of resourcesRef.current) + for (const resource of resourcesRef.current) { + if (!resource.isEnabled || resource.isFetching) continue; void resource.refetch().catch(() => {}); + } }, READ_INTERVAL_MS); return () => clearInterval(timer); }, []); - const isRefreshing = resources.some(resource => resource.isFetching); + const isRefreshing = resources.some( + resource => resource.isEnabled && resource.isFetching, + ); + + const eligible = resources.filter( + resource => resource.isEnabled && !resource.isFetching, + ); const refresh = async () => { if (isRefreshing) return; - await Promise.allSettled(resources.map(resource => resource.refetch())); + await Promise.allSettled(eligible.map(resource => resource.refetch())); }; // Retry only the HTTP read; source attempts are reported in resource metadata. const retry = async () => { - const failed = resources.filter(resource => resource.isError); + const failed = eligible.filter(resource => resource.isError); if (failed.length > 0) await Promise.allSettled(failed.map(resource => resource.refetch())); -- 2.54.0