mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Finish a basic recommanded card
This commit is contained in:
parent
941137ff51
commit
e4562648ba
@ -162,12 +162,14 @@ export type QueryIdentifier<T = unknown, Ret = T> = {
|
||||
getNext?: (item: unknown) => string | undefined;
|
||||
};
|
||||
|
||||
export type QueryPage<Props = {}, Items = unknown> = ComponentType<Props & { randomItems: Items[]}> & {
|
||||
export type QueryPage<Props = {}, Items = unknown> = ComponentType<
|
||||
Props & { randomItems: Items[] }
|
||||
> & {
|
||||
getFetchUrls?: (route: { [key: string]: string }) => QueryIdentifier<any>[];
|
||||
getLayout?:
|
||||
| QueryPage<{ page: ReactElement }>
|
||||
| { Layout: QueryPage<{ page: ReactElement }>; props: object };
|
||||
randomItems?: Items[]
|
||||
randomItems?: Items[];
|
||||
};
|
||||
|
||||
const toQueryKey = <Data, Ret>(query: QueryIdentifier<Data, Ret>) => {
|
||||
|
@ -26,6 +26,7 @@ export const catppuccin: ThemeBuilder = {
|
||||
// Catppuccin latte
|
||||
overlay0: "#9ca0b0",
|
||||
overlay1: "#7c7f93",
|
||||
lightOverlay: "#eff1f599",
|
||||
darkOverlay: "#4c4f6999",
|
||||
link: "#1e66f5",
|
||||
default: {
|
||||
@ -57,6 +58,7 @@ export const catppuccin: ThemeBuilder = {
|
||||
// Catppuccin mocha
|
||||
overlay0: "#6c7086",
|
||||
overlay1: "#9399b2",
|
||||
lightOverlay: "#f5f0f899",
|
||||
darkOverlay: "#11111b99",
|
||||
link: "#89b4fa",
|
||||
default: {
|
||||
|
@ -38,7 +38,9 @@ type Mode = {
|
||||
mode: "light" | "dark" | "auto";
|
||||
overlay0: Property.Color;
|
||||
overlay1: Property.Color;
|
||||
lightOverlay: Property.Color;
|
||||
darkOverlay: Property.Color;
|
||||
themeOverlay: Property.Color;
|
||||
link: Property.Color;
|
||||
contrast: Property.Color;
|
||||
variant: Variant;
|
||||
@ -73,8 +75,8 @@ declare module "yoshiki" {
|
||||
|
||||
export type { Theme } from "yoshiki";
|
||||
export type ThemeBuilder = {
|
||||
light: Omit<Mode, "contrast" | "mode"> & { default: Variant };
|
||||
dark: Omit<Mode, "contrast" | "mode"> & { default: Variant };
|
||||
light: Omit<Mode, "contrast" | "mode" | "themeOverlay"> & { default: Variant };
|
||||
dark: Omit<Mode, "contrast" | "mode" | "themeOverlay"> & { default: Variant };
|
||||
};
|
||||
|
||||
const selectMode = (
|
||||
@ -86,12 +88,14 @@ const selectMode = (
|
||||
...lightBuilder,
|
||||
...lightBuilder.default,
|
||||
contrast: lightBuilder.colors.black,
|
||||
themeOverlay: lightBuilder.lightOverlay,
|
||||
mode: "light",
|
||||
};
|
||||
const dark: Mode & Variant = {
|
||||
...darkBuilder,
|
||||
...darkBuilder.default,
|
||||
contrast: darkBuilder.colors.white,
|
||||
themeOverlay: darkBuilder.darkOverlay,
|
||||
mode: "dark",
|
||||
};
|
||||
if (Platform.OS !== "web" || mode !== "auto") {
|
||||
|
@ -36,6 +36,7 @@ import { Fetch } from "../fetch";
|
||||
import { ItemGrid } from "../browse/grid";
|
||||
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 { InfiniteFetch } from "../fetch-infinite";
|
||||
|
||||
export const GenreGrid = ({ genre }: { genre: Genre }) => {
|
||||
const ref = useRef<ScrollView>(null);
|
||||
@ -43,7 +44,7 @@ export const GenreGrid = ({ genre }: { genre: Genre }) => {
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View {...css({ marginX: ts(1), flexDirection: "row", justifyContent: "space-between" })}>
|
||||
<View {...css({ marginX: ts(1), flexDirection: "row", justifyContent: "space-between" })}>
|
||||
<H3>{genre}</H3>
|
||||
<View {...css({ flexDirection: "row" })}>
|
||||
<IconButton
|
||||
@ -56,28 +57,30 @@ export const GenreGrid = ({ genre }: { genre: Genre }) => {
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
<ScrollView ref={ref} horizontal>
|
||||
<Fetch query={GenreGrid.query(genre)}>
|
||||
{(x, i) => (
|
||||
<ItemGrid
|
||||
key={x.id ?? i}
|
||||
isLoading={x.isLoading as any}
|
||||
href={x.href}
|
||||
name={x.name}
|
||||
subtitle={
|
||||
x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined
|
||||
}
|
||||
poster={x.poster}
|
||||
/>
|
||||
)}
|
||||
</Fetch>
|
||||
</ScrollView>
|
||||
<InfiniteFetch
|
||||
query={GenreGrid.query(genre)}
|
||||
layout={{ ...ItemGrid.layout, layout: "horizontal" }}
|
||||
>
|
||||
{(x, i) => (
|
||||
<ItemGrid
|
||||
key={x.id ?? i}
|
||||
isLoading={x.isLoading as any}
|
||||
href={x.href}
|
||||
name={x.name}
|
||||
subtitle={
|
||||
x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined
|
||||
}
|
||||
poster={x.poster}
|
||||
/>
|
||||
)}
|
||||
</InfiniteFetch>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
GenreGrid.query = (genre: Genre): QueryIdentifier<Page<LibraryItem>> => ({
|
||||
parser: Paged(LibraryItemP) as any,
|
||||
GenreGrid.query = (genre: Genre): QueryIdentifier<LibraryItem> => ({
|
||||
parser: LibraryItemP,
|
||||
infinite: true,
|
||||
path: ["items"],
|
||||
params: {
|
||||
genres: genre,
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
Genre,
|
||||
ItemKind,
|
||||
KyooImage,
|
||||
LibraryItem,
|
||||
@ -28,11 +29,12 @@ import {
|
||||
QueryIdentifier,
|
||||
getDisplayDate,
|
||||
} from "@kyoo/models";
|
||||
import { Container, H3, ImageBackground, P, Poster, SubP, ts } from "@kyoo/primitives";
|
||||
import { Chip, Container, H3, ImageBackground, P, Poster, SubP, alpha, ts } from "@kyoo/primitives";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { View } from "react-native";
|
||||
import { percent, useYoshiki } from "yoshiki/native";
|
||||
import { Fetch, WithLoading } from "../fetch";
|
||||
import { ScrollView, View } from "react-native";
|
||||
import { percent, px, useYoshiki } from "yoshiki/native";
|
||||
import { Fetch, Layout, WithLoading } from "../fetch";
|
||||
import { InfiniteFetch } from "../fetch-infinite";
|
||||
|
||||
export const ItemDetails = ({
|
||||
isLoading,
|
||||
@ -41,35 +43,76 @@ export const ItemDetails = ({
|
||||
subtitle,
|
||||
overview,
|
||||
poster,
|
||||
genres,
|
||||
...props
|
||||
}: WithLoading<{
|
||||
name: string;
|
||||
tagline: string | null;
|
||||
subtitle: string;
|
||||
poster: KyooImage | null;
|
||||
genres: Genre[] | null;
|
||||
overview: string | null;
|
||||
}>) => {
|
||||
const { css } = useYoshiki();
|
||||
|
||||
return (
|
||||
<View {...css({ flexDirection: "row", bg: (theme) => theme.variant.background }, props)}>
|
||||
<View
|
||||
{...css(
|
||||
{
|
||||
height: ItemDetails.layout.size,
|
||||
flexDirection: "row",
|
||||
bg: (theme) => theme.variant.background,
|
||||
borderRadius: 6,
|
||||
overflow: "hidden",
|
||||
},
|
||||
props,
|
||||
)}
|
||||
>
|
||||
<ImageBackground
|
||||
src={poster}
|
||||
alt=""
|
||||
quality="low"
|
||||
gradient={false}
|
||||
{...css({ height: percent(100), aspectRatio: 2 / 3 })}
|
||||
>
|
||||
<P>{name}</P>
|
||||
{subtitle && <SubP>{subtitle}</SubP>}
|
||||
<View
|
||||
{...css({
|
||||
bg: (theme) => theme.darkOverlay,
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
p: ts(1),
|
||||
})}
|
||||
>
|
||||
<P {...css({ m: 0 })}>{name}</P>
|
||||
{subtitle && <SubP {...css({ m: 0 })}>{subtitle}</SubP>}
|
||||
</View>
|
||||
</ImageBackground>
|
||||
<View>
|
||||
{tagline && <P>{tagline}</P>}
|
||||
{overview && <SubP>{overview}</SubP>}
|
||||
<View {...css({ flexShrink: 1, flexGrow: 1 })}>
|
||||
{tagline && <P {...css({ p: ts(1) })}>{tagline}</P>}
|
||||
{overview && (
|
||||
<ScrollView>
|
||||
<SubP {...css({ pX: ts(1) })}>{overview}</SubP>
|
||||
</ScrollView>
|
||||
)}
|
||||
<View {...css({ bg: (theme) => theme.themeOverlay, flexDirection: "row" })}>
|
||||
{genres?.map((x) => (
|
||||
<Chip key={x} label={x} {...css({ mX: ts(.5) })} />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
ItemDetails.layout = {
|
||||
size: ts(36),
|
||||
numColumns: { xs: 1, md: 2, xl: 3 },
|
||||
layout: "grid",
|
||||
gap: ts(8),
|
||||
} satisfies Layout;
|
||||
|
||||
export const Recommanded = () => {
|
||||
const { t } = useTranslation();
|
||||
const { css } = useYoshiki();
|
||||
@ -77,7 +120,7 @@ export const Recommanded = () => {
|
||||
return (
|
||||
<View {...css({ marginX: ts(1) })}>
|
||||
<H3>{t("home.recommanded")}</H3>
|
||||
<Fetch query={Recommanded.query()}>
|
||||
<InfiniteFetch query={Recommanded.query()} layout={ItemDetails.layout}>
|
||||
{(x, i) => (
|
||||
<ItemDetails
|
||||
key={x.id ?? i}
|
||||
@ -89,16 +132,17 @@ export const Recommanded = () => {
|
||||
subtitle={
|
||||
x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined
|
||||
}
|
||||
{...css({ height: { xs: ts(15), md: ts(20) } })}
|
||||
genres={"genres" in x ? x.genres : null}
|
||||
/>
|
||||
)}
|
||||
</Fetch>
|
||||
</InfiniteFetch>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
Recommanded.query = (): QueryIdentifier<Page<LibraryItem>> => ({
|
||||
parser: Paged(LibraryItemP) as any,
|
||||
Recommanded.query = (): QueryIdentifier<LibraryItem> => ({
|
||||
parser: LibraryItemP,
|
||||
infinite: true,
|
||||
path: ["items"],
|
||||
params: {
|
||||
sortBy: "random",
|
||||
|
Loading…
x
Reference in New Issue
Block a user