mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-29 17:39:27 +01:00
central management of track player state
currentTrack implemented and working well now (only updates on change)
This commit is contained in:
parent
c676944b9a
commit
28ab092402
2
App.tsx
2
App.tsx
@ -5,6 +5,7 @@ import RootNavigator from './src/components/navigation/RootNavigator';
|
|||||||
import { Provider } from 'jotai';
|
import { Provider } from 'jotai';
|
||||||
import { StatusBar } from 'react-native';
|
import { StatusBar } from 'react-native';
|
||||||
import colors from './src/styles/colors';
|
import colors from './src/styles/colors';
|
||||||
|
import TrackPlayerState from './src/components/TrackPlayerState';
|
||||||
|
|
||||||
const theme = { ...DarkTheme };
|
const theme = { ...DarkTheme };
|
||||||
theme.colors.background = colors.gradient.high;
|
theme.colors.background = colors.gradient.high;
|
||||||
@ -12,6 +13,7 @@ theme.colors.background = colors.gradient.high;
|
|||||||
const App = () => (
|
const App = () => (
|
||||||
<Provider>
|
<Provider>
|
||||||
<StatusBar animated={true} backgroundColor={'rgba(0, 0, 0, 0.4)'} barStyle={'light-content'} translucent={true} />
|
<StatusBar animated={true} backgroundColor={'rgba(0, 0, 0, 0.4)'} barStyle={'light-content'} translucent={true} />
|
||||||
|
<TrackPlayerState />
|
||||||
<SplashPage>
|
<SplashPage>
|
||||||
<NavigationContainer theme={theme}>
|
<NavigationContainer theme={theme}>
|
||||||
<RootNavigator />
|
<RootNavigator />
|
||||||
|
|||||||
@ -11,10 +11,12 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-async-storage/async-storage": "^1.15.5",
|
"@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-native-community/masked-view": "^0.1.11",
|
||||||
"@react-navigation/bottom-tabs": "^5.11.11",
|
"@react-navigation/bottom-tabs": "^5.11.11",
|
||||||
"@react-navigation/material-top-tabs": "^5.3.15",
|
"@react-navigation/material-top-tabs": "^5.3.15",
|
||||||
"@react-navigation/native": "^5.9.4",
|
"@react-navigation/native": "^5.9.4",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
"jotai": "^1.1.0",
|
"jotai": "^1.1.0",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
|
|||||||
72
src/components/TrackPlayerState.tsx
Normal file
72
src/components/TrackPlayerState.tsx
Normal file
@ -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 = () => (
|
||||||
|
<View>
|
||||||
|
<TrackPlayerState />
|
||||||
|
<CurrentTrack />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ASDFSADFSAF;
|
||||||
@ -10,8 +10,9 @@ import {
|
|||||||
useWindowDimensions,
|
useWindowDimensions,
|
||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { useCurrentTrackId, useSetQueue } from '../../hooks/player';
|
import { useSetQueue } from '../../hooks/player';
|
||||||
import { albumAtomFamily } from '../../state/music';
|
import { albumAtomFamily } from '../../state/music';
|
||||||
|
import { currentTrackAtom } from '../../state/trackplayer';
|
||||||
import colors from '../../styles/colors';
|
import colors from '../../styles/colors';
|
||||||
import text from '../../styles/text';
|
import text from '../../styles/text';
|
||||||
import AlbumArt from './AlbumArt';
|
import AlbumArt from './AlbumArt';
|
||||||
@ -27,7 +28,7 @@ const SongItem: React.FC<{
|
|||||||
onPress: (event: GestureResponderEvent) => void;
|
onPress: (event: GestureResponderEvent) => void;
|
||||||
}> = ({ id, title, artist, onPress }) => {
|
}> = ({ id, title, artist, onPress }) => {
|
||||||
const [opacity, setOpacity] = useState(1);
|
const [opacity, setOpacity] = useState(1);
|
||||||
const currentTrackId = useCurrentTrackId();
|
const currentTrack = useAtomValue(currentTrackAtom);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@ -50,7 +51,7 @@ const SongItem: React.FC<{
|
|||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
...text.songListTitle,
|
...text.songListTitle,
|
||||||
color: currentTrackId === id ? colors.accent : colors.text.primary,
|
color: currentTrack?.id === id ? colors.accent : colors.text.primary,
|
||||||
}}>
|
}}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useUpdateAtom } from 'jotai/utils';
|
||||||
import TrackPlayer, { Track, useTrackPlayerEvents, Event, State } from 'react-native-track-player';
|
import TrackPlayer, { Track } from 'react-native-track-player';
|
||||||
import { Song } from '../models/music';
|
import { Song } from '../models/music';
|
||||||
|
import { currentTrackAtom } from '../state/trackplayer';
|
||||||
|
|
||||||
function mapSongToTrack(song: Song): Track {
|
function mapSongToTrack(song: Song): Track {
|
||||||
return {
|
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<string | null>(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 = () => {
|
export const useSetQueue = () => {
|
||||||
|
const setCurrentTrack = useUpdateAtom(currentTrackAtom);
|
||||||
|
|
||||||
return async (songs: Song[], playId?: string) => {
|
return async (songs: Song[], playId?: string) => {
|
||||||
await TrackPlayer.reset();
|
await TrackPlayer.reset();
|
||||||
const tracks = songs.map(mapSongToTrack);
|
const tracks = songs.map(mapSongToTrack);
|
||||||
|
|
||||||
|
if (playId) {
|
||||||
|
setCurrentTrack(tracks.find(t => t.id === playId));
|
||||||
|
}
|
||||||
|
|
||||||
if (!playId) {
|
if (!playId) {
|
||||||
await TrackPlayer.add(tracks);
|
await TrackPlayer.add(tracks);
|
||||||
} else if (playId === tracks[0].id) {
|
} else if (playId === tracks[0].id) {
|
||||||
@ -63,8 +40,8 @@ export const useSetQueue = () => {
|
|||||||
|
|
||||||
await TrackPlayer.add(tracks1, 0);
|
await TrackPlayer.add(tracks1, 0);
|
||||||
|
|
||||||
const queue = await TrackPlayer.getQueue();
|
// const queue = await TrackPlayer.getQueue();
|
||||||
console.log(`queue: ${JSON.stringify(queue.map(x => x.title))}`);
|
// console.log(`queue: ${JSON.stringify(queue.map(x => x.title))}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
16
src/state/trackplayer.ts
Normal file
16
src/state/trackplayer.ts
Normal file
@ -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<OptionalTrack>(undefined);
|
||||||
|
export const currentTrackAtom = atom<OptionalTrack, OptionalTrack>(
|
||||||
|
get => get(currentTrack),
|
||||||
|
(get, set, value) => {
|
||||||
|
if (equal(get(currentTrack), value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set(currentTrack, value);
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -1108,6 +1108,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.1.0.tgz#e42b1bef12d2415411519fd528e64b593b1363dc"
|
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.1.0.tgz#e42b1bef12d2415411519fd528e64b593b1363dc"
|
||||||
integrity sha512-W/J0fNYVO01tioHjvYWQ9m6RgndVtbElzYozBq1ZPrHO/iCzlqoySHl4gO/fpCl9QEFjvJfjPgtPMTMlsoq5DQ==
|
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":
|
"@react-native-community/masked-view@^0.1.11":
|
||||||
version "0.1.11"
|
version "0.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce"
|
resolved "https://registry.yarnpkg.com/@react-native-community/masked-view/-/masked-view-0.1.11.tgz#2f4c6e10bee0786abff4604e39a37ded6f3980ce"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user