mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Disable empty genre list during ssr
This commit is contained in:
parent
11712b5b13
commit
fc598838c4
@ -22,7 +22,7 @@ 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 { ComponentType, isValidElement, ReactElement, useRef } from "react";
|
||||||
import { EmptyView, ErrorView, Layout, WithLoading } from "./fetch";
|
import { EmptyView, ErrorView, Layout, WithLoading, addHeader } from "./fetch";
|
||||||
|
|
||||||
export const InfiniteFetch = <Data, Props, _>({
|
export const InfiniteFetch = <Data, Props, _>({
|
||||||
query,
|
query,
|
||||||
@ -33,7 +33,7 @@ export const InfiniteFetch = <Data, Props, _>({
|
|||||||
empty,
|
empty,
|
||||||
divider = false,
|
divider = false,
|
||||||
Header,
|
Header,
|
||||||
headerProps,
|
headerProps: hprops,
|
||||||
getItemType,
|
getItemType,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
@ -48,7 +48,7 @@ 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 }> | ReactElement;
|
Header?: ComponentType<Props & { children: JSX.Element; empty: boolean }> | 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 => {
|
||||||
@ -65,9 +65,13 @@ export const InfiniteFetch = <Data, Props, _>({
|
|||||||
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 empty;
|
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
|
||||||
return <EmptyView message={empty} />;
|
return addHeader(Header, <EmptyView message={empty} />, headerProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (incremental) items ??= oldItems.current;
|
if (incremental) items ??= oldItems.current;
|
||||||
|
@ -30,7 +30,7 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { Stylable, useYoshiki, ysMap } from "yoshiki";
|
import { Stylable, useYoshiki, ysMap } from "yoshiki";
|
||||||
import { EmptyView, ErrorView, Layout, WithLoading } from "./fetch";
|
import { EmptyView, ErrorView, Layout, WithLoading, addHeader } from "./fetch";
|
||||||
|
|
||||||
const InfiniteScroll = <Props,>({
|
const InfiniteScroll = <Props,>({
|
||||||
children,
|
children,
|
||||||
@ -139,7 +139,7 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
|
|||||||
empty,
|
empty,
|
||||||
divider: Divider = false,
|
divider: Divider = false,
|
||||||
Header,
|
Header,
|
||||||
headerProps,
|
headerProps: hprops,
|
||||||
getItemType,
|
getItemType,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
@ -165,10 +165,14 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
|
|||||||
});
|
});
|
||||||
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 empty;
|
if (typeof empty !== "string") return addHeader(Header, empty, headerProps);
|
||||||
return <EmptyView message={empty} />;
|
return addHeader(Header, <EmptyView message={empty} />, headerProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -196,20 +200,3 @@ export const InfiniteFetch = <Data, _, HeaderProps>({
|
|||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addHeader = <Props,>(
|
|
||||||
Header: ComponentType<{ children: JSX.Element } & Props> | ReactElement | undefined,
|
|
||||||
children: ReactElement,
|
|
||||||
headerProps?: Props,
|
|
||||||
) => {
|
|
||||||
if (!Header) return children;
|
|
||||||
return !isValidElement(Header) ? (
|
|
||||||
// @ts-ignore
|
|
||||||
<Header {...(headerProps ?? {})}>{children}</Header>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{Header}
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
import { Page, QueryIdentifier, useFetch, KyooErrors } from "@kyoo/models";
|
import { Page, QueryIdentifier, useFetch, KyooErrors } from "@kyoo/models";
|
||||||
import { Breakpoint, P } from "@kyoo/primitives";
|
import { Breakpoint, P } from "@kyoo/primitives";
|
||||||
|
import { ComponentType, ReactElement, isValidElement } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { useYoshiki } from "yoshiki/native";
|
import { useYoshiki } from "yoshiki/native";
|
||||||
|
|
||||||
@ -130,3 +131,20 @@ export const EmptyView = ({ message }: { message: string }) => {
|
|||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const addHeader = <Props,>(
|
||||||
|
Header: ComponentType<{ children: JSX.Element } & Props> | ReactElement | undefined,
|
||||||
|
children: ReactElement,
|
||||||
|
headerProps?: Props,
|
||||||
|
) => {
|
||||||
|
if (!Header) return children;
|
||||||
|
return !isValidElement(Header) ? (
|
||||||
|
// @ts-ignore
|
||||||
|
<Header {...(headerProps ?? {})}>{children}</Header>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{Header}
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -23,45 +23,63 @@ import {
|
|||||||
ItemKind,
|
ItemKind,
|
||||||
LibraryItem,
|
LibraryItem,
|
||||||
LibraryItemP,
|
LibraryItemP,
|
||||||
Page,
|
|
||||||
Paged,
|
|
||||||
QueryIdentifier,
|
QueryIdentifier,
|
||||||
getDisplayDate,
|
getDisplayDate,
|
||||||
} from "@kyoo/models";
|
} from "@kyoo/models";
|
||||||
import { H3, IconButton, ts } from "@kyoo/primitives";
|
import { H3, IconButton, ts } from "@kyoo/primitives";
|
||||||
import { useRef } from "react";
|
import { ReactElement, forwardRef, useRef } from "react";
|
||||||
import { ScrollView, View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { useYoshiki } from "yoshiki/native";
|
import { useYoshiki } from "yoshiki/native";
|
||||||
import { Fetch } from "../fetch";
|
|
||||||
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 } from "../fetch-infinite";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export const GenreGrid = ({ genre }: { genre: Genre }) => {
|
const Header = forwardRef<
|
||||||
const ref = useRef<ScrollView>(null);
|
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>
|
<View ref={ref}>
|
||||||
<View {...css({ marginX: ts(1), flexDirection: "row", justifyContent: "space-between" })}>
|
{!(empty && !displayEmpty) && (
|
||||||
<H3>{genre}</H3>
|
<View {...css({ marginX: ts(1), flexDirection: "row", justifyContent: "space-between" })}>
|
||||||
<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>
|
||||||
</View>
|
)}
|
||||||
<InfiniteFetch
|
{children}
|
||||||
query={GenreGrid.query(genre)}
|
</View>
|
||||||
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
|
);
|
||||||
>
|
});
|
||||||
{(x, i) => (
|
|
||||||
|
export const GenreGrid = ({ genre }: { genre: 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
|
<ItemGrid
|
||||||
key={x.id ?? i}
|
key={x.id ?? i}
|
||||||
isLoading={x.isLoading as any}
|
isLoading={x.isLoading as any}
|
||||||
@ -72,9 +90,9 @@ export const GenreGrid = ({ genre }: { genre: Genre }) => {
|
|||||||
}
|
}
|
||||||
poster={x.poster}
|
poster={x.poster}
|
||||||
/>
|
/>
|
||||||
)}
|
);
|
||||||
</InfiniteFetch>
|
}}
|
||||||
</View>
|
</InfiniteFetch>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"home": {
|
"home": {
|
||||||
"recommanded": "Recommanded",
|
"recommanded": "Recommanded",
|
||||||
"info": "See more"
|
"info": "See more",
|
||||||
|
"none": "No episodes"
|
||||||
},
|
},
|
||||||
"show": {
|
"show": {
|
||||||
"play": "Play",
|
"play": "Play",
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"home": {
|
"home": {
|
||||||
"recommanded": "Recommandé",
|
"recommanded": "Recommandé",
|
||||||
"info": "Voir plus"
|
"info": "Voir plus",
|
||||||
|
"none": "Aucun episode"
|
||||||
},
|
},
|
||||||
"show": {
|
"show": {
|
||||||
"play": "Lecture",
|
"play": "Lecture",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user