From 469e8b3f0148d0288411da6efe24ef8507190b26 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Tue, 20 Aug 2024 08:24:05 +0200 Subject: [PATCH] refactor: playing state --- app/(auth)/items/[id].tsx | 7 +- app/_layout.tsx | 189 +++++++++++++------------- components/CurrentlyPlayingBar.tsx | 211 +++++++++-------------------- components/PlayButton.tsx | 56 +++++--- providers/PlaybackProvider.tsx | 193 ++++++++++++++++++++++++++ 5 files changed, 392 insertions(+), 264 deletions(-) create mode 100644 providers/PlaybackProvider.tsx diff --git a/app/(auth)/items/[id].tsx b/app/(auth)/items/[id].tsx index a205d4f9..86b88913 100644 --- a/app/(auth)/items/[id].tsx +++ b/app/(auth)/items/[id].tsx @@ -280,12 +280,7 @@ const page: React.FC = () => { - + diff --git a/app/_layout.tsx b/app/_layout.tsx index 19150605..487f4f39 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -17,6 +17,7 @@ import { useKeepAwake } from "expo-keep-awake"; import { useSettings } from "@/utils/atoms/settings"; import { GestureHandlerRootView } from "react-native-gesture-handler"; import { BottomSheetModalProvider } from "@gorhom/bottom-sheet"; +import { PlaybackProvider } from "@/providers/PlaybackProvider"; // Prevent the splash screen from auto-hiding before asset loading is complete. SplashScreen.preventAutoHideAsync(); @@ -82,99 +83,101 @@ function Layout() { - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/components/CurrentlyPlayingBar.tsx b/components/CurrentlyPlayingBar.tsx index 911cb81e..90b81c73 100644 --- a/components/CurrentlyPlayingBar.tsx +++ b/components/CurrentlyPlayingBar.tsx @@ -17,7 +17,14 @@ import { useQuery, useQueryClient } from "@tanstack/react-query"; import { BlurView } from "expo-blur"; import { useRouter, useSegments } from "expo-router"; import { useAtom } from "jotai"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import { Alert, Platform, TouchableOpacity, View } from "react-native"; import Animated, { useAnimatedStyle, @@ -27,22 +34,24 @@ import Animated, { import Video, { OnProgressData, VideoRef } from "react-native-video"; import { Text } from "./common/Text"; import { Loader } from "./Loader"; +import { usePlayback } from "@/providers/PlaybackProvider"; export const CurrentlyPlayingBar: React.FC = () => { const segments = useSegments(); - const queryClient = useQueryClient(); + const { + currentlyPlaying, + pauseVideo, + playVideo, + setCurrentlyPlayingState, + stopVideo, + setIsPlaying, + isPlaying, + videoRef, + onProgress, + } = usePlayback(); const [api] = useAtom(apiAtom); const [user] = useAtom(userAtom); - const [playing, setPlaying] = useAtom(playingAtom); - const [currentlyPlaying, setCurrentlyPlaying] = useAtom( - currentlyPlayingItemAtom - ); - const [fullScreen, setFullScreen] = useAtom(fullScreenAtom); - const [show, setShow] = useAtom(showCurrentlyPlayingBarAtom); - - const videoRef = useRef(null); - const [progress, setProgress] = useState(0); const aBottom = useSharedValue(0); const aPadding = useSharedValue(0); @@ -90,124 +99,28 @@ export const CurrentlyPlayingBar: React.FC = () => { } }, [segments]); - const { data: item } = useQuery({ - queryKey: ["item", currentlyPlaying?.item.Id], - queryFn: async () => - await getUserItemData({ - api, - userId: user?.Id, - itemId: currentlyPlaying?.item.Id, - }), - enabled: !!currentlyPlaying?.item.Id && !!api, - staleTime: 60, - }); - - const { data: sessionData } = useQuery({ - queryKey: ["sessionData", currentlyPlaying?.item.Id], - queryFn: async () => { - if (!currentlyPlaying?.item.Id) return null; - const playbackData = await getMediaInfoApi(api!).getPlaybackInfo({ - itemId: currentlyPlaying?.item.Id, - userId: user?.Id, - }); - return playbackData.data; - }, - enabled: !!currentlyPlaying?.item.Id && !!api && !!user?.Id, - staleTime: 0, - }); - - const onProgress = useCallback( - ({ currentTime }: OnProgressData) => { - if ( - !sessionData?.PlaySessionId || - !api || - !currentlyPlaying?.item.Id || - !user?.Id || - !currentTime - ) { - return; - } - const newProgress = currentTime * 10000000; - setProgress(newProgress); - - reportPlaybackProgress({ - api, - itemId: currentlyPlaying?.item.Id, - positionTicks: newProgress, - sessionId: sessionData.PlaySessionId, - IsPaused: !playing, - }); - - queryClient.invalidateQueries({ - queryKey: ["nextUp", item?.SeriesId], - refetchType: "all", - }); - queryClient.invalidateQueries({ - queryKey: ["episodes"], - refetchType: "all", - }); - }, - [sessionData?.PlaySessionId, api, playing, currentlyPlaying?.item.Id] - ); - - useEffect(() => { - if (!item || !api) return; - - if (playing) { - videoRef.current?.resume(); - } else { - videoRef.current?.pause(); - - queryClient.invalidateQueries({ - queryKey: ["nextUp", item?.SeriesId], - refetchType: "all", - }); - queryClient.invalidateQueries({ - queryKey: ["episodes"], - refetchType: "all", - }); - } - }, [playing, progress, item, sessionData]); - - useEffect(() => { - if (fullScreen === true) { - videoRef.current?.presentFullscreenPlayer(); - } else { - videoRef.current?.dismissFullscreenPlayer(); - } - }, [fullScreen]); - - useEffect(() => { - if (!show && currentlyPlaying && item && sessionData && api) { - reportPlaybackStopped({ - api, - itemId: item?.Id, - sessionId: sessionData?.PlaySessionId, - positionTicks: progress, - }); - } - }, [show]); - const startPosition = useMemo( () => - item?.UserData?.PlaybackPositionTicks - ? Math.round(item.UserData.PlaybackPositionTicks / 10000) + currentlyPlaying?.item?.UserData?.PlaybackPositionTicks + ? Math.round( + currentlyPlaying?.item.UserData.PlaybackPositionTicks / 10000 + ) : 0, - [item] + [currentlyPlaying?.item] ); const backdropUrl = useMemo( () => getBackdropUrl({ api, - item, + item: currentlyPlaying?.item, quality: 70, width: 200, }), - [item] + [currentlyPlaying?.item, api] ); - if (show === false || !api) return null; + if (!api || !currentlyPlaying) return null; return ( { videoRef.current?.presentFullscreenPlayer(); }} className={`relative h-full bg-neutral-800 rounded-md overflow-hidden - ${item?.Type === "Audio" ? "aspect-square" : "aspect-video"} + ${ + currentlyPlaying.item?.Type === "Audio" + ? "aspect-square" + : "aspect-video" + } `} > - {currentlyPlaying?.playbackUrl && ( + {currentlyPlaying?.url && (