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}`} + + + + + ); +};