diff --git a/app/components/ProgressHook.tsx b/app/components/ProgressHook.tsx index f7d82f5..f72998a 100644 --- a/app/components/ProgressHook.tsx +++ b/app/components/ProgressHook.tsx @@ -1,15 +1,23 @@ import { useStore } from '@app/state/store' import { selectTrackPlayer } from '@app/state/trackplayer' import React, { useEffect } from 'react' -import { useProgress } from 'react-native-track-player' +import { State, useProgress } from 'react-native-track-player' const ProgressHook = () => { + const playerState = useStore(selectTrackPlayer.playerState) const setProgress = useStore(selectTrackPlayer.setProgress) const progress = useProgress(250) useEffect(() => { + if (playerState !== State.Playing) { + return + } + if (progress.buffered === 0 && progress.duration === 0 && progress.position === 0) { + return + } + setProgress(progress) - }, [setProgress, progress]) + }, [setProgress, progress, playerState]) return <> } diff --git a/app/hooks/trackplayer.ts b/app/hooks/trackplayer.ts index 4a51cd6..f19bb70 100644 --- a/app/hooks/trackplayer.ts +++ b/app/hooks/trackplayer.ts @@ -1,9 +1,16 @@ import { useCoverArtUri } from '@app/hooks/music' import { Song } from '@app/models/music' import { useStore } from '@app/state/store' -import { getCurrentTrack, getQueue, selectTrackPlayer, TrackExt, trackPlayerCommands } from '@app/state/trackplayer' +import { + getCurrentTrack, + getQueue, + getRepeatMode, + selectTrackPlayer, + TrackExt, + trackPlayerCommands, +} from '@app/state/trackplayer' import { useCallback } from 'react' -import TrackPlayer from 'react-native-track-player' +import TrackPlayer, { RepeatMode } from 'react-native-track-player' export const usePlay = () => { return () => trackPlayerCommands.enqueue(() => TrackPlayer.play()) @@ -14,14 +21,11 @@ export const usePause = () => { } export const usePrevious = () => { - // const setCurrentTrackIdx = useStore(selectTrackPlayer.setCurrentTrackIdx) - return () => trackPlayerCommands.enqueue(async () => { const [current] = await Promise.all([await TrackPlayer.getCurrentTrack(), await getQueue()]) if (current > 0) { await TrackPlayer.skipToPrevious() - // setCurrentTrackIdx(current - 1) } else { await TrackPlayer.seekTo(0) } @@ -30,23 +34,44 @@ export const usePrevious = () => { } export const useNext = () => { - // const setCurrentTrack = useUpdateAtom(currentTrackAtom) - return () => trackPlayerCommands.enqueue(async () => { const [current, queue] = await Promise.all([await TrackPlayer.getCurrentTrack(), await getQueue()]) if (current >= queue.length - 1) { await TrackPlayer.skip(0) await TrackPlayer.pause() - // setCurrentTrack(queue[0]) } else { await TrackPlayer.skipToNext() - // setCurrentTrack(queue[current + 1]) await TrackPlayer.play() } }) } +export const useToggleRepeat = () => { + const setRepeatMode = useStore(selectTrackPlayer.setRepeatMode) + + return () => + trackPlayerCommands.enqueue(async () => { + const repeatMode = await getRepeatMode() + let nextMode = RepeatMode.Off + + switch (repeatMode) { + case RepeatMode.Off: + nextMode = RepeatMode.Queue + break + case RepeatMode.Queue: + nextMode = RepeatMode.Track + break + default: + nextMode = RepeatMode.Off + break + } + + await TrackPlayer.setRepeatMode(nextMode) + setRepeatMode(nextMode) + }) +} + export const useReset = (enqueue = true) => { const resetStore = useStore(selectTrackPlayer.reset) diff --git a/app/screens/NowPlayingView.tsx b/app/screens/NowPlayingView.tsx index 5a46bc8..be587c2 100644 --- a/app/screens/NowPlayingView.tsx +++ b/app/screens/NowPlayingView.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect } from 'react' import { BackHandler, StatusBar, StyleSheet, Text, View } from 'react-native' -import { State } from 'react-native-track-player' +import { RepeatMode, State } from 'react-native-track-player' import IconFA from 'react-native-vector-icons/FontAwesome' import IconFA5 from 'react-native-vector-icons/FontAwesome5' import Icon from 'react-native-vector-icons/Ionicons' @@ -17,7 +17,7 @@ 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, useToggleShuffle } from '@app/hooks/trackplayer' +import { useNext, usePause, usePlay, usePrevious, useToggleRepeat, useToggleShuffle } from '@app/hooks/trackplayer' const NowPlayingHeader = React.memo<{ backHandler: () => void @@ -201,6 +201,8 @@ const PlayerControls = () => { const previous = usePrevious() const shuffled = useStore(selectTrackPlayer.shuffled) const toggleShuffle = useToggleShuffle() + const repeatMode = useStore(selectTrackPlayer.repeatMode) + const toggleRepeat = useToggleRepeat() let playPauseIcon: string let playPauseAction: undefined | (() => void) @@ -225,8 +227,9 @@ const PlayerControls = () => { - - + toggleRepeat()} disabled={disabled}> + + 1 @@ -285,6 +288,14 @@ const controlsStyles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, + repeatExt: { + color: colors.accent, + fontFamily: font.bold, + fontSize: 14, + position: 'absolute', + top: 26, + opacity: 0, + }, }) type RootStackParamList = { diff --git a/app/state/trackplayer.ts b/app/state/trackplayer.ts index 6bcd662..66c5ab5 100644 --- a/app/state/trackplayer.ts +++ b/app/state/trackplayer.ts @@ -1,6 +1,6 @@ import PromiseQueue from '@app/util/PromiseQueue' import produce from 'immer' -import TrackPlayer, { State, Track } from 'react-native-track-player' +import TrackPlayer, { RepeatMode, State, Track } from 'react-native-track-player' import { GetState, SetState } from 'zustand' import { Store } from './store' @@ -22,6 +22,9 @@ export type TrackPlayerSlice = { shuffleOrder?: number[] setShuffleOrder: (shuffleOrder?: number[]) => void + repeatMode: RepeatMode + setRepeatMode: (repeatMode: RepeatMode) => void + playerState: State setPlayerState: (playerState: State) => void @@ -48,6 +51,9 @@ export const selectTrackPlayer = { setShuffleOrder: (store: TrackPlayerSlice) => store.setShuffleOrder, shuffled: (store: TrackPlayerSlice) => !!store.shuffleOrder, + repeatMode: (store: TrackPlayerSlice) => store.repeatMode, + setRepeatMode: (store: TrackPlayerSlice) => store.setRepeatMode, + playerState: (store: TrackPlayerSlice) => store.playerState, setPlayerState: (store: TrackPlayerSlice) => store.setPlayerState, @@ -75,6 +81,9 @@ export const createTrackPlayerSlice = (set: SetState, get: GetState set({ shuffleOrder }), + repeatMode: RepeatMode.Off, + setRepeatMode: repeatMode => set({ repeatMode }), + playerState: State.None, setPlayerState: playerState => set({ playerState }), @@ -114,6 +123,7 @@ export const createTrackPlayerSlice = (set: SetState, get: GetState => { } export const getPlayerState = async (): Promise => { - const state = await TrackPlayer.getState() - return state || State.None + return (await TrackPlayer.getState()) || State.None +} + +export const getRepeatMode = async (): Promise => { + return (await TrackPlayer.getRepeatMode()) || RepeatMode.Off }