diff --git a/back/src/Kyoo.Abstractions/Models/Resources/User.cs b/back/src/Kyoo.Abstractions/Models/Resources/User.cs index b4f9913a..f0461775 100644 --- a/back/src/Kyoo.Abstractions/Models/Resources/User.cs +++ b/back/src/Kyoo.Abstractions/Models/Resources/User.cs @@ -17,7 +17,6 @@ // along with Kyoo. If not, see . using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models.Attributes; diff --git a/back/src/Kyoo.Authentication/Views/AuthApi.cs b/back/src/Kyoo.Authentication/Views/AuthApi.cs index 3de4f0e0..e14b5889 100644 --- a/back/src/Kyoo.Authentication/Views/AuthApi.cs +++ b/back/src/Kyoo.Authentication/Views/AuthApi.cs @@ -18,7 +18,6 @@ using System; using System.Linq; -using System.Security.Claims; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; diff --git a/back/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs b/back/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs index 0aad78b4..10248071 100644 --- a/back/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs +++ b/back/src/Kyoo.Core/Views/Helper/Serializers/JsonSerializerContract.cs @@ -25,6 +25,7 @@ using Kyoo.Abstractions.Models.Attributes; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using static System.Text.Json.JsonNamingPolicy; namespace Kyoo.Core.Api { @@ -99,7 +100,7 @@ namespace Kyoo.Core.Api PropertyName = "kind", UnderlyingName = "kind", PropertyType = typeof(string), - ValueProvider = new FixedValueProvider(type.Name), + ValueProvider = new FixedValueProvider(CamelCase.ConvertName(type.Name)), Readable = true, Writable = false, TypeNameHandling = TypeNameHandling.None, diff --git a/front/packages/models/src/accounts.tsx b/front/packages/models/src/accounts.tsx index c603d614..6fd1961d 100644 --- a/front/packages/models/src/accounts.tsx +++ b/front/packages/models/src/accounts.tsx @@ -19,7 +19,7 @@ */ import { ReactNode, createContext, useContext, useEffect, useMemo, useRef } from "react"; -import { UserP } from "./resources"; +import { User, UserP } from "./resources"; import { z } from "zod"; import { zdate } from "./utils"; import { removeAccounts, setAccountCookie, updateAccount } from "./account-internal"; @@ -38,13 +38,13 @@ export const TokenP = z.object({ }); export type Token = z.infer; -export const AccountP = UserP.and( - z.object({ - token: TokenP, - apiUrl: z.string(), - selected: z.boolean(), - }), -); +export const AccountP = UserP.merge(z.object({ + // set it optional for accounts logged in before the kind was present + kind: z.literal("user").optional(), + token: TokenP, + apiUrl: z.string(), + selected: z.boolean(), +})); export type Account = z.infer; const AccountContext = createContext<(Account & { select: () => void; remove: () => void })[]>([]); @@ -100,7 +100,7 @@ export const AccountProvider = ({ const user = useFetch({ path: ["auth", "me"], parser: UserP, - placeholderData: selected, + placeholderData: selected as User, enabled: !!selected, timeout: 5_000, }); diff --git a/front/packages/models/src/resources/collection.ts b/front/packages/models/src/resources/collection.ts index 2e7c1cc1..41954fc7 100644 --- a/front/packages/models/src/resources/collection.ts +++ b/front/packages/models/src/resources/collection.ts @@ -22,7 +22,7 @@ import { z } from "zod"; import { withImages, ResourceP } from "../traits"; export const CollectionP = withImages( - ResourceP.extend({ + ResourceP("collection").extend({ /** * The title of this collection. */ @@ -31,8 +31,7 @@ export const CollectionP = withImages( * The summary of this show. */ overview: z.string().nullable(), - }), - "collections", + }) ).transform((x) => ({ ...x, href: `/collection/${x.slug}`, diff --git a/front/packages/models/src/resources/episode.base.ts b/front/packages/models/src/resources/episode.base.ts index 6da1d810..ab6907d3 100644 --- a/front/packages/models/src/resources/episode.base.ts +++ b/front/packages/models/src/resources/episode.base.ts @@ -24,7 +24,7 @@ import { withImages, imageFn } from "../traits"; import { ResourceP } from "../traits/resource"; export const BaseEpisodeP = withImages( - ResourceP.extend({ + ResourceP("episode").extend({ /** * The season in witch this episode is in. */ @@ -72,7 +72,6 @@ export const BaseEpisodeP = withImages( */ showId: z.string(), }), - "episodes", ) .transform((x) => ({ ...x, diff --git a/front/packages/models/src/resources/library-item.ts b/front/packages/models/src/resources/library-item.ts index d3f648b7..aab0ab11 100644 --- a/front/packages/models/src/resources/library-item.ts +++ b/front/packages/models/src/resources/library-item.ts @@ -23,28 +23,19 @@ import { CollectionP } from "./collection"; import { MovieP } from "./movie"; import { ShowP } from "./show"; -/** - * The type of item, ether a show, a movie or a collection. - */ -export enum ItemKind { - Show = "Show", - Movie = "Movie", - Collection = "Collection", -} - export const LibraryItemP = z.union([ /* * Either a Show */ - ShowP.and(z.object({ kind: z.literal(ItemKind.Show) })), + ShowP, /* * Or a Movie */ - MovieP.and(z.object({ kind: z.literal(ItemKind.Movie) })), + MovieP, /* * Or a Collection */ - CollectionP.and(z.object({ kind: z.literal(ItemKind.Collection) })), + CollectionP, ]); /** diff --git a/front/packages/models/src/resources/movie.ts b/front/packages/models/src/resources/movie.ts index c0d1ae35..d2343876 100644 --- a/front/packages/models/src/resources/movie.ts +++ b/front/packages/models/src/resources/movie.ts @@ -29,7 +29,7 @@ import { MetadataP } from "./metadata"; import { WatchStatusP } from "./watch-status"; export const MovieP = withImages( - ResourceP.extend({ + ResourceP("movie").extend({ /** * The title of this movie. */ @@ -105,7 +105,6 @@ export const MovieP = withImages( */ watchStatus: WatchStatusP.optional().nullable(), }), - "movies", ) .transform((x) => ({ ...x, diff --git a/front/packages/models/src/resources/news.ts b/front/packages/models/src/resources/news.ts index f27d21a4..30f724a7 100644 --- a/front/packages/models/src/resources/news.ts +++ b/front/packages/models/src/resources/news.ts @@ -22,23 +22,15 @@ import { z } from "zod"; import { MovieP } from "./movie"; import { EpisodeP } from "./episode"; -/** - * The type of item, ether a a movie or an episode. - */ -export enum NewsKind { - Episode = "Episode", - Movie = "Movie", -} - export const NewsP = z.union([ /* * Either an episode */ - EpisodeP.and(z.object({ kind: z.literal(NewsKind.Episode) })), + EpisodeP, /* * Or a Movie */ - MovieP.and(z.object({ kind: z.literal(NewsKind.Movie) })), + MovieP, ]); /** diff --git a/front/packages/models/src/resources/person.ts b/front/packages/models/src/resources/person.ts index 5f3f3872..00994383 100644 --- a/front/packages/models/src/resources/person.ts +++ b/front/packages/models/src/resources/person.ts @@ -23,7 +23,7 @@ import { withImages } from "../traits"; import { ResourceP } from "../traits/resource"; export const PersonP = withImages( - ResourceP.extend({ + ResourceP("people").extend({ /** * The name of this person. */ @@ -40,7 +40,6 @@ export const PersonP = withImages( */ role: z.string().optional(), }), - "people", ); /** diff --git a/front/packages/models/src/resources/season.ts b/front/packages/models/src/resources/season.ts index 99edb3b1..839e1519 100644 --- a/front/packages/models/src/resources/season.ts +++ b/front/packages/models/src/resources/season.ts @@ -24,7 +24,7 @@ import { withImages } from "../traits"; import { ResourceP } from "../traits/resource"; export const SeasonP = withImages( - ResourceP.extend({ + ResourceP("season").extend({ /** * The name of this season. */ @@ -50,7 +50,6 @@ export const SeasonP = withImages( */ episodesCount: z.number(), }), - "seasons", ); /** diff --git a/front/packages/models/src/resources/show.ts b/front/packages/models/src/resources/show.ts index 7c5fce74..9275751a 100644 --- a/front/packages/models/src/resources/show.ts +++ b/front/packages/models/src/resources/show.ts @@ -22,10 +22,8 @@ import { z } from "zod"; import { zdate } from "../utils"; import { withImages, ResourceP } from "../traits"; import { Genre } from "./genre"; -import { SeasonP } from "./season"; import { StudioP } from "./studio"; import { BaseEpisodeP } from "./episode.base"; -import { CollectionP } from "./collection"; import { MetadataP } from "./metadata"; import { ShowWatchStatusP } from "./watch-status"; @@ -40,7 +38,7 @@ export enum Status { } export const ShowP = withImages( - ResourceP.extend({ + ResourceP("show").extend({ /** * The title of this show. */ @@ -106,7 +104,6 @@ export const ShowP = withImages( */ episodesCount: z.number().int().gte(0).optional(), }), - "shows", ) .transform((x) => { if (!x.thumbnail && x.poster) { diff --git a/front/packages/models/src/resources/studio.ts b/front/packages/models/src/resources/studio.ts index ae29f283..15330a53 100644 --- a/front/packages/models/src/resources/studio.ts +++ b/front/packages/models/src/resources/studio.ts @@ -21,7 +21,7 @@ import { z } from "zod"; import { ResourceP } from "../traits/resource"; -export const StudioP = ResourceP.extend({ +export const StudioP = ResourceP("studio").extend({ /** * The name of this studio. */ diff --git a/front/packages/models/src/resources/user.ts b/front/packages/models/src/resources/user.ts index ff37697a..e63b4c74 100644 --- a/front/packages/models/src/resources/user.ts +++ b/front/packages/models/src/resources/user.ts @@ -21,10 +21,7 @@ import { z } from "zod"; import { ResourceP } from "../traits/resource"; -/** - * The library that will contain Shows, Collections... - */ -export const UserP = ResourceP.extend({ +export const UserP = ResourceP("user").extend({ /** * The name of this user. */ diff --git a/front/packages/models/src/resources/watchlist.ts b/front/packages/models/src/resources/watchlist.ts index fd533afc..3f6e0e4e 100644 --- a/front/packages/models/src/resources/watchlist.ts +++ b/front/packages/models/src/resources/watchlist.ts @@ -22,23 +22,15 @@ import { z } from "zod"; import { MovieP } from "./movie"; import { ShowP } from "./show"; -/** - * The type of item, ether a show, a movie or an episode. - */ -export enum WatchlistKind { - Show = "Show", - Movie = "Movie", -} - export const WatchlistP = z.union([ /* * Either a show */ - ShowP.and(z.object({ kind: z.literal(WatchlistKind.Show) })), + ShowP, /* * Or a Movie */ - MovieP.and(z.object({ kind: z.literal(WatchlistKind.Movie) })), + MovieP, ]); /** diff --git a/front/packages/models/src/traits/images.ts b/front/packages/models/src/traits/images.ts index ef77b67c..f7978d8f 100644 --- a/front/packages/models/src/traits/images.ts +++ b/front/packages/models/src/traits/images.ts @@ -62,13 +62,13 @@ const addQualities = (x: object | null | undefined, href: string) => { }; }; -export const withImages = (parser: ZodObject, type: string) => { +export const withImages = (parser: ZodObject) => { return parser.merge(ImagesP).transform((x) => { return { ...x, - poster: addQualities(x.poster, `/${type}/${x.slug}/poster`), - thumbnail: addQualities(x.thumbnail, `/${type}/${x.slug}/thumbnail`), - logo: addQualities(x.logo, `/${type}/${x.slug}/logo`), + poster: addQualities(x.poster, `/${x.kind}/${x.slug}/poster`), + thumbnail: addQualities(x.thumbnail, `/${x.kind}/${x.slug}/thumbnail`), + logo: addQualities(x.logo, `/${x.kind}/${x.slug}/logo`), }; }); }; diff --git a/front/packages/models/src/traits/resource.ts b/front/packages/models/src/traits/resource.ts index 8377d716..87833f19 100644 --- a/front/packages/models/src/traits/resource.ts +++ b/front/packages/models/src/traits/resource.ts @@ -20,20 +20,26 @@ import { z } from "zod"; -export const ResourceP = z.object({ - /** - * A unique ID for this type of resource. This can't be changed and duplicates are not allowed. - */ - id: z.string(), +export const ResourceP = (kind: T) => + z.object({ + /** + * A unique ID for this type of resource. This can't be changed and duplicates are not allowed. + */ + id: z.string(), - /** - * A human-readable identifier that can be used instead of an ID. A slug must be unique for a type - * of resource but it can be changed. - */ - slug: z.string(), -}); + /** + * A human-readable identifier that can be used instead of an ID. A slug must be unique for a type + * of resource but it can be changed. + */ + slug: z.string(), + + /** + * The type of resource + */ + kind: z.literal(kind), + }); /** * The base trait used to represent identifiable resources. */ -export type Resource = z.infer; +export type Resource = z.infer>; diff --git a/front/packages/primitives/src/image/index.tsx b/front/packages/primitives/src/image/index.tsx index 61f0ba67..a7dfdf48 100644 --- a/front/packages/primitives/src/image/index.tsx +++ b/front/packages/primitives/src/image/index.tsx @@ -71,7 +71,7 @@ export const ImageBackground = ({ }: { as?: ComponentType; gradient?: Partial | boolean; - children: ReactNode; + children?: ReactNode; containerStyle?: YoshikiEnhanced; imageStyle?: YoshikiEnhanced; hideLoad?: boolean; diff --git a/front/packages/ui/src/browse/index.tsx b/front/packages/ui/src/browse/index.tsx index ea28fd42..fab7aa09 100644 --- a/front/packages/ui/src/browse/index.tsx +++ b/front/packages/ui/src/browse/index.tsx @@ -23,7 +23,6 @@ import { QueryPage, LibraryItem, LibraryItemP, - ItemKind, getDisplayDate, } from "@kyoo/models"; import { ComponentProps, useState } from "react"; @@ -47,16 +46,16 @@ export const itemMap = ( isLoading: item.isLoading, slug: item.slug, name: item.name, - subtitle: item.kind !== ItemKind.Collection ? getDisplayDate(item) : undefined, + subtitle: item.kind !== "collection" ? getDisplayDate(item) : undefined, href: item.href, poster: item.poster, thumbnail: item.thumbnail, - watchStatus: item.kind !== ItemKind.Collection ? item.watchStatus?.status ?? null : null, - type: item.kind.toLowerCase() as any, + watchStatus: item.kind !== "collection" ? item.watchStatus?.status ?? null : null, + type: item.kind, watchPercent: - item.kind !== ItemKind.Collection ? item.watchStatus?.watchedPercent ?? null : null, + item.kind !== "collection" ? item.watchStatus?.watchedPercent ?? null : null, unseenEpisodesCount: - item.kind === ItemKind.Show + item.kind === "show" ? item.watchStatus?.unseenEpisodesCount ?? item.episodesCount! : null, }; diff --git a/front/packages/ui/src/collection/index.tsx b/front/packages/ui/src/collection/index.tsx index 2eda6cae..1a6e0326 100644 --- a/front/packages/ui/src/collection/index.tsx +++ b/front/packages/ui/src/collection/index.tsx @@ -21,7 +21,6 @@ import { Collection, CollectionP, - ItemKind, LibraryItem, LibraryItemP, QueryIdentifier, @@ -161,20 +160,20 @@ export const CollectionPage: QueryPage<{ slug: string }> = ({ slug }) => { diff --git a/front/packages/ui/src/details/episode.tsx b/front/packages/ui/src/details/episode.tsx index 5f0bc35a..88244724 100644 --- a/front/packages/ui/src/details/episode.tsx +++ b/front/packages/ui/src/details/episode.tsx @@ -37,7 +37,7 @@ import { percent, rem, Stylable, Theme, useYoshiki } from "yoshiki/native"; import { KyooImage, WatchStatusV } from "@kyoo/models"; import { ItemProgress } from "../browse/grid"; import { EpisodesContext } from "../components/context-menus"; -import { useRef, useState } from "react"; +import { useState } from "react"; export const episodeDisplayNumber = ( episode: { diff --git a/front/packages/ui/src/home/index.tsx b/front/packages/ui/src/home/index.tsx index 67fdc160..b92e0b07 100644 --- a/front/packages/ui/src/home/index.tsx +++ b/front/packages/ui/src/home/index.tsx @@ -18,7 +18,7 @@ * along with Kyoo. If not, see . */ -import { Genre, ItemKind, QueryPage } from "@kyoo/models"; +import { Genre, QueryPage } from "@kyoo/models"; import { Fetch } from "../fetch"; import { Header } from "./header"; import { DefaultLayout } from "../layout"; @@ -40,7 +40,7 @@ export const HomePage: QueryPage<{}, Genre> = ({ randomItems }) => { tagline={"tagline" in x ? x.tagline : null} overview={x.overview} thumbnail={x.thumbnail} - link={x.kind !== ItemKind.Collection && !x.isLoading ? x.playHref : undefined} + link={x.kind !== "collection" && !x.isLoading ? x.playHref : undefined} infoLink={x.href} /> )} diff --git a/front/packages/ui/src/home/news.tsx b/front/packages/ui/src/home/news.tsx index a7a6bb98..45202f62 100644 --- a/front/packages/ui/src/home/news.tsx +++ b/front/packages/ui/src/home/news.tsx @@ -18,7 +18,7 @@ * along with Kyoo. If not, see . */ -import { News, NewsKind, NewsP, QueryIdentifier, getDisplayDate } from "@kyoo/models"; +import { News, NewsP, QueryIdentifier, getDisplayDate } from "@kyoo/models"; import { ItemGrid } from "../browse/grid"; import { InfiniteFetch } from "../fetch-infinite"; import { useTranslation } from "react-i18next"; @@ -37,13 +37,13 @@ export const NewsList = () => { query={NewsList.query()} layout={{ ...ItemGrid.layout, layout: "horizontal" }} getItemType={(x, i) => - x.kind === NewsKind.Movie || (x.isLoading && i % 2) ? "movie" : "episode" + x.kind === "movie" || (x.isLoading && i % 2) ? "movie" : "episode" } getItemSize={(kind) => (kind === "episode" ? 2 : 1)} empty={t("home.none")} > {(x, i) => - x.kind === NewsKind.Movie || (x.isLoading && i % 2) ? ( + x.kind === "movie" || (x.isLoading && i % 2) ? ( { { )} diff --git a/front/packages/ui/src/home/watchlist.tsx b/front/packages/ui/src/home/watchlist.tsx index 8f21d2f6..544f6192 100644 --- a/front/packages/ui/src/home/watchlist.tsx +++ b/front/packages/ui/src/home/watchlist.tsx @@ -21,7 +21,6 @@ import { QueryIdentifier, Watchlist, - WatchlistKind, WatchlistP, getDisplayDate, useAccount, @@ -48,7 +47,7 @@ export const WatchlistList = () => { query={WatchlistList.query()} layout={{ ...ItemGrid.layout, layout: "horizontal" }} getItemType={(x, i) => - (x.kind === WatchlistKind.Show && x.watchStatus?.nextEpisode) || (x.isLoading && i % 2) + (x.kind === "show" && x.watchStatus?.nextEpisode) || (x.isLoading && i % 2) ? "episode" : "item" } @@ -56,8 +55,8 @@ export const WatchlistList = () => { empty={t("home.none")} > {(x, i) => { - const episode = x.kind === WatchlistKind.Show ? x.watchStatus?.nextEpisode : null; - return (x.kind === WatchlistKind.Show && x.watchStatus?.nextEpisode) || + const episode = x.kind === "show" ? x.watchStatus?.nextEpisode : null; + return (x.kind === "show" && x.watchStatus?.nextEpisode) || (x.isLoading && i % 2) ? ( { watchStatus={x.watchStatus?.status || null} watchPercent={x.watchStatus?.watchedPercent || null} unseenEpisodesCount={ - x.kind === WatchlistKind.Show ? x.watchStatus?.unseenEpisodesCount : null + x.kind === "show" ? x.watchStatus?.unseenEpisodesCount : null } - type={x.kind === WatchlistKind.Movie ? "movie" : "show"} + type={x.kind} /> ); }}