better/shared list player controls w/shuffle

also faked download list/song (by star for now, also faked)
This commit is contained in:
austinried
2021-07-23 17:46:29 +09:00
parent 4f69b36c7b
commit 1ed9ac0870
5 changed files with 128 additions and 27 deletions

View File

@@ -5,12 +5,15 @@ import { GestureResponderEvent, StyleSheet, Text } from 'react-native'
import PressableOpacity from './PressableOpacity'
const Button: React.FC<{
title: string
title?: string
buttonStyle?: 'hollow' | 'highlight'
onPress: (event: GestureResponderEvent) => void
}> = ({ title, onPress }) => {
}> = ({ title, buttonStyle, onPress, children }) => {
return (
<PressableOpacity onPress={onPress} style={styles.container}>
<Text style={styles.text}>{title}</Text>
<PressableOpacity
onPress={onPress}
style={[styles.container, buttonStyle !== undefined ? styles[buttonStyle] : {}]}>
{title ? <Text style={styles.text}>{title}</Text> : children}
</PressableOpacity>
)
}
@@ -18,16 +21,26 @@ const Button: React.FC<{
const styles = StyleSheet.create({
container: {
backgroundColor: colors.accent,
paddingHorizontal: 24,
paddingHorizontal: 10,
minHeight: 42,
justifyContent: 'center',
borderRadius: 1000,
},
hollow: {
backgroundColor: 'transparent',
borderColor: colors.text.secondary,
borderWidth: 1.5,
},
highlight: {
borderColor: colors.text.primary,
borderWidth: 1.5,
},
text: {
fontSize: 15,
fontSize: 16,
fontFamily: font.bold,
color: colors.text.primary,
paddingHorizontal: 14,
},
})
export default React.memo(Button)
export default Button

View File

@@ -0,0 +1,58 @@
import Button from '@app/components/Button'
import { Song } from '@app/models/music'
import { useSetQueue } from '@app/state/trackplayer'
import colors from '@app/styles/colors'
import React, { useState } from 'react'
import { StyleSheet, View, ViewStyle } from 'react-native'
import Icon from 'react-native-vector-icons/Ionicons'
import IconMat from 'react-native-vector-icons/MaterialIcons'
const ListPlayerControls = React.memo<{
songs: Song[]
typeName: string
queueName: string
style?: ViewStyle
}>(({ songs, typeName, queueName, style }) => {
const [downloaded, setDownloaded] = useState(false)
const setQueue = useSetQueue()
return (
<View style={[styles.controls, style]}>
<View style={styles.controlsSide}>
<Button buttonStyle={downloaded ? 'highlight' : 'hollow'} onPress={() => setDownloaded(!downloaded)}>
{downloaded ? (
<IconMat name="file-download-done" size={26} color={colors.text.primary} />
) : (
<IconMat name="file-download" size={26} color={colors.text.secondary} />
)}
</Button>
</View>
<View style={styles.controlsCenter}>
<Button title={`Play ${typeName}`} onPress={() => setQueue(songs, queueName, undefined, false)} />
</View>
<View style={styles.controlsSide}>
<Button onPress={() => setQueue(songs, queueName, undefined, true)}>
<Icon name="shuffle" size={26} color="white" />
</Button>
</View>
</View>
)
})
const styles = StyleSheet.create({
controls: {
flexDirection: 'row',
},
controlsSide: {
flex: 4,
flexDirection: 'row',
justifyContent: 'center',
},
controlsCenter: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
})
export default ListPlayerControls

View File

@@ -3,9 +3,11 @@ import { currentTrackAtom } from '@app/state/trackplayer'
import colors from '@app/styles/colors'
import font from '@app/styles/font'
import { useAtomValue } from 'jotai/utils'
import React from 'react'
import React, { useState } from 'react'
import { GestureResponderEvent, StyleSheet, Text, View } from 'react-native'
import IconFA from 'react-native-vector-icons/FontAwesome'
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
import IconMat from 'react-native-vector-icons/MaterialIcons'
import CoverArt from './CoverArt'
import PressableOpacity from './PressableOpacity'
@@ -16,23 +18,42 @@ const SongItem: React.FC<{
subtitle?: 'artist' | 'album'
}> = ({ song, onPress, showArt, subtitle }) => {
const currentTrack = useAtomValue(currentTrackAtom)
const [starred, setStarred] = useState(false)
subtitle = subtitle || 'artist'
const playing = currentTrack?.id === song.id
return (
<View style={styles.container}>
<PressableOpacity onPress={onPress} style={styles.item}>
{showArt ? <CoverArt coverArtUri={song.coverArtThumbUri} style={styles.art} /> : <></>}
<View style={styles.text}>
<Text style={[styles.title, { color: currentTrack?.id === song.id ? colors.accent : colors.text.primary }]}>
{song.title}
</Text>
<Text style={styles.subtitle}>{song[subtitle]}</Text>
<View style={styles.textLine}>
{playing ? <IconFA5 name="play" size={10} color={colors.accent} style={styles.playingIcon} /> : <></>}
<Text style={[styles.title, { color: playing ? colors.accent : colors.text.primary }]}>{song.title}</Text>
</View>
<View style={styles.textLine}>
{starred ? (
<IconMat
name="file-download-done"
size={17}
color={colors.text.secondary}
style={styles.downloadedIcon}
/>
) : (
<></>
)}
<Text style={styles.subtitle}>{song[subtitle]}</Text>
</View>
</View>
</PressableOpacity>
<View style={styles.controls}>
<PressableOpacity onPress={undefined}>
<IconFA name="star-o" size={26} color={colors.text.secondary} />
<PressableOpacity onPress={() => setStarred(!starred)}>
{starred ? (
<IconFA name="star" size={26} color={colors.accent} />
) : (
<IconFA name="star-o" size={26} color={colors.text.secondary} />
)}
</PressableOpacity>
</View>
</View>
@@ -60,10 +81,23 @@ const styles = StyleSheet.create({
text: {
flex: 1,
},
textLine: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
},
title: {
fontSize: 16,
fontFamily: font.semiBold,
},
playingIcon: {
marginRight: 5,
marginLeft: 1,
},
downloadedIcon: {
marginRight: 2,
marginLeft: -3,
},
subtitle: {
fontSize: 14,
fontFamily: font.regular,