diff --git a/src/Laudiolin.tsx b/src/Laudiolin.tsx index 91283a2..984606a 100644 --- a/src/Laudiolin.tsx +++ b/src/Laudiolin.tsx @@ -1,7 +1,10 @@ +import { useRef } from "react"; import { SafeAreaView, StatusBar, View } from "react-native"; -import { BottomTabBar, createBottomTabNavigator } from "@react-navigation/bottom-tabs"; import { NavigationContainer } from "@react-navigation/native"; +import { NavigationContainerRef } from "@react-navigation/core"; +import { GestureHandlerRootView } from "react-native-gesture-handler"; +import { BottomTabBar, createBottomTabNavigator } from "@react-navigation/bottom-tabs"; import { HomeIcon, SearchIcon, SettingsIcon } from "@ui/Icons"; @@ -25,46 +28,50 @@ const Tab = createBottomTabNavigator(); function Laudiolin(props: IProps) { const global = useGlobal(); + const navigator = useRef>(null); + return ( <> - { - global.showTrackPage && - } - { - global.showLoginPage && - } + + { + global.showTrackPage && + } + { + global.showLoginPage && + } - - - ( - <> - - - - )} - screenOptions={{ - headerShown: false, - tabBarShowLabel: false, - tabBarStyle: style.App_TabBar, - }} - sceneContainerStyle={style.App_Scene} - > - HomeIcon(focused) }} - name={"Home"} component={Home} /> - SearchIcon(focused) }} - name={"Search"} component={Search} /> - SettingsIcon(focused) }} - name={"Settings"} component={Settings} /> - - - + + + ( + <> + + + + )} + screenOptions={{ + headerShown: false, + tabBarShowLabel: false, + tabBarStyle: style.App_TabBar, + }} + sceneContainerStyle={style.App_Scene} + > + HomeIcon(focused) }} + name={"Home"} component={Home} /> + SearchIcon(focused) }} + name={"Search"} component={Search} /> + SettingsIcon(focused) }} + name={"Settings"} component={Settings} /> + + + + ); diff --git a/src/ui/Debug.tsx b/src/ui/Debug.tsx index 15b9b8f..b43611e 100644 --- a/src/ui/Debug.tsx +++ b/src/ui/Debug.tsx @@ -1,13 +1,14 @@ -import { useEffect, useState } from "react"; +import { useState } from "react"; import { StyleProp, View, ViewStyle } from "react-native"; import { useNavigation } from "@react-navigation/native"; -import TrackPlayer, { Track, useActiveTrack, usePlaybackState } from "react-native-track-player"; +import TrackPlayer, { RepeatMode, useActiveTrack, usePlaybackState } from "react-native-track-player"; import StyledText from "@components/StyledText"; import StyledButton from "@components/StyledButton"; import { useDebug } from "@backend/stores"; +import Player, { currentlyPlaying, useQueue } from "@backend/player"; import { colors, value } from "@style/Laudiolin"; @@ -24,14 +25,12 @@ function Debug() { const [queueInfo, showQueueInfo] = useState(false); const [trackInfo, showTrackInfo] = useState(false); + const [newQueueInfo, setNewQueueInfo] = useState(false); - const [queue, setQueue] = useState([]); + const [repeatMode, setRepeatMode] = useState(RepeatMode.Off); + const [songIndex, setSongIndex] = useState(0); - useEffect(() => { - if (queueInfo) { - TrackPlayer.getQueue().then(setQueue); - } - }, [playerState]); + const queue = useQueue(); return ( @@ -55,19 +54,51 @@ function Debug() { { - showQueueInfo(!queueInfo); - TrackPlayer.getQueue().then(setQueue); - }} + onPress={() => showQueueInfo(!queueInfo)} /> { queueInfo && ( - TrackPlayer.removeUpcomingTracks()} /> + TrackPlayer.getActiveTrackIndex().then(setSongIndex)} + /> + { + Player.nextRepeatMode().then(setRepeatMode); + }} + /> + + ) } + + setNewQueueInfo(!newQueueInfo)} + /> + + { newQueueInfo && ( + + + + + + { + Player.nextRepeatMode().then(setRepeatMode); + }} + /> + + queue.dequeue()} + /> ) } diff --git a/src/ui/NowPlaying.tsx b/src/ui/NowPlaying.tsx index 4765dfe..bba1fe3 100644 --- a/src/ui/NowPlaying.tsx +++ b/src/ui/NowPlaying.tsx @@ -16,6 +16,8 @@ import TrackPlayer, { usePlaybackState, useProgress, } from "react-native-track-player"; +import { NavigationContainerRef } from "@react-navigation/core"; +import { GestureDetector, Gesture, Directions } from "react-native-gesture-handler"; import ProgressBar from "@widgets/ProgressBar"; import StyledText, { Size } from "@components/StyledText"; @@ -36,7 +38,7 @@ function RepeatIcon({ loop }: { loop: RepeatMode }) { } } -function NowPlaying() { +function NowPlaying({ navigation }: { navigation: NavigationContainerRef }) { const global = useGlobal(); const track = useActiveTrack(); @@ -49,82 +51,98 @@ function NowPlaying() { TrackPlayer.getRepeatMode().then(setRepeatMode); }); + const goBack = () => global.setShowTrackPage(false); + const openQueue = () => { + navigation.navigate("Named List", { + title: "Queue", fetcher: "queue", render: "tracks" + }); + global.setShowTrackPage(false); + }; + const backGesture = Gesture.Fling() + .direction(Directions.DOWN) + .onEnd(goBack); + const queueGesture = Gesture.Fling() + .direction(Directions.UP) + .onEnd(openQueue); + return ( - - - global.setShowTrackPage(false)}> - - - - { global.fromPlaylist && ( - - + + + global.setShowTrackPage(false)}> + + + + { global.fromPlaylist && ( + + + + + ) } + + null}> + + {/* TODO: Move favorite track into context menu. */} + + + + + + + + 700 ? 3 : 2} + /> + + - - ) } - - null}> - - {/* TODO: Move favorite track into context menu. */} - - - - - - - 700 ? 3 : 2} - /> - - - - - - - Player.shuffle()}> - - - - TrackPlayer.skipToPrevious()}> - - - - { - if (state == State.Paused) { - await TrackPlayer.play(); - } else if (state == State.Playing) { - await TrackPlayer.pause(); - } - }}> - - - - TrackPlayer.skipToNext()}> - - - - Player.nextRepeatMode().then(setRepeatMode)}> - - + + Player.shuffle()}> + + + + Player.skipToPrevious()}> + + + + { + if (state == State.Paused) { + await TrackPlayer.play(); + } else if (state == State.Playing) { + await TrackPlayer.pause(); + } + }}> + + + + Player.skipToNext()}> + + + + Player.nextRepeatMode().then(setRepeatMode)}> + + + - + ); } @@ -171,7 +189,8 @@ const style = StyleSheet.create({ bottom: 10, flexDirection: "row", alignItems: "center", - justifyContent: "space-between", + gap: 30, + alignSelf: "center", paddingTop: 30, paddingLeft: 25, paddingRight: 25 diff --git a/src/ui/Playlist.tsx b/src/ui/Playlist.tsx index 5e9b270..b6c1a31 100644 --- a/src/ui/Playlist.tsx +++ b/src/ui/Playlist.tsx @@ -109,10 +109,7 @@ function Playlist(props: IProps) { style={{ marginRight: 5 }} />} buttonStyle={{ backgroundColor: colors.contrast }} - onPress={() => Player.queue({ - tracks: playlist?.tracks, - clear: true, start: true, - })} + onPress={() => Player.play(playlist?.tracks, { clear: true })} /> } buttonStyle={{ backgroundColor: colors.accent }} - onPress={() => Player.queue({ - playlist, - tracks: playlist?.tracks, - clear: true, start: true, shuffle: true + onPress={() => Player.play(playlist?.tracks, { + playlist, clear: true, shuffle: true })} /> diff --git a/src/ui/components/NamedList.tsx b/src/ui/components/NamedList.tsx index de469d3..fcefcdf 100644 --- a/src/ui/components/NamedList.tsx +++ b/src/ui/components/NamedList.tsx @@ -10,6 +10,7 @@ import BackButton from "@widgets/BackButton"; import PlaylistStripe from "@widgets/PlaylistStripe"; import { TrackInfo } from "@backend/types"; +import { useQueue } from "@backend/player"; import { value } from "@style/Laudiolin"; @@ -18,11 +19,17 @@ const renderers: { [key: string]: (data: any) => ReactElement } = { playlists: (playlist: any) => }; +const fetchers: { [key: string]: () => any } = { + queue: useQueue +}; + interface RouteParams { title: string; - - items: T[]; render: string; + renderLimit?: number | undefined; + + items?: T[]; + fetcher?: string; } interface IProps { @@ -32,10 +39,18 @@ interface IProps { function NamedList(props: IProps) { const { route, navigation } = props; - const { title, items, render } = route.params as RouteParams; + const { + title, render, items: providedItems, + fetcher, renderLimit + } = route.params as RouteParams; const renderer = renderers[render] as (item: T) => ReactElement; + let items = fetcher ? fetchers[fetcher]() : providedItems; + if ("values" in items) { + items = items.values(); + } + return ( @@ -45,7 +60,7 @@ function NamedList(props: IProps) { renderer(item)} /> diff --git a/src/ui/widgets/MediaPlayer.tsx b/src/ui/widgets/MediaPlayer.tsx index 687fa2f..eb9a880 100644 --- a/src/ui/widgets/MediaPlayer.tsx +++ b/src/ui/widgets/MediaPlayer.tsx @@ -8,6 +8,7 @@ import TrackPlayer, { State, useActiveTrack, usePlaybackState } from "react-nati import StyledText, { Size } from "@components/StyledText"; +import Player from "@backend/player"; import { artist } from "@backend/search"; import { useGlobal } from "@backend/stores"; @@ -47,7 +48,7 @@ function MediaPlayer() { - TrackPlayer.skipToNext()}> + Player.skipToNext()}> diff --git a/src/ui/widgets/Track.tsx b/src/ui/widgets/Track.tsx index 94b9b7a..aa9952f 100644 --- a/src/ui/widgets/Track.tsx +++ b/src/ui/widgets/Track.tsx @@ -45,8 +45,8 @@ function Track(props: IProps) { /> - 25} /> + 25} />