mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-01 04:34:50 -04:00
Clean up fetch story by allowing hook fetch
This commit is contained in:
parent
034f048883
commit
e8b929d4ca
@ -104,7 +104,7 @@ export const EpisodeList = <Props,>({
|
|||||||
}: {
|
}: {
|
||||||
slug: string;
|
slug: string;
|
||||||
season: string | number;
|
season: string | number;
|
||||||
Header: ComponentType<Props & { children: JSX.Element; empty: boolean }>;
|
Header: ComponentType<Props & { children: JSX.Element }>;
|
||||||
headerProps: Props;
|
headerProps: Props;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -21,10 +21,10 @@
|
|||||||
import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
|
import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
|
||||||
import { useBreakpointMap, HR } from "@kyoo/primitives";
|
import { useBreakpointMap, HR } from "@kyoo/primitives";
|
||||||
import { FlashList } from "@shopify/flash-list";
|
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";
|
import { EmptyView, ErrorView, Layout, WithLoading, addHeader } from "./fetch";
|
||||||
|
|
||||||
export const InfiniteFetch = <Data, Props, _>({
|
export const InfiniteFetchList = <Data, Props, _>({
|
||||||
query,
|
query,
|
||||||
placeholderCount = 15,
|
placeholderCount = 15,
|
||||||
incremental = false,
|
incremental = false,
|
||||||
@ -33,11 +33,11 @@ export const InfiniteFetch = <Data, Props, _>({
|
|||||||
empty,
|
empty,
|
||||||
divider = false,
|
divider = false,
|
||||||
Header,
|
Header,
|
||||||
headerProps: hprops,
|
headerProps,
|
||||||
getItemType,
|
getItemType,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
query: QueryIdentifier<_, Data>;
|
query: ReturnType<typeof useInfiniteFetch<_, Data>>;
|
||||||
placeholderCount?: number;
|
placeholderCount?: number;
|
||||||
layout: Layout;
|
layout: Layout;
|
||||||
horizontal?: boolean;
|
horizontal?: boolean;
|
||||||
@ -48,27 +48,16 @@ export const InfiniteFetch = <Data, Props, _>({
|
|||||||
empty?: string | JSX.Element;
|
empty?: string | JSX.Element;
|
||||||
incremental?: boolean;
|
incremental?: boolean;
|
||||||
divider?: boolean | ComponentType;
|
divider?: boolean | ComponentType;
|
||||||
Header?: ComponentType<Props & { children: JSX.Element; empty: boolean }> | ReactElement;
|
Header?: ComponentType<Props & { children: JSX.Element }> | ReactElement;
|
||||||
headerProps?: Props;
|
headerProps?: Props;
|
||||||
getItemType?: (item: Data, index: number) => string | number;
|
getItemType?: (item: Data, index: number) => string | number;
|
||||||
}): JSX.Element | null => {
|
}): JSX.Element | null => {
|
||||||
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
|
|
||||||
|
|
||||||
const { numColumns, size } = useBreakpointMap(layout);
|
const { numColumns, size } = useBreakpointMap(layout);
|
||||||
const oldItems = useRef<Data[] | undefined>();
|
const oldItems = useRef<Data[] | undefined>();
|
||||||
let { items, error, fetchNextPage, hasNextPage, refetch, isRefetching } = useInfiniteFetch(
|
let { items, error, fetchNextPage, hasNextPage, refetch, isRefetching } = query;
|
||||||
query,
|
|
||||||
{
|
|
||||||
useErrorBoundary: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (incremental && items) oldItems.current = items;
|
if (incremental && items) oldItems.current = items;
|
||||||
|
|
||||||
if (error) return <ErrorView error={error} />;
|
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 (empty && items && items.length === 0) {
|
||||||
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
|
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
|
||||||
return addHeader(Header, <EmptyView message={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} />;
|
||||||
|
};
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
|
import { Page, QueryIdentifier, useInfiniteFetch } from "@kyoo/models";
|
||||||
import { HR } from "@kyoo/primitives";
|
import { HR } from "@kyoo/primitives";
|
||||||
import {
|
import {
|
||||||
|
ComponentProps,
|
||||||
ComponentType,
|
ComponentType,
|
||||||
Fragment,
|
Fragment,
|
||||||
isValidElement,
|
isValidElement,
|
||||||
@ -131,7 +132,7 @@ const InfiniteScroll = <Props,>({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InfiniteFetch = <Data, _, HeaderProps>({
|
export const InfiniteFetchList = <Data, _, HeaderProps>({
|
||||||
query,
|
query,
|
||||||
incremental = false,
|
incremental = false,
|
||||||
placeholderCount = 15,
|
placeholderCount = 15,
|
||||||
@ -140,11 +141,11 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
|
|||||||
empty,
|
empty,
|
||||||
divider: Divider = false,
|
divider: Divider = false,
|
||||||
Header,
|
Header,
|
||||||
headerProps: hprops,
|
headerProps,
|
||||||
getItemType,
|
getItemType,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
query: QueryIdentifier<_, Data>;
|
query: ReturnType<typeof useInfiniteFetch<_, Data>>;
|
||||||
incremental?: boolean;
|
incremental?: boolean;
|
||||||
placeholderCount?: number;
|
placeholderCount?: number;
|
||||||
layout: Layout;
|
layout: Layout;
|
||||||
@ -158,18 +159,10 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
|
|||||||
headerProps: HeaderProps;
|
headerProps: HeaderProps;
|
||||||
getItemType?: (item: Data, index: number) => string | number;
|
getItemType?: (item: Data, index: number) => string | number;
|
||||||
}): JSX.Element | null => {
|
}): JSX.Element | null => {
|
||||||
if (!query.infinite) console.warn("A non infinite query was passed to an InfiniteFetch.");
|
|
||||||
|
|
||||||
const oldItems = useRef<Data[] | undefined>();
|
const oldItems = useRef<Data[] | undefined>();
|
||||||
const { items, error, fetchNextPage, hasNextPage, isFetching } = useInfiniteFetch(query, {
|
const { items, error, fetchNextPage, hasNextPage, isFetching } = query;
|
||||||
useErrorBoundary: false,
|
|
||||||
});
|
|
||||||
if (incremental && items) oldItems.current = items;
|
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 (error) return addHeader(Header, <ErrorView error={error} />, headerProps);
|
||||||
if (empty && items && items.length === 0) {
|
if (empty && items && items.length === 0) {
|
||||||
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
|
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
|
||||||
@ -201,3 +194,17 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
|
|||||||
</InfiniteScroll>
|
</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} />;
|
||||||
|
};
|
||||||
|
@ -25,6 +25,7 @@ import {
|
|||||||
LibraryItemP,
|
LibraryItemP,
|
||||||
QueryIdentifier,
|
QueryIdentifier,
|
||||||
getDisplayDate,
|
getDisplayDate,
|
||||||
|
useInfiniteFetch,
|
||||||
} from "@kyoo/models";
|
} from "@kyoo/models";
|
||||||
import { H3, IconButton, ts } from "@kyoo/primitives";
|
import { H3, IconButton, ts } from "@kyoo/primitives";
|
||||||
import { ReactElement, forwardRef, useRef } from "react";
|
import { ReactElement, forwardRef, useRef } from "react";
|
||||||
@ -33,74 +34,69 @@ import { px, useYoshiki } from "yoshiki/native";
|
|||||||
import { ItemGrid } from "../browse/grid";
|
import { ItemGrid } from "../browse/grid";
|
||||||
import ChevronLeft from "@material-symbols/svg-400/rounded/chevron_left-fill.svg";
|
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 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";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const Header = forwardRef<
|
export const Header = ({ title }: { title: string }) => {
|
||||||
View,
|
|
||||||
{ empty: boolean; displayEmpty: boolean; genre: Genre; children: ReactElement }
|
|
||||||
>(function Header({ empty, displayEmpty, genre, children }, ref) {
|
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View ref={ref}>
|
<View
|
||||||
{!(empty && !displayEmpty) && (
|
{...css({
|
||||||
<View
|
marginTop: ItemGrid.layout.gap,
|
||||||
{...css({
|
marginX: ItemGrid.layout.gap,
|
||||||
marginTop: ItemGrid.layout.gap,
|
pX: ts(0.5),
|
||||||
marginX: ItemGrid.layout.gap,
|
flexDirection: "row",
|
||||||
pX: ts(0.5),
|
justifyContent: "space-between",
|
||||||
flexDirection: "row",
|
})}
|
||||||
justifyContent: "space-between",
|
>
|
||||||
})}
|
<H3>{title}</H3>
|
||||||
>
|
{/* <View {...css({ flexDirection: "row" })}> */}
|
||||||
<H3>{genre}</H3>
|
{/* <IconButton */}
|
||||||
{/* <View {...css({ flexDirection: "row" })}> */}
|
{/* icon={ChevronLeft} */}
|
||||||
{/* <IconButton */}
|
{/* // onPress={() => ref.current?.scrollTo({ x: 0, animated: true })} */}
|
||||||
{/* icon={ChevronLeft} */}
|
{/* /> */}
|
||||||
{/* // onPress={() => ref.current?.scrollTo({ x: 0, animated: true })} */}
|
{/* <IconButton */}
|
||||||
{/* /> */}
|
{/* icon={ChevronRight} */}
|
||||||
{/* <IconButton */}
|
{/* // onPress={() => ref.current?.scrollTo({ x: 0, animated: true })} */}
|
||||||
{/* icon={ChevronRight} */}
|
{/* /> */}
|
||||||
{/* // onPress={() => ref.current?.scrollTo({ x: 0, animated: true })} */}
|
{/* </View> */}
|
||||||
{/* /> */}
|
|
||||||
{/* </View> */}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
{children}
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|
||||||
export const GenreGrid = ({ genre }: { genre: Genre }) => {
|
export const GenreGrid = ({ genre }: { genre: Genre }) => {
|
||||||
|
const query = useInfiniteFetch(GenreGrid.query(genre));
|
||||||
const displayEmpty = useRef(false);
|
const displayEmpty = useRef(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InfiniteFetch
|
<>
|
||||||
query={GenreGrid.query(genre)}
|
{(displayEmpty.current || query.items?.length !== 0) && <Header title={genre} />}
|
||||||
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
|
<InfiniteFetchList
|
||||||
empty={displayEmpty.current ? t("home.none") : undefined}
|
query={query}
|
||||||
Header={Header}
|
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
|
||||||
headerProps={{ genre, displayEmpty: displayEmpty.current }}
|
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)
|
{(x, i) => {
|
||||||
if (x.isLoading) displayEmpty.current = true;
|
// only display empty list if a loading as been displayed (not durring ssr)
|
||||||
return (
|
if (x.isLoading) displayEmpty.current = true;
|
||||||
<ItemGrid
|
return (
|
||||||
key={x.id ?? i}
|
<ItemGrid
|
||||||
isLoading={x.isLoading as any}
|
key={x.id ?? i}
|
||||||
href={x.href}
|
isLoading={x.isLoading as any}
|
||||||
name={x.name}
|
href={x.href}
|
||||||
subtitle={
|
name={x.name}
|
||||||
x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined
|
subtitle={
|
||||||
}
|
x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined
|
||||||
poster={x.poster}
|
}
|
||||||
/>
|
poster={x.poster}
|
||||||
);
|
/>
|
||||||
}}
|
);
|
||||||
</InfiniteFetch>
|
}}
|
||||||
|
</InfiniteFetchList>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,5 +107,7 @@ GenreGrid.query = (genre: Genre): QueryIdentifier<LibraryItem> => ({
|
|||||||
params: {
|
params: {
|
||||||
genres: genre,
|
genres: genre,
|
||||||
sortBy: "random",
|
sortBy: "random",
|
||||||
|
// Limit the inital numbers of items
|
||||||
|
limit: 10,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user