mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 20:24:27 -04:00
Split loaders for recommanded items
This commit is contained in:
parent
393c58b10a
commit
2a1b805a7f
@ -18,7 +18,7 @@
|
|||||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { TextProps } from "react-native";
|
import { View, type TextProps } from "react-native";
|
||||||
import { type Theme, px, rem, useYoshiki } from "yoshiki/native";
|
import { type Theme, px, rem, useYoshiki } from "yoshiki/native";
|
||||||
import { Link } from "./links";
|
import { Link } from "./links";
|
||||||
import { Skeleton } from "./skeleton";
|
import { Skeleton } from "./skeleton";
|
||||||
@ -63,6 +63,7 @@ export const Chip = ({
|
|||||||
pX: ts(2.5 * sizeMult),
|
pX: ts(2.5 * sizeMult),
|
||||||
borderRadius: ts(3),
|
borderRadius: ts(3),
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
|
justifyContent: "center",
|
||||||
},
|
},
|
||||||
outline && {
|
outline && {
|
||||||
borderColor: color ?? ((theme: Theme) => theme.accent),
|
borderColor: color ?? ((theme: Theme) => theme.accent),
|
||||||
@ -102,3 +103,40 @@ export const Chip = ({
|
|||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Chip.Loader = ({
|
||||||
|
color,
|
||||||
|
size = "medium",
|
||||||
|
outline = false,
|
||||||
|
...props
|
||||||
|
}: { color?: string; size?: "small" | "medium" | "large"; outline?: boolean }) => {
|
||||||
|
const { css } = useYoshiki();
|
||||||
|
const sizeMult = size === "medium" ? 1 : size === "small" ? 0.5 : 1.5;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
{...css(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
pY: ts(1 * sizeMult),
|
||||||
|
pX: ts(2.5 * sizeMult),
|
||||||
|
borderRadius: ts(3),
|
||||||
|
overflow: "hidden",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
outline && {
|
||||||
|
borderColor: color ?? ((theme: Theme) => theme.accent),
|
||||||
|
borderStyle: "solid",
|
||||||
|
borderWidth: px(1),
|
||||||
|
},
|
||||||
|
!outline && {
|
||||||
|
bg: color ?? ((theme: Theme) => theme.accent),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
props,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Skeleton {...css({ width: rem(3) })} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { getCurrentToken } from "@kyoo/models";
|
import { getCurrentToken } from "@kyoo/models";
|
||||||
import { useState } from "react";
|
import { ReactElement, useState } from "react";
|
||||||
import { type FlexStyle, type ImageStyle, View, type ViewStyle } from "react-native";
|
import { type FlexStyle, type ImageStyle, View, type ViewStyle } from "react-native";
|
||||||
import { Blurhash } from "react-native-blurhash";
|
import { Blurhash } from "react-native-blurhash";
|
||||||
import FastImage from "react-native-fast-image";
|
import FastImage from "react-native-fast-image";
|
||||||
@ -94,9 +94,9 @@ export const Image = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Image.Loader = ({ layout, ...props }: { layout: ImageLayout }) => {
|
Image.Loader = ({ layout, ...props }: { layout: ImageLayout; children?: ReactElement }) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
const border = { borderRadius: 6, overflow: "hidden" } satisfies ViewStyle;
|
const border = { borderRadius: 6, overflow: "hidden" } satisfies ViewStyle;
|
||||||
|
|
||||||
return <Skeleton variant="custom" {...css([layout, border], props)} />;
|
return <Skeleton variant="custom" show {...css([layout, border], props)} />;
|
||||||
};
|
};
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import NextImage from "next/image";
|
import NextImage from "next/image";
|
||||||
import { useState } from "react";
|
import { ReactElement, useState } from "react";
|
||||||
import { type ImageStyle, View, type ViewStyle } from "react-native";
|
import { type ImageStyle, View, type ViewStyle } from "react-native";
|
||||||
import { useYoshiki } from "yoshiki/native";
|
import { useYoshiki } from "yoshiki/native";
|
||||||
import { imageBorderRadius } from "../constants";
|
import { imageBorderRadius } from "../constants";
|
||||||
@ -74,9 +74,9 @@ export const Image = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Image.Loader = ({ layout, ...props }: { layout: ImageLayout }) => {
|
Image.Loader = ({ layout, ...props }: { layout: ImageLayout, children?: ReactElement }) => {
|
||||||
const { css } = useYoshiki();
|
const { css } = useYoshiki();
|
||||||
const border = { borderRadius: 6, overflow: "hidden" } satisfies ViewStyle;
|
const border = { borderRadius: 6, overflow: "hidden" } satisfies ViewStyle;
|
||||||
|
|
||||||
return <Skeleton variant="custom" {...css([layout, border], props)} />;
|
return <Skeleton variant="custom" show {...css([layout, border], props)} />;
|
||||||
};
|
};
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { LinearGradient, type LinearGradientProps } from "expo-linear-gradient";
|
import { LinearGradient, type LinearGradientProps } from "expo-linear-gradient";
|
||||||
import type { ComponentProps, ComponentType, ReactNode } from "react";
|
import type { ComponentProps, ComponentType, ReactElement, ReactNode } from "react";
|
||||||
import { type ImageStyle, View, type ViewProps, type ViewStyle } from "react-native";
|
import { type ImageStyle, View, type ViewProps, type ViewStyle } from "react-native";
|
||||||
import { percent } from "yoshiki/native";
|
import { percent } from "yoshiki/native";
|
||||||
import { imageBorderRadius } from "../constants";
|
import { imageBorderRadius } from "../constants";
|
||||||
@ -43,6 +43,7 @@ Poster.Loader = ({
|
|||||||
layout,
|
layout,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
|
children?: ReactElement;
|
||||||
layout: YoshikiEnhanced<{ width: ImageStyle["width"] } | { height: ImageStyle["height"] }>;
|
layout: YoshikiEnhanced<{ width: ImageStyle["width"] } | { height: ImageStyle["height"] }>;
|
||||||
}) => <Image.Loader layout={{ aspectRatio: 2 / 3, ...layout }} {...props} />;
|
}) => <Image.Loader layout={{ aspectRatio: 2 / 3, ...layout }} {...props} />;
|
||||||
|
|
||||||
|
@ -19,11 +19,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { LinearGradient as LG } from "expo-linear-gradient";
|
import { LinearGradient as LG } from "expo-linear-gradient";
|
||||||
import { AnimatePresence, MotiView, motify } from "moti";
|
import { MotiView, motify } from "moti";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Platform, View, type ViewProps } from "react-native";
|
import { Platform, View, type ViewProps } from "react-native";
|
||||||
import { em, percent, px, rem, useYoshiki } from "yoshiki/native";
|
import { em, percent, px, rem, useYoshiki } from "yoshiki/native";
|
||||||
import { hiddenIfNoJs } from "./utils/nojs";
|
|
||||||
|
|
||||||
const LinearGradient = motify(LG)();
|
const LinearGradient = motify(LG)();
|
||||||
|
|
||||||
@ -99,71 +98,59 @@ export const Skeleton = ({
|
|||||||
props,
|
props,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<AnimatePresence>
|
{(forcedShow || !children || children === true) &&
|
||||||
{children}
|
[...Array(lines)].map((_, i) => (
|
||||||
{(forcedShow || !children || children === true) &&
|
<MotiView
|
||||||
[...Array(lines)].map((_, i) => (
|
key={`skeleton_${i}`}
|
||||||
<MotiView
|
// No clue why it is a number on mobile and a string on web but /shrug
|
||||||
key={`skeleton_${i}`}
|
animate={{ opacity: Platform.OS === "web" ? ("1" as any) : 1 }}
|
||||||
// No clue why it is a number on mobile and a string on web but /shrug
|
exit={{ opacity: 0 }}
|
||||||
animate={{ opacity: Platform.OS === "web" ? ("1" as any) : 1 }}
|
transition={{ type: "timing" }}
|
||||||
exit={{ opacity: 0 }}
|
onLayout={(e) => setWidth(e.nativeEvent.layout.width)}
|
||||||
transition={{ type: "timing" }}
|
{...css([
|
||||||
onLayout={(e) => setWidth(e.nativeEvent.layout.width)}
|
{
|
||||||
{...css(
|
bg: (theme) => theme.overlay0,
|
||||||
[
|
},
|
||||||
{
|
lines === 1 && {
|
||||||
bg: (theme) => theme.overlay0,
|
position: "absolute",
|
||||||
},
|
top: 0,
|
||||||
lines === 1 && {
|
bottom: 0,
|
||||||
position: "absolute",
|
left: 0,
|
||||||
top: 0,
|
right: 0,
|
||||||
bottom: 0,
|
},
|
||||||
left: 0,
|
lines !== 1 && {
|
||||||
right: 0,
|
width: i === lines - 1 ? percent(40) : percent(100),
|
||||||
},
|
height: rem(1.2),
|
||||||
lines !== 1 && {
|
marginBottom: rem(0.5),
|
||||||
width: i === lines - 1 ? percent(40) : percent(100),
|
overflow: "hidden",
|
||||||
height: rem(1.2),
|
borderRadius: px(6),
|
||||||
marginBottom: rem(0.5),
|
},
|
||||||
overflow: "hidden",
|
])}
|
||||||
borderRadius: px(6),
|
>
|
||||||
},
|
<LinearGradient
|
||||||
],
|
start={{ x: 0, y: 0.5 }}
|
||||||
hiddenIfNoJs,
|
end={{ x: 1, y: 0.5 }}
|
||||||
)}
|
colors={["transparent", theme.overlay1, "transparent"]}
|
||||||
>
|
transition={{
|
||||||
<LinearGradient
|
loop: true,
|
||||||
start={{ x: 0, y: 0.5 }}
|
repeatReverse: false,
|
||||||
end={{ x: 1, y: 0.5 }}
|
}}
|
||||||
colors={["transparent", theme.overlay1, "transparent"]}
|
animate={{
|
||||||
transition={{
|
translateX: width
|
||||||
loop: true,
|
? [perc(-100), { value: perc(100), type: "timing", duration: 800, delay: 800 }]
|
||||||
repeatReverse: false,
|
: undefined,
|
||||||
}}
|
}}
|
||||||
animate={{
|
{...css({
|
||||||
translateX: width
|
position: "absolute",
|
||||||
? [perc(-100), { value: perc(100), type: "timing", duration: 800, delay: 800 }]
|
top: 0,
|
||||||
: undefined,
|
bottom: 0,
|
||||||
}}
|
left: 0,
|
||||||
{...css([
|
right: 0,
|
||||||
{
|
})}
|
||||||
position: "absolute",
|
/>
|
||||||
top: 0,
|
</MotiView>
|
||||||
bottom: 0,
|
))}
|
||||||
left: 0,
|
{children}
|
||||||
right: 0,
|
|
||||||
},
|
|
||||||
Platform.OS === "web" && {
|
|
||||||
// @ts-ignore Web only properties
|
|
||||||
animation: "skeleton 1.6s linear 0.5s infinite",
|
|
||||||
transform: "translateX(-100%)",
|
|
||||||
},
|
|
||||||
])}
|
|
||||||
/>
|
|
||||||
</MotiView>
|
|
||||||
))}
|
|
||||||
</AnimatePresence>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
import { LinearGradient } from "expo-linear-gradient";
|
import { LinearGradient } from "expo-linear-gradient";
|
||||||
import { View, type ViewProps } from "react-native";
|
import { View, type ViewProps } from "react-native";
|
||||||
import { em, percent, px, rem, useYoshiki } from "yoshiki/native";
|
import { em, percent, px, rem, useYoshiki } from "yoshiki/native";
|
||||||
import { hiddenIfNoJs } from "./utils/nojs";
|
|
||||||
|
|
||||||
export const SkeletonCss = () => (
|
export const SkeletonCss = () => (
|
||||||
<style jsx global>{`
|
<style jsx global>{`
|
||||||
@ -90,33 +89,29 @@ export const Skeleton = ({
|
|||||||
props,
|
props,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
|
||||||
{(forcedShow || !children || children === true) &&
|
{(forcedShow || !children || children === true) &&
|
||||||
[...Array(lines)].map((_, i) => (
|
[...Array(lines)].map((_, i) => (
|
||||||
<View
|
<View
|
||||||
key={`skeleton_${i}`}
|
key={`skeleton_${i}`}
|
||||||
{...css(
|
{...css([
|
||||||
[
|
{
|
||||||
{
|
bg: (theme) => theme.overlay0,
|
||||||
bg: (theme) => theme.overlay0,
|
},
|
||||||
},
|
lines === 1 && {
|
||||||
lines === 1 && {
|
position: "absolute",
|
||||||
position: "absolute",
|
top: 0,
|
||||||
top: 0,
|
bottom: 0,
|
||||||
bottom: 0,
|
left: 0,
|
||||||
left: 0,
|
right: 0,
|
||||||
right: 0,
|
},
|
||||||
},
|
lines !== 1 && {
|
||||||
lines !== 1 && {
|
width: i === lines - 1 ? percent(40) : percent(100),
|
||||||
width: i === lines - 1 ? percent(40) : percent(100),
|
height: rem(1.2),
|
||||||
height: rem(1.2),
|
marginBottom: rem(0.5),
|
||||||
marginBottom: rem(0.5),
|
overflow: "hidden",
|
||||||
overflow: "hidden",
|
borderRadius: px(6),
|
||||||
borderRadius: px(6),
|
},
|
||||||
},
|
])}
|
||||||
],
|
|
||||||
hiddenIfNoJs,
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<LinearGradient
|
<LinearGradient
|
||||||
start={{ x: 0, y: 0.5 }}
|
start={{ x: 0, y: 0.5 }}
|
||||||
@ -137,6 +132,7 @@ export const Skeleton = ({
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
{children}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -33,6 +33,7 @@ import {
|
|||||||
IconFab,
|
IconFab,
|
||||||
Link,
|
Link,
|
||||||
P,
|
P,
|
||||||
|
Poster,
|
||||||
PosterBackground,
|
PosterBackground,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
SubP,
|
SubP,
|
||||||
@ -48,11 +49,10 @@ import { ScrollView, View } from "react-native";
|
|||||||
import { type Theme, calc, percent, px, rem, useYoshiki } from "yoshiki/native";
|
import { type Theme, calc, percent, px, rem, useYoshiki } from "yoshiki/native";
|
||||||
import { ItemGrid, ItemWatchStatus } from "../browse/grid";
|
import { ItemGrid, ItemWatchStatus } from "../browse/grid";
|
||||||
import { ItemContext } from "../components/context-menus";
|
import { ItemContext } from "../components/context-menus";
|
||||||
import type { Layout, WithLoading } from "../fetch";
|
import type { Layout } from "../fetch";
|
||||||
import { InfiniteFetch } from "../fetch-infinite";
|
import { InfiniteFetch } from "../fetch-infinite";
|
||||||
|
|
||||||
export const ItemDetails = ({
|
export const ItemDetails = ({
|
||||||
isLoading,
|
|
||||||
slug,
|
slug,
|
||||||
type,
|
type,
|
||||||
name,
|
name,
|
||||||
@ -66,12 +66,12 @@ export const ItemDetails = ({
|
|||||||
watchStatus,
|
watchStatus,
|
||||||
unseenEpisodesCount,
|
unseenEpisodesCount,
|
||||||
...props
|
...props
|
||||||
}: WithLoading<{
|
}: {
|
||||||
slug: string;
|
slug: string;
|
||||||
type: "movie" | "show" | "collection";
|
type: "movie" | "show" | "collection";
|
||||||
name: string;
|
name: string;
|
||||||
tagline: string | null;
|
tagline: string | null;
|
||||||
subtitle: string;
|
subtitle: string | null;
|
||||||
poster: KyooImage | null;
|
poster: KyooImage | null;
|
||||||
genres: Genre[] | null;
|
genres: Genre[] | null;
|
||||||
overview: string | null;
|
overview: string | null;
|
||||||
@ -79,7 +79,7 @@ export const ItemDetails = ({
|
|||||||
playHref: string | null;
|
playHref: string | null;
|
||||||
watchStatus: WatchStatusV | null;
|
watchStatus: WatchStatusV | null;
|
||||||
unseenEpisodesCount: number | null;
|
unseenEpisodesCount: number | null;
|
||||||
}>) => {
|
}) => {
|
||||||
const [moreOpened, setMoreOpened] = useState(false);
|
const [moreOpened, setMoreOpened] = useState(false);
|
||||||
const { css } = useYoshiki("recommended-card");
|
const { css } = useYoshiki("recommended-card");
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -124,7 +124,6 @@ export const ItemDetails = ({
|
|||||||
src={poster}
|
src={poster}
|
||||||
alt=""
|
alt=""
|
||||||
quality="low"
|
quality="low"
|
||||||
forcedLoading={isLoading}
|
|
||||||
layout={{ height: percent(100) }}
|
layout={{ height: percent(100) }}
|
||||||
style={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}
|
style={{ borderTopRightRadius: 0, borderBottomRightRadius: 0 }}
|
||||||
>
|
>
|
||||||
@ -138,18 +137,8 @@ export const ItemDetails = ({
|
|||||||
p: ts(1),
|
p: ts(1),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Skeleton {...css({ width: percent(100) })}>
|
<P {...css([{ m: 0, color: (theme: Theme) => theme.colors.white }, "title"])}>{name}</P>
|
||||||
{isLoading || (
|
{subtitle && <SubP {...css({ m: 0 })}>{subtitle}</SubP>}
|
||||||
<P {...css([{ m: 0, color: (theme: Theme) => theme.colors.white }, "title"])}>
|
|
||||||
{name}
|
|
||||||
</P>
|
|
||||||
)}
|
|
||||||
</Skeleton>
|
|
||||||
{(subtitle || isLoading) && (
|
|
||||||
<Skeleton {...css({ height: rem(0.8) })}>
|
|
||||||
{isLoading || <SubP {...css({ m: 0 })}>{subtitle}</SubP>}
|
|
||||||
</Skeleton>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
<ItemWatchStatus watchStatus={watchStatus} unseenEpisodesCount={unseenEpisodesCount} />
|
<ItemWatchStatus watchStatus={watchStatus} unseenEpisodesCount={unseenEpisodesCount} />
|
||||||
</PosterBackground>
|
</PosterBackground>
|
||||||
@ -163,7 +152,7 @@ export const ItemDetails = ({
|
|||||||
alignContent: "flex-start",
|
alignContent: "flex-start",
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{slug && type && type !== "collection" && watchStatus !== undefined && (
|
{type !== "collection" && (
|
||||||
<ItemContext
|
<ItemContext
|
||||||
type={type}
|
type={type}
|
||||||
slug={slug}
|
slug={slug}
|
||||||
@ -173,18 +162,10 @@ export const ItemDetails = ({
|
|||||||
force
|
force
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{(isLoading || tagline) && (
|
{tagline && <P {...css({ p: ts(1) })}>{tagline}</P>}
|
||||||
<Skeleton {...css({ m: ts(1), marginVertical: ts(2) })}>
|
|
||||||
{isLoading || <P {...css({ p: ts(1) })}>{tagline}</P>}
|
|
||||||
</Skeleton>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
<ScrollView {...css({ pX: ts(1) })}>
|
<ScrollView {...css({ pX: ts(1) })}>
|
||||||
<Skeleton lines={5} {...css({ height: rem(0.8) })}>
|
<SubP {...css({ textAlign: "justify" })}>{overview ?? t("show.noOverview")}</SubP>
|
||||||
{isLoading || (
|
|
||||||
<SubP {...css({ textAlign: "justify" })}>{overview ?? t("show.noOverview")}</SubP>
|
|
||||||
)}
|
|
||||||
</Skeleton>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
</Link>
|
</Link>
|
||||||
@ -209,9 +190,9 @@ export const ItemDetails = ({
|
|||||||
height: px(50),
|
height: px(50),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{(isLoading || genres) && (
|
{genres && (
|
||||||
<ScrollView horizontal contentContainerStyle={{ alignItems: "center" }}>
|
<ScrollView horizontal contentContainerStyle={{ alignItems: "center" }}>
|
||||||
{(genres || [...Array(3)])?.map((x, i) => (
|
{genres.map((x, i) => (
|
||||||
<Chip key={x ?? i} label={x} size="small" {...css({ mX: ts(0.5) })} />
|
<Chip key={x ?? i} label={x} size="small" {...css({ mX: ts(0.5) })} />
|
||||||
))}
|
))}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
@ -231,6 +212,65 @@ export const ItemDetails = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ItemDetails.Loader = (props: object) => {
|
||||||
|
const { css } = useYoshiki();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
{...css(
|
||||||
|
{
|
||||||
|
height: ItemDetails.layout.size,
|
||||||
|
flexDirection: "row",
|
||||||
|
bg: (theme) => theme.variant.background,
|
||||||
|
borderRadius: calc(px(imageBorderRadius), "+", ts(0.25)),
|
||||||
|
overflow: "hidden",
|
||||||
|
borderColor: (theme) => theme.background,
|
||||||
|
borderWidth: ts(0.25),
|
||||||
|
borderStyle: "solid",
|
||||||
|
},
|
||||||
|
props,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Poster.Loader
|
||||||
|
layout={{ height: percent(100) }}
|
||||||
|
{...css({ borderTopRightRadius: 0, borderBottomRightRadius: 0 })}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
{...css({
|
||||||
|
bg: (theme) => theme.darkOverlay,
|
||||||
|
position: "absolute",
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
p: ts(1),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Skeleton {...css({ width: percent(100) })} />
|
||||||
|
<Skeleton {...css({ height: rem(0.8) })} />
|
||||||
|
</View>
|
||||||
|
</Poster.Loader>
|
||||||
|
<View {...css({ flexShrink: 1, flexGrow: 1 })}>
|
||||||
|
<View {...css({ flexGrow: 1, flexShrink: 1, pX: ts(1) })}>
|
||||||
|
<Skeleton {...css({ marginVertical: ts(2) })} />
|
||||||
|
<Skeleton lines={5} {...css({ height: rem(0.8) })} />
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
{...css({
|
||||||
|
bg: (theme) => theme.themeOverlay,
|
||||||
|
pX: 4,
|
||||||
|
height: px(50),
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Chip.Loader size="small" {...css({ mX: ts(0.5) })} />
|
||||||
|
<Chip.Loader size="small" {...css({ mX: ts(0.5) })} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
ItemDetails.layout = {
|
ItemDetails.layout = {
|
||||||
size: ts(36),
|
size: ts(36),
|
||||||
numColumns: { xs: 1, md: 2, xl: 3 },
|
numColumns: { xs: 1, md: 2, xl: 3 },
|
||||||
@ -252,29 +292,28 @@ export const Recommended = () => {
|
|||||||
fetchMore={false}
|
fetchMore={false}
|
||||||
nested
|
nested
|
||||||
contentContainerStyle={{ padding: 0, paddingHorizontal: 0 }}
|
contentContainerStyle={{ padding: 0, paddingHorizontal: 0 }}
|
||||||
>
|
Render={({ item }) => (
|
||||||
{(x) => (
|
|
||||||
<ItemDetails
|
<ItemDetails
|
||||||
isLoading={x.isLoading as any}
|
slug={item.slug}
|
||||||
slug={x.slug}
|
type={item.kind}
|
||||||
type={x.kind}
|
name={item.name}
|
||||||
name={x.name}
|
tagline={"tagline" in item ? item.tagline : null}
|
||||||
tagline={"tagline" in x ? x.tagline : null}
|
overview={item.overview}
|
||||||
overview={x.overview}
|
poster={item.poster}
|
||||||
poster={x.poster}
|
subtitle={item.kind !== "collection" ? getDisplayDate(item) : null}
|
||||||
subtitle={x.kind !== "collection" && !x.isLoading ? getDisplayDate(x) : undefined}
|
genres={"genres" in item ? item.genres : null}
|
||||||
genres={"genres" in x ? x.genres : null}
|
href={item.href}
|
||||||
href={x.href}
|
playHref={item.kind !== "collection" ? item.playHref : null}
|
||||||
playHref={x.kind !== "collection" && !x.isLoading ? x.playHref : undefined}
|
watchStatus={(item.kind !== "collection" && item.watchStatus?.status) || null}
|
||||||
watchStatus={
|
|
||||||
!x.isLoading && x.kind !== "collection" ? x.watchStatus?.status ?? null : null
|
|
||||||
}
|
|
||||||
unseenEpisodesCount={
|
unseenEpisodesCount={
|
||||||
x.kind === "show" ? x.watchStatus?.unseenEpisodesCount ?? x.episodesCount! : null
|
item.kind === "show"
|
||||||
|
? item.watchStatus?.unseenEpisodesCount ?? item.episodesCount!
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</InfiniteFetch>
|
Loader={ItemDetails.Loader}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user