chore:- track-player-hook (#907)

Co-authored-by: riteshshukla04 <riteshshukla2381@gmail.com>
This commit is contained in:
Violet Caulfield
2026-01-10 05:50:56 -06:00
committed by GitHub
parent 6164f07195
commit 066fc3bc70
17 changed files with 176 additions and 107 deletions
+2 -3
View File
@@ -1,6 +1,5 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "jellify",
@@ -47,7 +46,7 @@
"react-native-nitro-fetch": "^0.1.6",
"react-native-nitro-modules": "^0.32.1",
"react-native-nitro-ota": "^0.10.0",
"react-native-nitro-player": "0.3.0-alpha.7",
"react-native-nitro-player": "0.3.0-alpha.9",
"react-native-pager-view": "8.0.0",
"react-native-reanimated": "4.1.6",
"react-native-safe-area-context": "5.6.2",
@@ -1933,7 +1932,7 @@
"react-native-nitro-ota": ["react-native-nitro-ota@0.10.0", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "0.32.0" } }, "sha512-pxmdaeNdUdnYdD1M8BpbtQo4mZrtljWFg0gspuIohTJqi97JYIRq0b+SReN0sMMo0w912k4XXSGMr/IduGoMNg=="],
"react-native-nitro-player": ["react-native-nitro-player@0.3.0-alpha.7", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }, "sha512-zyfd9zSWCpaac88PDnRK3wHhODgJKpBw5P0ttiDDtP/VUlTxAMSZLHjY7SaqoZuqY9TSJQcMKo+gaaJJJuzeow=="],
"react-native-nitro-player": ["react-native-nitro-player@0.3.0-alpha.9", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }, "sha512-R/+JmILdYFfqMJB+rm+ihmdMIQuI371aeK+nBxHc60l4XHBGonqWcpnTkZ+CZNec8dYEHlVwpTAlQ0EMLOP8dg=="],
"react-native-pager-view": ["react-native-pager-view@8.0.0", "", { "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-oAwlWT1lhTkIs9HhODnjNNl/owxzn9DP1MbP+az6OTUdgbmzA16Up83sBH8NRKwrH8rNm7iuWnX1qMqiiWOLhg=="],
+3 -3
View File
@@ -139,7 +139,7 @@ PODS:
- SSZipArchive
- Yoga
- NitroOtaBundleManager (0.10.0)
- NitroPlayer (0.3.0-alpha.7):
- NitroPlayer (0.3.0-alpha.8):
- boost
- DoubleConversion
- fast_float
@@ -3596,14 +3596,14 @@ SPEC CHECKSUMS:
Gifu: 9f7e52357d41c0739709019eb80a71ad9aab1b6d
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
google-cast-sdk: 32f65af50d164e3c475e79ad123db3cc26fbcd37
hermes-engine: 83ac7cadb2a3a158ae6d9e4192417c5232065e99
hermes-engine: 6878b8fefe82b91b24caae48ac97164746244d09
MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df
NitroFetch: 660adfb47f84b28db664f97b50e5dc28506ab6c1
NitroMmkv: 8ed7ef6f41b91785fc580c975f68d6d675214767
NitroModules: bbd7f9f8913dc8af187349463e4368a1144d6e31
NitroOta: 92d4eb528566b6babf5e4a30adbda44bfa803a9b
NitroOtaBundleManager: 8fad871db2daf6b9ee6f04a100c79605cfa81e8d
NitroPlayer: 797de080a522568436d073089f41ddca718d4883
NitroPlayer: 8251782cd9a1c8c3ed3e6b9a0baa02eedf159454
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
RCTDeprecation: a41bbdd9af30bf2e5715796b313e44ec43eefff1
+1 -1
View File
@@ -79,7 +79,7 @@
"react-native-nitro-fetch": "^0.1.6",
"react-native-nitro-modules": "^0.32.1",
"react-native-nitro-ota": "^0.10.0",
"react-native-nitro-player": "0.3.0-alpha.7",
"react-native-nitro-player": "0.3.0-alpha.9",
"react-native-pager-view": "8.0.0",
"react-native-reanimated": "4.1.6",
"react-native-safe-area-context": "5.6.2",
+10 -2
View File
@@ -3,7 +3,9 @@ import LyricsQueryKey from './keys'
import { isUndefined } from 'lodash'
import { fetchRawLyrics } from './utils'
import { useApi } from '../../../stores'
import { useCurrentTrack } from '../../../stores/player/queue'
import { usePlayerQueueStore } from '../../../stores/player/queue'
import { useNowPlaying } from 'react-native-nitro-player'
import JellifyTrack from '../../../types/JellifyTrack'
/**
* A hook that will return a {@link useQuery}
@@ -12,7 +14,13 @@ import { useCurrentTrack } from '../../../stores/player/queue'
*/
const useRawLyrics = () => {
const api = useApi()
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
const currentTrack = playerState.currentTrack
const queue = usePlayerQueueStore((state) => state.queue)
// Find the full JellifyTrack in the queue by ID
const nowPlaying = currentTrack
? ((queue.find((t) => t.id === currentTrack.id) as JellifyTrack | undefined) ?? undefined)
: undefined
return useQuery({
queryKey: LyricsQueryKey(nowPlaying),
@@ -4,11 +4,19 @@ import { useWindowDimensions } from 'react-native'
import LinearGradient from 'react-native-linear-gradient'
import { getBlurhashFromDto } from '../../../utils/parsing/blurhash'
import { Blurhash } from 'react-native-blurhash'
import { useCurrentTrack } from '../../../stores/player/queue'
import { usePlayerQueueStore } from '../../../stores/player/queue'
import { useNowPlaying } from 'react-native-nitro-player'
import JellifyTrack from '../../../types/JellifyTrack'
import useIsLightMode from '../../../hooks/use-is-light-mode'
export default function BlurredBackground(): React.JSX.Element {
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
const currentTrack = playerState.currentTrack
const queue = usePlayerQueueStore((state) => state.queue)
// Find the full JellifyTrack in the queue by ID
const nowPlaying = currentTrack
? ((queue.find((t) => t.id === currentTrack.id) as JellifyTrack | undefined) ?? undefined)
: undefined
const { width, height } = useWindowDimensions()
+24 -11
View File
@@ -5,6 +5,7 @@ import { useTogglePlayback } from '../../../hooks/player/callbacks'
import { usePlaybackState } from '../../../hooks/player/queries'
import React from 'react'
import Icon from '../../Global/components/icon'
import { TrackPlayer, useNowPlaying, useOnPlaybackStateChange } from 'react-native-nitro-player'
export default function PlayPauseButton({
size,
@@ -14,11 +15,12 @@ export default function PlayPauseButton({
flex?: number | undefined
}): React.JSX.Element {
const togglePlayback = useTogglePlayback()
const playerState = useNowPlaying()
const state = playerState.currentState
const PlaybackState = useOnPlaybackStateChange()
const state = usePlaybackState()
const handlePlaybackToggle = async () => await togglePlayback(state)
const handlePlaybackToggle = async () => await togglePlayback(PlaybackState.state)
console.log('from PlayPauseButton', state)
const largeIcon = isUndefined(size) || size >= 24
return (
@@ -32,9 +34,15 @@ export default function PlayPauseButton({
circular
largeIcon={largeIcon}
size={size}
name={state === 'playing' ? 'pause' : 'play'}
name={PlaybackState.state === 'playing' ? 'pause' : 'play'}
testID='play-button-test-id'
onPress={handlePlaybackToggle}
onPress={async () => {
if (TrackPlayer.getState().currentState === 'playing') {
TrackPlayer.pause()
} else {
TrackPlayer.play()
}
}}
/>
)}
</View>
@@ -42,10 +50,15 @@ export default function PlayPauseButton({
}
export function PlayPauseIcon(): React.JSX.Element {
const togglePlayback = useTogglePlayback()
const state = usePlaybackState()
const handlePlaybackToggle = async () => await togglePlayback(state)
const playerState = useNowPlaying()
const state = playerState.currentState
const togglePlayback = () => {
if (state === 'playing') {
TrackPlayer.pause()
} else {
TrackPlayer.play()
}
}
return ['stopped'].includes(state ?? 'stopped') ? (
<Spinner margin={10} color={'$primary'} />
@@ -53,7 +66,7 @@ export function PlayPauseIcon(): React.JSX.Element {
<Icon
name={state === 'playing' ? 'pause' : 'play'}
color='$primary'
onPress={handlePlaybackToggle}
onPress={togglePlayback}
/>
)
}
+10 -2
View File
@@ -10,7 +10,9 @@ import { useEffect } from 'react'
import usePlayerEngineStore from '../../../stores/player/engine'
import useRawLyrics from '../../../api/queries/lyrics'
import Animated, { Easing, FadeIn, FadeOut } from 'react-native-reanimated'
import { useCurrentTrack } from '../../../stores/player/queue'
import { usePlayerQueueStore } from '../../../stores/player/queue'
import { useNowPlaying } from 'react-native-nitro-player'
import JellifyTrack from '../../../types/JellifyTrack'
export default function Footer(): React.JSX.Element {
const navigation = useNavigation<NativeStackNavigationProp<PlayerParamList>>()
@@ -19,7 +21,13 @@ export default function Footer(): React.JSX.Element {
const remoteMediaClient = useRemoteMediaClient()
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
const currentTrack = playerState.currentTrack
const queue = usePlayerQueueStore((state) => state.queue)
// Find the full JellifyTrack in the queue by ID
const nowPlaying = currentTrack
? ((queue.find((t) => t.id === currentTrack.id) as JellifyTrack | undefined) ?? undefined)
: undefined
const { data: lyrics } = useRawLyrics()
+12 -4
View File
@@ -12,7 +12,9 @@ import Animated, {
import { LayoutChangeEvent } from 'react-native'
import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons'
import navigationRef from '../../../../navigation'
import { useCurrentTrack, useQueueRef } from '../../../stores/player/queue'
import { useQueueRef, usePlayerQueueStore } from '../../../stores/player/queue'
import { useNowPlaying } from 'react-native-nitro-player'
import JellifyTrack from '../../../types/JellifyTrack'
import TextTicker from 'react-native-text-ticker'
import { TextTickerConfig } from '../component.config'
@@ -60,7 +62,13 @@ export default function PlayerHeader(): React.JSX.Element {
}
function PlayerArtwork(): React.JSX.Element {
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
const currentTrack = playerState.currentTrack
const queue = usePlayerQueueStore((state) => state.queue)
// Find the full JellifyTrack in the queue by ID
const nowPlaying = currentTrack
? ((queue.find((t) => t.id === currentTrack.id) as JellifyTrack | undefined) ?? undefined)
: undefined
const artworkMaxHeight = useSharedValue<number>(200)
const artworkMaxWidth = useSharedValue<number>(200)
@@ -87,9 +95,9 @@ function PlayerArtwork(): React.JSX.Element {
onLayout={handleLayout}
>
{nowPlaying && (
<Animated.View key={`${nowPlaying!.item.AlbumId}-item-image`} style={animatedStyle}>
<Animated.View key={`${nowPlaying.id}-item-image`} style={animatedStyle}>
<ItemImage
item={nowPlaying!.item}
item={nowPlaying.item}
testID='player-image-test-id'
imageOptions={{ maxWidth: 800, maxHeight: 800 }}
/>
+13 -5
View File
@@ -10,14 +10,22 @@ import { useProgress } from '../../../hooks/player/queries'
import QualityBadge from './quality-badge'
import { useDisplayAudioQualityBadge } from '../../../stores/settings/player'
import useHapticFeedback from '../../../hooks/use-haptic-feedback'
import { useCurrentTrack } from '../../../stores/player/queue'
import { usePlayerQueueStore } from '../../../stores/player/queue'
import { useNowPlaying } from 'react-native-nitro-player'
import JellifyTrack from '../../../types/JellifyTrack'
// Create a simple pan gesture
const scrubGesture = Gesture.Pan()
export default function Scrubber(): React.JSX.Element {
const seekTo = useSeekTo()
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
const currentTrack = playerState.currentTrack
const queue = usePlayerQueueStore((state) => state.queue)
// Find the full JellifyTrack in the queue by ID
const nowPlaying = currentTrack
? ((queue.find((t) => t.id === currentTrack.id) as JellifyTrack | undefined) ?? undefined)
: undefined
const { width } = useSafeAreaFrame()
const trigger = useHapticFeedback()
@@ -28,7 +36,7 @@ export default function Scrubber(): React.JSX.Element {
const { position } = useProgress()
// ...instead we use the duration on the track object
const { duration } = nowPlaying!
const duration = currentTrack?.duration ?? 0
// Single source of truth for the current position
const [displayPosition, setDisplayPosition] = useState<number>(0)
@@ -60,7 +68,7 @@ export default function Scrubber(): React.JSX.Element {
// Handle track changes
useEffect(() => {
const currentTrackId = nowPlaying?.id || null
const currentTrackId = currentTrack?.id || null
if (currentTrackId !== currentTrackIdRef.current) {
// Track changed - reset position immediately
setDisplayPosition(0)
@@ -69,7 +77,7 @@ export default function Scrubber(): React.JSX.Element {
lastSeekTimeRef.current = 0
currentTrackIdRef.current = currentTrackId
}
}, [nowPlaying?.id])
}, [currentTrack?.id])
const handleSeek = async (position: number) => {
const seekTime = Math.max(0, position / ProgressMultiplier)
+23 -14
View File
@@ -17,7 +17,9 @@ import type { SharedValue } from 'react-native-reanimated'
import { runOnJS } from 'react-native-worklets'
import { usePrevious, useSkip } from '../../../hooks/player/callbacks'
import useHapticFeedback from '../../../hooks/use-haptic-feedback'
import { useCurrentTrack } from '../../../stores/player/queue'
import { usePlayerQueueStore } from '../../../stores/player/queue'
import { useNowPlaying } from 'react-native-nitro-player'
import JellifyTrack from '../../../types/JellifyTrack'
import { useApi } from '../../../stores'
import formatArtistNames from '../../../utils/formatting/artist-names'
@@ -66,21 +68,27 @@ export default function SongInfo({ swipeX }: SongInfoProps = {}): React.JSX.Elem
}
})
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
const currentTrack = playerState.currentTrack
const queue = usePlayerQueueStore((state) => state.queue)
// Find the full JellifyTrack in the queue by ID
const nowPlaying = currentTrack
? ((queue.find((t) => t.id === currentTrack.id) as JellifyTrack | undefined) ?? undefined)
: undefined
const { data: album } = useQuery({
queryKey: [QueryKeys.Album, nowPlaying!.item.AlbumId],
queryKey: [QueryKeys.Album, nowPlaying?.item.AlbumId],
queryFn: () => fetchItem(api, nowPlaying!.item.AlbumId!),
enabled: !!nowPlaying?.item.AlbumId && !!api,
})
// Memoize expensive computations
const trackTitle = nowPlaying!.title ?? 'Untitled Track'
const trackTitle = nowPlaying?.title ?? 'Untitled Track'
const { artistItems, artists } = {
artistItems: nowPlaying!.item.ArtistItems,
artistItems: nowPlaying?.item.ArtistItems,
artists: formatArtistNames(
nowPlaying!.item.ArtistItems?.map((artist) => getItemName(artist)) ?? [],
nowPlaying?.item.ArtistItems?.map((artist) => getItemName(artist)) ?? [],
),
}
@@ -110,7 +118,7 @@ export default function SongInfo({ swipeX }: SongInfoProps = {}): React.JSX.Elem
<TextTicker
{...TextTickerConfig}
style={{ height: getToken('$9') }}
key={`${nowPlaying!.item.Id}-title`}
key={`${nowPlaying?.id ?? 'no-track'}-title`}
>
<Text bold fontSize={'$6'} onPress={handleTrackPress}>
{trackTitle}
@@ -120,7 +128,7 @@ export default function SongInfo({ swipeX }: SongInfoProps = {}): React.JSX.Elem
<TextTicker
{...TextTickerConfig}
style={{ height: getToken('$8') }}
key={`${nowPlaying!.item.Id}-artist`}
key={`${nowPlaying?.id ?? 'no-track'}-artist`}
>
<Text fontSize={'$6'} color={'$color'} onPress={handleArtistPress}>
{nowPlaying?.artist ?? 'Unknown Artist'}
@@ -132,21 +140,22 @@ export default function SongInfo({ swipeX }: SongInfoProps = {}): React.JSX.Elem
<Icon
name='dots-horizontal-circle-outline'
onPress={() =>
nowPlaying &&
navigationRef.navigate('Context', {
item: nowPlaying!.item,
item: nowPlaying.item,
streamingMediaSourceInfo:
nowPlaying!.sourceType === 'stream'
? nowPlaying!.mediaSourceInfo
nowPlaying.sourceType === 'stream'
? nowPlaying.mediaSourceInfo
: undefined,
downloadedMediaSourceInfo:
nowPlaying!.sourceType === 'download'
? nowPlaying!.mediaSourceInfo
nowPlaying.sourceType === 'download'
? nowPlaying.mediaSourceInfo
: undefined,
})
}
/>
<FavoriteButton item={nowPlaying!.item} />
{nowPlaying && <FavoriteButton item={nowPlaying.item} />}
</XStack>
</XStack>
)
+6 -2
View File
@@ -21,7 +21,8 @@ import { runOnJS } from 'react-native-worklets'
import { usePrevious, useSkip } from '../../hooks/player/callbacks'
import useHapticFeedback from '../../hooks/use-haptic-feedback'
import Icon from '../Global/components/icon'
import { useCurrentTrack } from '../../stores/player/queue'
import { TrackPlayer, useNowPlaying, useOnPlaybackStateChange } from 'react-native-nitro-player'
import { usePlaybackState } from '@/src/hooks/player/queries'
export default function PlayerScreen(): React.JSX.Element {
usePerformanceMonitor('PlayerScreen', 5)
@@ -29,7 +30,10 @@ export default function PlayerScreen(): React.JSX.Element {
const skip = useSkip()
const previous = usePrevious()
const trigger = useHapticFeedback()
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
TrackPlayer.setRepeatMode('Playlist')
const nowPlaying = playerState.currentTrack
const isAndroid = Platform.OS === 'android'
+16 -6
View File
@@ -22,17 +22,27 @@ import { RootStackParamList } from '../../screens/types'
import { NativeStackNavigationProp } from '@react-navigation/native-stack'
import ItemImage from '../Global/components/image'
import { usePrevious, useSkip } from '../../hooks/player/callbacks'
import { useCurrentTrack } from '../../stores/player/queue'
import {
TrackPlayer,
useOnChangeTrack,
useOnPlaybackStateChange,
useNowPlaying,
} from 'react-native-nitro-player'
export default function Miniplayer(): React.JSX.Element {
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
const nowPlaying = playerState.currentTrack
console.log('nowPlaying', nowPlaying)
const skip = useSkip()
const previous = usePrevious()
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>()
const translateX = useSharedValue(0)
const track = useOnChangeTrack()
console.log('track', track)
const translateY = useSharedValue(0)
console.log('nowPlaying', nowPlaying)
const handleSwipe = (direction: string) => {
if (direction === 'Swiped Left') {
@@ -75,6 +85,7 @@ export default function Miniplayer(): React.JSX.Element {
const pressStyle = {
opacity: 0.6,
}
if (!nowPlaying) return null
return (
<GestureDetector gesture={gesture}>
@@ -96,14 +107,13 @@ export default function Miniplayer(): React.JSX.Element {
<Animated.View
entering={FadeIn.easing(Easing.in(Easing.ease))}
exiting={FadeOut.easing(Easing.out(Easing.ease))}
key={`${nowPlaying!.item.AlbumId}-album-image`}
>
<ItemImage
{/* <ItemImage
item={nowPlaying!.item}
width={'$11'}
height={'$11'}
imageOptions={{ maxWidth: 200, maxHeight: 200 }}
/>
/> */}
</Animated.View>
</YStack>
@@ -116,7 +126,7 @@ export default function Miniplayer(): React.JSX.Element {
<Animated.View
entering={FadeIn.easing(Easing.in(Easing.ease))}
exiting={FadeOut.easing(Easing.out(Easing.ease))}
key={`${nowPlaying!.item.Id}-mini-player-song-info`}
key={`${nowPlaying!.id}-mini-player-song-info`}
>
<TextTicker {...TextTickerConfig}>
<Text bold>{nowPlaying?.title ?? 'Nothing Playing'}</Text>
+1 -14
View File
@@ -24,7 +24,7 @@ export const useTogglePlayback = () => {
return async (state: TrackPlayerState | undefined) => {
trigger('impactMedium')
TrackPlayer.pause()
if (state === 'playing') {
if (isCasting && remoteClient) return await remoteClient.pause()
else return TrackPlayer.pause()
@@ -33,19 +33,6 @@ export const useTogglePlayback = () => {
const position = TrackPlayer.getState().currentPosition
const duration = TrackPlayer.getState().totalDuration
if (isCasting && remoteClient) {
const mediaStatus = await remoteClient.getMediaStatus()
const streamPosition = mediaStatus?.streamPosition
if (streamPosition && duration <= streamPosition) {
await remoteClient.seek({
position: 0,
resumeState: 'play',
})
}
await remoteClient.play()
return
}
// if the track has ended, seek to start and play
if (duration <= position) TrackPlayer.seek(0)
+1 -1
View File
@@ -75,7 +75,7 @@ export function loadQueue({
)
PlayerQueue.addTracksToPlaylist(playlistId, queue, finalStartIndex)
PlayerQueue.loadPlaylist(playlistId)
usePlayerQueueStore.getState().setCurrentIndex(finalStartIndex)
usePlayerQueueStore.getState().setQueueRef(queueRef)
usePlayerQueueStore.getState().setQueue(queue)
+36 -33
View File
@@ -3,14 +3,16 @@ import { PlayerEngine } from '../../stores/player/engine'
import { MediaPlayerState, useRemoteMediaClient, useStreamPosition } from 'react-native-google-cast'
import { useEffect, useState } from 'react'
import {
PlayerQueue,
TrackPlayer,
TrackPlayerState,
useNowPlaying,
useOnPlaybackProgressChange,
useOnPlaybackStateChange,
} from 'react-native-nitro-player'
export const useProgress = () => {
export const useProgress = (): { position: number; totalDuration: number } => {
const { position, totalDuration } = useOnPlaybackProgressChange()
const playerEngineData = usePlayerEngineStore((state) => state.playerEngineData)
const isCasting = playerEngineData === PlayerEngine.GOOGLE_CAST
@@ -46,43 +48,44 @@ const castToRNTPState = (state: MediaPlayerState): TrackPlayerState => {
}
export const usePlaybackState = (): TrackPlayerState | undefined => {
const { state } = useOnPlaybackStateChange()
// const { state } = useOnPlaybackStateChange()
const playerEngineData = usePlayerEngineStore((state) => state.playerEngineData)
// const playerEngineData = usePlayerEngineStore((state) => state.playerEngineData)
const client = useRemoteMediaClient()
// const client = useRemoteMediaClient()
const isCasting = playerEngineData === PlayerEngine.GOOGLE_CAST
const [playbackState, setPlaybackState] = useState<TrackPlayerState | undefined>(state)
// const isCasting = playerEngineData === PlayerEngine.GOOGLE_CAST
// const [playbackState, setPlaybackState] = useState<TrackPlayerState | undefined>(state)
useEffect(() => {
let unsubscribe: (() => void) | undefined
// useEffect(() => {
// let unsubscribe: (() => void) | undefined
if (client && isCasting) {
const handler = (status: { playerState?: MediaPlayerState | null } | null) => {
if (status?.playerState) {
setPlaybackState(castToRNTPState(status.playerState))
}
}
// if (client && isCasting) {
// const handler = (status: { playerState?: MediaPlayerState | null } | null) => {
// if (status?.playerState) {
// setPlaybackState(castToRNTPState(status.playerState))
// }
// }
const maybeUnsubscribe = client.onMediaStatusUpdated(handler)
// EmitterSubscription has a remove() method, wrap it as a function
if (
maybeUnsubscribe &&
typeof maybeUnsubscribe === 'object' &&
'remove' in maybeUnsubscribe
) {
const subscription = maybeUnsubscribe as { remove: () => void }
unsubscribe = () => subscription.remove()
}
} else {
setPlaybackState(state)
}
// const maybeUnsubscribe = client.onMediaStatusUpdated(handler)
// // EmitterSubscription has a remove() method, wrap it as a function
// if (
// maybeUnsubscribe &&
// typeof maybeUnsubscribe === 'object' &&
// 'remove' in maybeUnsubscribe
// ) {
// const subscription = maybeUnsubscribe as { remove: () => void }
// unsubscribe = () => subscription.remove()
// }
// } else {
// setPlaybackState(state)
// }
return () => {
if (unsubscribe) unsubscribe()
}
}, [client, isCasting, state])
// return () => {
// if (unsubscribe) unsubscribe()
// }
// }, [client, isCasting, state])
const playerState = useNowPlaying()
return playbackState
return playerState.currentState
}
+3 -2
View File
@@ -1,11 +1,12 @@
import useAppActive from './use-app-active'
import { useCurrentTrack } from '../stores/player/queue'
import { useIsPlayerFocused } from '../stores/player/display'
import { useNowPlaying } from 'react-native-nitro-player'
export default function useIsMiniPlayerActive(): boolean {
const isAppActive = useAppActive()
const nowPlaying = useCurrentTrack()
const playerState = useNowPlaying()
const nowPlaying = playerState.currentTrack
const isPlayerFocused = useIsPlayerFocused()
+5 -2
View File
@@ -7,7 +7,7 @@ import JellifyTrack, {
import { createVersionedMmkvStorage } from '../../constants/versioned-storage'
import { create } from 'zustand'
import { devtools, persist, PersistStorage, StorageValue } from 'zustand/middleware'
import { RepeatMode } from 'react-native-nitro-player'
import { RepeatMode, useNowPlaying } from 'react-native-nitro-player'
import { useShallow } from 'zustand/react/shallow'
/**
@@ -174,7 +174,10 @@ export const useCurrentTrack = () => usePlayerQueueStore((state) => state.curren
* Returns only the current track ID for efficient comparisons.
* Use this in list items to avoid re-renders when other track properties change.
*/
export const useCurrentTrackId = () => usePlayerQueueStore((state) => state.currentTrack?.item.Id)
export const useCurrentTrackId = () => {
const playerState = useNowPlaying()
return playerState.currentTrack?.id
}
export const useCurrentIndex = () => usePlayerQueueStore((state) => state.currentIndex)