diff --git a/app/(auth)/items/[id]/page.tsx b/app/(auth)/items/[id]/page.tsx
index 32a74f3d..9eca50d5 100644
--- a/app/(auth)/items/[id]/page.tsx
+++ b/app/(auth)/items/[id]/page.tsx
@@ -1,17 +1,15 @@
-import { Chromecast } from "@/components/Chromecast";
import { Text } from "@/components/common/Text";
import { DownloadItem } from "@/components/DownloadItem";
import { PlayedStatus } from "@/components/PlayedStatus";
import { CastAndCrew } from "@/components/series/CastAndCrew";
import { CurrentSeries } from "@/components/series/CurrentSeries";
import { SimilarItems } from "@/components/SimilarItems";
-import { VideoPlayer } from "@/components/VideoPlayer";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useQuery } from "@tanstack/react-query";
import { Image } from "expo-image";
import { router, useLocalSearchParams } from "expo-router";
import { useAtom } from "jotai";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useMemo, useState } from "react";
import {
ActivityIndicator,
ScrollView,
@@ -36,10 +34,13 @@ import ios12 from "@/utils/profiles/ios12";
import { currentlyPlayingItemAtom } from "@/components/CurrentlyPlayingBar";
import { AudioTrackSelector } from "@/components/AudioTrackSelector";
import { SubtitleTrackSelector } from "@/components/SubtitleTrackSelector";
-import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
-import { Button } from "@/components/Button";
-import { Ionicons } from "@expo/vector-icons";
import { NextEpisodeButton } from "@/components/series/NextEpisodeButton";
+import { Badge } from "@/components/Badge";
+import { FontAwesome, Ionicons } from "@expo/vector-icons";
+import { Ratings } from "@/components/Ratings";
+import { SeriesTitleHeader } from "@/components/series/SeriesTitleHeader";
+import { MoviesTitleHeader } from "@/components/movies/MoviesTitleHeader";
+import { OverviewText } from "@/components/OverviewText";
const page: React.FC = () => {
const local = useLocalSearchParams();
@@ -134,7 +135,7 @@ const page: React.FC = () => {
staleTime: 0,
});
- const [cp, setCp] = useAtom(currentlyPlayingItemAtom);
+ const [, setCp] = useAtom(currentlyPlayingItemAtom);
const client = useRemoteMediaClient();
const onPressPlay = useCallback(
@@ -212,50 +213,14 @@ const page: React.FC = () => {
{item.Type === "Episode" ? (
- <>
-
- router.push(`/(auth)/series/${item.SeriesId}/page`)
- }
- >
-
- {item?.SeriesName}
-
-
-
-
- {item?.Name}
-
-
-
-
- {}}>
-
- {item?.SeasonName}
-
-
- {"—"}
-
- {`Episode ${item.IndexNumber}`}
-
-
-
-
- {item.ProductionYear}
-
- >
+
) : (
<>
-
-
- {item?.Name}
-
-
-
- {item?.ProductionYear}
-
+
>
)}
+ {item?.ProductionYear}
+
@@ -266,7 +231,8 @@ const page: React.FC = () => {
)}
- {item.Overview}
+
+
diff --git a/components/Badge.tsx b/components/Badge.tsx
new file mode 100644
index 00000000..2a6f9ad8
--- /dev/null
+++ b/components/Badge.tsx
@@ -0,0 +1,36 @@
+import { View, ViewProps } from "react-native";
+import { Text } from "./common/Text";
+
+interface Props extends ViewProps {
+ text?: string | number | null;
+ variant?: "gray" | "purple";
+ iconLeft?: React.ReactNode;
+}
+
+export const Badge: React.FC = ({
+ iconLeft,
+ text,
+ variant = "purple",
+ ...props
+}) => {
+ return (
+
+ {iconLeft && {iconLeft}}
+
+ {text}
+
+
+ );
+};
diff --git a/components/OverviewText.tsx b/components/OverviewText.tsx
new file mode 100644
index 00000000..e25a6806
--- /dev/null
+++ b/components/OverviewText.tsx
@@ -0,0 +1,38 @@
+import { TouchableOpacity, View, ViewProps } from "react-native";
+import { Text } from "@/components/common/Text";
+import { tc } from "@/utils/textTools";
+import { useState } from "react";
+
+interface Props extends ViewProps {
+ text?: string | null;
+}
+
+const LIMIT = 150;
+
+export const OverviewText: React.FC = ({ text, ...props }) => {
+ const [limit, setLimit] = useState(LIMIT);
+
+ if (!text) return null;
+
+ if (text.length > LIMIT)
+ return (
+
+ setLimit((prev) => (prev === LIMIT ? text.length : LIMIT))
+ }
+ >
+
+ {tc(text, limit)}
+
+ {limit === LIMIT ? "Show more" : "Show less"}
+
+
+
+ );
+
+ return (
+
+ {text}
+
+ );
+};
diff --git a/components/_template.tsx b/components/_template.tsx
new file mode 100644
index 00000000..64e7fc7f
--- /dev/null
+++ b/components/_template.tsx
@@ -0,0 +1,12 @@
+import { View, ViewProps } from "react-native";
+import { Text } from "@/components/common/Text";
+
+interface Props extends ViewProps {}
+
+export const TitleHeader: React.FC = ({ ...props }) => {
+ return (
+
+
+
+ );
+};
diff --git a/components/movies/MoviesTitleHeader.tsx b/components/movies/MoviesTitleHeader.tsx
new file mode 100644
index 00000000..71a3f4af
--- /dev/null
+++ b/components/movies/MoviesTitleHeader.tsx
@@ -0,0 +1,21 @@
+import { TouchableOpacity, View, ViewProps } from "react-native";
+import { Text } from "@/components/common/Text";
+import { useRouter } from "expo-router";
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
+
+interface Props extends ViewProps {
+ item: BaseItemDto;
+}
+
+export const MoviesTitleHeader: React.FC = ({ item, ...props }) => {
+ const router = useRouter();
+ return (
+ <>
+
+
+ {item?.Name}
+
+
+ >
+ );
+};
diff --git a/components/series/SeriesTitleHeader.tsx b/components/series/SeriesTitleHeader.tsx
new file mode 100644
index 00000000..5d2a7dd6
--- /dev/null
+++ b/components/series/SeriesTitleHeader.tsx
@@ -0,0 +1,37 @@
+import { TouchableOpacity, View, ViewProps } from "react-native";
+import { Text } from "@/components/common/Text";
+import { useRouter } from "expo-router";
+import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
+
+interface Props extends ViewProps {
+ item: BaseItemDto;
+}
+
+export const SeriesTitleHeader: React.FC = ({ item, ...props }) => {
+ const router = useRouter();
+ return (
+ <>
+ router.push(`/(auth)/series/${item.SeriesId}/page`)}
+ >
+ {item?.SeriesName}
+
+
+
+ {item?.Name}
+
+
+
+
+ {}}>
+ {item?.SeasonName}
+
+ {"—"}
+
+ {`Episode ${item.IndexNumber}`}
+
+
+
+ >
+ );
+};