mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-29 01:19:28 +01:00
basic queue with skipTo support
This commit is contained in:
parent
075286e939
commit
df3e913125
@ -47,6 +47,18 @@ export const useNext = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useSkipTo = () => {
|
||||||
|
return (track: number) =>
|
||||||
|
trackPlayerCommands.enqueue(async () => {
|
||||||
|
const queue = await getQueue()
|
||||||
|
if (track < 0 || track >= queue.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await TrackPlayer.skip(track)
|
||||||
|
await TrackPlayer.play()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const useToggleRepeat = () => {
|
export const useToggleRepeat = () => {
|
||||||
const setRepeatMode = useStore(selectTrackPlayer.setRepeatMode)
|
const setRepeatMode = useStore(selectTrackPlayer.setRepeatMode)
|
||||||
|
|
||||||
@ -209,10 +221,6 @@ export const useSetQueue = () => {
|
|||||||
|
|
||||||
await TrackPlayer.add(tracks1, 0)
|
await TrackPlayer.add(tracks1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setQueue(await getQueue())
|
|
||||||
// setCurrentTrackIdx(playTrack)
|
|
||||||
// setQueueName(name)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,3 +236,16 @@ function mapSongToTrack(song: Song, coverArtUri: (coverArt?: string) => string |
|
|||||||
duration: song.duration,
|
duration: song.duration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mapTrackExtToSong(track: TrackExt): Song {
|
||||||
|
return {
|
||||||
|
itemType: 'song',
|
||||||
|
id: track.id,
|
||||||
|
title: track.title as string,
|
||||||
|
artist: track.artist,
|
||||||
|
album: track.album,
|
||||||
|
streamUri: track.url as string,
|
||||||
|
coverArt: track.coverArt,
|
||||||
|
duration: track.duration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,10 +1,37 @@
|
|||||||
import BottomTabNavigator from '@app/navigation/BottomTabNavigator'
|
import BottomTabNavigator from '@app/navigation/BottomTabNavigator'
|
||||||
|
import NowPlayingQueue from '@app/screens/NowPlayingQueue'
|
||||||
import NowPlayingView from '@app/screens/NowPlayingView'
|
import NowPlayingView from '@app/screens/NowPlayingView'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
|
import font from '@app/styles/font'
|
||||||
import { DarkTheme, NavigationContainer } from '@react-navigation/native'
|
import { DarkTheme, NavigationContainer } from '@react-navigation/native'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
|
||||||
|
|
||||||
|
const NowPlayingStack = createNativeStackNavigator()
|
||||||
|
|
||||||
|
const NowPlayingNavigator = () => (
|
||||||
|
<NowPlayingStack.Navigator>
|
||||||
|
<NowPlayingStack.Screen name="main" component={NowPlayingView} options={{ headerShown: false }} />
|
||||||
|
<NowPlayingStack.Screen
|
||||||
|
name="queue"
|
||||||
|
component={NowPlayingQueue}
|
||||||
|
options={{
|
||||||
|
title: 'Queue',
|
||||||
|
headerStyle: {
|
||||||
|
backgroundColor: colors.gradient.high,
|
||||||
|
},
|
||||||
|
headerTitleStyle: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontFamily: font.semiBold,
|
||||||
|
color: colors.text.primary,
|
||||||
|
},
|
||||||
|
headerHideShadow: true,
|
||||||
|
headerTintColor: 'white',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</NowPlayingStack.Navigator>
|
||||||
|
)
|
||||||
|
|
||||||
const RootStack = createNativeStackNavigator()
|
const RootStack = createNativeStackNavigator()
|
||||||
|
|
||||||
const theme = { ...DarkTheme }
|
const theme = { ...DarkTheme }
|
||||||
@ -15,24 +42,14 @@ const RootNavigator = () => (
|
|||||||
theme={theme}
|
theme={theme}
|
||||||
linking={{
|
linking={{
|
||||||
prefixes: ['trackplayer'],
|
prefixes: ['trackplayer'],
|
||||||
config: {
|
|
||||||
screens: {
|
|
||||||
main: {
|
|
||||||
path: ':/main',
|
|
||||||
},
|
|
||||||
'now-playing': {
|
|
||||||
path: ':/notification.click',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}>
|
}}>
|
||||||
<RootStack.Navigator
|
<RootStack.Navigator
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
}}
|
}}
|
||||||
initialRouteName="main">
|
initialRouteName="top">
|
||||||
<RootStack.Screen name="main" component={BottomTabNavigator} />
|
<RootStack.Screen name="top" component={BottomTabNavigator} />
|
||||||
<RootStack.Screen name="now-playing" component={NowPlayingView} />
|
<RootStack.Screen name="now-playing" component={NowPlayingNavigator} />
|
||||||
</RootStack.Navigator>
|
</RootStack.Navigator>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
)
|
)
|
||||||
|
|||||||
48
app/screens/NowPlayingQueue.tsx
Normal file
48
app/screens/NowPlayingQueue.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import GradientScrollView from '@app/components/GradientScrollView'
|
||||||
|
import ListItem from '@app/components/ListItem'
|
||||||
|
import NowPlayingBar from '@app/components/NowPlayingBar'
|
||||||
|
import { mapTrackExtToSong, useSkipTo } from '@app/hooks/trackplayer'
|
||||||
|
import { useStore } from '@app/state/store'
|
||||||
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
|
import React from 'react'
|
||||||
|
import { StyleSheet, View } from 'react-native'
|
||||||
|
|
||||||
|
const NowPlayingQueue = React.memo<{}>(() => {
|
||||||
|
const queue = useStore(selectTrackPlayer.queue)
|
||||||
|
const skipTo = useSkipTo()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.outerContainer}>
|
||||||
|
<GradientScrollView style={styles.container}>
|
||||||
|
<View style={styles.content}>
|
||||||
|
{queue.map(mapTrackExtToSong).map((song, i) => (
|
||||||
|
<ListItem
|
||||||
|
key={i}
|
||||||
|
item={song}
|
||||||
|
onPress={() => skipTo(i)}
|
||||||
|
showArt={true}
|
||||||
|
subtitle={`${song.artist} • ${song.album}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</GradientScrollView>
|
||||||
|
<NowPlayingBar />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
outerContainer: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingTop: 10,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default NowPlayingQueue
|
||||||
@ -1,26 +1,26 @@
|
|||||||
|
import CoverArt from '@app/components/CoverArt'
|
||||||
|
import ImageGradientBackground from '@app/components/ImageGradientBackground'
|
||||||
|
import PressableOpacity from '@app/components/PressableOpacity'
|
||||||
|
import Star from '@app/components/Star'
|
||||||
|
import { useStarred } from '@app/hooks/music'
|
||||||
|
import { useNext, usePause, usePlay, usePrevious, useToggleRepeat, useToggleShuffle } from '@app/hooks/trackplayer'
|
||||||
|
import { selectMusic } from '@app/state/music'
|
||||||
|
import { useStore } from '@app/state/store'
|
||||||
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
|
import colors from '@app/styles/colors'
|
||||||
|
import dimensions from '@app/styles/dimensions'
|
||||||
|
import font from '@app/styles/font'
|
||||||
|
import formatDuration from '@app/util/formatDuration'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import React, { useCallback, useEffect } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
import { BackHandler, StatusBar, StyleSheet, Text, View } from 'react-native'
|
import { StatusBar, StyleSheet, Text, View } from 'react-native'
|
||||||
|
import { NativeStackScreenProps } from 'react-native-screens/native-stack'
|
||||||
import { RepeatMode, State } from 'react-native-track-player'
|
import { RepeatMode, State } from 'react-native-track-player'
|
||||||
import IconFA from 'react-native-vector-icons/FontAwesome'
|
import IconFA from 'react-native-vector-icons/FontAwesome'
|
||||||
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
|
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
|
||||||
import Icon from 'react-native-vector-icons/Ionicons'
|
import Icon from 'react-native-vector-icons/Ionicons'
|
||||||
import IconMatCom from 'react-native-vector-icons/MaterialCommunityIcons'
|
import IconMatCom from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||||
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
||||||
import colors from '@app/styles/colors'
|
|
||||||
import font from '@app/styles/font'
|
|
||||||
import formatDuration from '@app/util/formatDuration'
|
|
||||||
import CoverArt from '@app/components/CoverArt'
|
|
||||||
import ImageGradientBackground from '@app/components/ImageGradientBackground'
|
|
||||||
import PressableOpacity from '@app/components/PressableOpacity'
|
|
||||||
import dimensions from '@app/styles/dimensions'
|
|
||||||
import { NativeStackScreenProps } from 'react-native-screens/native-stack'
|
|
||||||
import { useFocusEffect } from '@react-navigation/native'
|
|
||||||
import { useStore } from '@app/state/store'
|
|
||||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
|
||||||
import { useNext, usePause, usePlay, usePrevious, useToggleRepeat, useToggleShuffle } from '@app/hooks/trackplayer'
|
|
||||||
import Star from '@app/components/Star'
|
|
||||||
import { useStarred } from '@app/hooks/music'
|
|
||||||
import { selectMusic } from '@app/state/music'
|
|
||||||
|
|
||||||
const NowPlayingHeader = React.memo<{
|
const NowPlayingHeader = React.memo<{
|
||||||
backHandler: () => void
|
backHandler: () => void
|
||||||
@ -210,6 +210,7 @@ const PlayerControls = () => {
|
|||||||
const toggleShuffle = useToggleShuffle()
|
const toggleShuffle = useToggleShuffle()
|
||||||
const repeatMode = useStore(selectTrackPlayer.repeatMode)
|
const repeatMode = useStore(selectTrackPlayer.repeatMode)
|
||||||
const toggleRepeat = useToggleRepeat()
|
const toggleRepeat = useToggleRepeat()
|
||||||
|
const navigation = useNavigation()
|
||||||
|
|
||||||
let playPauseIcon: string
|
let playPauseIcon: string
|
||||||
let playPauseAction: undefined | (() => void)
|
let playPauseAction: undefined | (() => void)
|
||||||
@ -262,7 +263,7 @@ const PlayerControls = () => {
|
|||||||
<PressableOpacity onPress={undefined} disabled={disabled}>
|
<PressableOpacity onPress={undefined} disabled={disabled}>
|
||||||
<IconMatCom name="cast-audio" size={20} color="white" />
|
<IconMatCom name="cast-audio" size={20} color="white" />
|
||||||
</PressableOpacity>
|
</PressableOpacity>
|
||||||
<PressableOpacity onPress={undefined} disabled={disabled}>
|
<PressableOpacity onPress={() => navigation.navigate('queue')} disabled={disabled}>
|
||||||
<IconMatCom name="playlist-play" size={24} color="white" />
|
<IconMatCom name="playlist-play" size={24} color="white" />
|
||||||
</PressableOpacity>
|
</PressableOpacity>
|
||||||
</View>
|
</View>
|
||||||
@ -306,21 +307,16 @@ const controlsStyles = StyleSheet.create({
|
|||||||
})
|
})
|
||||||
|
|
||||||
type RootStackParamList = {
|
type RootStackParamList = {
|
||||||
|
top: undefined
|
||||||
main: undefined
|
main: undefined
|
||||||
'now-playing': undefined
|
|
||||||
}
|
}
|
||||||
type NowPlayingProps = NativeStackScreenProps<RootStackParamList, 'now-playing'>
|
type NowPlayingProps = NativeStackScreenProps<RootStackParamList, 'main'>
|
||||||
|
|
||||||
const NowPlayingView: React.FC<NowPlayingProps> = ({ navigation }) => {
|
const NowPlayingView: React.FC<NowPlayingProps> = ({ navigation }) => {
|
||||||
const track = useStore(selectTrackPlayer.currentTrack)
|
const track = useStore(selectTrackPlayer.currentTrack)
|
||||||
|
|
||||||
const back = useCallback(() => {
|
const back = useCallback(() => {
|
||||||
if (navigation.canGoBack()) {
|
navigation.navigate('top')
|
||||||
navigation.popToTop()
|
|
||||||
} else {
|
|
||||||
navigation.navigate('main')
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}, [navigation])
|
}, [navigation])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -329,14 +325,6 @@ const NowPlayingView: React.FC<NowPlayingProps> = ({ navigation }) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
BackHandler.addEventListener('hardwareBackPress', back)
|
|
||||||
|
|
||||||
return () => BackHandler.removeEventListener('hardwareBackPress', back)
|
|
||||||
}, [back]),
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ImageGradientBackground imageUri={track?.artwork as string} imageKey={`${track?.album}${track?.artist}`} />
|
<ImageGradientBackground imageUri={track?.artwork as string} imageKey={`${track?.album}${track?.artist}`} />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user