mirror of
https://github.com/Jellify-Music/App.git
synced 2026-04-21 00:58:32 -05:00
chore:- track-player-hook (#907)
Co-authored-by: riteshshukla04 <riteshshukla2381@gmail.com>
This commit is contained in:
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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,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,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 }}
|
||||
/>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user