mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Add placeholders for the homepage
This commit is contained in:
parent
3be409ec70
commit
a72691a81f
@ -66,6 +66,7 @@ export const EpisodeBox = ({
|
|||||||
href={href}
|
href={href}
|
||||||
{...css(
|
{...css(
|
||||||
{
|
{
|
||||||
|
alignItems: "center",
|
||||||
child: {
|
child: {
|
||||||
poster: {
|
poster: {
|
||||||
borderColor: (theme) => theme.background,
|
borderColor: (theme) => theme.background,
|
||||||
@ -90,17 +91,18 @@ export const EpisodeBox = ({
|
|||||||
src={thumbnail}
|
src={thumbnail}
|
||||||
quality="low"
|
quality="low"
|
||||||
alt=""
|
alt=""
|
||||||
|
forcedLoading={isLoading}
|
||||||
layout={{ width: percent(100), aspectRatio: 16 / 9 }}
|
layout={{ width: percent(100), aspectRatio: 16 / 9 }}
|
||||||
{...(css("poster") as any)}
|
{...(css("poster") as any)}
|
||||||
/>
|
/>
|
||||||
<Skeleton>
|
<Skeleton {...css({ width: percent(50) })}>
|
||||||
{isLoading || (
|
{isLoading || (
|
||||||
<P {...css([{ marginY: 0, textAlign: "center" }, "title"])}>
|
<P {...css([{ marginY: 0, textAlign: "center" }, "title"])}>
|
||||||
{name ?? t("show.episodeNoMetadata")}
|
{name ?? t("show.episodeNoMetadata")}
|
||||||
</P>
|
</P>
|
||||||
)}
|
)}
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
<Skeleton>
|
<Skeleton {...css({ width: percent(75), height: rem(0.8) })}>
|
||||||
{isLoading || (
|
{isLoading || (
|
||||||
<SubP
|
<SubP
|
||||||
numberOfLines={3}
|
numberOfLines={3}
|
||||||
|
@ -56,7 +56,7 @@ export const InfiniteFetchList = <Data, Props, _>({
|
|||||||
}): JSX.Element | null => {
|
}): JSX.Element | null => {
|
||||||
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 } = query;
|
let { items, error, fetchNextPage, hasNextPage, isFetching, refetch, isRefetching } = query;
|
||||||
if (incremental && items) oldItems.current = items;
|
if (incremental && items) oldItems.current = items;
|
||||||
|
|
||||||
if (error) return <ErrorView error={error} />;
|
if (error) return <ErrorView error={error} />;
|
||||||
@ -76,7 +76,7 @@ export const InfiniteFetchList = <Data, Props, _>({
|
|||||||
return (
|
return (
|
||||||
<FlashList
|
<FlashList
|
||||||
renderItem={({ item, index }) => children({ isLoading: false, ...item } as any, index)}
|
renderItem={({ item, index }) => children({ isLoading: false, ...item } as any, index)}
|
||||||
data={hasNextPage !== false ? [...(items || []), ...placeholders] : items}
|
data={hasNextPage || isFetching ? [...(items || []), ...placeholders] : items}
|
||||||
horizontal={layout.layout === "horizontal"}
|
horizontal={layout.layout === "horizontal"}
|
||||||
keyExtractor={(item: any) => item.id?.toString()}
|
keyExtractor={(item: any) => item.id?.toString()}
|
||||||
numColumns={numColumns}
|
numColumns={numColumns}
|
||||||
|
@ -114,7 +114,7 @@ const InfiniteScroll = <Props,>({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
{hasMore && isFetching && loader}
|
{((hasMore && fetchMore) || isFetching) && loader}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ export const InfiniteFetchList = <Data, _, HeaderProps>({
|
|||||||
loadMore={fetchNextPage}
|
loadMore={fetchNextPage}
|
||||||
hasMore={hasNextPage!}
|
hasMore={hasNextPage!}
|
||||||
isFetching={isFetching}
|
isFetching={isFetching}
|
||||||
loader={[...Array(12)].map((_, i) => (
|
loader={[...Array(placeholderCount)].map((_, i) => (
|
||||||
<Fragment key={i.toString()}>
|
<Fragment key={i.toString()}>
|
||||||
{Divider && i !== 0 && (Divider === true ? <HR /> : <Divider />)}
|
{Divider && i !== 0 && (Divider === true ? <HR /> : <Divider />)}
|
||||||
{children({ isLoading: true } as any, i)}
|
{children({ isLoading: true } as any, i)}
|
||||||
|
@ -76,6 +76,7 @@ export const GenreGrid = ({ genre }: { genre: Genre }) => {
|
|||||||
<InfiniteFetchList
|
<InfiniteFetchList
|
||||||
query={query}
|
query={query}
|
||||||
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
|
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
|
||||||
|
placeholderCount={10}
|
||||||
empty={displayEmpty.current ? t("home.none") : undefined}
|
empty={displayEmpty.current ? t("home.none") : undefined}
|
||||||
>
|
>
|
||||||
{(x, i) => {
|
{(x, i) => {
|
||||||
|
@ -27,11 +27,12 @@ import {
|
|||||||
ImageBackground,
|
ImageBackground,
|
||||||
Link,
|
Link,
|
||||||
P,
|
P,
|
||||||
|
Skeleton,
|
||||||
tooltip,
|
tooltip,
|
||||||
ts,
|
ts,
|
||||||
} from "@kyoo/primitives";
|
} from "@kyoo/primitives";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { percent, useYoshiki } from "yoshiki/native";
|
import { percent, rem, useYoshiki } from "yoshiki/native";
|
||||||
import { WithLoading } from "../fetch";
|
import { WithLoading } from "../fetch";
|
||||||
import { Header as DetailsHeader } from "../details/header";
|
import { Header as DetailsHeader } from "../details/header";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -68,8 +69,10 @@ export const Header = ({
|
|||||||
<View
|
<View
|
||||||
{...css({ width: { md: percent(70) }, position: "absolute", bottom: 0, margin: ts(2) })}
|
{...css({ width: { md: percent(70) }, position: "absolute", bottom: 0, margin: ts(2) })}
|
||||||
>
|
>
|
||||||
<H1>{name}</H1>
|
<Skeleton {...css({ width: rem(8), height: rem(2.5) })}>
|
||||||
<View {...css({ flexDirection: "row" })}>
|
{isLoading || <H1>{name}</H1>}
|
||||||
|
</Skeleton>
|
||||||
|
<View {...css({ flexDirection: "row", alignItems: "center" })}>
|
||||||
{link !== null && (
|
{link !== null && (
|
||||||
<IconFab
|
<IconFab
|
||||||
icon={PlayArrow}
|
icon={PlayArrow}
|
||||||
@ -87,9 +90,13 @@ export const Header = ({
|
|||||||
{...tooltip(t("home.info"))}
|
{...tooltip(t("home.info"))}
|
||||||
{...css({ marginRight: ts(2) })}
|
{...css({ marginRight: ts(2) })}
|
||||||
/>
|
/>
|
||||||
<H2>{tagline}</H2>
|
<Skeleton {...css({ width: rem(25), height: rem(2) })}>
|
||||||
|
{isLoading || <H2>{tagline}</H2>}
|
||||||
|
</Skeleton>
|
||||||
</View>
|
</View>
|
||||||
<P {...css({ display: { xs: "none", md: "flex" } })}>{overview}</P>
|
<Skeleton lines={4} {...css({ marginTop: ts(1) })}>
|
||||||
|
{isLoading || <P {...css({ display: { xs: "none", md: "flex" } })}>{overview}</P>}
|
||||||
|
</Skeleton>
|
||||||
</View>
|
</View>
|
||||||
</ImageBackground>
|
</ImageBackground>
|
||||||
);
|
);
|
||||||
|
@ -27,11 +27,11 @@ import { GenreGrid } from "./genre";
|
|||||||
import { Recommanded } from "./recommanded";
|
import { Recommanded } from "./recommanded";
|
||||||
import { VerticalRecommanded } from "./vertical";
|
import { VerticalRecommanded } from "./vertical";
|
||||||
import { NewsList } from "./news";
|
import { NewsList } from "./news";
|
||||||
import { useLayoutEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export const HomePage: QueryPage<{}, Genre> = ({ randomItems }) => {
|
export const HomePage: QueryPage<{}, Genre> = ({ randomItems }) => {
|
||||||
const [isClient, setClient] = useState(false);
|
const [isClient, setClient] = useState(false);
|
||||||
useLayoutEffect(() => setClient(true), []);
|
useEffect(() => setClient(true), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
|
@ -34,6 +34,7 @@ import {
|
|||||||
ImageBackground,
|
ImageBackground,
|
||||||
Link,
|
Link,
|
||||||
P,
|
P,
|
||||||
|
Skeleton,
|
||||||
SubP,
|
SubP,
|
||||||
focusReset,
|
focusReset,
|
||||||
tooltip,
|
tooltip,
|
||||||
@ -42,7 +43,7 @@ import {
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Pressable, ScrollView, View } from "react-native";
|
import { Pressable, ScrollView, View } from "react-native";
|
||||||
import { useRouter } from "solito/router";
|
import { useRouter } from "solito/router";
|
||||||
import { Theme, percent, px, useYoshiki } from "yoshiki/native";
|
import { Theme, percent, px, rem, useYoshiki } from "yoshiki/native";
|
||||||
import { Layout, WithLoading } from "../fetch";
|
import { Layout, WithLoading } from "../fetch";
|
||||||
import { InfiniteFetch } from "../fetch-infinite";
|
import { InfiniteFetch } from "../fetch-infinite";
|
||||||
import PlayArrow from "@material-symbols/svg-400/rounded/play_arrow-fill.svg";
|
import PlayArrow from "@material-symbols/svg-400/rounded/play_arrow-fill.svg";
|
||||||
@ -104,6 +105,7 @@ export const ItemDetails = ({
|
|||||||
alt=""
|
alt=""
|
||||||
quality="low"
|
quality="low"
|
||||||
gradient={false}
|
gradient={false}
|
||||||
|
forcedLoading={isLoading}
|
||||||
{...css({ height: percent(100), aspectRatio: 2 / 3 })}
|
{...css({ height: percent(100), aspectRatio: 2 / 3 })}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
@ -116,17 +118,29 @@ export const ItemDetails = ({
|
|||||||
p: ts(1),
|
p: ts(1),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<P {...css([{ m: 0 }, "title"])}>{name}</P>
|
<Skeleton {...css({ width: percent(100) })}>
|
||||||
{subtitle && <SubP {...css({ m: 0 })}>{subtitle}</SubP>}
|
{isLoading || <P {...css([{ m: 0 }, "title"])}>{name}</P>}
|
||||||
|
</Skeleton>
|
||||||
|
{(subtitle || isLoading) && (
|
||||||
|
<Skeleton {...css({ height: rem(0.8) })}>
|
||||||
|
{isLoading || <SubP {...css({ m: 0 })}>{subtitle}</SubP>}
|
||||||
|
</Skeleton>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</ImageBackground>
|
</ImageBackground>
|
||||||
<View {...css({ flexShrink: 1, flexGrow: 1 })}>
|
<View {...css({ flexShrink: 1, flexGrow: 1, justifyContent: "flex-end" })}>
|
||||||
{tagline && <P {...css({ p: ts(1) })}>{tagline}</P>}
|
{(isLoading || tagline) && (
|
||||||
{overview && (
|
<Skeleton {...css({ m: ts(1), marginVertical: ts(2) })}>
|
||||||
<ScrollView>
|
{isLoading || <P {...css({ p: ts(1) })}>{tagline}</P>}
|
||||||
<SubP {...css({ pX: ts(1) })}>{overview}</SubP>
|
</Skeleton>
|
||||||
</ScrollView>
|
|
||||||
)}
|
)}
|
||||||
|
<ScrollView {...css({ pX: ts(1) })}>
|
||||||
|
<Skeleton lines={5} {...css({ height: rem(0.8) })}>
|
||||||
|
{isLoading || (
|
||||||
|
<SubP {...css({ textAlign: "justify" })}>{overview ?? t("show.noOverview")}</SubP>
|
||||||
|
)}
|
||||||
|
</Skeleton>
|
||||||
|
</ScrollView>
|
||||||
<View
|
<View
|
||||||
{...css({
|
{...css({
|
||||||
bg: (theme) => theme.themeOverlay,
|
bg: (theme) => theme.themeOverlay,
|
||||||
@ -136,7 +150,11 @@ export const ItemDetails = ({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ScrollView horizontal {...css({ alignItems: "center" })}>
|
<ScrollView horizontal {...css({ alignItems: "center" })}>
|
||||||
{genres?.map((x) => <Chip key={x} size="small" label={x} {...css({ mX: ts(0.5) })} />)}
|
{(genres || [...Array(3)])?.map((x, i) => (
|
||||||
|
<Chip key={x ?? i} size="small" {...css({ mX: ts(0.5) })}>
|
||||||
|
{x ?? <Skeleton {...css({ width: rem(3), height: rem(0.8) })} />}
|
||||||
|
</Chip>
|
||||||
|
))}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
{playHref !== null && (
|
{playHref !== null && (
|
||||||
<IconFab
|
<IconFab
|
||||||
@ -171,6 +189,7 @@ export const Recommanded = () => {
|
|||||||
<InfiniteFetch
|
<InfiniteFetch
|
||||||
query={Recommanded.query()}
|
query={Recommanded.query()}
|
||||||
layout={ItemDetails.layout}
|
layout={ItemDetails.layout}
|
||||||
|
placeholderCount={6}
|
||||||
fetchMore={false}
|
fetchMore={false}
|
||||||
{...css({ padding: 0 })}
|
{...css({ padding: 0 })}
|
||||||
>
|
>
|
||||||
|
@ -36,6 +36,7 @@ export const VerticalRecommanded = () => {
|
|||||||
<H3 {...css({ mX: ItemGrid.layout.gap })}>{t("home.recommanded")}</H3>
|
<H3 {...css({ mX: ItemGrid.layout.gap })}>{t("home.recommanded")}</H3>
|
||||||
<InfiniteFetch
|
<InfiniteFetch
|
||||||
query={VerticalRecommanded.query()}
|
query={VerticalRecommanded.query()}
|
||||||
|
placeholderCount={3}
|
||||||
layout={{ ...ItemList.layout, layout: "vertical" }}
|
layout={{ ...ItemList.layout, layout: "vertical" }}
|
||||||
fetchMore={false}
|
fetchMore={false}
|
||||||
>
|
>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user