From aa0fbd6fad59a93bb2aa5552b55f5c81a13fa618 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Thu, 15 Aug 2024 16:46:50 +0200 Subject: [PATCH] fix: refactor --- app/(auth)/(tabs)/home/index.tsx | 216 +++++++++----------- components/common/HorrizontalScroll.tsx | 42 ++-- components/common/Input.tsx | 10 +- components/home/ScrollingCollectionList.tsx | 64 ++++++ components/settings/SettingToggles.tsx | 37 +++- 5 files changed, 229 insertions(+), 140 deletions(-) create mode 100644 components/home/ScrollingCollectionList.tsx diff --git a/app/(auth)/(tabs)/home/index.tsx b/app/(auth)/(tabs)/home/index.tsx index 07483337..8e2ded17 100644 --- a/app/(auth)/(tabs)/home/index.tsx +++ b/app/(auth)/(tabs)/home/index.tsx @@ -31,6 +31,8 @@ import NetInfo, { NetInfoState } from "@react-native-community/netinfo"; import { Button } from "@/components/Button"; import { Ionicons } from "@expo/vector-icons"; import MoviePoster from "@/components/MoviePoster"; +import { ScrollingCollectionList } from "@/components/home/ScrollingCollectionList"; +import { useSettings } from "@/utils/atoms/settings"; export default function index() { const router = useRouter(); @@ -40,6 +42,7 @@ export default function index() { const [user] = useAtom(userAtom); const [loading, setLoading] = useState(false); + const [settings, _] = useSettings(); const { data, isLoading, isError } = useQuery({ queryKey: ["resumeItems", user?.Id], @@ -55,7 +58,7 @@ export default function index() { staleTime: 60, }); - const { data: _nextUpData } = useQuery({ + const { data: _nextUpData, isLoading: isLoadingNextUp } = useQuery({ queryKey: ["nextUp-all", user?.Id], queryFn: async () => (api && @@ -74,7 +77,7 @@ export default function index() { return _nextUpData?.filter((i) => !data?.find((d) => d.Id === i.Id)); }, [_nextUpData]); - const { data: collections } = useQuery({ + const { data: collections, isLoading: isLoadingCollections } = useQuery({ queryKey: ["collections", user?.Id], queryFn: async () => { if (!api || !user?.Id) { @@ -87,6 +90,8 @@ export default function index() { }) ).data; + console.log("Collections", JSON.stringify(data.Items)); + const order = ["boxsets", "tvshows", "movies"]; const cs = data.Items?.sort((a, b) => { @@ -113,7 +118,10 @@ export default function index() { return collections?.find((c) => c.CollectionType === "tvshows")?.Id; }, [collections]); - const { data: recentlyAddedInMovies } = useQuery({ + const { + data: recentlyAddedInMovies, + isLoading: isLoadingRecentlyAddedMovies, + } = useQuery({ queryKey: ["recentlyAddedInMovies", user?.Id, movieCollectionId], queryFn: async () => (api && @@ -132,7 +140,10 @@ export default function index() { staleTime: 60, }); - const { data: recentlyAddedInTVShows } = useQuery({ + const { + data: recentlyAddedInTVShows, + isLoading: isLoadingRecentlyAddedTVShows, + } = useQuery({ queryKey: ["recentlyAddedInTVShows", user?.Id, tvShowCollectionId], queryFn: async () => (api && @@ -151,7 +162,9 @@ export default function index() { staleTime: 60, }); - const { data: suggestions } = useQuery({ + const { data: suggestions, isLoading: isLoadingSuggestions } = useQuery< + BaseItemDto[] + >({ queryKey: ["suggestions", user?.Id], queryFn: async () => (api && @@ -167,6 +180,46 @@ export default function index() { staleTime: 60, }); + const { data: mediaListCollection } = useQuery({ + queryKey: ["mediaListCollection", user?.Id], + queryFn: async () => { + if (!api || !user?.Id) return null; + + const response = await getItemsApi(api).getItems({ + userId: user.Id, + tags: ["medialist", "promoted"], + recursive: true, + fields: ["Tags"], + includeItemTypes: ["BoxSet"], + }); + + return response.data.Items?.[0].Id || null; + }, + enabled: !!api && !!user?.Id && settings?.usePopularPlugin === true, + staleTime: 60, + }); + + const { data: popularItems, isLoading: isLoadingPopular } = useQuery< + BaseItemDto[] + >({ + queryKey: ["popular", user?.Id], + queryFn: async () => { + if (!api || !user?.Id || !mediaListCollection) return []; + + const response = await getItemsApi(api).getItems({ + userId: user.Id, + parentId: mediaListCollection, + limit: 10, + }); + + console.log("Popular", response.data.Items?.length); + + return response.data.Items || []; + }, + enabled: !!api && !!user?.Id && !!mediaListCollection, + staleTime: 60, + }); + const refetch = useCallback(async () => { setLoading(true); await queryClient.refetchQueries({ queryKey: ["resumeItems", user?.Id] }); @@ -243,123 +296,52 @@ export default function index() { } > - - - Continue Watching - - - data={data} - renderItem={(item, index) => ( - router.push(`/items/${item.Id}/page`)} - className="flex flex-col w-48" - > - - - - - - )} - /> - + - - Next Up - - data={nextUpData} - renderItem={(item, index) => ( - router.push(`/items/${item.Id}/page`)} - className="flex flex-col w-48" - > - - - - - - )} - /> - + - - - Recently Added in Movies - - - data={recentlyAddedInMovies} - renderItem={(item, index) => ( - router.push(`/items/${item.Id}/page`)} - className="flex flex-col w-32" - > - - - - - - )} - /> - + - - - Recently Added in TV-Shows - - - data={recentlyAddedInTVShows} - renderItem={(item, index) => ( - router.push(`/series/${item.Id}/page`)} - className="flex flex-col w-32" - > - - - - - - )} - /> - + - - Collections - - data={collections} - renderItem={(item, index) => ( - router.push(`/collections/${item.Id}/page`)} - className="flex flex-col w-48" - > - - - - - - )} - /> - + - - Suggestions - - data={suggestions} - renderItem={(item, index) => ( - router.push(`/items/${item.Id}/page`)} - className="flex flex-col w-48" - > - - - - )} - /> - + + + ); diff --git a/components/common/HorrizontalScroll.tsx b/components/common/HorrizontalScroll.tsx index 97192be6..40a8deaf 100644 --- a/components/common/HorrizontalScroll.tsx +++ b/components/common/HorrizontalScroll.tsx @@ -11,6 +11,7 @@ import Animated, { useSharedValue, withTiming, } from "react-native-reanimated"; +import { Text } from "./Text"; interface HorizontalScrollProps extends ScrollViewProps { data?: T[] | null; @@ -19,18 +20,33 @@ interface HorizontalScrollProps extends ScrollViewProps { contentContainerStyle?: ViewStyle; loadingContainerStyle?: ViewStyle; height?: number; + loading?: boolean; } export function HorizontalScroll({ - data, + data = [], renderItem, containerStyle, contentContainerStyle, loadingContainerStyle, + loading = false, height = 164, ...props }: HorizontalScrollProps): React.ReactElement { - if (!data) { + const animatedOpacity = useSharedValue(0); + const animatedStyle1 = useAnimatedStyle(() => { + return { + opacity: withTiming(animatedOpacity.value, { duration: 250 }), + }; + }); + + useEffect(() => { + if (data) { + animatedOpacity.value = 1; + } + }, [data]); + + if (data === undefined || data === null || loading) { return ( ({ flex: 1, justifyContent: "center", alignItems: "center", - height, }, loadingContainerStyle, ]} @@ -48,18 +63,6 @@ export function HorizontalScroll({ ); } - const opacity = useSharedValue(0); - - const animatedStyle = useAnimatedStyle(() => { - return { - opacity: withTiming(opacity.value, { duration: 250 }), - }; - }); - - useEffect(() => { - if (data && data.length > 0) opacity.value = 1; - }, [data]); - return ( ({ className={` flex flex-row px-4 `} - style={[animatedStyle]} + style={[animatedStyle1]} > - {data?.map((item, index) => ( + {data.map((item, index) => ( {renderItem(item, index)} ))} + {data.length === 0 && ( + + No data available + + )} ); diff --git a/components/common/Input.tsx b/components/common/Input.tsx index 63477f58..60a60778 100644 --- a/components/common/Input.tsx +++ b/components/common/Input.tsx @@ -1,3 +1,4 @@ +import { useFocusEffect } from "expo-router"; import React, { useEffect } from "react"; import { TextInputProps, TextProps } from "react-native"; import { TextInput } from "react-native"; @@ -5,9 +6,11 @@ export function Input(props: TextInputProps) { const { style, ...otherProps } = props; const inputRef = React.useRef(null); - useEffect(() => { - inputRef.current?.focus(); - }, []); + useFocusEffect( + React.useCallback(() => { + inputRef.current?.focus(); + }, []), + ); return ( ); } diff --git a/components/home/ScrollingCollectionList.tsx b/components/home/ScrollingCollectionList.tsx new file mode 100644 index 00000000..99a154c0 --- /dev/null +++ b/components/home/ScrollingCollectionList.tsx @@ -0,0 +1,64 @@ +import { TouchableOpacity, View, ViewProps } from "react-native"; +import { Text } from "@/components/common/Text"; +import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"; +import { router } from "expo-router"; +import ContinueWatchingPoster from "../ContinueWatchingPoster"; +import { ItemCardText } from "../ItemCardText"; +import { HorizontalScroll } from "../common/HorrizontalScroll"; +import MoviePoster from "../MoviePoster"; + +interface Props extends ViewProps { + title: string; + loading?: boolean; + orientation?: "horizontal" | "vertical"; + data?: BaseItemDto[] | null; + height?: "small" | "large"; + disabled?: boolean; +} + +export const ScrollingCollectionList: React.FC = ({ + title, + data, + orientation = "vertical", + height = "small", + loading = false, + disabled = false, + ...props +}) => { + if (disabled) return null; + + return ( + + {title} + + data={data} + height={orientation === "vertical" ? 247 : 164} + loading={loading} + renderItem={(item, index) => ( + { + if (item.Type === "Series") + router.push(`/series/${item.Id}/page`); + else if (item.Type === "CollectionFolder") + router.push(`/collections/${item.Id}/page`); + else router.push(`/items/${item.Id}/page`); + }} + className={`flex flex-col + ${orientation === "vertical" ? "w-32" : "w-48"} + `} + > + + {orientation === "vertical" ? ( + + ) : ( + + )} + + + + )} + /> + + ); +}; diff --git a/components/settings/SettingToggles.tsx b/components/settings/SettingToggles.tsx index 64fa083f..cc066d29 100644 --- a/components/settings/SettingToggles.tsx +++ b/components/settings/SettingToggles.tsx @@ -1,4 +1,4 @@ -import { Switch, View } from "react-native"; +import { Linking, Switch, TouchableOpacity, View } from "react-native"; import { Text } from "../common/Text"; import { useAtom } from "jotai"; import { useSettings } from "@/utils/atoms/settings"; @@ -9,14 +9,26 @@ export const SettingToggles: React.FC = () => { return ( - Auto rotate + + Auto rotate + + Important on android since the video player orientation is locked to + the app orientation. + + updateSettings({ autoRotate: value })} /> - Start videos in fullscreen + + Start videos in fullscreen + + Clicking a video will start it in fullscreen mode, instead of + inline. + + @@ -24,6 +36,25 @@ export const SettingToggles: React.FC = () => { } /> + + + Use popular lists plugin + Made by: lostb1t + { + Linking.openURL( + "https://github.com/lostb1t/jellyfin-plugin-media-lists", + ); + }} + > + More info + + + updateSettings({ usePopularPlugin: value })} + /> + ); };