diff --git a/front/src/app/(app)/collections/[slug].tsx b/front/src/app/(app)/collections/[slug].tsx
new file mode 100644
index 00000000..0134a106
--- /dev/null
+++ b/front/src/app/(app)/collections/[slug].tsx
@@ -0,0 +1,3 @@
+import { CollectionDetails } from "~/ui/details";
+
+export default CollectionDetails;
diff --git a/front/src/query/query.tsx b/front/src/query/query.tsx
index 67deca0e..705970fd 100644
--- a/front/src/query/query.tsx
+++ b/front/src/query/query.tsx
@@ -159,7 +159,10 @@ const toQueryKey = (query: {
...query.path,
query.params
? `?${Object.entries(query.params)
- .filter(([_, v]) => v !== undefined)
+ .filter(
+ ([_, v]) =>
+ v !== undefined && (Array.isArray(v) ? v.length > 0 : true),
+ )
.map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`)
.join("&")}`
: undefined,
diff --git a/front/src/ui/details/collection.tsx b/front/src/ui/details/collection.tsx
index c1e1a151..2edb2647 100644
--- a/front/src/ui/details/collection.tsx
+++ b/front/src/ui/details/collection.tsx
@@ -1,132 +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 .
- */
+import { useState } from "react";
+import Animated from "react-native-reanimated";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { useQueryState } from "~/utils";
+import { HeaderBackground, useScrollNavbar } from "../navbar";
+import { Header } from "./header";
-/** biome-ignore-all lint/correctness/noUnusedImports: TODO */
-
-import {
- type Collection,
- CollectionP,
- type KyooImage,
- type QueryIdentifier,
- useInfiniteFetch,
-} from "@kyoo/models";
-import {
- Container,
- focusReset,
- GradientImageBackground,
- H2,
- ImageBackground,
- Link,
- P,
- ts,
-} from "@kyoo/primitives";
-import { useTranslation } from "react-i18next";
-import { type Theme, useYoshiki } from "yoshiki/native";
-
-export const PartOf = ({
- name,
- overview,
- thumbnail,
- href,
-}: {
- name: string;
- overview: string | null;
- thumbnail: KyooImage | null;
- href: string;
-}) => {
- const { css, theme } = useYoshiki("part-of-collection");
- const { t } = useTranslation();
+export const CollectionDetails = () => {
+ const [slug] = useQueryState("slug", undefined!);
+ const insets = useSafeAreaInsets();
+ const [imageHeight, setHeight] = useState(300);
+ const { scrollHandler, headerProps } = useScrollNavbar({ imageHeight });
return (
- theme.background,
- fover: {
- self: { ...focusReset, borderColor: (theme: Theme) => theme.accent },
- title: { textDecorationLine: "underline" },
- },
- })}
- >
-
+
+
-
- {t("show.partOf")} {name}
-
- {overview}
-
-
- );
-};
-
-export const DetailsCollections = ({
- type,
- slug,
-}: {
- type: "movie" | "show";
- slug: string;
-}) => {
- const { items } = useInfiniteFetch(DetailsCollections.query(type, slug));
- const { css } = useYoshiki();
-
- // Since most items dont have collections, not having a skeleton reduces layout shifts.
- if (!items) return null;
-
- return (
-
- {items.map((x) => (
- setHeight(e.nativeEvent.layout.height)}
/>
- ))}
-
+
+ >
);
};
-
-DetailsCollections.query = (
- type: "movie" | "show",
- slug: string,
-): QueryIdentifier => ({
- parser: CollectionP,
- path: [type, slug, "collections"],
- params: {
- limit: 0,
- },
- infinite: true,
-});
diff --git a/front/src/ui/details/header.tsx b/front/src/ui/details/header.tsx
index a46fb7e0..7042fdf3 100644
--- a/front/src/ui/details/header.tsx
+++ b/front/src/ui/details/header.tsx
@@ -380,7 +380,7 @@ export const Header = ({
slug,
onImageLayout,
}: {
- kind: "movie" | "serie";
+ kind: "movie" | "serie" | "collection";
slug: string;
onImageLayout?: ViewProps["onLayout"];
}) => {
@@ -458,6 +458,9 @@ Header.query = (
parser: Show,
path: ["api", `${kind}s`, slug],
params: {
- with: ["studios", ...(kind === "serie" ? ["firstEntry", "nextEntry"] : [])],
+ with: [
+ ...(kind !== "collection" ? ["studios"] : []),
+ ...(kind === "serie" ? ["firstEntry", "nextEntry"] : []),
+ ],
},
});
diff --git a/front/src/ui/details/index.tsx b/front/src/ui/details/index.tsx
index d7b81206..4b165b80 100644
--- a/front/src/ui/details/index.tsx
+++ b/front/src/ui/details/index.tsx
@@ -1,2 +1,3 @@
+export { CollectionDetails } from "./collection";
export { MovieDetails } from "./movie";
export { SerieDetails } from "./serie";
diff --git a/front/src/ui/details/part-of.tsx b/front/src/ui/details/part-of.tsx
new file mode 100644
index 00000000..864ab92e
--- /dev/null
+++ b/front/src/ui/details/part-of.tsx
@@ -0,0 +1,110 @@
+import {
+ type Collection,
+ CollectionP,
+ type KyooImage,
+ type QueryIdentifier,
+ useInfiniteFetch,
+} from "@kyoo/models";
+import {
+ Container,
+ focusReset,
+ GradientImageBackground,
+ H2,
+ ImageBackground,
+ Link,
+ P,
+ ts,
+} from "@kyoo/primitives";
+import { useTranslation } from "react-i18next";
+import { type Theme, useYoshiki } from "yoshiki/native";
+
+export const PartOf = ({
+ name,
+ overview,
+ thumbnail,
+ href,
+}: {
+ name: string;
+ overview: string | null;
+ thumbnail: KyooImage | null;
+ href: string;
+}) => {
+ const { css, theme } = useYoshiki("part-of-collection");
+ const { t } = useTranslation();
+
+ return (
+ theme.background,
+ fover: {
+ self: { ...focusReset, borderColor: (theme: Theme) => theme.accent },
+ title: { textDecorationLine: "underline" },
+ },
+ })}
+ >
+
+
+ {t("show.partOf")} {name}
+
+ {overview}
+
+
+ );
+};
+
+export const DetailsCollections = ({
+ type,
+ slug,
+}: {
+ type: "movie" | "show";
+ slug: string;
+}) => {
+ const { items } = useInfiniteFetch(DetailsCollections.query(type, slug));
+ const { css } = useYoshiki();
+
+ // Since most items dont have collections, not having a skeleton reduces layout shifts.
+ if (!items) return null;
+
+ return (
+
+ {items.map((x) => (
+
+ ))}
+
+ );
+};
+
+DetailsCollections.query = (
+ type: "movie" | "show",
+ slug: string,
+): QueryIdentifier => ({
+ parser: CollectionP,
+ path: [type, slug, "collections"],
+ params: {
+ limit: 0,
+ },
+ infinite: true,
+});