diff --git a/app/models/music.ts b/app/models/music.ts index 6a9fd7e..5b80cad 100644 --- a/app/models/music.ts +++ b/app/models/music.ts @@ -36,6 +36,18 @@ export interface AlbumWithSongs extends Album { songs: Song[] } +export interface PlaylistListItem { + id: string + name: string + comment?: string + coverArtThumbUri?: string +} + +export interface PlaylistWithSongs extends PlaylistListItem { + songs: Song[] + coverArtUri?: string +} + export interface Song { id: string album?: string diff --git a/app/navigation/BottomTabNavigator.tsx b/app/navigation/BottomTabNavigator.tsx index bfda6f2..62d58ff 100644 --- a/app/navigation/BottomTabNavigator.tsx +++ b/app/navigation/BottomTabNavigator.tsx @@ -4,6 +4,7 @@ import AlbumView from '@app/screens/AlbumView' import ArtistsList from '@app/screens/ArtistsList' import ArtistView from '@app/screens/ArtistView' import Home from '@app/screens/Home' +import PlaylistView from '@app/screens/PlaylistView' import SettingsView from '@app/screens/Settings' import colors from '@app/styles/colors' import font from '@app/styles/font' @@ -17,6 +18,7 @@ type TabStackParamList = { TabMain: { toTop?: boolean } AlbumView: { id: string; title: string } ArtistView: { id: string; title: string } + PlaylistView: { id: string; title: string } } type AlbumScreenNavigationProp = NativeStackNavigationProp @@ -41,6 +43,17 @@ const ArtistScreen: React.FC = ({ route }) => ( ) +type PlaylistScreenNavigationProp = NativeStackNavigationProp +type PlaylistScreenRouteProp = RouteProp +type PlaylistScreenProps = { + route: PlaylistScreenRouteProp + navigation: PlaylistScreenNavigationProp +} + +const PlaylistScreen: React.FC = ({ route }) => ( + +) + const styles = StyleSheet.create({ stackheaderStyle: { backgroundColor: colors.gradient.high, @@ -75,6 +88,7 @@ function createTabStackNavigator(Component: React.ComponentType) { + ) } diff --git a/app/screens/LibraryAlbums.tsx b/app/screens/LibraryAlbums.tsx index 234dc68..03b1888 100644 --- a/app/screens/LibraryAlbums.tsx +++ b/app/screens/LibraryAlbums.tsx @@ -74,6 +74,7 @@ const AlbumsList = () => { return ( item.album.id} @@ -99,6 +100,9 @@ const AlbumsTab = () => ( ) const styles = StyleSheet.create({ + listContent: { + minHeight: '100%', + }, container: { flex: 1, }, diff --git a/app/screens/LibraryArtists.tsx b/app/screens/LibraryArtists.tsx index 0a6e812..8004de1 100644 --- a/app/screens/LibraryArtists.tsx +++ b/app/screens/LibraryArtists.tsx @@ -10,24 +10,20 @@ import { useAtomValue } from 'jotai/utils' import React, { useEffect } from 'react' import { StyleSheet, Text } from 'react-native' -const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => { +const ArtistItem = React.memo<{ item: Artist }>(({ item }) => { const navigation = useNavigation() return ( navigation.navigate('ArtistView', { id: item.id, title: item.name })}> - + {item.name} ) -} +}) -const ArtistItemLoader: React.FC<{ item: Artist }> = props => ( - Loading...}> - - -) +const ArtistRenderItem: React.FC<{ item: Artist }> = ({ item }) => const ArtistsList = () => { const artists = useAtomValue(artistsAtom) @@ -40,12 +36,11 @@ const ArtistsList = () => { } }) - const renderItem: React.FC<{ item: Artist }> = ({ item }) => - return ( item.id} onRefresh={updateArtists} refreshing={updating} @@ -54,9 +49,10 @@ const ArtistsList = () => { ) } -const ArtistsTab = () => - const styles = StyleSheet.create({ + listContent: { + minHeight: '100%', + }, item: { flexDirection: 'row', alignItems: 'center', @@ -68,8 +64,12 @@ const styles = StyleSheet.create({ fontFamily: font.semiBold, fontSize: 16, color: colors.text.primary, - marginLeft: 14, + marginLeft: 10, + }, + art: { + height: 70, + width: 70, }, }) -export default ArtistsTab +export default ArtistsList diff --git a/app/screens/LibraryPlaylists.tsx b/app/screens/LibraryPlaylists.tsx index a90ef90..b6761db 100644 --- a/app/screens/LibraryPlaylists.tsx +++ b/app/screens/LibraryPlaylists.tsx @@ -1,6 +1,93 @@ -import React from 'react' -import GradientBackground from '@app/components/GradientBackground' +import CoverArt from '@app/components/CoverArt' +import GradientFlatList from '@app/components/GradientFlatList' +import PressableOpacity from '@app/components/PressableOpacity' +import { PlaylistListItem } from '@app/models/music' +import { playlistsAtom, playlistsUpdatingAtom, useUpdatePlaylists } from '@app/state/music' +import colors from '@app/styles/colors' +import font from '@app/styles/font' +import { useNavigation } from '@react-navigation/native' +import { useAtomValue } from 'jotai/utils' +import React, { useEffect } from 'react' +import { StyleSheet, Text, View } from 'react-native' -const PlaylistsTab = () => +const PlaylistItem = React.memo<{ item: PlaylistListItem }>(({ item }) => { + const navigation = useNavigation() -export default PlaylistsTab + return ( + navigation.navigate('PlaylistView', { id: item.id, title: item.name })}> + + + + {item.name} + + {item.comment ? ( + + {item.comment} + + ) : ( + <> + )} + + + ) +}) + +const PlaylistRenderItem: React.FC<{ item: PlaylistListItem }> = ({ item }) => + +const PlaylistsList = () => { + const playlists = useAtomValue(playlistsAtom) + const updating = useAtomValue(playlistsUpdatingAtom) + const updatePlaylists = useUpdatePlaylists() + + useEffect(() => { + if (playlists.length === 0) { + updatePlaylists() + } + }) + + return ( + item.id} + onRefresh={updatePlaylists} + refreshing={updating} + overScrollMode="never" + /> + ) +} + +const styles = StyleSheet.create({ + listContent: { + minHeight: '100%', + }, + item: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'flex-start', + marginVertical: 6, + marginLeft: 10, + }, + text: { + marginLeft: 10, + }, + title: { + fontFamily: font.semiBold, + fontSize: 16, + color: colors.text.primary, + }, + subtitle: { + fontFamily: font.regular, + fontSize: 14, + color: colors.text.secondary, + }, + art: { + height: 70, + width: 70, + }, +}) + +export default PlaylistsList diff --git a/app/screens/PlaylistView.tsx b/app/screens/PlaylistView.tsx new file mode 100644 index 0000000..55a5b7f --- /dev/null +++ b/app/screens/PlaylistView.tsx @@ -0,0 +1,117 @@ +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 SongItem from '@app/components/SongItem' +import { playlistAtomFamily } from '@app/state/music' +import { useSetQueue } from '@app/state/trackplayer' +import colors from '@app/styles/colors' +import font from '@app/styles/font' +import { useNavigation } from '@react-navigation/native' +import { useAtomValue } from 'jotai/utils' +import React, { useEffect } from 'react' +import { ActivityIndicator, StyleSheet, Text, View } from 'react-native' + +const PlaylistDetails: React.FC<{ + id: string +}> = ({ id }) => { + const playlist = useAtomValue(playlistAtomFamily(id)) + const setQueue = useSetQueue() + + if (!playlist) { + return <> + } + + return ( + + + + {playlist.name} + {playlist.comment ? {playlist.comment} : <>} + +