Add tags on shows and movies

This commit is contained in:
Zoe Roux 2023-08-12 01:21:17 +09:00
parent be3724c6b1
commit fd3c2e5f9b
No known key found for this signature in database
7 changed files with 57 additions and 44 deletions

View File

@ -22,17 +22,24 @@ import { rem, Stylable, Theme, useYoshiki } from "yoshiki/native";
import { P } from "./text"; import { P } from "./text";
import { ts } from "./utils"; import { ts } from "./utils";
export const Chip = ({ label, color, ...props }: { label: string; color?: string } & Stylable) => { export const Chip = ({
label,
color,
size = "medium",
...props
}: { label: string; color?: string; size?: "small" | "medium" | "large" } & Stylable) => {
const { css } = useYoshiki(); const { css } = useYoshiki();
const sizeMult = size == "medium" ? 1 : size == "small" ? 0.75 : 1.25;
return ( return (
<P <P
{...css( {...css(
{ {
pY: ts(1), pY: ts(1 * sizeMult),
pX: ts(1.5), pX: ts(1.5 * sizeMult),
borderRadius: ts(3), borderRadius: ts(3),
fontSize: rem(.8), fontSize: rem(0.8),
color: (theme: Theme) => theme.contrast, color: (theme: Theme) => theme.contrast,
bg: color ?? ((theme: Theme) => theme.accent), bg: color ?? ((theme: Theme) => theme.accent),
}, },

View File

@ -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 { ComponentType, ComponentProps } from "react"; import { ComponentType, ComponentProps, ReactNode } from "react";
import { Platform, Text, TextProps, TextStyle, StyleProp } from "react-native"; import { Platform, Text, TextProps, TextStyle, StyleProp } from "react-native";
import { percent, rem, useYoshiki } from "yoshiki/native"; import { percent, rem, useYoshiki } from "yoshiki/native";
import { import {
@ -37,7 +37,7 @@ const styleText = (
type?: "header" | "sub", type?: "header" | "sub",
custom?: TextStyle, custom?: TextStyle,
) => { ) => {
const Text = (props: Omit<ComponentProps<typeof EP>, "style"> & { style?: StyleProp<TextStyle> }) => { const Text = (props: Omit<ComponentProps<typeof EP>, "style"> & { style?: StyleProp<TextStyle>, children?: TextProps["children"] }) => {
const { css, theme } = useYoshiki(); const { css, theme } = useYoshiki();
return ( return (

View File

@ -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 { Movie, QueryIdentifier, Show, getDisplayDate, Genre, Studio } from "@kyoo/models"; import { Movie, QueryIdentifier, Show, getDisplayDate, Genre, Studio, KyooImage } from "@kyoo/models";
import { import {
Container, Container,
H1, H1,
@ -37,11 +37,11 @@ import {
LI, LI,
A, A,
ts, ts,
Button, Chip,
} from "@kyoo/primitives"; } from "@kyoo/primitives";
import { Fragment } from "react"; import { Fragment } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Platform, Pressable, PressableProps, View } from "react-native"; import { Platform, View } from "react-native";
import { import {
Theme, Theme,
md, md,
@ -73,7 +73,7 @@ const TitleLine = ({
slug: string; slug: string;
name?: string; name?: string;
date?: string; date?: string;
poster?: string | null; poster?: KyooImage | null;
studio?: Studio | null; studio?: Studio | null;
trailerUrl?: string | null; trailerUrl?: string | null;
} & Stylable) => { } & Stylable) => {
@ -99,12 +99,13 @@ const TitleLine = ({
<Poster <Poster
src={poster} src={poster}
alt={name} alt={name}
quality="high"
isLoading={isLoading} isLoading={isLoading}
layout={{ layout={{
width: { xs: percent(50), md: percent(25) }, width: { xs: percent(50), md: percent(25) },
}} }}
{...css({ {...css({
maxWidth: { xs: px(175), sm: Platform.OS === "web" ? "unset" : 99999999 }, maxWidth: { xs: px(175), sm: Platform.OS === "web" ? "unset" as any : 99999999 },
flexShrink: 0, flexShrink: 0,
})} })}
/> />
@ -227,11 +228,13 @@ const TitleLine = ({
const Description = ({ const Description = ({
isLoading, isLoading,
overview, overview,
tags,
genres, genres,
...props ...props
}: { }: {
isLoading: boolean; isLoading: boolean;
overview?: string | null; overview?: string | null;
tags?: string[];
genres?: Genre[]; genres?: Genre[];
} & Stylable) => { } & Stylable) => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -249,40 +252,40 @@ const Description = ({
})} })}
> >
{t("show.genre")}:{" "} {t("show.genre")}:{" "}
{(isLoading ? [...Array(3)] : genres!).map((genre, i) => ( {(isLoading ? [...Array<Genre>(3)] : genres!).map((genre, i) => (
<Fragment key={genre?.slug ?? i.toString()}> <Fragment key={genre ?? i.toString()}>
<P {...css({ m: 0 })}>{i !== 0 && ", "}</P> <P {...css({ m: 0 })}>{i !== 0 && ", "}</P>
{isLoading ? ( {isLoading ? (
<Skeleton {...css({ width: rem(5) })} /> <Skeleton {...css({ width: rem(5) })} />
) : ( ) : (
<A href={`/genres/${genre.slug}`}>{genre.name}</A> <A href={`/genres/${genre.toLowerCase()}`}>{genre}</A>
)} )}
</Fragment> </Fragment>
))} ))}
</P> </P>
<Skeleton <View {...css({
lines={4} flexDirection: "column",
{...css({ flexGrow: 1,
width: percent(100), flexBasis: { sm: 0 },
flexBasis: 0, paddingTop: ts(4),
flexGrow: 1, })}>
paddingTop: ts(4), <Skeleton lines={4} >
})} {isLoading || (
> <P
{isLoading || ( {...css({ textAlign: "justify", })}
<P >
{...css({ {overview ?? t("show.noOverview")}
flexBasis: 0, </P>
flexGrow: 1, )}
textAlign: "justify", </Skeleton>
paddingTop: ts(4), <View {...css({ flexWrap: "wrap", flexDirection: "row", marginTop: ts(.5) })}>
})} <P {...css({ marginRight: ts(.5) })}>{t("show.tags")}:</P>
> {(isLoading ? [...Array<string>(3)] : tags!).map((tag, i) => (
{overview ?? t("show.noOverview")} <Chip key={tag ?? i} label={tag} size="small" {...css({ m: ts(.5) })} />
</P> ))}
)} </View>
</Skeleton> </View>
<HR <HR
orientation="vertical" orientation="vertical"
{...css({ marginX: ts(2), display: { xs: "none", sm: "flex" } })} {...css({ marginX: ts(2), display: { xs: "none", sm: "flex" } })}
@ -291,12 +294,12 @@ const Description = ({
<H2>{t("show.genre")}</H2> <H2>{t("show.genre")}</H2>
{isLoading || genres?.length ? ( {isLoading || genres?.length ? (
<UL> <UL>
{(isLoading ? [...Array(3)] : genres!).map((genre, i) => ( {(isLoading ? [...Array<Genre>(3)] : genres!).map((genre, i) => (
<LI key={genre?.id ?? i}> <LI key={genre ?? i}>
{isLoading ? ( {isLoading ? (
<Skeleton {...css({ marginBottom: 0 })} /> <Skeleton {...css({ marginBottom: 0 })} />
) : ( ) : (
<A href={`/genres/${genre.slug}`}>{genre.name}</A> <A href={`/genres/${genre.toLowerCase()}`}>{genre}</A>
)} )}
</LI> </LI>
))} ))}
@ -352,6 +355,7 @@ export const Header = ({ query, slug }: { query: QueryIdentifier<Show | Movie>;
isLoading={isLoading} isLoading={isLoading}
overview={data?.overview} overview={data?.overview}
genres={data?.genres} genres={data?.genres}
tags={data?.tags}
{...css({ paddingTop: { xs: 0, md: ts(2) } })} {...css({ paddingTop: { xs: 0, md: ts(2) } })}
/> />
</> </>

View File

@ -26,9 +26,9 @@ import { Header } from "./header";
const query = (slug: string): QueryIdentifier<Movie> => ({ const query = (slug: string): QueryIdentifier<Movie> => ({
parser: MovieP, parser: MovieP,
path: ["shows", slug], path: ["movies", slug],
params: { params: {
fields: ["genres", "studio"], fields: ["studio"],
}, },
}); });

View File

@ -87,7 +87,7 @@ const query = (slug: string): QueryIdentifier<Show> => ({
parser: ShowP, parser: ShowP,
path: ["shows", slug], path: ["shows", slug],
params: { params: {
fields: ["genres", "studio"], fields: ["studio"],
}, },
}); });

View File

@ -9,7 +9,8 @@
"staff-none": "The staff is unknown", "staff-none": "The staff is unknown",
"noOverview": "No overview available", "noOverview": "No overview available",
"episode-none": "There is no episodes in this season", "episode-none": "There is no episodes in this season",
"episodeNoMetadata": "No metadata available" "episodeNoMetadata": "No metadata available",
"tags": "Tags"
}, },
"browse": { "browse": {
"sortby": "Sort by {{key}}", "sortby": "Sort by {{key}}",

View File

@ -9,7 +9,8 @@
"staff-none": "Aucun membre du staff connu", "staff-none": "Aucun membre du staff connu",
"noOverview": "Aucune description disponible", "noOverview": "Aucune description disponible",
"episode-none": "Il n'y a pas d'épisodes dans cette saison", "episode-none": "Il n'y a pas d'épisodes dans cette saison",
"episodeNoMetadata": "Aucune metadonnée disponible" "episodeNoMetadata": "Aucune metadonnée disponible",
"tags": "Tags"
}, },
"browse": { "browse": {
"sortby": "Trier par {{key}}", "sortby": "Trier par {{key}}",