/* * 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 . */ import { type Genre, type KyooImage, type LibraryItem, LibraryItemP, type QueryIdentifier, type WatchStatusV, getDisplayDate, } from "@kyoo/models"; import { Chip, H3, IconFab, Link, P, Poster, PosterBackground, Skeleton, SubP, focusReset, imageBorderRadius, tooltip, ts, } from "@kyoo/primitives"; import PlayArrow from "@material-symbols/svg-400/rounded/play_arrow-fill.svg"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { ScrollView, View } from "react-native"; import { type Theme, calc, percent, px, rem, useYoshiki } from "yoshiki/native"; import { ItemGrid, ItemWatchStatus } from "../browse/grid"; import { ItemContext } from "../components/context-menus"; import type { Layout } from "../fetch"; import { InfiniteFetch } from "../fetch-infinite"; export const ItemDetails = ({ slug, type, name, tagline, subtitle, overview, poster, genres, href, playHref, watchStatus, unseenEpisodesCount, ...props }: { slug: string; type: "movie" | "show" | "collection"; name: string; tagline: string | null; subtitle: string | null; poster: KyooImage | null; genres: Genre[] | null; overview: string | null; href: string; playHref: string | null; watchStatus: WatchStatusV | null; unseenEpisodesCount: number | null; }) => { const [moreOpened, setMoreOpened] = useState(false); const { css } = useYoshiki("recommended-card"); const { t } = useTranslation(); return ( setMoreOpened(true)} {...css({ position: "absolute", top: 0, left: 0, right: 0, bottom: 0, flexDirection: "row", bg: (theme) => theme.variant.background, borderRadius: calc(px(imageBorderRadius), "+", ts(0.25)), overflow: "hidden", borderColor: (theme) => theme.background, borderWidth: ts(0.25), borderStyle: "solid", fover: { self: { ...focusReset, borderColor: (theme: Theme) => theme.accent, }, title: { textDecorationLine: "underline", }, }, })} > theme.darkOverlay, position: "absolute", left: 0, right: 0, bottom: 0, p: ts(1), })} >

theme.colors.white }, "title"])}>{name}

{subtitle && {subtitle}}
{type !== "collection" && ( setMoreOpened(v)} force /> )} {tagline &&

{tagline}

}
{overview ?? t("show.noOverview")}
{/* This view needs to be out of the Link because nested are not allowed on the web */} theme.themeOverlay, flexDirection: "row", pX: 4, justifyContent: "flex-end", height: px(50), })} > {genres && ( {genres.map((x, i) => ( ))} )} {playHref !== null && ( )}
); }; ItemDetails.Loader = (props: object) => { const { css } = useYoshiki(); return ( theme.variant.background, borderRadius: calc(px(imageBorderRadius), "+", ts(0.25)), overflow: "hidden", borderColor: (theme) => theme.background, borderWidth: ts(0.25), borderStyle: "solid", }, props, )} > theme.darkOverlay, position: "absolute", left: 0, right: 0, bottom: 0, p: ts(1), })} > theme.themeOverlay, pX: 4, height: px(50), flexDirection: "row", alignItems: "center", })} > ); }; ItemDetails.layout = { size: ts(36), numColumns: { xs: 1, md: 2, xl: 3 }, layout: "grid", gap: ts(8), } satisfies Layout; export const Recommended = () => { const { t } = useTranslation(); const { css } = useYoshiki(); return (

{t("home.recommended")}

( )} Loader={ItemDetails.Loader} />
); }; Recommended.query = (): QueryIdentifier => ({ parser: LibraryItemP, infinite: true, path: ["items"], params: { sortBy: "random", limit: 6, fields: ["firstEpisode", "episodesCount", "watchStatus"], }, });