From aacf688b1f3c227cec3c27c9f5439166c4f9711c Mon Sep 17 00:00:00 2001 From: Violet Caulfield Date: Tue, 14 Jan 2025 07:10:58 -0600 Subject: [PATCH] Moar hooks in the player provider to unify controls and feedback --- components/Global/components/track.tsx | 4 +- components/Home/helpers/recently-played.tsx | 4 +- components/Player/mini-player.tsx | 5 +- components/Player/screens/index.tsx | 11 ++- components/Player/screens/queue.tsx | 5 +- player/provider.tsx | 77 ++++++++++++++++++--- 6 files changed, 82 insertions(+), 24 deletions(-) diff --git a/components/Global/components/track.tsx b/components/Global/components/track.tsx index f5b2b8f4..6004e775 100644 --- a/components/Global/components/track.tsx +++ b/components/Global/components/track.tsx @@ -30,7 +30,7 @@ export default function Track({ onPress?: () => void | undefined }) : React.JSX.Element { - const { nowPlaying, playNewQueue } = usePlayerContext(); + const { nowPlaying, usePlayNewQueue } = usePlayerContext(); const isPlaying = nowPlaying?.ItemId === track.Id @@ -43,7 +43,7 @@ export default function Track({ if (onPress) { onPress(); } else { - playNewQueue.mutate({ + usePlayNewQueue.mutate({ track, index, tracklist, diff --git a/components/Home/helpers/recently-played.tsx b/components/Home/helpers/recently-played.tsx index cafa1f39..5421617a 100644 --- a/components/Home/helpers/recently-played.tsx +++ b/components/Home/helpers/recently-played.tsx @@ -8,7 +8,7 @@ import { usePlayerContext } from "../../../player/provider"; export default function RecentlyPlayed(): React.JSX.Element { - const { playNewQueue } = usePlayerContext(); + const { usePlayNewQueue } = usePlayerContext(); const { apiClient, sessionId } = useApiClientContext(); const { recentTracks } = useHomeContext(); @@ -25,7 +25,7 @@ export default function RecentlyPlayed(): React.JSX.Element { width={150} itemId={recentlyPlayedTrack.AlbumId!} onPress={() => { - playNewQueue.mutate({ + usePlayNewQueue.mutate({ track: recentlyPlayedTrack, index: index, tracklist: recentTracks, diff --git a/components/Player/mini-player.tsx b/components/Player/mini-player.tsx index b893bcf6..75265bbc 100644 --- a/components/Player/mini-player.tsx +++ b/components/Player/mini-player.tsx @@ -15,12 +15,11 @@ import { queryConfig } from "../../api/queries/query.config"; import { useApiClientContext } from "../jellyfin-api-provider"; import TextTicker from 'react-native-text-ticker'; import PlayPauseButton from "./helpers/buttons"; -import { skipToNext } from "react-native-track-player/lib/src/trackPlayer"; import { useSafeAreaFrame } from "react-native-safe-area-context"; export function Miniplayer({ navigation }: { navigation : NavigationHelpers }) : React.JSX.Element { - const { nowPlaying } = usePlayerContext(); + const { nowPlaying, useSkip } = usePlayerContext(); const { apiClient } = useApiClientContext(); @@ -88,7 +87,7 @@ export function Miniplayer({ navigation }: { navigation : NavigationHelpers skipToNext()} + onPress={() => useSkip.mutate(undefined)} /> diff --git a/components/Player/screens/index.tsx b/components/Player/screens/index.tsx index 727febb6..c34f90e4 100644 --- a/components/Player/screens/index.tsx +++ b/components/Player/screens/index.tsx @@ -10,7 +10,6 @@ import { getImageApi } from "@jellyfin/sdk/lib/utils/api"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import React, { useState, useEffect } from "react"; import { SafeAreaView, useSafeAreaFrame } from "react-native-safe-area-context"; -import { seekBy, skipToPrevious, skipToNext } from "react-native-track-player/lib/src/trackPlayer"; import { YStack, XStack, Spacer } from "tamagui"; import PlayPauseButton from "../helpers/buttons"; import { H5, Text } from "@/components/Global/helpers/text"; @@ -22,7 +21,7 @@ export default function PlayerScreen({ navigation }: { navigation: NativeStackNa const { apiClient } = useApiClientContext(); - const { playbackState, nowPlaying, progress, useSeekTo, queueName } = usePlayerContext(); + const { playbackState, nowPlaying, progress, useSeekTo, useSkip, usePrevious, queueName } = usePlayerContext(); const [seeking, setSeeking] = useState(false); const [progressState, setProgressState] = useState(progress!.position); @@ -160,13 +159,13 @@ export default function PlayerScreen({ navigation }: { navigation: NativeStackNa seekBy(-15)} + onPress={() => useSeekTo.mutate(progress!.position - 15)} /> skipToPrevious()} + onPress={() => usePrevious.mutate()} /> @@ -174,12 +173,12 @@ export default function PlayerScreen({ navigation }: { navigation: NativeStackNa skipToNext()} + onPress={() => useSkip.mutate(undefined)} /> seekBy(15)} + onPress={() => useSeekTo.mutate(progress!.position + 15)} /> diff --git a/components/Player/screens/queue.tsx b/components/Player/screens/queue.tsx index 424e56a9..f36c4edc 100644 --- a/components/Player/screens/queue.tsx +++ b/components/Player/screens/queue.tsx @@ -2,11 +2,10 @@ import Track from "@/components/Global/components/track"; import { usePlayerContext } from "@/player/provider"; import { FlatList } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; -import { skip } from "react-native-track-player/lib/src/trackPlayer"; export default function Queue(): React.JSX.Element { - const { queue } = usePlayerContext(); + const { queue, useSkip } = usePlayerContext(); return ( @@ -21,7 +20,7 @@ export default function Queue(): React.JSX.Element { index={index} showArtwork onPress={() => { - skip(index); + useSkip.mutate(index); }} /> ) diff --git a/player/provider.tsx b/player/provider.tsx index 9fc5b752..38c8717d 100644 --- a/player/provider.tsx +++ b/player/provider.tsx @@ -17,7 +17,7 @@ import { QueueMutation } from "./interfaces"; import { mapDtoToTrack } from "@/helpers/mappings"; import { QueuingType } from "@/enums/queuing-type"; import { trigger } from "react-native-haptic-feedback"; -import { pause, seekTo } from "react-native-track-player/lib/src/trackPlayer"; +import { pause, seekTo, skip, skipToNext, skipToPrevious } from "react-native-track-player/lib/src/trackPlayer"; import { convertRunTimeTicksToSeconds } from "@/helpers/runtimeticks"; interface PlayerContext { @@ -30,7 +30,9 @@ interface PlayerContext { queueName: string | undefined; useTogglePlayback: UseMutationResult; useSeekTo: UseMutationResult; - playNewQueue: UseMutationResult; + useSkip: UseMutationResult; + usePrevious: UseMutationResult; + usePlayNewQueue: UseMutationResult; playbackState: State | undefined; progress: Progress | undefined; } @@ -97,7 +99,7 @@ const PlayerContextInitializer = () => { trigger('impactLight'); await seekTo(position); - await handlePlaybackProgressUpdated(sessionId, playStateApi, nowPlaying!, { + handlePlaybackProgressUpdated(sessionId, playStateApi, nowPlaying!, { buffered: 0, position, duration: convertRunTimeTicksToSeconds(nowPlaying!.duration!) @@ -105,7 +107,24 @@ const PlayerContextInitializer = () => { } }); - const playNewQueue = useMutation({ + const useSkip = useMutation({ + mutationFn: async (index?: number) => { + trigger("impactLight") + if (index) + skip(index) + else + skipToNext(); + } + }); + + const usePrevious = useMutation({ + mutationFn: async () => { + trigger("impactLight") + await skipToPrevious(); + } + }) + + const usePlayNewQueue = useMutation({ mutationFn: async (mutation: QueueMutation) => { trigger("impactLight"); await resetQueue(false); @@ -184,7 +203,9 @@ const PlayerContextInitializer = () => { queueName, useTogglePlayback, useSeekTo, - playNewQueue, + useSkip, + usePrevious, + usePlayNewQueue, playbackState, progress, } @@ -236,7 +257,43 @@ export const PlayerContext = createContext({ failureReason: null, submittedAt: 0 }, - playNewQueue: { + useSkip: { + mutate: () => {}, + mutateAsync: async () => {}, + data: undefined, + error: null, + variables: undefined, + isError: false, + isIdle: true, + isPaused: false, + isPending: false, + isSuccess: false, + status: "idle", + reset: () => {}, + context: {}, + failureCount: 0, + failureReason: null, + submittedAt: 0 + }, + usePrevious: { + mutate: () => {}, + mutateAsync: async () => {}, + data: undefined, + error: null, + variables: undefined, + isError: false, + isIdle: true, + isPaused: false, + isPending: false, + isSuccess: false, + status: "idle", + reset: () => {}, + context: {}, + failureCount: 0, + failureReason: null, + submittedAt: 0 + }, + usePlayNewQueue: { mutate: () => {}, mutateAsync: async () => {}, data: undefined, @@ -270,7 +327,9 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS queueName, useTogglePlayback, useSeekTo, - playNewQueue, + useSkip, + usePrevious, + usePlayNewQueue, playbackState, progress } = PlayerContextInitializer(); @@ -285,7 +344,9 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS queueName, useTogglePlayback, useSeekTo, - playNewQueue, + useSkip, + usePrevious, + usePlayNewQueue, playbackState, progress }}>