mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-29 09:29:29 +01:00
started player controls
This commit is contained in:
parent
b908dd87f6
commit
3c969c2972
BIN
res/next-fill.png
Normal file
BIN
res/next-fill.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
BIN
res/pause_circle-fill.png
Normal file
BIN
res/pause_circle-fill.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
res/play_circle-fill.png
Normal file
BIN
res/play_circle-fill.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
res/previous-fill.png
Normal file
BIN
res/previous-fill.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@ -1,8 +1,9 @@
|
|||||||
import { useAtomValue } from 'jotai/utils';
|
import { useAtomValue } from 'jotai/utils';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StatusBar, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
|
import { Pressable, StatusBar, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer';
|
import TrackPlayer, { State } from 'react-native-track-player';
|
||||||
|
import { currentQueueNameAtom, currentTrackAtom, playerStateAtom } from '../state/trackplayer';
|
||||||
import text from '../styles/text';
|
import text from '../styles/text';
|
||||||
import CoverArt from './common/CoverArt';
|
import CoverArt from './common/CoverArt';
|
||||||
import ImageGradientBackground from './common/ImageGradientBackground';
|
import ImageGradientBackground from './common/ImageGradientBackground';
|
||||||
@ -14,7 +15,7 @@ const NowPlayingHeader = () => {
|
|||||||
<View style={headerStyles.container}>
|
<View style={headerStyles.container}>
|
||||||
<FastImage source={require('../../res/arrow_left-fill.png')} style={headerStyles.backArrow} tintColor="white" />
|
<FastImage source={require('../../res/arrow_left-fill.png')} style={headerStyles.backArrow} tintColor="white" />
|
||||||
<Text numberOfLines={2} style={headerStyles.queueName}>
|
<Text numberOfLines={2} style={headerStyles.queueName}>
|
||||||
{queueName}
|
{queueName || 'Nothing playing...'}
|
||||||
</Text>
|
</Text>
|
||||||
<FastImage source={require('../../res/more_vertical.png')} style={headerStyles.more} tintColor="white" />
|
<FastImage source={require('../../res/more_vertical.png')} style={headerStyles.more} tintColor="white" />
|
||||||
</View>
|
</View>
|
||||||
@ -48,16 +49,12 @@ const SongCoverArt = () => {
|
|||||||
const track = useAtomValue(currentTrackAtom);
|
const track = useAtomValue(currentTrackAtom);
|
||||||
const layout = useWindowDimensions();
|
const layout = useWindowDimensions();
|
||||||
|
|
||||||
const size = layout.width - layout.width / 6;
|
const size = layout.width - layout.width / 7;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={coverArtStyles.container}>
|
<View style={coverArtStyles.container}>
|
||||||
<CoverArt
|
<CoverArt
|
||||||
PlaceholderComponent={() => (
|
PlaceholderComponent={() => <View style={{ height: size, width: size }} />}
|
||||||
<View style={{ height: size, width: size }}>
|
|
||||||
<Text>Failed</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
height={size}
|
height={size}
|
||||||
width={size}
|
width={size}
|
||||||
coverArtUri={track?.artwork as string}
|
coverArtUri={track?.artwork as string}
|
||||||
@ -70,7 +67,7 @@ const coverArtStyles = StyleSheet.create({
|
|||||||
container: {
|
container: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginTop: 20,
|
marginTop: 10,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,6 +101,81 @@ const infoStyles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const PlayerControls = () => {
|
||||||
|
const state = useAtomValue(playerStateAtom);
|
||||||
|
|
||||||
|
let playPauseIcon: number;
|
||||||
|
let playPauseStyle: any;
|
||||||
|
let playPauseAction: () => void;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case State.Playing:
|
||||||
|
playPauseIcon = require('../../res/pause_circle-fill.png');
|
||||||
|
playPauseStyle = controlsStyles.enabled;
|
||||||
|
playPauseAction = () => TrackPlayer.pause();
|
||||||
|
break;
|
||||||
|
case State.Paused:
|
||||||
|
playPauseIcon = require('../../res/play_circle-fill.png');
|
||||||
|
playPauseStyle = controlsStyles.enabled;
|
||||||
|
playPauseAction = () => TrackPlayer.play();
|
||||||
|
break;
|
||||||
|
case State.Buffering:
|
||||||
|
case State.Connecting:
|
||||||
|
playPauseIcon = require('../../res/pause_circle-fill.png');
|
||||||
|
playPauseStyle = controlsStyles.disabled;
|
||||||
|
playPauseAction = () => {};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
playPauseIcon = require('../../res/play_circle-fill.png');
|
||||||
|
playPauseStyle = controlsStyles.disabled;
|
||||||
|
playPauseAction = () => {};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={controlsStyles.container}>
|
||||||
|
<FastImage
|
||||||
|
source={require('../../res/previous-fill.png')}
|
||||||
|
tintColor="white"
|
||||||
|
style={{ ...controlsStyles.skip, ...playPauseStyle }}
|
||||||
|
/>
|
||||||
|
<Pressable onPress={playPauseAction}>
|
||||||
|
<FastImage source={playPauseIcon} tintColor="white" style={{ ...controlsStyles.play, ...playPauseStyle }} />
|
||||||
|
</Pressable>
|
||||||
|
<FastImage
|
||||||
|
source={require('../../res/next-fill.png')}
|
||||||
|
tintColor="white"
|
||||||
|
style={{ ...controlsStyles.skip, ...playPauseStyle }}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const controlsStyles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
width: '100%',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginTop: 40,
|
||||||
|
},
|
||||||
|
skip: {
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
marginHorizontal: 18,
|
||||||
|
},
|
||||||
|
play: {
|
||||||
|
height: 90,
|
||||||
|
width: 90,
|
||||||
|
},
|
||||||
|
enabled: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
opacity: 0.35,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const NowPlayingLayout = () => {
|
const NowPlayingLayout = () => {
|
||||||
const track = useAtomValue(currentTrackAtom);
|
const track = useAtomValue(currentTrackAtom);
|
||||||
|
|
||||||
@ -117,6 +189,7 @@ const NowPlayingLayout = () => {
|
|||||||
<NowPlayingHeader />
|
<NowPlayingHeader />
|
||||||
<SongCoverArt />
|
<SongCoverArt />
|
||||||
<SongInfo />
|
<SongInfo />
|
||||||
|
<PlayerControls />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import TrackPlayer, { Event, useTrackPlayerEvents } from 'react-native-track-player';
|
import TrackPlayer, { Event, State, useTrackPlayerEvents } from 'react-native-track-player';
|
||||||
import { useAppState } from '@react-native-community/hooks';
|
import { useAppState } from '@react-native-community/hooks';
|
||||||
import { useUpdateAtom, useAtomValue } from 'jotai/utils';
|
import { useUpdateAtom, useAtomValue } from 'jotai/utils';
|
||||||
import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer';
|
import { currentQueueNameAtom, currentTrackAtom, playerStateAtom } from '../state/trackplayer';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
const CurrentTrackState = () => {
|
const CurrentTrackState = () => {
|
||||||
@ -67,6 +67,42 @@ const CurrentQueueName = () => {
|
|||||||
setCurrentQueueName(undefined);
|
setCurrentQueueName(undefined);
|
||||||
}, [setCurrentQueueName]);
|
}, [setCurrentQueueName]);
|
||||||
|
|
||||||
|
useTrackPlayerEvents(
|
||||||
|
[Event.PlaybackState, Event.PlaybackQueueEnded, Event.PlaybackMetadataReceived, Event.RemoteDuck, Event.RemoteStop],
|
||||||
|
event => {
|
||||||
|
if (event.type === Event.PlaybackState) {
|
||||||
|
if (event.state === State.Stopped || event.state === State.None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (appState === 'active') {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}, [appState, update]);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlayerState = () => {
|
||||||
|
const setPlayerState = useUpdateAtom(playerStateAtom);
|
||||||
|
const appState = useAppState();
|
||||||
|
|
||||||
|
const update = useCallback(
|
||||||
|
async (state?: State) => {
|
||||||
|
setPlayerState(state || (await TrackPlayer.getState()));
|
||||||
|
},
|
||||||
|
[setPlayerState],
|
||||||
|
);
|
||||||
|
|
||||||
|
useTrackPlayerEvents([Event.PlaybackState], event => {
|
||||||
|
update(event.state);
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (appState === 'active') {
|
if (appState === 'active') {
|
||||||
update();
|
update();
|
||||||
@ -90,6 +126,7 @@ const TrackPlayerState = () => (
|
|||||||
<View>
|
<View>
|
||||||
<CurrentTrackState />
|
<CurrentTrackState />
|
||||||
<CurrentQueueName />
|
<CurrentQueueName />
|
||||||
|
<PlayerState />
|
||||||
<Debug />
|
<Debug />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { atom } from 'jotai';
|
import { atom } from 'jotai';
|
||||||
import { Track } from 'react-native-track-player';
|
import { State, Track } from 'react-native-track-player';
|
||||||
import equal from 'fast-deep-equal';
|
import equal from 'fast-deep-equal';
|
||||||
|
|
||||||
type OptionalTrack = Track | undefined;
|
type OptionalTrack = Track | undefined;
|
||||||
@ -25,3 +25,13 @@ export const currentQueueNameAtom = atom<OptionalString, OptionalString>(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const playerState = atom<State>(State.None);
|
||||||
|
export const playerStateAtom = atom<State, State>(
|
||||||
|
get => get(playerState),
|
||||||
|
(get, set, value) => {
|
||||||
|
if (get(playerState) !== value) {
|
||||||
|
set(playerState, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user