diff --git a/front/packages/models/src/utils.ts b/front/packages/models/src/utils.ts index 6d3557de..51678098 100644 --- a/front/packages/models/src/utils.ts +++ b/front/packages/models/src/utils.ts @@ -42,6 +42,7 @@ export const getDisplayDate = (data: Show | Movie) => { if (airDate) { return airDate.getFullYear().toString(); } + return null; }; export const useLocalSetting = (setting: string, def: string) => { diff --git a/front/packages/primitives/src/image/image.tsx b/front/packages/primitives/src/image/image.tsx index a117d76d..288a892f 100644 --- a/front/packages/primitives/src/image/image.tsx +++ b/front/packages/primitives/src/image/image.tsx @@ -93,3 +93,10 @@ export const Image = ({ ); }; + +Image.Loader = ({ layout, ...props }: { layout: ImageLayout }) => { + const { css } = useYoshiki(); + const border = { borderRadius: 6, overflow: "hidden" } satisfies ViewStyle; + + return ; +}; diff --git a/front/packages/primitives/src/image/image.web.tsx b/front/packages/primitives/src/image/image.web.tsx index 3c5f6514..7a459e55 100644 --- a/front/packages/primitives/src/image/image.web.tsx +++ b/front/packages/primitives/src/image/image.web.tsx @@ -73,3 +73,10 @@ export const Image = ({ ); }; + +Image.Loader = ({ layout, ...props }: { layout: ImageLayout }) => { + const { css } = useYoshiki(); + const border = { borderRadius: 6, overflow: "hidden" } satisfies ViewStyle; + + return ; +}; diff --git a/front/packages/primitives/src/image/index.tsx b/front/packages/primitives/src/image/index.tsx index daffe995..ca793d31 100644 --- a/front/packages/primitives/src/image/index.tsx +++ b/front/packages/primitives/src/image/index.tsx @@ -39,6 +39,13 @@ export const Poster = ({ layout: YoshikiEnhanced<{ width: ImageStyle["width"] } | { height: ImageStyle["height"] }>; }) => {alt!}; +Poster.Loader = ({ + layout, + ...props +}: { + layout: YoshikiEnhanced<{ width: ImageStyle["width"] } | { height: ImageStyle["height"] }>; +}) => ; + export const PosterBackground = ({ alt, layout, diff --git a/front/packages/ui/src/browse/grid.tsx b/front/packages/ui/src/browse/grid.tsx index cf708683..10c23a9d 100644 --- a/front/packages/ui/src/browse/grid.tsx +++ b/front/packages/ui/src/browse/grid.tsx @@ -23,6 +23,7 @@ import { Icon, Link, P, + Poster, PosterBackground, Skeleton, SubP, @@ -35,7 +36,7 @@ import { useState } from "react"; import { type ImageStyle, Platform, View } from "react-native"; import { type Stylable, type Theme, max, percent, px, rem, useYoshiki } from "yoshiki/native"; import { ItemContext } from "../components/context-menus"; -import type { Layout, WithLoading } from "../fetch"; +import type { Layout } from "../fetch"; export const ItemWatchStatus = ({ watchStatus, @@ -113,23 +114,21 @@ export const ItemGrid = ({ type, subtitle, poster, - isLoading, watchStatus, watchPercent, unseenEpisodesCount, ...props -}: WithLoading<{ +}: { href: string; slug: string; name: string; - subtitle?: string; - poster?: KyooImage | null; + subtitle: string | null; + poster: KyooImage | null; watchStatus: WatchStatusV | null; watchPercent: number | null; type: "movie" | "show" | "collection"; unseenEpisodesCount: number | null; -}> & - Stylable<"text">) => { +} & Stylable<"text">) => { const [moreOpened, setMoreOpened] = useState(false); const { css } = useYoshiki("grid"); @@ -172,13 +171,12 @@ export const ItemGrid = ({ src={poster} alt={name} quality="low" - forcedLoading={isLoading} layout={{ width: percent(100) }} {...(css("poster") as { style: ImageStyle })} > {type === "movie" && watchPercent && } - {slug && watchStatus !== undefined && type && type !== "collection" && ( + {type !== "collection" && ( )} - - {isLoading || ( -

- {name} -

- )} -
- {(isLoading || subtitle) && ( - - {isLoading || ( - - {subtitle} - - )} - +

+ {name} +

+ {subtitle && ( + + {subtitle} + )} ); }; +ItemGrid.Loader = (props: Stylable) => { + const { css } = useYoshiki(); + + return ( + + theme.background, + borderWidth: ts(0.5), + borderStyle: "solid", + })} + /> + + + + ); +}; + ItemGrid.layout = { size: px(150), numColumns: { xs: 3, sm: 4, md: 5, lg: 6, xl: 8 }, diff --git a/front/packages/ui/src/browse/index.tsx b/front/packages/ui/src/browse/index.tsx index 9b038f69..9863657e 100644 --- a/front/packages/ui/src/browse/index.tsx +++ b/front/packages/ui/src/browse/index.tsx @@ -27,7 +27,6 @@ import { } from "@kyoo/models"; import { type ComponentProps, useState } from "react"; import { createParam } from "solito"; -import type { WithLoading } from "../fetch"; import { InfiniteFetch } from "../fetch-infinite"; import { DefaultLayout } from "../layout"; import { ItemGrid } from "./grid"; @@ -38,25 +37,20 @@ import { Layout, SortBy, SortOrd } from "./types"; const { useParam } = createParam<{ sortBy?: string }>(); export const itemMap = ( - item: WithLoading, -): WithLoading & ComponentProps> => { - if (item.isLoading) return item as any; - - return { - isLoading: item.isLoading, - slug: item.slug, - name: item.name, - subtitle: item.kind !== "collection" ? getDisplayDate(item) : undefined, - href: item.href, - poster: item.poster, - thumbnail: item.thumbnail, - watchStatus: item.kind !== "collection" ? item.watchStatus?.status ?? null : null, - type: item.kind, - watchPercent: item.kind !== "collection" ? item.watchStatus?.watchedPercent ?? null : null, - unseenEpisodesCount: - item.kind === "show" ? item.watchStatus?.unseenEpisodesCount ?? item.episodesCount! : null, - }; -}; + item: LibraryItem, +): ComponentProps & ComponentProps => ({ + slug: item.slug, + name: item.name, + subtitle: item.kind !== "collection" ? getDisplayDate(item) : null, + href: item.href, + poster: item.poster, + thumbnail: item.thumbnail, + watchStatus: item.kind !== "collection" ? item.watchStatus?.status ?? null : null, + type: item.kind, + watchPercent: item.kind !== "collection" ? item.watchStatus?.watchedPercent ?? null : null, + unseenEpisodesCount: + item.kind === "show" ? item.watchStatus?.unseenEpisodesCount ?? item.episodesCount! : null, +}); const query = (sortKey?: SortBy, sortOrd?: SortOrd): QueryIdentifier => ({ parser: LibraryItemP, @@ -92,9 +86,9 @@ export const BrowsePage: QueryPage = () => { setLayout={setLayout} /> } - > - {(item) => } - + Render={({ item }) => } + Loader={() => } + /> ); }; diff --git a/front/packages/ui/src/browse/list.tsx b/front/packages/ui/src/browse/list.tsx index 699d1868..889afacf 100644 --- a/front/packages/ui/src/browse/list.tsx +++ b/front/packages/ui/src/browse/list.tsx @@ -24,6 +24,7 @@ import { ImageBackground, Link, P, + Poster, PosterBackground, Skeleton, imageBorderRadius, @@ -34,8 +35,9 @@ import { useState } from "react"; import { Platform, View } from "react-native"; import { percent, px, rem, useYoshiki } from "yoshiki/native"; import { ItemContext } from "../components/context-menus"; -import type { Layout, WithLoading } from "../fetch"; +import type { Layout } from "../fetch"; import { ItemWatchStatus } from "./grid"; +import { Stylable } from "yoshiki"; export const ItemList = ({ href, @@ -45,22 +47,21 @@ export const ItemList = ({ subtitle, thumbnail, poster, - isLoading, watchStatus, unseenEpisodesCount, ...props -}: WithLoading<{ +}: { href: string; slug: string; type: "movie" | "show" | "collection"; name: string; - subtitle?: string; - poster?: KyooImage | null; - thumbnail?: KyooImage | null; + subtitle: string | null; + poster: KyooImage | null; + thumbnail: KyooImage | null; watchStatus: WatchStatusV | null; unseenEpisodesCount: number | null; -}>) => { - const { css } = useYoshiki(); +}) => { + const { css } = useYoshiki("line"); const [moreOpened, setMoreOpened] = useState(false); return ( @@ -114,25 +115,21 @@ export const ItemList = ({ justifyContent: "center", })} > - - {isLoading || ( - - {name} - - )} - - {slug && watchStatus !== undefined && type && type !== "collection" && ( + + {name} + + {type !== "collection" && ( )} - {(isLoading || subtitle) && ( - - {isLoading || ( -

- {subtitle} -

- )} -
+ {subtitle && ( +

+ {subtitle} +

)} - + ); }; +ItemList.Loader = (props: Stylable) => { + const { css } = useYoshiki(); + + return ( + theme.dark.background, + marginX: ItemList.layout.gap, + }, + props, + )} + > + + + + + + + ); +}; + ItemList.layout = { numColumns: 1, size: 300, layout: "vertical", gap: ts(2) } satisfies Layout;