Moar hooks in the player provider to unify controls and feedback

This commit is contained in:
Violet Caulfield
2025-01-14 07:10:58 -06:00
parent 75bf5ef9f5
commit aacf688b1f
6 changed files with 82 additions and 24 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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<ParamListBase, BottomTabNavigationEventMap> }) : React.JSX.Element {
const { nowPlaying } = usePlayerContext();
const { nowPlaying, useSkip } = usePlayerContext();
const { apiClient } = useApiClientContext();
@@ -88,7 +87,7 @@ export function Miniplayer({ navigation }: { navigation : NavigationHelpers<Para
<Icon
large
name="skip-next"
onPress={() => skipToNext()}
onPress={() => useSkip.mutate(undefined)}
/>
</XStack>
</XStack>

View File

@@ -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<boolean>(false);
const [progressState, setProgressState] = useState<number>(progress!.position);
@@ -160,13 +159,13 @@ export default function PlayerScreen({ navigation }: { navigation: NativeStackNa
<XStack justifyContent="space-evenly" marginVertical={"$3"}>
<Icon
name="rewind-15"
onPress={() => seekBy(-15)}
onPress={() => useSeekTo.mutate(progress!.position - 15)}
/>
<Icon
large
name="skip-previous"
onPress={() => skipToPrevious()}
onPress={() => usePrevious.mutate()}
/>
<PlayPauseButton />
@@ -174,12 +173,12 @@ export default function PlayerScreen({ navigation }: { navigation: NativeStackNa
<Icon
large
name="skip-next"
onPress={() => skipToNext()}
onPress={() => useSkip.mutate(undefined)}
/>
<Icon
name="fast-forward-15"
onPress={() => seekBy(15)}
onPress={() => useSeekTo.mutate(progress!.position + 15)}
/>
</XStack>

View File

@@ -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 (
<SafeAreaView edges={["right", "left"]}>
@@ -21,7 +20,7 @@ export default function Queue(): React.JSX.Element {
index={index}
showArtwork
onPress={() => {
skip(index);
useSkip.mutate(index);
}}
/>
)

View File

@@ -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<void, Error, number | undefined, unknown>;
useSeekTo: UseMutationResult<void, Error, number, unknown>;
playNewQueue: UseMutationResult<void, Error, QueueMutation, unknown>;
useSkip: UseMutationResult<void, Error, number | undefined, unknown>;
usePrevious: UseMutationResult<void, Error, void, unknown>;
usePlayNewQueue: UseMutationResult<void, Error, QueueMutation, unknown>;
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<PlayerContext>({
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
}}>