diff --git a/lib/app/router.dart b/lib/app/router.dart index abd4f77..7150f9c 100644 --- a/lib/app/router.dart +++ b/lib/app/router.dart @@ -4,6 +4,7 @@ import 'screens/album_screen.dart'; import 'screens/artist_screen.dart'; import 'screens/library_screen.dart'; import 'screens/now_playing_screen.dart'; +import 'screens/playlist_screen.dart'; import 'screens/preload_screen.dart'; import 'screens/root_shell_screen.dart'; import 'screens/settings_screen.dart'; @@ -23,14 +24,19 @@ final router = GoRouter( builder: (context, state) => LibraryScreen(), routes: [ GoRoute( - path: 'album/:id', + path: 'albums/:id', builder: (context, state) => AlbumScreen(id: state.pathParameters['id']!), ), GoRoute( - path: 'artist', + path: 'artists', builder: (context, state) => ArtistScreen(), ), + GoRoute( + path: 'playlists/:id', + builder: (context, state) => + PlaylistScreen(id: state.pathParameters['id']!), + ), ], ), ], diff --git a/lib/app/screens/playlist_screen.dart b/lib/app/screens/playlist_screen.dart new file mode 100644 index 0000000..89a1694 --- /dev/null +++ b/lib/app/screens/playlist_screen.dart @@ -0,0 +1,77 @@ +import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import '../../database/query.dart'; +import '../../l10n/generated/app_localizations.dart'; +import '../state/database.dart'; +import '../state/source.dart'; +import '../ui/cover_art_theme.dart'; +import '../ui/gradient.dart'; +import '../ui/lists/header.dart'; +import '../ui/lists/items.dart'; +import '../ui/lists/songs_list.dart'; + +class PlaylistScreen extends HookConsumerWidget { + const PlaylistScreen({ + super.key, + required this.id, + }); + + final String id; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final l = AppLocalizations.of(context); + + final db = ref.watch(databaseProvider); + final sourceId = ref.watch(sourceIdProvider); + + final getPlaylist = useMemoized( + () => db.libraryDao.getPlaylist(sourceId, id).getSingle(), + ); + final playlist = useFuture(getPlaylist).data; + + if (playlist == null) { + return Container(); + } + + final query = SongsQuery( + sourceId: sourceId, + filter: IList([SongsFilter.playlistId(playlist.id)]), + sort: IList([ + SortingTerm.songsAsc(SongsColumn.playlistPosition), + ]), + ); + + return CoverArtTheme( + coverArt: playlist.coverArt, + child: Scaffold( + body: GradientScrollView( + slivers: [ + SliverToBoxAdapter( + child: SongsListHeader( + title: playlist.name, + // subtitle: playlist.albumArtist, + coverArt: playlist.coverArt, + playText: l.resourcesPlaylistActionsPlay, + onPlay: () {}, + onMore: () {}, + ), + ), + SongsList( + query: query, + itemBuilder: (context, item, index) => SongListTile( + song: item.song, + coverArt: item.albumCoverArt, + showLeading: true, + onTap: () {}, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/app/ui/lists/albums_grid.dart b/lib/app/ui/lists/albums_grid.dart index 00c4011..bb7566c 100644 --- a/lib/app/ui/lists/albums_grid.dart +++ b/lib/app/ui/lists/albums_grid.dart @@ -53,7 +53,7 @@ class AlbumsGrid extends HookConsumerWidget { itemBuilder: (context, item, index) => AlbumGridTile( album: item, onTap: () async { - context.push('/album/${item.id}'); + context.push('/albums/${item.id}'); }, ), ), diff --git a/lib/app/ui/lists/artists_list.dart b/lib/app/ui/lists/artists_list.dart index 5e564b8..a6177a9 100644 --- a/lib/app/ui/lists/artists_list.dart +++ b/lib/app/ui/lists/artists_list.dart @@ -52,7 +52,7 @@ class ArtistsList extends HookConsumerWidget { artist: artist, albumCount: albumCount, onTap: () async { - context.push('/artist/${artist.id}'); + context.push('/artists/${artist.id}'); }, ); }, diff --git a/lib/app/ui/lists/header.dart b/lib/app/ui/lists/header.dart index 671ba25..be101c0 100644 --- a/lib/app/ui/lists/header.dart +++ b/lib/app/ui/lists/header.dart @@ -62,11 +62,12 @@ class SongsListHeader extends HookConsumerWidget { style: theme.textTheme.headlineMedium, textAlign: TextAlign.center, ), - Text( - subtitle ?? '', - style: theme.textTheme.headlineSmall, - textAlign: TextAlign.center, - ), + if (subtitle != null) + Text( + subtitle!, + style: theme.textTheme.headlineSmall, + textAlign: TextAlign.center, + ), ], ), const SizedBox(height: 20), diff --git a/lib/app/ui/lists/playlists_list.dart b/lib/app/ui/lists/playlists_list.dart index b4c964e..aea957f 100644 --- a/lib/app/ui/lists/playlists_list.dart +++ b/lib/app/ui/lists/playlists_list.dart @@ -1,5 +1,6 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; @@ -47,7 +48,9 @@ class PlaylistsList extends HookConsumerWidget { itemBuilder: (context, item, index) { return PlaylistListTile( playlist: item, - onTap: () {}, + onTap: () { + context.push('/playlists/${item.id}'); + }, ); }, ), diff --git a/lib/database/dao/library_dao.dart b/lib/database/dao/library_dao.dart index 2cec8fa..7e0059b 100644 --- a/lib/database/dao/library_dao.dart +++ b/lib/database/dao/library_dao.dart @@ -163,6 +163,7 @@ class LibraryDao extends DatabaseAccessor SongsColumn.album => songs.album, SongsColumn.artist => songs.artist, SongsColumn.albumArtist => albums.albumArtist, + SongsColumn.playlistPosition => playlistSongs.position, }, mode: sort.dir.toMode(), ), @@ -224,6 +225,12 @@ class LibraryDao extends DatabaseAccessor ); } + Selectable getPlaylist(int sourceId, String id) { + return db.managers.playlists.filter( + (f) => f.sourceId.equals(sourceId) & f.id.equals(id), + ); + } + void _limitQuery({ required LimitContainerMixin query, required int? limit, diff --git a/lib/database/query.dart b/lib/database/query.dart index f4a2847..ec9f22d 100644 --- a/lib/database/query.dart +++ b/lib/database/query.dart @@ -30,6 +30,7 @@ enum SongsColumn { album, artist, albumArtist, + playlistPosition, } enum PlaylistsColumn { diff --git a/lib/database/query.g.dart b/lib/database/query.g.dart index 2418355..32f97a3 100644 --- a/lib/database/query.g.dart +++ b/lib/database/query.g.dart @@ -74,6 +74,7 @@ const _$SongsColumnEnumMap = { SongsColumn.album: 'album', SongsColumn.artist: 'artist', SongsColumn.albumArtist: 'albumArtist', + SongsColumn.playlistPosition: 'playlistPosition', }; PlaylistsSortingTerm _$PlaylistsSortingTermFromJson(