diff --git a/App.tsx b/App.tsx index a1aff72..571b2ce 100644 --- a/App.tsx +++ b/App.tsx @@ -5,6 +5,7 @@ import RootNavigator from './src/components/navigation/RootNavigator'; import { Provider } from 'jotai'; import { StatusBar } from 'react-native'; import colors from './src/styles/colors'; +import TrackPlayerState from './src/components/TrackPlayerState'; const theme = { ...DarkTheme }; theme.colors.background = colors.gradient.high; @@ -12,6 +13,7 @@ theme.colors.background = colors.gradient.high; const App = () => ( + diff --git a/package.json b/package.json index 899eb19..6d813f3 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,12 @@ }, "dependencies": { "@react-native-async-storage/async-storage": "^1.15.5", + "@react-native-community/hooks": "^2.6.0", "@react-native-community/masked-view": "^0.1.11", "@react-navigation/bottom-tabs": "^5.11.11", "@react-navigation/material-top-tabs": "^5.3.15", "@react-navigation/native": "^5.9.4", + "fast-deep-equal": "^3.1.3", "jotai": "^1.1.0", "md5": "^2.3.0", "react": "17.0.1", diff --git a/src/components/TrackPlayerState.tsx b/src/components/TrackPlayerState.tsx new file mode 100644 index 0000000..ab732dd --- /dev/null +++ b/src/components/TrackPlayerState.tsx @@ -0,0 +1,72 @@ +import React, { useCallback, useEffect } from 'react'; +import TrackPlayer, { Event, State, Track, useTrackPlayerEvents } from 'react-native-track-player'; +import { useAppState } from '@react-native-community/hooks'; +import { useUpdateAtom, useAtomValue } from 'jotai/utils'; +import { currentTrackAtom } from '../state/trackplayer'; +import { View } from 'react-native'; + +const TrackPlayerState = () => { + const setCurrentTrack = useUpdateAtom(currentTrackAtom); + const appState = useAppState(); + + const updateCurrentTrack = useCallback(async () => { + const index = await TrackPlayer.getCurrentTrack(); + + if (index !== null && index >= 0) { + const track = await TrackPlayer.getTrack(index); + if (track !== null) { + setCurrentTrack(track); + return; + } + } + + setCurrentTrack(undefined); + }, [setCurrentTrack]); + + useTrackPlayerEvents( + [ + // Event.PlaybackState, + // Event.PlaybackTrackChanged, + Event.PlaybackQueueEnded, + Event.PlaybackMetadataReceived, + Event.RemoteDuck, + Event.RemoteNext, + Event.RemotePrevious, + Event.RemoteStop, + ], + event => { + if (event.type === Event.PlaybackQueueEnded && 'track' in event) { + setCurrentTrack(undefined); + return; + } + updateCurrentTrack(); + }, + ); + + useEffect(() => { + if (appState === 'active') { + updateCurrentTrack(); + } + }, [appState, updateCurrentTrack]); + + return <>; +}; + +const CurrentTrack = () => { + const currentTrack = useAtomValue(currentTrackAtom); + + useEffect(() => { + console.log(currentTrack?.title); + }, [currentTrack]); + + return <>; +}; + +const ASDFSADFSAF = () => ( + + + + +); + +export default ASDFSADFSAF; diff --git a/src/components/common/AlbumView.tsx b/src/components/common/AlbumView.tsx index 513d857..7ad91ae 100644 --- a/src/components/common/AlbumView.tsx +++ b/src/components/common/AlbumView.tsx @@ -10,8 +10,9 @@ import { useWindowDimensions, View, } from 'react-native'; -import { useCurrentTrackId, useSetQueue } from '../../hooks/player'; +import { useSetQueue } from '../../hooks/player'; import { albumAtomFamily } from '../../state/music'; +import { currentTrackAtom } from '../../state/trackplayer'; import colors from '../../styles/colors'; import text from '../../styles/text'; import AlbumArt from './AlbumArt'; @@ -27,7 +28,7 @@ const SongItem: React.FC<{ onPress: (event: GestureResponderEvent) => void; }> = ({ id, title, artist, onPress }) => { const [opacity, setOpacity] = useState(1); - const currentTrackId = useCurrentTrackId(); + const currentTrack = useAtomValue(currentTrackAtom); return ( {title} diff --git a/src/hooks/player.ts b/src/hooks/player.ts index 489a39e..281dd06 100644 --- a/src/hooks/player.ts +++ b/src/hooks/player.ts @@ -1,6 +1,7 @@ -import { useState } from 'react'; -import TrackPlayer, { Track, useTrackPlayerEvents, Event, State } from 'react-native-track-player'; +import { useUpdateAtom } from 'jotai/utils'; +import TrackPlayer, { Track } from 'react-native-track-player'; import { Song } from '../models/music'; +import { currentTrackAtom } from '../state/trackplayer'; function mapSongToTrack(song: Song): Track { return { @@ -13,41 +14,17 @@ function mapSongToTrack(song: Song): Track { }; } -const currentTrackEvents = [Event.PlaybackState, Event.PlaybackTrackChanged, Event.RemoteStop]; - -export const useCurrentTrackId = () => { - const [currentTrackId, setCurrentTrackId] = useState(null); - - useTrackPlayerEvents(currentTrackEvents, async event => { - switch (event.type) { - case Event.PlaybackState: - switch (event.state) { - case State.None: - case State.Stopped: - setCurrentTrackId(null); - break; - } - break; - case Event.PlaybackTrackChanged: - const trackIndex = await TrackPlayer.getCurrentTrack(); - setCurrentTrackId((await TrackPlayer.getTrack(trackIndex)).id); - break; - case Event.RemoteStop: - setCurrentTrackId(null); - break; - default: - break; - } - }); - - return currentTrackId; -}; - export const useSetQueue = () => { + const setCurrentTrack = useUpdateAtom(currentTrackAtom); + return async (songs: Song[], playId?: string) => { await TrackPlayer.reset(); const tracks = songs.map(mapSongToTrack); + if (playId) { + setCurrentTrack(tracks.find(t => t.id === playId)); + } + if (!playId) { await TrackPlayer.add(tracks); } else if (playId === tracks[0].id) { @@ -63,8 +40,8 @@ export const useSetQueue = () => { await TrackPlayer.add(tracks1, 0); - const queue = await TrackPlayer.getQueue(); - console.log(`queue: ${JSON.stringify(queue.map(x => x.title))}`); + // const queue = await TrackPlayer.getQueue(); + // console.log(`queue: ${JSON.stringify(queue.map(x => x.title))}`); } }; }; diff --git a/src/state/trackplayer.ts b/src/state/trackplayer.ts new file mode 100644 index 0000000..16003e6 --- /dev/null +++ b/src/state/trackplayer.ts @@ -0,0 +1,16 @@ +import { atom } from 'jotai'; +import { Track } from 'react-native-track-player'; +import equal from 'fast-deep-equal'; + +type OptionalTrack = Track | undefined; + +const currentTrack = atom(undefined); +export const currentTrackAtom = atom( + get => get(currentTrack), + (get, set, value) => { + if (equal(get(currentTrack), value)) { + return; + } + set(currentTrack, value); + }, +); diff --git a/yarn.lock b/yarn.lock index 5f5f1d4..5649fb0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1108,6 +1108,11 @@ resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.1.0.tgz#e42b1bef12d2415411519fd528e64b593b1363dc" integrity sha512-W/J0fNYVO01tioHjvYWQ9m6RgndVtbElzYozBq1ZPrHO/iCzlqoySHl4gO/fpCl9QEFjvJfjPgtPMTMlsoq5DQ== +"@react-native-community/hooks@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@react-native-community/hooks/-/hooks-2.6.0.tgz#dd5f19601eb3684c6bcdd3df3d0c04cf44c24cff" + integrity sha512-emBGKvhJ0h++lLJQ5ejsj+od9G67nEaihjvfSx7/JWvNrQGAhP9U0OZqgb9dkKzor9Ufaj9SGt8RNY97cGzttw== + "@react-native-community/masked-view@^0.1.11": version "0.1.11" resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce"