Clean up fetch story by allowing hook fetch

This commit is contained in:
Zoe Roux 2023-10-25 00:47:26 +02:00
parent 034f048883
commit e8b929d4ca
4 changed files with 93 additions and 85 deletions

View File

@ -104,7 +104,7 @@ export const EpisodeList = <Props,>({
}: {
slug: string;
season: string | number;
Header: ComponentType<Props & { children: JSX.Element; empty: boolean }>;
Header: ComponentType<Props & { children: JSX.Element }>;
headerProps: Props;
}) => {
const { t } = useTranslation();

View File

@ -21,10 +21,10 @@
import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
import { useBreakpointMap, HR } from "@kyoo/primitives";
import { FlashList } from "@shopify/flash-list";
import { ComponentType, isValidElement, ReactElement, useRef } from "react";
import { ComponentProps, ComponentType, isValidElement, ReactElement, useRef } from "react";
import { EmptyView, ErrorView, Layout, WithLoading, addHeader } from "./fetch";
export const InfiniteFetch = <Data, Props, _>({
export const InfiniteFetchList = <Data, Props, _>({
query,
placeholderCount = 15,
incremental = false,
@ -33,11 +33,11 @@ export const InfiniteFetch = <Data, Props, _>({
empty,
divider = false,
Header,
headerProps: hprops,
headerProps,
getItemType,
...props
}: {
query: QueryIdentifier<_, Data>;
query: ReturnType<typeof useInfiniteFetch<_, Data>>;
placeholderCount?: number;
layout: Layout;
horizontal?: boolean;
@ -48,27 +48,16 @@ export const InfiniteFetch = <Data, Props, _>({
empty?: string | JSX.Element;
incremental?: boolean;
divider?: boolean | ComponentType;
Header?: ComponentType<Props & { children: JSX.Element; empty: boolean }> | ReactElement;
Header?: ComponentType<Props & { children: JSX.Element }> | ReactElement;
headerProps?: Props;
getItemType?: (item: Data, index: number) => string | number;
}): JSX.Element | null => {
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
const { numColumns, size } = useBreakpointMap(layout);
const oldItems = useRef<Data[] | undefined>();
let { items, error, fetchNextPage, hasNextPage, refetch, isRefetching } = useInfiniteFetch(
query,
{
useErrorBoundary: false,
},
);
let { items, error, fetchNextPage, hasNextPage, refetch, isRefetching } = query;
if (incremental && items) oldItems.current = items;
if (error) return <ErrorView error={error} />;
// @ts-ignore
const headerProps: Props & { empty: boolean } = hprops
? { ...hprops, empty: items?.length === 0 }
: { empty: items?.length === 0 };
if (empty && items && items.length === 0) {
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
return addHeader(Header, <EmptyView message={empty} />, headerProps);
@ -101,3 +90,17 @@ export const InfiniteFetch = <Data, Props, _>({
/>
);
};
export const InfiniteFetch = <Data, Props, _>({
query,
...props
}: {
query: QueryIdentifier<_, Data>;
} & Omit<ComponentProps<typeof InfiniteFetchList<Data, Props, _>>, "query">) => {
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
const ret = useInfiniteFetch(query, {
useErrorBoundary: false,
});
return <InfiniteFetchList query={ret} {...props} />;
};

View File

@ -21,6 +21,7 @@
import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
import { HR } from "@kyoo/primitives";
import {
ComponentProps,
ComponentType,
Fragment,
isValidElement,
@ -131,7 +132,7 @@ const InfiniteScroll = <Props,>({
);
};
export const InfiniteFetch = <Data, _, HeaderProps>({
export const InfiniteFetchList = <Data, _, HeaderProps>({
query,
incremental = false,
placeholderCount = 15,
@ -140,11 +141,11 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
empty,
divider: Divider = false,
Header,
headerProps: hprops,
headerProps,
getItemType,
...props
}: {
query: QueryIdentifier<_, Data>;
query: ReturnType<typeof useInfiniteFetch<_, Data>>;
incremental?: boolean;
placeholderCount?: number;
layout: Layout;
@ -158,18 +159,10 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
headerProps: HeaderProps;
getItemType?: (item: Data, index: number) => string | number;
}): JSX.Element | null => {
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
const oldItems = useRef<Data[] | undefined>();
const { items, error, fetchNextPage, hasNextPage, isFetching } = useInfiniteFetch(query, {
useErrorBoundary: false,
});
const { items, error, fetchNextPage, hasNextPage, isFetching } = query;
if (incremental && items) oldItems.current = items;
// @ts-ignore
const headerProps: HeaderProps & { empty: boolean } = hprops
? { ...hprops, empty: items?.length === 0 }
: { empty: items?.length === 0 };
if (error) return addHeader(Header, <ErrorView error={error} />, headerProps);
if (empty && items && items.length === 0) {
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
@ -201,3 +194,17 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
</InfiniteScroll>
);
};
export const InfiniteFetch = <Data, Props, _>({
query,
...props
}: {
query: QueryIdentifier<_, Data>;
} & Omit<ComponentProps<typeof InfiniteFetchList<Data, Props, _>>, "query">) => {
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
const ret = useInfiniteFetch(query, {
useErrorBoundary: false,
});
return <InfiniteFetchList query={ret} {...props} />;
};

View File

@ -25,6 +25,7 @@ import {
LibraryItemP,
QueryIdentifier,
getDisplayDate,
useInfiniteFetch,
} from "@kyoo/models";
import { H3, IconButton, ts } from "@kyoo/primitives";
import { ReactElement, forwardRef, useRef } from "react";
@ -33,74 +34,69 @@ import { px, useYoshiki } from "yoshiki/native";
import { ItemGrid } from "../browse/grid";
import ChevronLeft from "@material-symbols/svg-400/rounded/chevron_left-fill.svg";
import ChevronRight from "@material-symbols/svg-400/rounded/chevron_right-fill.svg";
import { InfiniteFetch } from "../fetch-infinite";
import { InfiniteFetch, InfiniteFetchList } from "../fetch-infinite";
import { useTranslation } from "react-i18next";
const Header = forwardRef<
View,
{ empty: boolean; displayEmpty: boolean; genre: Genre; children: ReactElement }
>(function Header({ empty, displayEmpty, genre, children }, ref) {
export const Header = ({ title }: { title: string }) => {
const { css } = useYoshiki();
return (
<View ref={ref}>
{!(empty && !displayEmpty) && (
<View
{...css({
marginTop: ItemGrid.layout.gap,
marginX: ItemGrid.layout.gap,
pX: ts(0.5),
flexDirection: "row",
justifyContent: "space-between",
})}
>
<H3>{genre}</H3>
{/* <View {...css({ flexDirection: "row" })}> */}
{/* <IconButton */}
{/* icon={ChevronLeft} */}
{/* // onPress={() => ref.current?.scrollTo({ x: 0, animated: true })} */}
{/* /> */}
{/* <IconButton */}
{/* icon={ChevronRight} */}
{/* // onPress={() => ref.current?.scrollTo({ x: 0, animated: true })} */}
{/* /> */}
{/* </View> */}
</View>
)}
{children}
<View
{...css({
marginTop: ItemGrid.layout.gap,
marginX: ItemGrid.layout.gap,
pX: ts(0.5),
flexDirection: "row",
justifyContent: "space-between",
})}
>
<H3>{title}</H3>
{/* <View {...css({ flexDirection: "row" })}> */}
{/* <IconButton */}
{/* icon={ChevronLeft} */}
{/* // onPress={() => ref.current?.scrollTo({ x: 0, animated: true })} */}
{/* /> */}
{/* <IconButton */}
{/* icon={ChevronRight} */}
{/* // onPress={() => ref.current?.scrollTo({ x: 0, animated: true })} */}
{/* /> */}
{/* </View> */}
</View>
);
});
};
export const GenreGrid = ({ genre }: { genre: Genre }) => {
const query = useInfiniteFetch(GenreGrid.query(genre));
const displayEmpty = useRef(false);
const { t } = useTranslation();
return (
<InfiniteFetch
query={GenreGrid.query(genre)}
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
empty={displayEmpty.current ? t("home.none") : undefined}
Header={Header}
headerProps={{ genre, displayEmpty: displayEmpty.current }}
>
{(x, i) => {
// only display empty list if a loading as been displayed (not durring ssr)
if (x.isLoading) displayEmpty.current = true;
return (
<ItemGrid
key={x.id ?? i}
isLoading={x.isLoading as any}
href={x.href}
name={x.name}
subtitle={
x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined
}
poster={x.poster}
/>
);
}}
</InfiniteFetch>
<>
{(displayEmpty.current || query.items?.length !== 0) && <Header title={genre} />}
<InfiniteFetchList
query={query}
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
empty={displayEmpty.current ? t("home.none") : undefined}
headerProps={{ title: genre, displayEmpty: displayEmpty.current }}
>
{(x, i) => {
// only display empty list if a loading as been displayed (not durring ssr)
if (x.isLoading) displayEmpty.current = true;
return (
<ItemGrid
key={x.id ?? i}
isLoading={x.isLoading as any}
href={x.href}
name={x.name}
subtitle={
x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined
}
poster={x.poster}
/>
);
}}
</InfiniteFetchList>
</>
);
};
@ -111,5 +107,7 @@ GenreGrid.query = (genre: Genre): QueryIdentifier<LibraryItem> => ({
params: {
genres: genre,
sortBy: "random",
// Limit the inital numbers of items
limit: 10,
},
});