import { LegendList } from "@legendapp/list"; import { type ComponentType, type ReactElement, useRef } from "react"; import { type Breakpoint, HR, useBreakpointMap } from "~/primitives"; import { useSetError } from "~/providers/error-provider"; import { type QueryIdentifier, useInfiniteFetch } from "~/query"; import { ErrorView } from "../ui/errors"; export type Layout = { numColumns: Breakpoint; size: Breakpoint; gap: Breakpoint; layout: "grid" | "horizontal" | "vertical"; }; export const InfiniteFetch = ({ query, placeholderCount = 2, incremental = false, Render, Loader, layout, Empty, divider, Header, headerProps, getItemType, getItemSize, fetchMore = true, nested = false, ...props }: { query: QueryIdentifier; placeholderCount?: number; layout: Layout; horizontal?: boolean; Render: (props: { item: Data; index: number }) => ReactElement | null; Loader: (props: { index: number }) => ReactElement | null; Empty?: JSX.Element; incremental?: boolean; divider?: true | ComponentType; Header?: ComponentType | ReactElement; headerProps?: Props; getItemType?: (item: Data | null, index: number) => Kind; getItemSize?: (kind: Kind) => number; fetchMore?: boolean; nested?: boolean; }): JSX.Element | null => { const { numColumns, size, gap } = useBreakpointMap(layout); const [setOffline, clearOffline] = useSetError("offline"); const oldItems = useRef(undefined); let { items, isPaused, error, fetchNextPage, isFetching, refetch, isRefetching, } = useInfiniteFetch(query); if (incremental && items) oldItems.current = items; if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch."); if (isPaused) setOffline(); else clearOffline(); if (error) return ; if (incremental) items ??= oldItems.current; const count = items ? numColumns - (items.length % numColumns) : placeholderCount; const placeholders = [...Array(count === 0 ? numColumns : count)].fill(null); const data = isFetching || !items ? [...(items || []), ...placeholders] : items; return ( item ? : } keyExtractor={(item: any, index) => (item ? item.id : index)} estimatedItemSize={size} horizontal={layout.layout === "horizontal"} numColumns={layout.layout === "horizontal" ? 1 : numColumns} onEndReached={fetchMore ? () => fetchNextPage() : undefined} onEndReachedThreshold={0.5} onRefresh={layout.layout !== "horizontal" ? refetch : undefined} refreshing={isRefetching} ListHeaderComponent={Header} ItemSeparatorComponent={ divider === true ? HR : (divider as any) || undefined } ListEmptyComponent={Empty} contentContainerStyle={{ gap, marginHorizontal: gap }} {...props} /> ); };