fix(favorites): report unknown empty state on query errors

A first fetch that fails with no cached data settles with isLoading false
and zero items, which reported the section as empty and let the screen
show the no-data view on transport/auth failures. Report null (unknown)
instead so the aggregate empty state stays hidden.
This commit is contained in:
Gauvain
2026-07-04 18:22:16 +02:00
parent 75f30842f5
commit 28bf70c0ac
2 changed files with 39 additions and 26 deletions

View File

@@ -60,6 +60,7 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
const {
data,
isLoading,
isError,
isFetchingNextPage,
hasNextPage,
fetchNextPage,
@@ -111,13 +112,16 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
return deduped;
}, [data]);
// Report emptiness on every settle (incl. cache hits). Callback held in a ref
// so an inline parent callback doesn't retrigger the effect each render.
// Report emptiness on every settle (incl. cache hits). Errors report null
// (unknown) so a failed fetch never reads as "no content". Callback held in
// a ref so an inline parent callback doesn't retrigger the effect each render.
const onEmptyStateChangeRef = useRef(onEmptyStateChange);
onEmptyStateChangeRef.current = onEmptyStateChange;
useEffect(() => {
onEmptyStateChangeRef.current?.(isLoading ? null : allItems.length === 0);
}, [isLoading, allItems.length]);
onEmptyStateChangeRef.current?.(
isLoading || isError ? null : allItems.length === 0,
);
}, [isLoading, isError, allItems.length]);
const snapOffsets = useMemo(() => {
const itemWidth = orientation === "horizontal" ? 184 : 120; // w-44 (176px) + mr-2 (8px) or w-28 (112px) + mr-2 (8px)

View File

@@ -153,24 +153,30 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
[onItemFocus],
);
const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } =
useInfiniteQuery({
queryKey: queryKey,
queryFn: ({ pageParam = 0, ...context }) =>
queryFn({ ...context, queryKey, pageParam }),
getNextPageParam: (lastPage, allPages) => {
if (lastPage.length < effectivePageSize) {
return undefined;
}
return allPages.reduce((acc, page) => acc + page.length, 0);
},
initialPageParam: 0,
staleTime: 60 * 1000,
refetchInterval: 60 * 1000,
refetchOnWindowFocus: false,
refetchOnReconnect: true,
enabled,
});
const {
data,
isLoading,
isError,
isFetchingNextPage,
hasNextPage,
fetchNextPage,
} = useInfiniteQuery({
queryKey: queryKey,
queryFn: ({ pageParam = 0, ...context }) =>
queryFn({ ...context, queryKey, pageParam }),
getNextPageParam: (lastPage, allPages) => {
if (lastPage.length < effectivePageSize) {
return undefined;
}
return allPages.reduce((acc, page) => acc + page.length, 0);
},
initialPageParam: 0,
staleTime: 60 * 1000,
refetchInterval: 60 * 1000,
refetchOnWindowFocus: false,
refetchOnReconnect: true,
enabled,
});
const { t } = useTranslation();
@@ -190,13 +196,16 @@ export const InfiniteScrollingCollectionList: React.FC<Props> = ({
return deduped;
}, [data]);
// Report emptiness on every settle (incl. cache hits). Callback held in a ref
// so an inline parent callback doesn't retrigger the effect each render.
// Report emptiness on every settle (incl. cache hits). Errors report null
// (unknown) so a failed fetch never reads as "no content". Callback held in
// a ref so an inline parent callback doesn't retrigger the effect each render.
const onEmptyStateChangeRef = useRef(onEmptyStateChange);
onEmptyStateChangeRef.current = onEmptyStateChange;
useEffect(() => {
onEmptyStateChangeRef.current?.(isLoading ? null : allItems.length === 0);
}, [isLoading, allItems.length]);
onEmptyStateChangeRef.current?.(
isLoading || isError ? null : allItems.length === 0,
);
}, [isLoading, isError, allItems.length]);
const itemWidth =
orientation === "horizontal" ? posterSizes.episode : posterSizes.poster;