Rework toggleplayback, add haptic feedback

This commit is contained in:
Violet Caulfield
2025-01-10 07:13:21 -06:00
parent a224c4b444
commit 26e774d036
6 changed files with 68 additions and 42 deletions

View File

@@ -9,20 +9,20 @@ import { ImageType } from "@jellyfin/sdk/lib/generated-client/models";
import { queryConfig } from "../../api/queries/query.config";
import { Text } from "../Global/helpers/text";
import { SafeAreaView } from "react-native-safe-area-context";
import { playPauseButton } from "./helpers/buttons";
import { BottomTabNavigationEventMap } from "@react-navigation/bottom-tabs";
import { NavigationHelpers, ParamListBase } from "@react-navigation/native";
import { HorizontalSlider } from "../Global/helpers/slider";
import PlayPauseButton from "./helpers/buttons";
export default function Player({ navigation }: { navigation : NavigationHelpers<ParamListBase, BottomTabNavigationEventMap> }): React.JSX.Element {
const { apiClient } = useApiClientContext();
const { queue, playbackState, nowPlaying, play, pause, progress } = usePlayerContext();
const { queue, playbackState, nowPlaying, useTogglePlayback, progress } = usePlayerContext();
return (
<SafeAreaView>
{ nowPlaying && (
<YStack alignItems="center">
<YStack>
<XStack alignItems="center">
@@ -45,7 +45,6 @@ export default function Player({ navigation }: { navigation : NavigationHelpers<
<XStack
marginVertical={10}
flex={1}
>
<YStack justifyContent="flex-start" flex={4}>
@@ -63,14 +62,14 @@ export default function Player({ navigation }: { navigation : NavigationHelpers<
{nowPlaying.artist ?? "Unknown Artist"}</Text>
</YStack>
<XStack alignItems="center">
<XStack alignItems="center" flex={1}>
{/* Buttons for favorites, song menu go here */}
</XStack>
</XStack>
<XStack>
<XStack alignItems="center">
{/* playback progress goes here */}
<HorizontalSlider
value={progress!.position}
@@ -80,12 +79,12 @@ export default function Player({ navigation }: { navigation : NavigationHelpers<
</XStack>
<XStack>
{playPauseButton(playbackState, play, pause)}
<XStack alignItems="center">
<PlayPauseButton />
</XStack>
</YStack>
</YStack>
)}
</SafeAreaView>
);

View File

@@ -2,14 +2,17 @@ import { State } from "react-native-track-player";
import { Colors } from "react-native/Libraries/NewAppScreen";
import { Spinner } from "tamagui";
import Icon from "../../Global/helpers/icon";
import { usePlayerContext } from "@/player/provider";
export function playPauseButton(playbackState: State | undefined, play: Function, pause: Function) {
export default function PlayPauseButton() : React.JSX.Element {
const { playbackState, useTogglePlayback } = usePlayerContext();
let button : React.JSX.Element;
switch (playbackState) {
case (State.Playing) : {
button = <Icon name="pause" large onPress={() => pause()} />;
button = <Icon name="pause" large onPress={() => useTogglePlayback.mutate(undefined)} />;
break;
}
@@ -20,7 +23,7 @@ export function playPauseButton(playbackState: State | undefined, play: Function
}
default : {
button = <Icon name="play" large onPress={() => play()} />
button = <Icon name="play" large onPress={() => useTogglePlayback.mutate(undefined)} />
break;
}
}

View File

@@ -14,12 +14,12 @@ import { getImageApi } from "@jellyfin/sdk/lib/utils/api";
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 PlayPauseButton from "./helpers/buttons";
import { skipToNext } from "react-native-track-player/lib/src/trackPlayer";
export function Miniplayer({ navigation }: { navigation : NavigationHelpers<ParamListBase, BottomTabNavigationEventMap> }) : React.JSX.Element {
const { nowPlaying, playbackState, play, pause } = usePlayerContext();
const { nowPlaying } = usePlayerContext();
const { apiClient } = useApiClientContext();
@@ -77,7 +77,7 @@ export function Miniplayer({ navigation }: { navigation : NavigationHelpers<Para
</YStack>
<XStack flex={2}>
{ playPauseButton(playbackState, play, pause) }
<PlayPauseButton />
<Icon
large

13
package-lock.json generated
View File

@@ -37,6 +37,7 @@
"react-native-device-info": "^11.1.0",
"react-native-file-access": "^3.1.1",
"react-native-gesture-handler": "^2.20.0",
"react-native-haptic-feedback": "^2.3.3",
"react-native-mmkv": "2.12.2",
"react-native-reanimated": "^3.16.3",
"react-native-safe-area-context": "^4.11.1",
@@ -20447,6 +20448,18 @@
"react-native": "*"
}
},
"node_modules/react-native-haptic-feedback": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/react-native-haptic-feedback/-/react-native-haptic-feedback-2.3.3.tgz",
"integrity": "sha512-svS4D5PxfNv8o68m9ahWfwje5NqukM3qLS48+WTdhbDkNUkOhP9rDfDSRHzlhk4zq+ISjyw95EhLeh8NkKX5vQ==",
"license": "MIT",
"workspaces": [
"example"
],
"peerDependencies": {
"react-native": ">=0.60.0"
}
},
"node_modules/react-native-mmkv": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/react-native-mmkv/-/react-native-mmkv-2.12.2.tgz",

View File

@@ -39,6 +39,7 @@
"react-native-device-info": "^11.1.0",
"react-native-file-access": "^3.1.1",
"react-native-gesture-handler": "^2.20.0",
"react-native-haptic-feedback": "^2.3.3",
"react-native-mmkv": "2.12.2",
"react-native-reanimated": "^3.16.3",
"react-native-safe-area-context": "^4.11.1",

View File

@@ -16,6 +16,8 @@ import { useMutation, UseMutationResult } from "@tanstack/react-query";
import { QueueMutation } from "./interfaces";
import { mapDtoToTrack } from "@/helpers/mappings";
import { QueuingType } from "@/enums/queuing-type";
import { trigger } from "react-native-haptic-feedback";
import { pause } from "react-native-track-player/lib/src/trackPlayer";
interface PlayerContext {
showPlayer: boolean;
@@ -24,10 +26,7 @@ interface PlayerContext {
setShowMiniplayer: React.Dispatch<SetStateAction<boolean>>;
nowPlaying: JellifyTrack | undefined;
queue: JellifyTrack[];
play: (index?: number | undefined) => Promise<void>,
pause: () => Promise<void>,
resetQueue: (hideMiniplayer : boolean | undefined) => Promise<void>;
addToQueue: (tracks: JellifyTrack[]) => Promise<void>;
useTogglePlayback: UseMutationResult<void, Error, number | undefined, unknown>;
playNewQueue: UseMutationResult<void, Error, QueueMutation, unknown>;
playbackState: State | undefined;
progress: Progress | undefined;
@@ -56,13 +55,6 @@ const PlayerContextInitializer = () => {
TrackPlayer.skip(index)
TrackPlayer.play();
handlePlaybackStarted(sessionId, playStateApi, nowPlaying!)
}
const pause = async () => {
TrackPlayer.pause();
handlePlaybackStopped(sessionId, playStateApi, nowPlaying!);
}
const resetQueue = async (hideMiniplayer?: boolean | undefined) => {
@@ -86,8 +78,19 @@ const PlayerContextInitializer = () => {
//#endregion Functions
//#region Hooks
const useTogglePlayback = useMutation({
mutationFn: async (index?: number | undefined) => {
trigger("impactLight");
if (playbackState === State.Playing)
await pause();
else
await play(index);
}
})
const playNewQueue = useMutation({
mutationFn: async (mutation: QueueMutation) => {
trigger("impactLight");
await resetQueue(false)
await addToQueue(mutation.tracklist.map((track) => {
return mapDtoToTrack(apiClient!, sessionId, track, QueuingType.FromSelection)
@@ -160,10 +163,7 @@ const PlayerContextInitializer = () => {
setShowMiniplayer,
nowPlaying,
queue,
play,
pause,
addToQueue,
resetQueue,
useTogglePlayback,
playNewQueue,
playbackState,
progress,
@@ -171,6 +171,7 @@ const PlayerContextInitializer = () => {
//#endregion return
}
//#region Create PlayerContext
export const PlayerContext = createContext<PlayerContext>({
showPlayer: false,
setShowPlayer: () => {},
@@ -178,10 +179,24 @@ export const PlayerContext = createContext<PlayerContext>({
setShowMiniplayer: () => {},
nowPlaying: undefined,
queue: [],
play: async (index?: number | undefined) => {},
pause: async () => {},
resetQueue: async () => {},
addToQueue: async ([]) => {},
useTogglePlayback: {
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
},
playNewQueue: {
mutate: () => {},
mutateAsync: async () => {},
@@ -203,6 +218,7 @@ export const PlayerContext = createContext<PlayerContext>({
playbackState: undefined,
progress: undefined,
});
//#endregion Create PlayerContext
export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JSX.Element = ({ children }: { children: ReactNode }) => {
const {
@@ -212,10 +228,7 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS
setShowMiniplayer,
nowPlaying,
queue,
play,
pause,
resetQueue,
addToQueue,
useTogglePlayback,
playNewQueue,
playbackState,
progress
@@ -228,10 +241,7 @@ export const PlayerProvider: ({ children }: { children: ReactNode }) => React.JS
setShowMiniplayer,
nowPlaying,
queue,
play,
pause,
resetQueue,
addToQueue,
useTogglePlayback,
playNewQueue,
playbackState,
progress