Create a collection page

This commit is contained in:
Zoe Roux 2023-11-03 12:35:38 +01:00
parent 66fff153e1
commit 7622420f06
7 changed files with 234 additions and 12 deletions

View File

@ -0,0 +1,27 @@
/*
* 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 { CollectionPage } from "@kyoo/ui";
import { withRoute } from "../../../utils";
export default withRoute(CollectionPage, {
options: { headerTransparent: true, headerStyle: { backgroundColor: "transparent" } },
statusBar: { barStyle: "light-content" },
});

View File

@ -0,0 +1,24 @@
/*
* 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 { CollectionPage } from "@kyoo/ui";
import { withRoute } from "~/router";
export default withRoute(CollectionPage);

View File

@ -0,0 +1,171 @@
/*
* 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 {
Collection,
CollectionP,
ItemKind,
LibraryItem,
LibraryItemP,
QueryIdentifier,
QueryPage,
getDisplayDate,
} from "@kyoo/models";
import { Header as ShowHeader, TitleLine } from "../details/header";
import { Container, ImageBackground, P, Skeleton, ts } from "@kyoo/primitives";
import { percent, px, useYoshiki } from "yoshiki/native";
import { useTranslation } from "react-i18next";
import { forwardRef } from "react";
import { Platform, View, ViewProps } from "react-native";
import { Fetch } from "../fetch";
import { InfiniteFetch } from "../fetch-infinite";
import { DefaultLayout } from "../layout";
import { ItemDetails } from "../home/recommanded";
import { SvgWave } from "../details/show";
const Header = ({ slug }: { slug: string }) => {
const { css } = useYoshiki();
const { t } = useTranslation();
return (
<Fetch query={Header.query(slug)}>
{({ isLoading, ...data }) => (
<>
<ImageBackground
src={data?.thumbnail}
quality="high"
alt=""
containerStyle={ShowHeader.containerStyle}
>
<TitleLine
isLoading={isLoading}
type={"collection"}
playHref={null}
name={data?.name}
tagline={null}
date={null}
rating={null}
runtime={null}
poster={data?.poster}
trailerUrl={null}
studio={null}
{...css(ShowHeader.childStyle)}
/>
</ImageBackground>
<Container
{...css({
paddingTop: ts(4),
marginBottom: ts(4),
})}
>
<Skeleton lines={4}>
{isLoading || (
<P {...css({ textAlign: "justify" })}>{data.overview ?? t("show.noOverview")}</P>
)}
</Skeleton>
</Container>
</>
)}
</Fetch>
);
};
Header.query = (slug: string): QueryIdentifier<Collection> => ({
parser: CollectionP,
path: ["collections", slug],
});
const CollectionHeader = forwardRef<View, ViewProps & { slug: string }>(function ShowHeader(
{ children, slug, ...props },
ref,
) {
const { css, theme } = useYoshiki();
return (
<View
ref={ref}
{...css(
[
{ bg: (theme) => theme.variant.background },
Platform.OS === "web" && {
flexGrow: 1,
flexShrink: 1,
// @ts-ignore Web only property
overflowY: "auto" as any,
},
],
props,
)}
>
<Header slug={slug} />
<SvgWave fill={theme.background} {...css({ flexShrink: 0, flexGrow: 1, display: "flex" })} />
<View {...css({ bg: theme.background, paddingTop: { xs: ts(8), md: 0 } })}>
<View
{...css({
maxWidth: { xs: percent(100), lg: px(1170) },
alignSelf: "center",
})}
>
{children}
</View>
</View>
</View>
);
});
const query = (slug: string): QueryIdentifier<LibraryItem> => ({
parser: LibraryItemP,
path: ["collections", slug, "items"],
infinite: true,
});
export const CollectionPage: QueryPage<{ slug: string }> = ({ slug }) => {
const { css } = useYoshiki();
return (
<InfiniteFetch
query={query(slug)}
placeholderCount={15}
layout={{ ...ItemDetails.layout, numColumns: { xs: 1, md: 2 } }}
Header={CollectionHeader}
headerProps={{ slug }}
{...css({ padding: { xs: ts(1), sm: ts(8) } })}
>
{(x) => (
<ItemDetails
isLoading={x.isLoading as any}
name={x.name}
tagline={"tagline" in x ? x.tagline : null}
overview={x.overview}
poster={x.poster}
subtitle={x.kind !== ItemKind.Collection && !x.isLoading ? getDisplayDate(x) : undefined}
genres={"genres" in x ? x.genres : null}
href={x.href}
playHref={x.kind !== ItemKind.Collection && !x.isLoading ? x.playHref : undefined}
/>
)}
</InfiniteFetch>
);
};
CollectionPage.getLayout = { Layout: DefaultLayout, props: { transparent: true } };
CollectionPage.getFetchUrls = ({ slug }) => [query(slug), Header.query(slug)];

View File

@ -70,7 +70,7 @@ import Theaters from "@material-symbols/svg-400/rounded/theaters-fill.svg";
import { Rating } from "../components/rating";
import { displayRuntime } from "./episode";
const TitleLine = ({
export const TitleLine = ({
isLoading,
playHref,
name,
@ -89,12 +89,12 @@ const TitleLine = ({
name?: string;
tagline?: string | null;
date?: string | null;
rating?: number;
rating?: number | null;
runtime?: number | null;
poster?: KyooImage | null;
studio?: Studio | null;
trailerUrl?: string | null;
type: "movie" | "show";
type: "movie" | "show" | "collection";
} & Stylable) => {
const { css, theme } = useYoshiki();
const { t } = useTranslation();
@ -222,8 +222,12 @@ const TitleLine = ({
{...tooltip(t("show.trailer"))}
/>
)}
<DottedSeparator />
<Rating rating={rating} />
{rating !== null && (
<>
<DottedSeparator />
<Rating rating={rating} />
</>
)}
{runtime && (
<>
<DottedSeparator />

View File

@ -28,7 +28,7 @@ import Svg, { Path, SvgProps } from "react-native-svg";
import { Container } from "@kyoo/primitives";
import { forwardRef } from "react";
const SvgWave = (props: SvgProps) => {
export const SvgWave = (props: SvgProps) => {
const { css } = useYoshiki();
const width = 612;
const height = 52.771;
@ -58,11 +58,7 @@ const ShowHeader = forwardRef<View, ViewProps & { slug: string }>(function ShowH
flexGrow: 1,
flexShrink: 1,
// @ts-ignore Web only property
overflow: "auto" as any,
// @ts-ignore Web only property
overflowX: "hidden",
// @ts-ignore Web only property
overflowY: "overlay",
overflowY: "auto" as any,
},
],
props,

View File

@ -176,7 +176,6 @@ export const Recommanded = () => {
>
{(x, i) => (
<ItemDetails
key={x.id ?? i}
isLoading={x.isLoading as any}
name={x.name}
tagline={"tagline" in x ? x.tagline : null}

View File

@ -22,6 +22,7 @@ export * from "./navbar";
export { HomePage } from "./home";
export { BrowsePage } from "./browse";
export { MovieDetails, ShowDetails } from "./details";
export { CollectionPage } from "./collection";
export { Player } from "./player";
export { SearchPage } from "./search";
export { LoginPage, RegisterPage } from "./login";