fix: respect query eligibility in screen sync (WealthLedger/frontend#54) #82
@@ -78,4 +78,35 @@ describe("read-only screen synchronization", () => {
|
|||||||
await act(async () => result.current.refresh());
|
await act(async () => result.current.refresh());
|
||||||
await waitFor(() => expect(gets).toBe(2));
|
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";
|
import type { InvestmentResourceMeta } from "@/shared/api";
|
||||||
|
|
||||||
type ScreenResource = {
|
type ScreenResource = {
|
||||||
|
isEnabled: boolean;
|
||||||
isError: boolean;
|
isError: boolean;
|
||||||
isFetching: boolean;
|
isFetching: boolean;
|
||||||
meta?: InvestmentResourceMeta;
|
meta?: InvestmentResourceMeta;
|
||||||
@@ -27,23 +28,31 @@ function useScreenSync(_scope: ScreenScope, resources: ScreenResource[]) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setInterval(() => {
|
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(() => {});
|
void resource.refetch().catch(() => {});
|
||||||
|
}
|
||||||
}, READ_INTERVAL_MS);
|
}, READ_INTERVAL_MS);
|
||||||
|
|
||||||
return () => clearInterval(timer);
|
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 () => {
|
const refresh = async () => {
|
||||||
if (isRefreshing) return;
|
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.
|
// Retry only the HTTP read; source attempts are reported in resource metadata.
|
||||||
const retry = async () => {
|
const retry = async () => {
|
||||||
const failed = resources.filter(resource => resource.isError);
|
const failed = eligible.filter(resource => resource.isError);
|
||||||
|
|
||||||
if (failed.length > 0)
|
if (failed.length > 0)
|
||||||
await Promise.allSettled(failed.map(resource => resource.refetch()));
|
await Promise.allSettled(failed.map(resource => resource.refetch()));
|
||||||
|
|||||||
Reference in New Issue
Block a user