Finish movie's header rework

This commit is contained in:
Zoe Roux 2022-12-13 14:13:15 +09:00
parent e5b236f51c
commit 1b76bbf6c2
14 changed files with 464 additions and 283 deletions

View File

@ -31,7 +31,7 @@
"react-native-safe-area-context": "4.4.1", "react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.0", "react-native-screens": "~3.18.0",
"react-native-svg": "13.4.0", "react-native-svg": "13.4.0",
"yoshiki": "0.2.11" "yoshiki": "0.3.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.19.3", "@babel/core": "^7.19.3",

View File

@ -41,7 +41,7 @@
"react-native-web": "^0.18.10", "react-native-web": "^0.18.10",
"solito": "^2.0.5", "solito": "^2.0.5",
"superjson": "^1.11.0", "superjson": "^1.11.0",
"yoshiki": "0.2.11", "yoshiki": "0.3.1",
"zod": "^3.19.1" "zod": "^3.19.1"
}, },
"devDependencies": { "devDependencies": {

View File

@ -19,7 +19,7 @@
*/ */
import { View, ViewProps } from "react-native"; import { View, ViewProps } from "react-native";
import { useYoshiki } from "yoshiki/native"; import { percent, px, useYoshiki } from "yoshiki/native";
export const Container = (props: ViewProps) => { export const Container = (props: ViewProps) => {
const { css } = useYoshiki(); const { css } = useYoshiki();
@ -29,12 +29,13 @@ export const Container = (props: ViewProps) => {
{...css( {...css(
{ {
display: "flex", display: "flex",
paddingHorizontal: "15px", paddingHorizontal: px(15),
marginHorizontal: "auto", marginHorizontal: "auto",
width: { width: {
sm: "540px", xs: percent(100),
md: "880px", sm: px(540),
lg: "1170px", md: px(880),
lg: px(1170),
}, },
}, },
props, props,

View File

@ -0,0 +1,56 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import { HR as EHR } from "@expo/html-elements";
import { percent, px, Stylable, useYoshiki } from "yoshiki/native";
import { alpha, ts } from ".";
export const HR = ({
orientation,
...props
}: { orientation: "vertical" | "horizontal" } & Stylable) => {
const { css } = useYoshiki();
return (
<EHR
{...css(
[
{
bg: (theme) => alpha(theme.overlay0, 0.7),
borderWidth: 0,
},
orientation === "vertical" && {
width: px(1),
height: "auto",
marginVertical: ts(1),
marginHorizontal: ts(2),
},
orientation === "horizontal" && {
height: px(1),
width: "auto",
marginHorizontal: ts(1),
marginVertical: ts(2),
},
],
props,
)}
/>
);
};

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/>.
*/ */
export { Header, Main, Nav, Footer } from "@expo/html-elements"; export { Header, Main, Nav, Footer, UL } from "@expo/html-elements";
export * from "./text"; export * from "./text";
export * from "./themes"; export * from "./themes";
export * from "./icons"; export * from "./icons";
@ -28,11 +28,13 @@ export * from "./image";
export * from "./skeleton"; export * from "./skeleton";
export * from "./tooltip"; export * from "./tooltip";
export * from "./container"; export * from "./container";
export * from "./divider";
export * from "./animated"; export * from "./animated";
export * from "./utils/breakpoints"; export * from "./utils/breakpoints";
export * from "./utils/nojs"; export * from "./utils/nojs";
export * from "./utils/head";
import { px } from "yoshiki/native"; import { px } from "yoshiki/native";

View File

@ -43,10 +43,13 @@ export const A = ({
textProps={css( textProps={css(
{ {
// TODO: use a real font here. // TODO: use a real font here.
fontFamily: Platform.OS === "web" ? theme.fonts.paragraph : undefined, // fontFamily: theme.fonts.paragraph,
color: theme.paragraph, color: theme.link,
},
{
selectable: true,
...props,
}, },
props,
)} )}
> >
{children} {children}
@ -83,8 +86,8 @@ export const Link = ({
})} })}
> >
{Platform.select<ReactNode>({ {Platform.select<ReactNode>({
android: <View {...props}>{children}</View>, android: <View {...noFocusProps}>{children}</View>,
ios: <View {...props}>{children}</View>, ios: <View {...noFocusProps}>{children}</View>,
default: children, default: children,
})} })}
</LinkCore> </LinkCore>

View File

@ -46,11 +46,13 @@ export const SkeletonCss = () => (
export const Skeleton = ({ export const Skeleton = ({
children, children,
show: forcedShow, show: forcedShow,
lines = 1,
variant = "text", variant = "text",
...props ...props
}: Omit<ViewProps, "children"> & { }: Omit<ViewProps, "children"> & {
children?: JSX.Element | JSX.Element[] | boolean | null; children?: JSX.Element | JSX.Element[] | boolean | null;
show?: boolean; show?: boolean;
lines?: number;
variant?: "text" | "round" | "custom"; variant?: "text" | "round" | "custom";
}) => { }) => {
const { css, theme } = useYoshiki(); const { css, theme } = useYoshiki();
@ -65,13 +67,14 @@ export const Skeleton = ({
[ [
{ {
position: "relative", position: "relative",
overflow: "hidden",
borderRadius: px(6),
},
variant === "text" && {
width: percent(75),
height: rem(1.2),
}, },
lines === 1 && { overflow: "hidden", borderRadius: px(6) },
variant === "text" &&
lines === 1 && {
width: percent(75),
height: rem(1.2),
marginBottom: rem(0.5),
},
variant === "round" && { variant === "round" && {
borderRadius: 9999999, borderRadius: 9999999,
}, },
@ -81,56 +84,68 @@ export const Skeleton = ({
> >
<AnimatePresence> <AnimatePresence>
{children} {children}
{(forcedShow || !children || children === true) && ( {(forcedShow || !children || children === true) &&
<MotiView [...Array(lines)].map((_, i) => (
key="skeleton" <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" : 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" : 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, [
position: "absolute", {
top: 0, bg: (theme) => theme.overlay0,
bottom: 0, },
left: 0, lines === 1 && {
right: 0, position: "absolute",
}, top: 0,
hiddenIfNoJs, bottom: 0,
)} left: 0,
> right: 0,
<LinearGradient },
start={{ x: 0, y: 0.5 }} lines !== 1 && {
end={{ x: 1, y: 0.5 }} width: i === lines - 1 ? percent(40) : percent(100),
colors={["transparent", theme.overlay1, "transparent"]} height: rem(1.2),
transition={{ marginBottom: rem(0.5),
loop: true, overflow: "hidden",
repeatReverse: false, borderRadius: px(6),
}} },
animate={{ ],
translateX: width hiddenIfNoJs,
? [perc(-100), { value: perc(100), type: "timing", duration: 800, delay: 800 }] )}
: undefined, >
}} <LinearGradient
{...css([ start={{ x: 0, y: 0.5 }}
{ end={{ x: 1, y: 0.5 }}
position: "absolute", colors={["transparent", theme.overlay1, "transparent"]}
top: 0, transition={{
bottom: 0, loop: true,
left: 0, repeatReverse: false,
right: 0, }}
}, animate={{
Platform.OS === "web" && { translateX: width
// @ts-ignore Web only properties ? [perc(-100), { value: perc(100), type: "timing", duration: 800, delay: 800 }]
animation: "skeleton 1.6s linear 0.5s infinite", : undefined,
transform: "translateX(-100%)", }}
}, {...css([
])} {
/> position: "absolute",
</MotiView> top: 0,
)} bottom: 0,
left: 0,
right: 0,
},
Platform.OS === "web" && {
// @ts-ignore Web only properties
animation: "skeleton 1.6s linear 0.5s infinite",
transform: "translateX(-100%)",
},
])}
/>
</MotiView>
))}
</AnimatePresence> </AnimatePresence>
</View> </View>
); );

View File

@ -19,8 +19,8 @@
*/ */
import { ComponentType, ComponentProps } from "react"; import { ComponentType, ComponentProps } from "react";
import { Platform, TextProps } from "react-native"; import { Platform, Text, TextProps, TextStyle } from "react-native";
import { rem, useYoshiki } from "yoshiki/native"; import { percent, px, rem, useYoshiki } from "yoshiki/native";
import { import {
H1 as EH1, H1 as EH1,
H2 as EH2, H2 as EH2,
@ -29,11 +29,14 @@ import {
H5 as EH5, H5 as EH5,
H6 as EH6, H6 as EH6,
P as EP, P as EP,
LI as ELI,
} from "@expo/html-elements"; } from "@expo/html-elements";
import { ts } from ".";
const styleText = ( const styleText = (
Component: ComponentType<ComponentProps<typeof EP>>, Component: ComponentType<ComponentProps<typeof EP>>,
type?: "header" | "sub", type?: "header" | "sub",
custom?: TextStyle,
) => { ) => {
const Text = (props: ComponentProps<typeof EP>) => { const Text = (props: ComponentProps<typeof EP>) => {
const { css, theme } = useYoshiki(); const { css, theme } = useYoshiki();
@ -43,16 +46,13 @@ const styleText = (
{...css( {...css(
[ [
{ {
// TODO: use custom fonts on mobile also. marginTop: 0,
fontFamily: marginBottom: rem(0.5),
Platform.OS === "web" // fontFamily: type === "header" ? theme.fonts.heading : theme.fonts.paragraph,
? type === "header"
? theme.fonts.heading
: theme.fonts.paragraph
: undefined,
color: type === "header" ? theme.heading : theme.paragraph, color: type === "header" ? theme.heading : theme.paragraph,
}, },
type === "sub" && { fontWeight: "300", opacity: 0.8, fontSize: rem(0.8) }, type === "sub" && { fontWeight: "300", opacity: 0.8, fontSize: rem(0.8) },
custom,
], ],
props as TextProps, props as TextProps,
)} )}
@ -62,12 +62,31 @@ const styleText = (
return Text; return Text;
}; };
export const H1 = styleText(EH1, "header"); export const H1 = styleText(EH1, "header", { fontSize: rem(3) });
export const H2 = styleText(EH2, "header"); export const H2 = styleText(EH2, "header", { fontSize: rem(2) });
export const H3 = styleText(EH3, "header"); export const H3 = styleText(EH3, "header");
export const H4 = styleText(EH4, "header"); export const H4 = styleText(EH4, "header");
export const H5 = styleText(EH5, "header"); export const H5 = styleText(EH5, "header");
export const H6 = styleText(EH6, "header"); export const H6 = styleText(EH6, "header");
export const Heading = styleText(EP, "header"); export const Heading = styleText(EP, "header");
export const P = styleText(EP); export const P = styleText(EP, undefined, { fontSize: rem(1) });
export const SubP = styleText(EP, "sub"); export const SubP = styleText(EP, "sub");
export const LI = ({ children, ...props }: TextProps) => {
const { css } = useYoshiki();
return (
<P accessibilityRole="listitem" {...props}>
<Text
{...css({
height: percent(100),
marginBottom: rem(0.5),
paddingRight: ts(1),
})}
>
{String.fromCharCode(0x2022)}
</Text>
{children}
</P>
);
};

View File

@ -31,6 +31,7 @@ export const catppuccin: ThemeBuilder = {
appbar: "#e64553", appbar: "#e64553",
overlay0: "#9ca0b0", overlay0: "#9ca0b0",
overlay1: "#7c7f93", overlay1: "#7c7f93",
link: "#1e66f5",
default: { default: {
background: "#eff1f5", background: "#eff1f5",
accent: "#ea76cb", accent: "#ea76cb",
@ -61,6 +62,7 @@ export const catppuccin: ThemeBuilder = {
appbar: "#94e2d5", appbar: "#94e2d5",
overlay0: "#6c7086", overlay0: "#6c7086",
overlay1: "#9399b2", overlay1: "#9399b2",
link: "#89b4fa",
default: { default: {
background: "#1e1e2e", background: "#1e1e2e",
accent: "#f5c2e7", accent: "#f5c2e7",

View File

@ -37,6 +37,7 @@ type Mode = {
appbar: Property.Color; appbar: Property.Color;
overlay0: Property.Color; overlay0: Property.Color;
overlay1: Property.Color; overlay1: Property.Color;
link: Property.Color;
variant: Variant; variant: Variant;
colors: { colors: {
red: Property.Color; red: Property.Color;
@ -64,13 +65,13 @@ declare module "yoshiki" {
user: Mode & Variant; user: Mode & Variant;
} }
} }
// declare module "yoshiki/native" { declare module "yoshiki/native" {
// export interface Theme extends ThemeSettings, Mode, Variant { export interface Theme extends ThemeSettings, Mode, Variant {
// light: Mode & Variant; light: Mode & Variant;
// dark: Mode & Variant; dark: Mode & Variant;
// user: Mode & Variant; user: Mode & Variant;
// } }
// } }
export type { Theme } from "yoshiki"; export type { Theme } from "yoshiki";
export type ThemeBuilder = ThemeSettings & { export type ThemeBuilder = ThemeSettings & {
@ -158,5 +159,5 @@ export const ContrastArea = ({
}; };
export const alpha = (color: Property.Color, alpha: number) => { export const alpha = (color: Property.Color, alpha: number) => {
return color + (alpha * 255).toString(16); return color + Math.round(alpha * 255).toString(16);
}; };

View File

@ -0,0 +1,23 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
export const Head = ({ title, description }: { title?: string | null; description?: string | null }) => {
return null;
};

View File

@ -0,0 +1,30 @@
/*
* Kyoo - A portable and vast media library solution.
* Copyright (c) Kyoo.
*
* See AUTHORS.md and LICENSE file in the project root for full license information.
*
* Kyoo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Kyoo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import NextHead from "next/head";
export const Head = ({ title, description }: { title?: string | null; description?: string | null }) => {
return (
<NextHead>
{title && <title>{title + "- Kyoo"}</title>}
{description && <meta name="description" content={description} />}
</NextHead>
);
};

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 } from "@kyoo/models"; import { Movie, QueryIdentifier, Show, getDisplayDate, Genre, Studio } from "@kyoo/models";
import { import {
Container, Container,
H1, H1,
@ -31,43 +31,40 @@ import {
Link, Link,
IconButton, IconButton,
IconFab, IconFab,
Head,
HR,
H2,
UL,
LI,
A,
ts,
} from "@kyoo/primitives"; } from "@kyoo/primitives";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Platform, StyleSheet, View } from "react-native"; import { StyleSheet, View } from "react-native";
import { em, percent, rem, vh, useYoshiki, Stylable } from "yoshiki/native"; import {
import { Fetch, WithLoading } from "../fetch"; Theme,
sm,
md,
px,
min,
max,
em,
percent,
rem,
vh,
useYoshiki,
Stylable,
} from "yoshiki/native";
import { Fetch } from "../fetch";
import { Navbar } from "../navbar"; import { Navbar } from "../navbar";
// const StudioText = ({
// studio,
// loading = false,
// sx,
// }: {
// studio?: Studio | null;
// loading?: boolean;
// sx?: SxProps;
// }) => {
// const { t } = useTranslation("browse");
// if (!loading && !studio) return null;
// return (
// <Typography sx={sx}>
// {t("show.studio")}:{" "}
// {loading ? (
// <Skeleton width="5rem" sx={{ display: "inline-flex" }} />
// ) : (
// <Link href={`/studio/${studio!.slug}`}>{studio!.name}</Link>
// )}
// </Typography>
// );
// };
const TitleLine = ({ const TitleLine = ({
isLoading, isLoading,
slug, slug,
name, name,
date, date,
poster, poster,
studio,
...props ...props
}: { }: {
isLoading: boolean; isLoading: boolean;
@ -75,6 +72,7 @@ const TitleLine = ({
name?: string; name?: string;
date?: string; date?: string;
poster?: string | null; poster?: string | null;
studio?: Studio | null;
} & Stylable) => { } & Stylable) => {
const { css, theme } = useYoshiki(); const { css, theme } = useYoshiki();
const { t } = useTranslation(); const { t } = useTranslation();
@ -83,162 +81,197 @@ const TitleLine = ({
<Container <Container
{...css( {...css(
{ {
flexDirection: { xs: "column", sm: "row" }, flexDirection: { xs: "column", md: "row" },
alignItems: { xs: "center", sm: "flex-start" },
}, },
props, props,
)} )}
> >
<Poster
src={poster}
alt={name}
isLoading={isLoading}
layout={{
width: { xs: percent(50), md: percent(25) },
}}
{...css({ maxWidth: { xs: px(175), sm: "unset" }, flexShrink: 0 })}
/>
<View <View
{...css({ {...css({
alignSelf: { xs: "center", sm: "flex-end", md: "center" }, flexDirection: { xs: "column", sm: "row" },
alignItems: { xs: "center", sm: "flex-start" }, alignItems: { xs: "center", sm: "flex-start" },
paddingLeft: { sm: em(2.5) }, flexGrow: 1,
flexShrink: 1,
})} })}
> >
<Skeleton {...css({ width: rem(15), height: rem(3), marginBottom: rem(0.5) })}> <Poster
{isLoading || ( src={poster}
<H1 alt={name}
{...css({ isLoading={isLoading}
fontWeight: { md: "900" }, layout={{
fontSize: rem(3), width: { xs: percent(50), md: percent(25) },
marginTop: 0, }}
marginBottom: rem(0.5), {...css({ maxWidth: { xs: px(175), sm: "unset" }, flexShrink: 0 })}
textAlign: { xs: "center", sm: "flex-start" }, />
color: (theme) => ({ xs: theme.user.heading, md: theme.heading }), <View
})} {...css({
> alignSelf: { xs: "center", sm: "flex-end", md: "center" },
{name} alignItems: { xs: "center", sm: "flex-start" },
</H1> paddingLeft: { sm: em(2.5) },
)} flexShrink: 1,
</Skeleton> flexGrow: 1,
{(isLoading || date) && ( })}
<Skeleton >
{...css({ <Skeleton {...css({ width: rem(15), height: rem(3) })}>
width: rem(5),
height: rem(1.5),
marginBottom: rem(0.5),
})}
>
{isLoading || ( {isLoading || (
<P <H1
{...css({ {...css({
fontWeight: "300", fontWeight: { md: "900" },
fontSize: rem(1.5), textAlign: { xs: "center", sm: "left" },
letterSpacing: 0, color: (theme: Theme) => ({ xs: theme.user.heading, md: theme.heading }),
marginTop: 0,
marginBottom: rem(0.5),
textAlign: { xs: "center", sm: "flex-start" },
color: (theme) => ({ xs: theme.user.heading, md: theme.heading }),
})} })}
> >
{date} {name}
</P> </H1>
)} )}
</Skeleton> </Skeleton>
)} {(isLoading || date) && (
<View {...css({ flexDirection: "row" })} /*sx={{ "& > *": { m: ".3rem !important" } }} */> <Skeleton
<IconFab {...css({
icon="play-arrow" width: rem(5),
as={Link} height: rem(1.5),
href={`/watch/${slug}`} })}
color={{ xs: theme.user.colors.black, md: theme.colors.black }} >
{...css({ bg: { xs: theme.user.accent, md: theme.accent } })} {isLoading || (
{...tooltip(t("show.play"))} <P
/> {...css({
<IconButton fontWeight: "300",
icon="local-movies" fontSize: rem(1.5),
color={{ xs: theme.user.colors.black, md: theme.colors.white }} letterSpacing: 0,
{...tooltip(t("show.trailer"))} textAlign: { xs: "center", sm: "left" },
/> color: (theme: Theme) => ({ xs: theme.user.heading, md: theme.heading }),
})}
>
{date}
</P>
)}
</Skeleton>
)}
<View {...css({ flexDirection: "row" })}>
<IconFab
icon="play-arrow"
as={Link}
href={`/watch/${slug}`}
color={{ xs: theme.user.colors.black, md: theme.colors.black }}
{...css({ bg: { xs: theme.user.accent, md: theme.accent } })}
{...tooltip(t("show.play"))}
/>
<IconButton
icon="local-movies"
color={{ xs: theme.user.colors.black, md: theme.colors.white }}
{...tooltip(t("show.trailer"))}
/>
</View>
</View> </View>
</View> </View>
{/* <View */} <View
{/* {...css({ */} {...css([
{/* display: { xs: "none", md: "flex" }, */} {
{/* flexDirection: "column", */} paddingTop: { xs: ts(3), sm: ts(8) },
{/* alignSelf: "flex-end", */} alignSelf: { xs: "flex-start", md: "flex-end" },
{/* paddingRight: px(15), */} justifyContent: "flex-end",
{/* })} */} flexDirection: "column",
{/* > */} },
{/* {(isLoading || logo || true) && ( */} md({
{/* <Image */} position: "absolute",
{/* src={logo} */} top: 0,
{/* alt="" */} bottom: 0,
{/* layout={{ */} right: 0,
{/* width: "100%", */} width: percent(25),
{/* height: px(100), */} height: percent(100),
{/* }} */} paddingRight: ts(3),
{/* // sx={{ display: { xs: "none", lg: "unset" } }} */} }),
{/* /> */} ])}
{/* )} */} >
{/* {/1* <StudioText loading={!data} studio={data?.studio} sx={{ mt: "auto", mb: 3 }} /> *1/} */} <P
{/* </View> */} {...css({
color: (theme) => theme.user.paragraph,
})}
>
{t("show.studio")}:{" "}
{isLoading ? (
<Skeleton />
) : (
<A href={`/studio/${studio!.slug}`} {...css({ color: (theme) => theme.user.link })}>
{studio!.name}
</A>
)}
</P>
</View>
</Container> </Container>
); );
}; };
// const Tata = () => { const Description = ({
// return ( isLoading,
// <Container sx={{ pt: 2 }}> overview,
// <Typography align="justify" sx={{ flexBasis: 0, flexGrow: 1, pt: { sm: 2 } }}> genres,
// {data ...props
// ? data.overview ?? t("show.noOverview") }: {
// : [...Array(4)].map((_, i) => <Skeleton key={i} />)} isLoading: boolean;
// </Typography> overview?: string | null;
// <Divider genres?: Genre[];
// orientation="vertical" } & Stylable) => {
// variant="middle" const { t } = useTranslation();
// flexItem const { css } = useYoshiki();
// sx={{ mx: 2, display: { xs: "none", sm: "block" } }}
// />
// <Box sx={{ flexBasis: "25%", display: { xs: "none", sm: "block" } }}>
// <StudioText
// loading={!data}
// studio={data?.studio}
// sx={{ display: { xs: "none", sm: "block", md: "none" }, pb: 2 }}
// />
// <Typography variant="h4" component="h2"> return (
// {t("show.genre")} <Container {...css({ flexDirection: { xs: "column", sm: "row" } }, props)}>
// </Typography> <P
// {!data || data.genres?.length ? ( {...css({
// <ul> display: { xs: "flex", sm: "none" },
// {(data ? data.genres! : [...Array(3)]).map((genre, i) => ( color: (theme: Theme) => theme.user.paragraph,
// <li key={genre?.id ?? i}> })}
// <Typography> >
// {genre ? <Link href={`/genres/${genre.slug}`}>{genre.name}</Link> : <Skeleton />} {t("show.genre")}:{" "}
// </Typography> {(isLoading ? [...Array(3)] : genres!).map((genre, i) => (
// </li> <>
// ))} {i !== 0 && ", "}
// </ul> {isLoading ? (
// ) : ( <Skeleton key={i} />
// <Typography>{t("show.genre-none")}</Typography> ) : (
// )} <A key={genre.slug} href={`/genres/${genre.slug}`}>
// </Box> {genre.name}
// </Container> </A>
// ); )}
// }; </>
))}
</P>
const min = Platform.OS === "web" <Skeleton
? (...values: number[]): number => `min(${values.join(", ")})` as unknown as number lines={4}
: (...values: number[]): number => Math.min(...values); {...css({ width: percent(100), flexBasis: 0, flexGrow: 1, paddingTop: ts(4) })}
const max = Platform.OS === "web" >
? (...values: number[]): number => `max(${values.join(", ")})` as unknown as number {isLoading || (
: (...values: number[]): number => Math.max(...values); <P {...css({ flexBasis: 0, flexGrow: 1, textAlign: "justify", paddingTop: ts(4) })}>
const px = Platform.OS === "web" {overview ?? t("show.noOverview")}
? (value: number): number => `${value}px` as unknown as number </P>
: (value: number): number => value; )}
</Skeleton>
<HR
orientation="vertical"
{...css({ marginX: ts(2), display: { xs: "none", sm: "flex" } })}
/>
<View {...css({ flexBasis: percent(25), display: { xs: "none", sm: "flex" } })}>
<H2>{t("show.genre")}</H2>
{isLoading || genres?.length ? (
<UL>
{(isLoading ? [...Array(3)] : genres!).map((genre, i) => (
<LI key={genre?.id ?? i}>
{isLoading ? (
<Skeleton {...css({ marginBottom: 0 })} />
) : (
<A href={`/genres/${genre.slug}`}>{genre.name}</A>
)}
</LI>
))}
</UL>
) : (
<P>{t("show.genre-none")}</P>
)}
</View>
</Container>
);
};
export const ShowHeader = ({ export const ShowHeader = ({
query, query,
@ -256,19 +289,22 @@ export const ShowHeader = ({
<Navbar {...css({ bg: "transparent" })} /> <Navbar {...css({ bg: "transparent" })} />
<Fetch query={query}> <Fetch query={query}>
{({ isLoading, ...data }) => ( {({ isLoading, ...data }) => (
<> <Main {...css(StyleSheet.absoluteFillObject)}>
{/* TODO: HEAD element for SEO*/} <Head title={data?.name} description={data?.overview} />
{/* TODO: Add a shadow on navbar items */} {/* TODO: Add a shadow on navbar items */}
{/* TODO: Put the navbar outside of the scrollbox */} {/* TODO: Put the navbar outside of the scrollbox */}
<ImageBackground <ImageBackground
src={data?.thumbnail} src={data?.thumbnail}
alt="" alt=""
as={Main}
containerStyle={{ containerStyle={{
height: { xs: vh(40), sm: min(vh(60), px(750)), lg: vh(70) }, height: {
minHeight: { xs: px(350), sm: px(500), lg: px(600) }, xs: vh(40),
sm: min(vh(60), px(750)),
md: min(vh(60), px(680)),
lg: vh(70),
},
minHeight: { xs: px(350), sm: px(300), md: px(400), lg: px(600) },
}} }}
{...css(StyleSheet.absoluteFillObject)}
> >
<TitleLine <TitleLine
isLoading={isLoading} isLoading={isLoading}
@ -276,31 +312,24 @@ export const ShowHeader = ({
name={data?.name} name={data?.name}
date={data ? getDisplayDate(data as any) : undefined} date={data ? getDisplayDate(data as any) : undefined}
poster={data?.poster} poster={data?.poster}
studio={data?.studio}
{...css({ {...css({
marginTop: { xs: max(vh(20), px(200)), sm: vh(45), md: vh(35) } marginTop: {
xs: max(vh(20), px(200)),
sm: vh(45),
md: max(vh(30), px(150)),
lg: max(vh(35), px(200)),
},
})} })}
/> />
{/* <Container sx={{ display: { xs: "block", sm: "none" }, pt: 3 }}> */}
{/* <StudioText loading={!data} studio={data?.studio} sx={{ mb: 1 }} /> */}
{/* <Typography sx={{ mb: 1 }}> */}
{/* {t("show.genre")} */}
{/* {": "} */}
{/* {!data ? ( */}
{/* <Skeleton width="10rem" sx={{ display: "inline-flex" }} /> */}
{/* ) : data?.genres && data.genres.length ? ( */}
{/* data.genres.map((genre, i) => [ */}
{/* i > 0 && ", ", */}
{/* <Link key={genre.id} href={`/genres/${genre.slug}`}> */}
{/* {genre.name} */}
{/* </Link>, */}
{/* ]) */}
{/* ) : ( */}
{/* t("show.genre-none") */}
{/* )} */}
{/* </Typography> */}
{/* </Container> */}
</ImageBackground> </ImageBackground>
</> <Description
isLoading={isLoading}
overview={data?.overview}
genres={data?.genres}
{...css({ paddingTop: { xs: 0, md: ts(2) } })}
/>
</Main>
)} )}
</Fetch> </Fetch>
</> </>

View File

@ -9682,7 +9682,7 @@ __metadata:
react-native-screens: ~3.18.0 react-native-screens: ~3.18.0
react-native-svg: 13.4.0 react-native-svg: 13.4.0
typescript: ^4.6.3 typescript: ^4.6.3
yoshiki: 0.2.11 yoshiki: 0.3.1
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -13319,7 +13319,7 @@ __metadata:
superjson: ^1.11.0 superjson: ^1.11.0
typescript: ^4.9.3 typescript: ^4.9.3
webpack: ^5.75.0 webpack: ^5.75.0
yoshiki: 0.2.11 yoshiki: 0.3.1
zod: ^3.19.1 zod: ^3.19.1
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -13644,9 +13644,9 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yoshiki@npm:0.2.11": "yoshiki@npm:0.3.1":
version: 0.2.11 version: 0.3.1
resolution: "yoshiki@npm:0.2.11" resolution: "yoshiki@npm:0.3.1"
dependencies: dependencies:
"@types/node": 18.x.x "@types/node": 18.x.x
"@types/react": 18.x.x "@types/react": 18.x.x
@ -13661,7 +13661,7 @@ __metadata:
optional: true optional: true
react-native-web: react-native-web:
optional: true optional: true
checksum: 5a2bbb62b2270d3456f114cfbb24a84ad6b8a94b147687929ccffe2d179560ef40b46df1d4054eda91310d295a9a674bbb201765deb86dc96a2133bfd702235a checksum: 9448b628b61bbcc4485af7aed667a1c0f8490a2066fa35953b4a02126f1d31d94f90e27a592797f8ecedc0ce2220976a7651ba989f4ff3c68513496b2f9fdd0b
languageName: node languageName: node
linkType: hard linkType: hard