fix: respect query eligibility in screen sync (WealthLedger/frontend#54) #82

Merged
codex-bot merged 1 commits from fix/54-periodic-read-lifecycle into develop 2026-10-14 17:01:53 +03:00
2 changed files with 44 additions and 4 deletions
@@ -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();
}
});
});
@@ -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()));