diff --git a/front/apps/mobile/app/browse/index.tsx b/front/apps/mobile/app/browse/index.tsx index 1815e516..a9cc8d79 100644 --- a/front/apps/mobile/app/browse/index.tsx +++ b/front/apps/mobile/app/browse/index.tsx @@ -1,3 +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 . + */ + import { BrowsePage } from "@kyoo/ui"; -export default BrowsePage +export default BrowsePage; diff --git a/front/apps/mobile/app/movie/index.tsx b/front/apps/mobile/app/movie/index.tsx new file mode 100644 index 00000000..c19e0d73 --- /dev/null +++ b/front/apps/mobile/app/movie/index.tsx @@ -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 . + */ + +import { MovieDetails } from "@kyoo/ui"; + +export default MovieDetails; diff --git a/front/apps/mobile/package.json b/front/apps/mobile/package.json index 500e9197..31aa7031 100644 --- a/front/apps/mobile/package.json +++ b/front/apps/mobile/package.json @@ -31,7 +31,7 @@ "react-native-safe-area-context": "4.4.1", "react-native-screens": "~3.18.0", "react-native-svg": "13.4.0", - "yoshiki": "0.2.9" + "yoshiki": "0.2.11" }, "devDependencies": { "@babel/core": "^7.19.3", diff --git a/front/apps/web/package.json b/front/apps/web/package.json index 4ffdd705..decc5b87 100644 --- a/front/apps/web/package.json +++ b/front/apps/web/package.json @@ -41,7 +41,7 @@ "react-native-web": "^0.18.10", "solito": "^2.0.5", "superjson": "^1.11.0", - "yoshiki": "0.2.9", + "yoshiki": "0.2.11", "zod": "^3.19.1" }, "devDependencies": { diff --git a/front/apps/web/src/pages/browse/[slug].tsx b/front/apps/web/src/pages/browse/[slug].tsx index 47273d31..f1d11355 100644 --- a/front/apps/web/src/pages/browse/[slug].tsx +++ b/front/apps/web/src/pages/browse/[slug].tsx @@ -18,6 +18,7 @@ * along with Kyoo. If not, see . */ -import BrowsePage from "./index"; +import { BrowsePage } from "@kyoo/ui"; +import { withRoute } from "~/utils/router"; -export default BrowsePage; +export default withRoute(BrowsePage); diff --git a/front/apps/web/src/pages/browse/index.tsx b/front/apps/web/src/pages/browse/index.tsx index 98cae566..f1d11355 100644 --- a/front/apps/web/src/pages/browse/index.tsx +++ b/front/apps/web/src/pages/browse/index.tsx @@ -22,423 +22,3 @@ import { BrowsePage } from "@kyoo/ui"; import { withRoute } from "~/utils/router"; export default withRoute(BrowsePage); - -/* import { FilterList, GridView, North, Sort, South, ViewList } from "@mui/icons-material"; */ -/* import { */ -/* Box, */ -/* Button, */ -/* ButtonGroup, */ -/* ListItemIcon, */ -/* ListItemText, */ -/* MenuItem, */ -/* Menu, */ -/* Skeleton, */ -/* Divider, */ -/* Tooltip, */ -/* Typography, */ -/* } from "@mui/material"; */ -/* import useTranslation from "next-translate/useTranslation"; */ -/* import { useRouter } from "next/router"; */ -/* import { useState } from "react"; */ -/* import { ErrorPage } from "~/components/errors"; */ -/* import { Navbar } from "@kyoo/ui"; */ -/* import { Poster, Image } from "@kyoo/primitives"; */ -/* import { ItemType, LibraryItem, LibraryItemP } from "~/models"; */ -/* import { getDisplayDate } from "@kyoo/models"; */ -/* import { InfiniteScroll } from "~/utils/infinite-scroll"; */ -/* import { Link } from "~/utils/link"; */ -/* import { withRoute } from "~/utils/router"; */ -/* import { QueryIdentifier, QueryPage, useInfiniteFetch } from "@kyoo/models"; */ -/* import { px } from "yoshiki/native"; */ - -/* enum SortBy { */ -/* Name = "name", */ -/* StartAir = "startAir", */ -/* EndAir = "endAir", */ -/* } */ - -/* enum SortOrd { */ -/* Asc = "asc", */ -/* Desc = "desc", */ -/* } */ - -/* enum Layout { */ -/* Grid, */ -/* List, */ -/* } */ - -/* const ItemGrid = ({ */ -/* href, */ -/* name, */ -/* subtitle, */ -/* poster, */ -/* loading, */ -/* }: { */ -/* href?: string; */ -/* name?: string; */ -/* subtitle?: string | null; */ -/* poster?: string | null; */ -/* loading?: boolean; */ -/* }) => { */ -/* return ( */ -/* */ -/* */ -/* {name ?? } */ -/* {(loading || subtitle) && ( */ -/* */ -/* {subtitle ?? } */ -/* */ -/* )} */ -/* */ -/* ); */ -/* }; */ - -/* const ItemList = ({ */ -/* href, */ -/* name, */ -/* subtitle, */ -/* thumbnail, */ -/* poster, */ -/* loading, */ -/* }: { */ -/* href?: string; */ -/* name?: string; */ -/* subtitle?: string | null; */ -/* poster?: string | null; */ -/* thumbnail?: string | null; */ -/* loading?: boolean; */ -/* }) => { */ -/* return ( */ -/* */ -/* {name} */ -/* */ -/* */ -/* {name ?? } */ -/* */ -/* {(loading || subtitle) && ( */ -/* */ -/* {subtitle ?? } */ -/* */ -/* )} */ -/* */ -/* */ -/* */ -/* ); */ -/* }; */ - -/* const Item = ({ item, layout }: { item?: LibraryItem; layout: Layout }) => { */ -/* let href; */ -/* if (item?.type === ItemType.Movie) href = `/movie/${item.slug}`; */ -/* else if (item?.type === ItemType.Show) href = `/show/${item.slug}`; */ -/* else if (item?.type === ItemType.Collection) href = `/collection/${item.slug}`; */ - -/* switch (layout) { */ -/* case Layout.Grid: */ -/* return ( */ -/* */ -/* ); */ -/* case Layout.List: */ -/* return ( */ -/* */ -/* ); */ -/* } */ -/* }; */ - -/* const SortByMenu = ({ */ -/* sortKey, */ -/* setSort, */ -/* sortOrd, */ -/* setSortOrd, */ -/* anchor, */ -/* onClose, */ -/* }: { */ -/* sortKey: SortBy; */ -/* setSort: (sort: SortBy) => void; */ -/* sortOrd: SortOrd; */ -/* setSortOrd: (sort: SortOrd) => void; */ -/* anchor: HTMLElement; */ -/* onClose: () => void; */ -/* }) => { */ -/* const router = useRouter(); */ -/* const { t } = useTranslation("browse"); */ - -/* return ( */ -/* */ -/* {Object.values(SortBy).map((x) => ( */ -/* setSort(x)} */ -/* component={Link} */ -/* to={{ query: { ...router.query, sortBy: `${sortKey}-${sortOrd}` } }} */ -/* shallow */ -/* replace */ -/* > */ -/* {t(`browse.sortkey.${x}`)} */ -/* */ -/* ))} */ -/* */ -/* setSortOrd(SortOrd.Asc)} */ -/* component={Link} */ -/* to={{ query: { ...router.query, sortBy: `${sortKey}-${sortOrd}` } }} */ -/* shallow */ -/* replace */ -/* > */ -/* */ -/* */ -/* */ -/* {t("browse.sortord.asc")} */ -/* */ -/* setSortOrd(SortOrd.Desc)} */ -/* component={Link} */ -/* to={{ query: { ...router.query, sortBy: `${sortKey}-${sortOrd}` } }} */ -/* shallow */ -/* replace */ -/* > */ -/* */ -/* */ -/* */ -/* {t("browse.sortord.desc")} */ -/* */ -/* */ -/* ); */ -/* }; */ - -/* const BrowseSettings = ({ */ -/* sortKey, */ -/* setSort, */ -/* sortOrd, */ -/* setSortOrd, */ -/* layout, */ -/* setLayout, */ -/* }: { */ -/* sortKey: SortBy; */ -/* setSort: (sort: SortBy) => void; */ -/* sortOrd: SortOrd; */ -/* setSortOrd: (sort: SortOrd) => void; */ -/* layout: Layout; */ -/* setLayout: (layout: Layout) => void; */ -/* }) => { */ -/* const [sortAnchor, setSortAnchor] = useState(null); */ -/* const { t } = useTranslation("browse"); */ - -/* const switchViewTitle = */ -/* layout === Layout.Grid ? t("browse.switchToList") : t("browse.switchToGrid"); */ - -/* return ( */ -/* <> */ -/* */ -/* */ -/* */ -/* */ -/* */ -/* */ -/* */ -/* */ -/* */ -/* */ -/* */ -/* {sortAnchor && ( */ -/* setSortAnchor(null)} */ -/* /> */ -/* )} */ -/* */ -/* ); */ -/* }; */ - -/* const query = ( */ -/* slug?: string, */ -/* sortKey?: SortBy, */ -/* sortOrd?: SortOrd, */ -/* ): QueryIdentifier => ({ */ -/* parser: LibraryItemP, */ -/* path: slug ? ["library", slug, "items"] : ["items"], */ -/* infinite: true, */ -/* params: { */ -/* // The API still uses title isntead of name */ -/* sortBy: sortKey */ -/* ? `${sortKey === SortBy.Name ? "title" : sortKey}:${sortOrd ?? "asc"}` */ -/* : "title:asc", */ -/* }, */ -/* }); */ - -/* const BrowsePage: QueryPage<{ slug?: string }> = ({ slug }) => { */ -/* const [sortKey, setSort] = useState(SortBy.Name); */ -/* const [sortOrd, setSortOrd] = useState(SortOrd.Asc); */ -/* const [layout, setLayout] = useState(Layout.Grid); */ -/* const { items, fetchNextPage, hasNextPage, error } = useInfiniteFetch( */ -/* query(slug, sortKey, sortOrd), */ -/* ); */ - -/* if (error) return ; */ - -/* return ( */ -/* <> */ -/* */ -/* )]} */ -/* sx={{ */ -/* display: "flex", */ -/* flexWrap: "wrap", */ -/* alignItems: "flex-start", */ -/* justifyContent: "center", */ -/* }} */ -/* > */ -/* {(items ?? [...Array(12)]).map((x, i) => ( */ -/* */ -/* ))} */ -/* */ -/* */ -/* ); */ -/* }; */ - -/* BrowsePage.getLayout = (page) => { */ -/* return ( */ -/* <> */ -/* */ -/*
{page}
*/ -/* */ -/* ); */ -/* }; */ - -/* BrowsePage.getFetchUrls = ({ slug, sortBy }) => [ */ -/* query(slug, sortBy?.split("-")[0] as SortBy, sortBy?.split("-")[1] as SortOrd), */ -/* Navbar.query(), */ -/* ]; */ - -/* export default withRoute(BrowsePage); */ diff --git a/front/apps/web/src/pages/movie/[slug].tsx b/front/apps/web/src/pages/movie/[slug].tsx index f62ceed6..3b0adf8f 100644 --- a/front/apps/web/src/pages/movie/[slug].tsx +++ b/front/apps/web/src/pages/movie/[slug].tsx @@ -18,294 +18,7 @@ * along with Kyoo. If not, see . */ -import { LocalMovies, PlayArrow } from "@mui/icons-material"; -import { - Box, - Divider, - Fab, - IconButton, - Skeleton, - SxProps, - Tooltip, - Typography, -} from "@mui/material"; -import useTranslation from "next-translate/useTranslation"; -import Head from "next/head"; -import { Navbar } from "~/components/navbar"; -import { Image, Poster } from "~/components/poster"; -import { Movie, MovieP, Show } from "~/models"; -import { QueryIdentifier, QueryPage, useFetch, useInfiniteFetch } from "~/utils/query"; -import { getDisplayDate } from "~/models/utils"; +import { MovieDetails } from "@kyoo/ui"; import { withRoute } from "~/utils/router"; -import { Container } from "~/components/container"; -import { makeTitle } from "~/utils/utils"; -import { Link } from "~/utils/link"; -import { Studio } from "~/models/resources/studio"; -import { Person, PersonP } from "~/models"; -import { PersonAvatar } from "~/components/person"; -import { ErrorComponent, ErrorPage } from "~/components/errors"; -import { HorizontalList } from "~/components/horizontal-list"; -import NextLink from "next/link"; - -const StudioText = ({ - studio, - loading = false, - sx, -}: { - studio?: Studio | null; - loading?: boolean; - sx?: SxProps; -}) => { - const { t } = useTranslation("browse"); - - if (!loading && !studio) return null; - return ( - - {t("show.studio")}:{" "} - {loading ? ( - - ) : ( - {studio!.name} - )} - - ); -}; - -export const ShowHeader = ({ data }: { data?: Show | Movie }) => { - /* const scroll = useScroll(); */ - const { t } = useTranslation("browse"); - // TODO: tweek the navbar color with the theme. - - return ( - <> - {/* TODO: Add a shadow on navbar items */} - {/* TODO: Put the navbar outside of the scrollbox */} - - - - - - - - {data?.name ?? } - - {(!data || getDisplayDate(data)) && ( - - {data != undefined ? ( - getDisplayDate(data) - ) : ( - - )} - - )} - *": { m: ".3rem !important" } }}> - - - - - - - - - - - - - - - - {data?.logo && ( - - )} - - - - - - - - {t("show.genre")} - {": "} - {!data ? ( - - ) : data?.genres && data.genres.length ? ( - data.genres.map((genre, i) => [ - i > 0 && ", ", - - {genre.name} - , - ]) - ) : ( - t("show.genre-none") - )} - - - - - - {data - ? data.overview ?? t("show.noOverview") - : [...Array(4)].map((_, i) => )} - - - - - - - {t("show.genre")} - - {!data || data.genres?.length ? ( -
    - {(data ? data.genres! : [...Array(3)]).map((genre, i) => ( -
  • - - {genre ? ( - {genre.name} - ) : ( - - )} - -
  • - ))} -
- ) : ( - {t("show.genre-none")} - )} -
-
- - ); -}; - -export const ShowStaff = ({ slug }: { slug: string }) => { - const { items, isError, error } = useInfiniteFetch(ShowStaff.query(slug)); - const { t } = useTranslation("browse"); - - // TODO: handle infinite scroll - - if (isError) return ; - - return ( - - {(items ?? [...Array(20)]).map((x, i) => ( - - ))} - - ); -}; - -ShowStaff.query = (slug: string): QueryIdentifier => ({ - parser: PersonP, - path: ["shows", slug, "people"], - infinite: true, -}); - -const query = (slug: string): QueryIdentifier => ({ - parser: MovieP, - path: ["shows", slug], - params: { - fields: ["genres", "studio"], - }, -}); - -const MovieDetails: QueryPage<{ slug: string }> = ({ slug }) => { - const { data, error } = useFetch(query(slug)); - - if (error) return ; - - return ( - <> - - {makeTitle(data?.name)} - - - - - - ); -}; - -MovieDetails.getFetchUrls = ({ slug }) => [query(slug), ShowStaff.query(slug), Navbar.query()]; export default withRoute(MovieDetails); diff --git a/front/packages/primitives/src/container.tsx b/front/packages/primitives/src/container.tsx new file mode 100644 index 00000000..b7064bb6 --- /dev/null +++ b/front/packages/primitives/src/container.tsx @@ -0,0 +1,44 @@ +/* + * 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 { View, ViewProps } from "react-native"; +import { useYoshiki } from "yoshiki/native"; + +export const Container = (props: ViewProps) => { + const { css } = useYoshiki(); + + return ( + + ); +}; diff --git a/front/packages/primitives/src/icons.tsx b/front/packages/primitives/src/icons.tsx index 71fb941a..69824a0b 100644 --- a/front/packages/primitives/src/icons.tsx +++ b/front/packages/primitives/src/icons.tsx @@ -19,28 +19,69 @@ */ import MIcon from "@expo/vector-icons/MaterialIcons"; -import { ComponentProps } from "react"; -import { Pressable, useTheme } from "yoshiki/native"; +import { ComponentProps, ComponentType } from "react"; +import { PressableProps } from "react-native"; +import { Pressable, px, useYoshiki } from "yoshiki/native"; +import { Breakpoint, ts } from "."; export type IconProps = { icon: ComponentProps["name"]; size?: number; - color?: string; + color?: Breakpoint; }; -export const Icon = ({ icon, size, color }: IconProps) => { - return ; +export const Icon = ({ icon, size = 24, color }: IconProps) => { + const { css, theme } = useYoshiki(); + return ( + + ); }; -export const IconButton = ({ +export const IconButton = ({ icon, size, color, - ...props -}: ComponentProps & IconProps) => { + as, + ...asProps +}: IconProps & { as?: ComponentType } & AsProps) => { + const { css } = useYoshiki(); + + const Container = as ?? Pressable; + return ( - + - + + ); +}; + +export const IconFab = ( + props: ComponentProps>, +) => { + const { css, theme } = useYoshiki(); + + return ( + theme.accent, + }, + props, + ) as any)} + /> ); }; diff --git a/front/packages/primitives/src/image.tsx b/front/packages/primitives/src/image.tsx index fbc624b1..557efeb7 100644 --- a/front/packages/primitives/src/image.tsx +++ b/front/packages/primitives/src/image.tsx @@ -132,8 +132,8 @@ export const ImageBackground = ({ as?: ComponentType; gradient?: Partial | boolean; children: ReactNode; - containerStyle?: StyleList; - imageStyle?: StyleList; + containerStyle?: YoshikiEnhanced; + imageStyle?: YoshikiEnhanced; } & AsProps & Props) => { const [isErrored, setErrored] = useState(false); diff --git a/front/packages/primitives/src/index.ts b/front/packages/primitives/src/index.ts index 20de36ff..38bba259 100644 --- a/front/packages/primitives/src/index.ts +++ b/front/packages/primitives/src/index.ts @@ -18,7 +18,7 @@ * along with Kyoo. If not, see . */ -export { Header, Nav, Footer } from "@expo/html-elements"; +export { Header, Main, Nav, Footer } from "@expo/html-elements"; export * from "./text"; export * from "./themes"; export * from "./icons"; @@ -27,23 +27,15 @@ export * from "./avatar"; export * from "./image"; export * from "./skeleton"; export * from "./tooltip"; +export * from "./container"; export * from "./animated"; export * from "./utils/breakpoints"; export * from "./utils/nojs"; -import { Dimensions } from "react-native"; import { px } from "yoshiki/native"; export const ts = (spacing: number) => { return px(spacing * 8); }; - -export const vw = (spacing: number) => { - return px(spacing * Dimensions.get('window').width / 100); -}; - -export const vh = (spacing: number) => { - return px(spacing * Dimensions.get('window').height / 100); -}; diff --git a/front/packages/primitives/src/skeleton.tsx b/front/packages/primitives/src/skeleton.tsx index bca1c654..2fef683b 100644 --- a/front/packages/primitives/src/skeleton.tsx +++ b/front/packages/primitives/src/skeleton.tsx @@ -69,7 +69,6 @@ export const Skeleton = ({ borderRadius: px(6), }, variant === "text" && { - margin: rem(1), width: percent(75), height: rem(1.2), }, diff --git a/front/packages/primitives/src/themes/catppuccin.ts b/front/packages/primitives/src/themes/catppuccin.ts index 09a3a614..99108b1c 100644 --- a/front/packages/primitives/src/themes/catppuccin.ts +++ b/front/packages/primitives/src/themes/catppuccin.ts @@ -63,7 +63,7 @@ export const catppuccin: ThemeBuilder = { overlay1: "#9399b2", default: { background: "#1e1e2e", - accent: "##f5c2e7", + accent: "#f5c2e7", divider: "#7f849c", heading: "#cdd6f4", paragraph: "#bac2de", diff --git a/front/packages/primitives/src/themes/theme.tsx b/front/packages/primitives/src/themes/theme.tsx index 88bfe388..399b0dca 100644 --- a/front/packages/primitives/src/themes/theme.tsx +++ b/front/packages/primitives/src/themes/theme.tsx @@ -23,6 +23,7 @@ import { Property } from "csstype"; import { Theme, ThemeProvider } from "yoshiki"; import { useTheme, useYoshiki } from "yoshiki/native"; import "yoshiki"; +import "yoshiki/native"; import { catppuccin } from "./catppuccin"; type ThemeSettings = { @@ -57,11 +58,19 @@ type Variant = { }; declare module "yoshiki" { - // TODO: Add specifics colors export interface Theme extends ThemeSettings, Mode, Variant { - builder: ThemeBuilder; + light: Mode & Variant; + dark: Mode & Variant; + user: Mode & Variant; } } +// declare module "yoshiki/native" { +// export interface Theme extends ThemeSettings, Mode, Variant { +// light: Mode & Variant; +// dark: Mode & Variant; +// user: Mode & Variant; +// } +// } export type { Theme } from "yoshiki"; export type ThemeBuilder = ThemeSettings & { @@ -69,14 +78,21 @@ export type ThemeBuilder = ThemeSettings & { dark: Mode & { default: Variant }; }; -export const selectMode = (theme: ThemeBuilder, mode: "light" | "dark"): Theme => { - const { light, dark, ...options } = theme; +const selectMode = (theme: ThemeBuilder, mode: "light" | "dark"): Theme => { + const { light: lightBuilder, dark: darkBuilder, ...options } = theme; + const light = { ...lightBuilder, ...lightBuilder.default }; + const dark = { ...darkBuilder, ...darkBuilder.default }; const value = mode === "light" ? light : dark; - const { default: def, ...modeOpt } = value; - return { ...options, ...modeOpt, ...def, variant: value.variant, builder: theme }; + return { + ...options, + ...value, + light, + dark, + user: value, + }; }; -export const switchVariant = (theme: Theme) => { +const switchVariant = (theme: Theme) => { return { ...theme, ...theme.variant, @@ -122,7 +138,7 @@ export const ContrastArea = ({ contrastText?: boolean; }) => { const oldTheme = useTheme(); - const theme = selectMode(oldTheme.builder, mode); + const theme: Theme = { ...oldTheme, ...oldTheme[mode] }; return ( . */ -import { ToastAndroid, PressableProps } from "react-native"; +import { ToastAndroid, Platform } from "react-native"; import { Theme } from "yoshiki/native"; export const tooltip = (tooltip: string) => - ({ - dataSet: { tooltip }, - onLongPress: () => { - // TODO handle IOS. - ToastAndroid.show(tooltip, ToastAndroid.SHORT); + Platform.select({ + web: { + dataSet: { tooltip, label: tooltip }, }, - } satisfies PressableProps); + android: { + onLongPress: () => { + ToastAndroid.show(tooltip, ToastAndroid.SHORT); + }, + }, + default: {}, + }); export const WebTooltip = ({ theme }: { theme: Theme }) => { const background = `${theme.colors.black}CC`; @@ -41,6 +45,7 @@ export const WebTooltip = ({ theme }: { theme: Theme }) => { [data-tooltip]::after { content: attr(data-tooltip); + display: flex; position: absolute; top: 100%; @@ -55,6 +60,7 @@ export const WebTooltip = ({ theme }: { theme: Theme }) => { color: ${theme.colors.white}; background-color: ${background}; font-family: ${theme.fonts.paragraph}; + text-align: center; opacity: 0; visibility: hidden; diff --git a/front/packages/ui/src/browse/header.tsx b/front/packages/ui/src/browse/header.tsx index 5db02e6f..1bba9361 100644 --- a/front/packages/ui/src/browse/header.tsx +++ b/front/packages/ui/src/browse/header.tsx @@ -18,6 +18,8 @@ * along with Kyoo. If not, see . */ +export {} + // const SortByMenu = ({ // sortKey, // setSort, diff --git a/front/packages/ui/src/details/header.tsx b/front/packages/ui/src/details/header.tsx new file mode 100644 index 00000000..aefcb5d3 --- /dev/null +++ b/front/packages/ui/src/details/header.tsx @@ -0,0 +1,308 @@ +/* + * 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 { Movie, QueryIdentifier, Show, getDisplayDate } from "@kyoo/models"; +import { + Container, + H1, + Main, + ImageBackground, + Skeleton, + Poster, + P, + tooltip, + Link, + IconButton, + IconFab, +} from "@kyoo/primitives"; +import { useTranslation } from "react-i18next"; +import { Platform, StyleSheet, View } from "react-native"; +import { em, percent, rem, vh, useYoshiki, Stylable } from "yoshiki/native"; +import { Fetch, WithLoading } from "../fetch"; +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 ( +// +// {t("show.studio")}:{" "} +// {loading ? ( +// +// ) : ( +// {studio!.name} +// )} +// +// ); +// }; + +const TitleLine = ({ + isLoading, + slug, + name, + date, + poster, + ...props +}: { + isLoading: boolean; + slug: string; + name?: string; + date?: string; + poster?: string | null; +} & Stylable) => { + const { css, theme } = useYoshiki(); + const { t } = useTranslation(); + + return ( + + + + + {isLoading || ( +

({ xs: theme.user.heading, md: theme.heading }), + })} + > + {name} +

+ )} +
+ {(isLoading || date) && ( + + {isLoading || ( +

({ xs: theme.user.heading, md: theme.heading }), + })} + > + {date} +

+ )} +
+ )} + *": { m: ".3rem !important" } }} */> + + + +
+ {/* */} + {/* {(isLoading || logo || true) && ( */} + {/* */} + {/* )} */} + {/* {/1* *1/} */} + {/* */} +
+ ); +}; + +// const Tata = () => { +// return ( +// +// +// {data +// ? data.overview ?? t("show.noOverview") +// : [...Array(4)].map((_, i) => )} +// +// +// +// + +// +// {t("show.genre")} +// +// {!data || data.genres?.length ? ( +//
    +// {(data ? data.genres! : [...Array(3)]).map((genre, i) => ( +//
  • +// +// {genre ? {genre.name} : } +// +//
  • +// ))} +//
+// ) : ( +// {t("show.genre-none")} +// )} +//
+//
+// ); +// }; + +const min = Platform.OS === "web" + ? (...values: number[]): number => `min(${values.join(", ")})` as unknown as number + : (...values: number[]): number => Math.min(...values); +const max = Platform.OS === "web" + ? (...values: number[]): number => `max(${values.join(", ")})` as unknown as number + : (...values: number[]): number => Math.max(...values); +const px = Platform.OS === "web" + ? (value: number): number => `${value}px` as unknown as number + : (value: number): number => value; + +export const ShowHeader = ({ + query, + slug, +}: { + query: QueryIdentifier; + slug: string; +}) => { + /* const scroll = useScroll(); */ + const { css } = useYoshiki(); + // TODO: tweek the navbar color with the theme. + + return ( + <> + + + {({ isLoading, ...data }) => ( + <> + {/* TODO: HEAD element for SEO*/} + {/* TODO: Add a shadow on navbar items */} + {/* TODO: Put the navbar outside of the scrollbox */} + + + {/* */} + {/* */} + {/* */} + {/* {t("show.genre")} */} + {/* {": "} */} + {/* {!data ? ( */} + {/* */} + {/* ) : data?.genres && data.genres.length ? ( */} + {/* data.genres.map((genre, i) => [ */} + {/* i > 0 && ", ", */} + {/* */} + {/* {genre.name} */} + {/* , */} + {/* ]) */} + {/* ) : ( */} + {/* t("show.genre-none") */} + {/* )} */} + {/* */} + {/* */} + + + )} + + + ); +}; diff --git a/front/packages/ui/src/details/index.tsx b/front/packages/ui/src/details/index.tsx new file mode 100644 index 00000000..f99e1951 --- /dev/null +++ b/front/packages/ui/src/details/index.tsx @@ -0,0 +1,47 @@ +/* + * 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 { Movie, MovieP, QueryIdentifier, QueryPage } from "@kyoo/models"; +import { ShowHeader } from "./header"; + +const query = (slug: string): QueryIdentifier => ({ + parser: MovieP, + path: ["shows", slug], + params: { + fields: ["genres", "studio"], + }, +}); + +export const MovieDetails: QueryPage<{ slug: string }> = ({ slug }) => { + return ( + <> + {/* */} + {/* {makeTitle(data?.name)} */} + {/* */} + {/* */} + + {/* */} + + ); +}; + +MovieDetails.getFetchUrls = ({ slug }) => [//query(slug), + // ShowStaff.query(slug), Navbar.query() +]; diff --git a/front/packages/ui/src/details/staff.tsx b/front/packages/ui/src/details/staff.tsx new file mode 100644 index 00000000..1976b648 --- /dev/null +++ b/front/packages/ui/src/details/staff.tsx @@ -0,0 +1,47 @@ +/* + * 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 . + */ + +export {} +// export const ShowStaff = ({ slug }: { slug: string }) => { +// const { items, isError, error } = useInfiniteFetch(ShowStaff.query(slug)); +// const { t } = useTranslation("browse"); + +// // TODO: handle infinite scroll + +// if (isError) return ; + +// return ( +// +// {(items ?? [...Array(20)]).map((x, i) => ( +// +// ))} +// +// ); +// }; + +// ShowStaff.query = (slug: string): QueryIdentifier => ({ +// parser: PersonP, +// path: ["shows", slug, "people"], +// infinite: true, +// }); diff --git a/front/packages/ui/src/fetch.tsx b/front/packages/ui/src/fetch.tsx index 49f4a074..5718726a 100644 --- a/front/packages/ui/src/fetch.tsx +++ b/front/packages/ui/src/fetch.tsx @@ -34,11 +34,11 @@ const isPage = (obj: unknown): obj is Page => export const Fetch = ({ query, - placeholderCount, + placeholderCount = 1, children, }: { query: QueryIdentifier; - placeholderCount: number; + placeholderCount?: number; children: ( item: Data extends Page ? WithLoading : WithLoading, i: number, @@ -52,7 +52,9 @@ export const Fetch = ({ <>{[...Array(placeholderCount)].map((_, i) => children({ isLoading: true } as any, i))} ); if (!isPage(data)) - return children(data ? { ...data, isLoading: false } : ({ isLoading: true } as any), 0); + return ( + <> {children(data ? { ...data, isLoading: false } : ({ isLoading: true } as any), 0)} + ); return <>{data.items.map((item, i) => children({ ...item, isLoading: false } as any, i))}; }; diff --git a/front/packages/ui/src/index.ts b/front/packages/ui/src/index.ts index 117a3655..fb01a8a7 100644 --- a/front/packages/ui/src/index.ts +++ b/front/packages/ui/src/index.ts @@ -20,3 +20,4 @@ export * from "./navbar"; export { BrowsePage } from "./browse"; +export { MovieDetails } from "./details"; diff --git a/front/packages/ui/src/navbar/index.tsx b/front/packages/ui/src/navbar/index.tsx index 6749d709..3443caf9 100644 --- a/front/packages/ui/src/navbar/index.tsx +++ b/front/packages/ui/src/navbar/index.tsx @@ -96,7 +96,7 @@ export const Navbar = (props: Stylable) => { {library.name} ) : ( - + ) } diff --git a/front/yarn.lock b/front/yarn.lock index 7d6bb5e6..09120266 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -9682,7 +9682,7 @@ __metadata: react-native-screens: ~3.18.0 react-native-svg: 13.4.0 typescript: ^4.6.3 - yoshiki: 0.2.9 + yoshiki: 0.2.11 languageName: unknown linkType: soft @@ -13319,7 +13319,7 @@ __metadata: superjson: ^1.11.0 typescript: ^4.9.3 webpack: ^5.75.0 - yoshiki: 0.2.9 + yoshiki: 0.2.11 zod: ^3.19.1 languageName: unknown linkType: soft @@ -13644,9 +13644,9 @@ __metadata: languageName: node linkType: hard -"yoshiki@npm:0.2.9": - version: 0.2.9 - resolution: "yoshiki@npm:0.2.9" +"yoshiki@npm:0.2.11": + version: 0.2.11 + resolution: "yoshiki@npm:0.2.11" dependencies: "@types/node": 18.x.x "@types/react": 18.x.x @@ -13661,7 +13661,7 @@ __metadata: optional: true react-native-web: optional: true - checksum: 41ff5ff7e4cd99b2bc7453749a1f17ceb63bc9e1123285bc62a12315cac1fb087b58366db50e08b275e7dd0b1ce862df4c3acae51872f8e3aa8765ee9b61d4bf + checksum: 5a2bbb62b2270d3456f114cfbb24a84ad6b8a94b147687929ccffe2d179560ef40b46df1d4054eda91310d295a9a674bbb201765deb86dc96a2133bfd702235a languageName: node linkType: hard