started player controls

This commit is contained in:
austinried 2021-07-04 14:27:25 +09:00
parent b908dd87f6
commit 3c969c2972
7 changed files with 133 additions and 13 deletions

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -1,8 +1,9 @@
import { useAtomValue } from 'jotai/utils';
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 { 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 CoverArt from './common/CoverArt';
import ImageGradientBackground from './common/ImageGradientBackground';
@ -14,7 +15,7 @@ const NowPlayingHeader = () => {
<View style={headerStyles.container}>
<FastImage source={require('../../res/arrow_left-fill.png')} style={headerStyles.backArrow} tintColor="white" />
<Text numberOfLines={2} style={headerStyles.queueName}>
{queueName}
{queueName || 'Nothing playing...'}
</Text>
<FastImage source={require('../../res/more_vertical.png')} style={headerStyles.more} tintColor="white" />
</View>
@ -48,16 +49,12 @@ const SongCoverArt = () => {
const track = useAtomValue(currentTrackAtom);
const layout = useWindowDimensions();
const size = layout.width - layout.width / 6;
const size = layout.width - layout.width / 7;
return (
<View style={coverArtStyles.container}>
<CoverArt
PlaceholderComponent={() => (
<View style={{ height: size, width: size }}>
<Text>Failed</Text>
</View>
)}
PlaceholderComponent={() => <View style={{ height: size, width: size }} />}
height={size}
width={size}
coverArtUri={track?.artwork as string}
@ -70,7 +67,7 @@ const coverArtStyles = StyleSheet.create({
container: {
width: '100%',
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 track = useAtomValue(currentTrackAtom);
@ -117,6 +189,7 @@ const NowPlayingLayout = () => {
<NowPlayingHeader />
<SongCoverArt />
<SongInfo />
<PlayerControls />
</View>
);
};

View File

@ -1,8 +1,8 @@
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 { useUpdateAtom, useAtomValue } from 'jotai/utils';
import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer';
import { currentQueueNameAtom, currentTrackAtom, playerStateAtom } from '../state/trackplayer';
import { View } from 'react-native';
const CurrentTrackState = () => {
@ -67,6 +67,42 @@ const CurrentQueueName = () => {
setCurrentQueueName(undefined);
}, [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(() => {
if (appState === 'active') {
update();
@ -90,6 +126,7 @@ const TrackPlayerState = () => (
<View>
<CurrentTrackState />
<CurrentQueueName />
<PlayerState />
<Debug />
</View>
);

View File

@ -1,5 +1,5 @@
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';
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);
}
},
);