mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 09:09:29 +01:00
better/shared list player controls w/shuffle
also faked download list/song (by star for now, also faked)
This commit is contained in:
parent
4f69b36c7b
commit
1ed9ac0870
@ -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
|
||||
|
||||
58
app/components/ListPlayerControls.tsx
Normal file
58
app/components/ListPlayerControls.tsx
Normal 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
|
||||
@ -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,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import Button from '@app/components/Button'
|
||||
import CoverArt from '@app/components/CoverArt'
|
||||
import GradientBackground from '@app/components/GradientBackground'
|
||||
import ImageGradientScrollView from '@app/components/ImageGradientScrollView'
|
||||
import ListPlayerControls from '@app/components/ListPlayerControls'
|
||||
import NothingHere from '@app/components/NothingHere'
|
||||
import SongItem from '@app/components/SongItem'
|
||||
import { albumAtomFamily } from '@app/state/music'
|
||||
@ -25,10 +25,7 @@ const AlbumDetails: React.FC<{
|
||||
|
||||
const Songs = () => (
|
||||
<>
|
||||
<View style={styles.controls}>
|
||||
<Button title="Play Album" onPress={() => setQueue(album.songs, album.name, undefined, false)} />
|
||||
<Button title="Shuffle" onPress={() => setQueue(album.songs, album.name, undefined, true)} />
|
||||
</View>
|
||||
<ListPlayerControls songs={album.songs} typeName="Album" queueName={album.name} />
|
||||
<View style={styles.songs}>
|
||||
{album.songs
|
||||
.sort((a, b) => {
|
||||
@ -114,9 +111,6 @@ const styles = StyleSheet.create({
|
||||
height: 220,
|
||||
width: 220,
|
||||
},
|
||||
controls: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
songs: {
|
||||
marginTop: 26,
|
||||
marginBottom: 30,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import Button from '@app/components/Button'
|
||||
import CoverArt from '@app/components/CoverArt'
|
||||
import GradientBackground from '@app/components/GradientBackground'
|
||||
import ImageGradientScrollView from '@app/components/ImageGradientScrollView'
|
||||
import ListPlayerControls from '@app/components/ListPlayerControls'
|
||||
import NothingHere from '@app/components/NothingHere'
|
||||
import SongItem from '@app/components/SongItem'
|
||||
import { playlistAtomFamily } from '@app/state/music'
|
||||
@ -25,9 +25,12 @@ const PlaylistDetails: React.FC<{
|
||||
|
||||
const Songs = () => (
|
||||
<>
|
||||
<View style={styles.controls}>
|
||||
<Button title="Play Playlist" onPress={() => setQueue(playlist.songs, playlist.name)} />
|
||||
</View>
|
||||
<ListPlayerControls
|
||||
songs={playlist.songs}
|
||||
typeName="Playlist"
|
||||
queueName={playlist.name}
|
||||
style={styles.controls}
|
||||
/>
|
||||
<View style={styles.songs}>
|
||||
{playlist.songs.map((s, i) => (
|
||||
<SongItem key={i} song={s} showArt={true} onPress={() => setQueue(playlist.songs, playlist.name, i)} />
|
||||
@ -107,7 +110,6 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
},
|
||||
controls: {
|
||||
flexDirection: 'row',
|
||||
marginTop: 20,
|
||||
},
|
||||
songs: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user