Fix infinite scroll behavior of details page

This commit is contained in:
Zoe Roux 2023-06-10 01:35:01 +09:00
parent 5d377654aa
commit 039c644453
4 changed files with 74 additions and 56 deletions

View File

@ -169,14 +169,16 @@ const TitleLine = ({
})} })}
{...tooltip(t("show.play"))} {...tooltip(t("show.play"))}
/> />
{trailerUrl && <IconButton {trailerUrl && (
icon={Theaters} <IconButton
as={Link} icon={Theaters}
href={trailerUrl} as={Link}
target="_blank" href={trailerUrl}
color={{ xs: theme.user.contrast, md: theme.colors.white }} target="_blank"
{...tooltip(t("show.trailer"))} color={{ xs: theme.user.contrast, md: theme.colors.white }}
/>} {...tooltip(t("show.trailer"))}
/>
)}
</View> </View>
</View> </View>
</View> </View>
@ -249,7 +251,7 @@ const Description = ({
{t("show.genre")}:{" "} {t("show.genre")}:{" "}
{(isLoading ? [...Array(3)] : genres!).map((genre, i) => ( {(isLoading ? [...Array(3)] : genres!).map((genre, i) => (
<Fragment key={genre?.slug ?? i.toString()}> <Fragment key={genre?.slug ?? i.toString()}>
<P>{i !== 0 && ", "}</P> <P {...css({ m: 0 })}>{i !== 0 && ", "}</P>
{isLoading ? ( {isLoading ? (
<Skeleton {...css({ width: rem(5) })} /> <Skeleton {...css({ width: rem(5) })} />
) : ( ) : (

View File

@ -41,7 +41,7 @@ export const EpisodeList = ({
return ( return (
<InfiniteFetch <InfiniteFetch
query={EpisodeList.query(slug, season)} query={EpisodeList.query(slug, season)}
placeholderCount={15} placeholderCount={10}
layout={EpisodeLine.layout} layout={EpisodeLine.layout}
empty={t("show.episode-none")} empty={t("show.episode-none")}
divider divider

View File

@ -26,6 +26,7 @@ import { EpisodeList } from "./season";
import { Header } from "./header"; import { Header } from "./header";
import Svg, { Path, SvgProps } from "react-native-svg"; import Svg, { Path, SvgProps } from "react-native-svg";
import { Container, SwitchVariant } from "@kyoo/primitives"; import { Container, SwitchVariant } from "@kyoo/primitives";
import { forwardRef } from "react";
const SvgWave = (props: SvgProps) => { const SvgWave = (props: SvgProps) => {
const { css } = useYoshiki(); const { css } = useYoshiki();
@ -52,38 +53,41 @@ const query = (slug: string): QueryIdentifier<Show> => ({
export const ShowDetails: QueryPage<{ slug: string; season: string }> = ({ slug, season }) => { export const ShowDetails: QueryPage<{ slug: string; season: string }> = ({ slug, season }) => {
const { css, theme } = useYoshiki(); const { css, theme } = useYoshiki();
const ShowHeader = ({ children, ...props }: ViewProps) => ( const ShowHeader = forwardRef<View, ViewProps>(function _ShowHeader({ children, ...props }, ref) {
<View return (
{...css( <View
[ ref={ref}
{ bg: (theme) => theme.background }, {...css(
Platform.OS === "web" && { [
flexGrow: 1, { bg: (theme) => theme.background },
flexShrink: 1, Platform.OS === "web" && {
// @ts-ignore Web only property flexGrow: 1,
overflow: "auto" as any, flexShrink: 1,
// @ts-ignore Web only property // @ts-ignore Web only property
overflowX: "hidden", overflow: "auto" as any,
// @ts-ignore Web only property // @ts-ignore Web only property
overflowY: "overlay", overflowX: "hidden",
}, // @ts-ignore Web only property
], overflowY: "overlay",
props, },
)} ],
> props,
{/* TODO: Remove the slug quickfix for the play button */} )}
<Header slug={`${slug}-s1e1`} query={query(slug)} /> >
{/* <Staff slug={slug} /> */} {/* TODO: Remove the slug quickfix for the play button */}
<SvgWave <Header slug={`${slug}-s1e1`} query={query(slug)} />
fill={theme.variant.background} {/* <Staff slug={slug} /> */}
{...css({ flexShrink: 0, flexGrow: 1, display: "flex" })} <SvgWave
/> fill={theme.variant.background}
{/* <SeasonTab slug={slug} season={season} /> */} {...css({ flexShrink: 0, flexGrow: 1, display: "flex" })}
<View {...css({ bg: theme.variant.background })}> />
<Container>{children}</Container> {/* <SeasonTab slug={slug} season={season} /> */}
<View {...css({ bg: theme.variant.background })}>
<Container>{children}</Container>
</View>
</View> </View>
</View> );
); });
return ( return (
<SwitchVariant> <SwitchVariant>

View File

@ -20,7 +20,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 { ComponentType, Fragment, ReactElement, useMemo, useRef } from "react"; import { ComponentType, Fragment, isValidElement, ReactElement, useMemo, useRef } from "react";
import { Stylable, useYoshiki } from "yoshiki"; import { Stylable, useYoshiki } from "yoshiki";
import { EmptyView, ErrorView, Layout, WithLoading } from "./fetch"; import { EmptyView, ErrorView, Layout, WithLoading } from "./fetch";
@ -31,6 +31,7 @@ const InfiniteScroll = ({
loadMore, loadMore,
hasMore = true, hasMore = true,
isFetching, isFetching,
Header,
...props ...props
}: { }: {
children?: ReactElement | (ReactElement | null)[] | null; children?: ReactElement | (ReactElement | null)[] | null;
@ -39,23 +40,25 @@ const InfiniteScroll = ({
loadMore: () => void; loadMore: () => void;
hasMore: boolean; hasMore: boolean;
isFetching: boolean; isFetching: boolean;
Header: ComponentType<{ children: JSX.Element }> | ReactElement | undefined;
} & Stylable) => { } & Stylable) => {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const { css } = useYoshiki(); const { css } = useYoshiki();
return ( const onScroll = () => {
<div if (!ref.current || !hasMore || isFetching) return;
ref={ref} const scroll =
onScroll={() => { layout === "horizontal"
if (!ref.current || !hasMore || isFetching) return; ? ref.current.scrollWidth - ref.current.scrollLeft
const scroll = : ref.current.scrollHeight - ref.current.scrollTop;
layout === "horizontal" const offset = layout === "horizontal" ? ref.current.offsetWidth : ref.current.offsetHeight;
? ref.current.scrollWidth - ref.current.scrollLeft
: ref.current.scrollHeight - ref.current.scrollTop;
const offset = layout === "horizontal" ? ref.current.offsetWidth : ref.current.offsetHeight;
if (scroll <= offset * 1.2) loadMore(); if (scroll <= offset * 1.2) loadMore();
}} };
const scrollProps = { ref, onScroll };
const list = (props: object) => (
<div
{...css( {...css(
[ [
{ {
@ -84,6 +87,15 @@ const InfiniteScroll = ({
{hasMore && isFetching && loader} {hasMore && isFetching && loader}
</div> </div>
); );
if (!Header) return list({ ...scrollProps, ...props });
if (!isValidElement(Header)) return <Header {...scrollProps}>{list(props)}</Header>;
return (
<>
{Header}
{list({ ...scrollProps, ...props })}
</>
);
}; };
export const InfiniteFetch = <Data,>({ export const InfiniteFetch = <Data,>({
@ -125,7 +137,7 @@ export const InfiniteFetch = <Data,>({
return <EmptyView message={empty} />; return <EmptyView message={empty} />;
} }
const list = ( return (
<InfiniteScroll <InfiniteScroll
layout={grid ? "grid" : horizontal ? "horizontal" : "vertical"} layout={grid ? "grid" : horizontal ? "horizontal" : "vertical"}
loadMore={fetchNextPage} loadMore={fetchNextPage}
@ -137,6 +149,7 @@ export const InfiniteFetch = <Data,>({
{children({ isLoading: true } as any, i)} {children({ isLoading: true } as any, i)}
</Fragment> </Fragment>
))} ))}
Header={Header}
{...props} {...props}
> >
{items?.map((item, i) => ( {items?.map((item, i) => (
@ -147,7 +160,6 @@ export const InfiniteFetch = <Data,>({
))} ))}
</InfiniteScroll> </InfiniteScroll>
); );
return addHeader(Header, list);
}; };
const addHeader = ( const addHeader = (