songs list tab

This commit is contained in:
austinried 2025-12-06 09:42:53 +09:00
parent 16a79c81cb
commit a4e4c6fa57
6 changed files with 113 additions and 32 deletions

View File

@ -10,6 +10,7 @@ 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 AlbumScreen extends HookConsumerWidget {
@ -61,7 +62,13 @@ class AlbumScreen extends HookConsumerWidget {
onMore: () {},
),
),
SongsList(query: query),
SongsList(
query: query,
itemBuilder: (context, item, index) => SongListTile(
song: item.song,
onTap: () {},
),
),
],
),
),

View File

@ -1,13 +1,18 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../../database/query.dart';
import '../../l10n/generated/app_localizations.dart';
import '../state/services.dart';
import '../state/source.dart';
import '../ui/lists/albums_grid.dart';
import '../ui/lists/artists_list.dart';
import '../ui/lists/items.dart';
import '../ui/lists/songs_list.dart';
import '../util/custom_scroll_fix.dart';
const kIconSize = 26.0;
@ -51,20 +56,7 @@ class LibraryScreen extends HookConsumerWidget {
builder: (context) => CustomScrollProvider(
tabController: tabController,
parent: PrimaryScrollController.of(context),
child: TabBarView(
controller: tabController,
children: LibraryTab.values
.map(
(tab) => TabScrollView(
index: LibraryTab.values.indexOf(tab),
sliver: switch (tab) {
LibraryTab.albums => AlbumsGrid(),
_ => ArtistsList(),
},
),
)
.toList(),
),
child: LibraryTabBarView(tabController: tabController),
),
),
),
@ -72,6 +64,56 @@ class LibraryScreen extends HookConsumerWidget {
}
}
class LibraryTabBarView extends HookConsumerWidget {
const LibraryTabBarView({
super.key,
required this.tabController,
});
final TabController tabController;
@override
Widget build(BuildContext context, WidgetRef ref) {
final sourceId = ref.watch(sourceIdProvider);
final songsQuery = SongsQuery(
sourceId: sourceId,
sort: IList([
SongsSortingTerm(dir: SortDirection.asc, by: SongsColumn.albumArtist),
SongsSortingTerm(dir: SortDirection.asc, by: SongsColumn.album),
SongsSortingTerm(dir: SortDirection.asc, by: SongsColumn.disc),
SongsSortingTerm(dir: SortDirection.asc, by: SongsColumn.track),
SongsSortingTerm(dir: SortDirection.asc, by: SongsColumn.title),
]),
);
return TabBarView(
controller: tabController,
children: LibraryTab.values
.map(
(tab) => TabScrollView(
index: LibraryTab.values.indexOf(tab),
sliver: switch (tab) {
LibraryTab.albums => AlbumsGrid(),
LibraryTab.artists => ArtistsList(),
LibraryTab.songs => SongsList(
query: songsQuery,
itemBuilder: (context, item, index) => SongListTile(
song: item.song,
coverArt: item.albumCoverArt,
showLeading: true,
onTap: () {},
),
),
_ => ArtistsList(),
},
),
)
.toList(),
);
}
}
class LibraryTabsHeader extends HookConsumerWidget {
const LibraryTabsHeader({
super.key,

View File

@ -66,19 +66,25 @@ class SongListTile extends StatelessWidget {
const SongListTile({
super.key,
required this.song,
this.coverArt,
this.showLeading = false,
this.onTap,
});
final Song song;
final String? coverArt;
final bool showLeading;
final void Function()? onTap;
@override
Widget build(BuildContext context) {
return ListTile(
// leading: CoverArtImage(
// coverArt: song.coverArt,
// thumbnail: true,
// ),
leading: showLeading
? CoverArtImage(
coverArt: coverArt,
thumbnail: true,
)
: null,
title: Text(song.title),
subtitle: Text(song.artist ?? ''),
onTap: onTap,

View File

@ -2,12 +2,11 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import '../../../database/dao/library_dao.dart';
import '../../../database/query.dart';
import '../../../sources/models.dart';
import '../../hooks/use_on_source.dart';
import '../../hooks/use_paging_controller.dart';
import '../../state/database.dart';
import 'items.dart';
const kPageSize = 30;
@ -15,14 +14,17 @@ class SongsList extends HookConsumerWidget {
const SongsList({
super.key,
required this.query,
required this.itemBuilder,
});
final SongsQuery query;
final Widget Function(BuildContext context, SongListItem item, int index)
itemBuilder;
@override
Widget build(BuildContext context, WidgetRef ref) {
final db = ref.watch(databaseProvider);
final controller = usePagingController<int, Song>(
final controller = usePagingController<int, SongListItem>(
getNextPageKey: (state) =>
state.lastPageIsEmpty ? null : state.nextIntPageKey,
fetchPage: (pageKey) => db.libraryDao.listSongs(
@ -42,13 +44,8 @@ class SongsList extends HookConsumerWidget {
return PagedSliverList(
state: state,
fetchNextPage: fetchNextPage,
builderDelegate: PagedChildBuilderDelegate<Song>(
itemBuilder: (context, item, index) {
return SongListTile(
song: item,
onTap: () async {},
);
},
builderDelegate: PagedChildBuilderDelegate<SongListItem>(
itemBuilder: itemBuilder,
),
);
},

View File

@ -6,7 +6,15 @@ import '../query.dart';
part 'library_dao.g.dart';
typedef AristListItem = ({models.Artist artist, int? albumCount});
typedef AristListItem = ({
models.Artist artist,
int? albumCount,
});
typedef SongListItem = ({
models.Song song,
String? albumCoverArt,
});
extension on SortDirection {
OrderingMode toMode() => switch (this) {
@ -111,7 +119,7 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
.get();
}
Future<List<models.Song>> listSongs(SongsQuery q) {
Future<List<SongListItem>> listSongs(SongsQuery q) {
var joinPlaylistSongs = false;
var filter = songs.sourceId.equals(q.sourceId);
@ -127,6 +135,11 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
final query =
songs.select().join([
leftOuterJoin(
albums,
albums.id.equalsExp(songs.albumId) &
albums.sourceId.equals(q.sourceId),
),
if (joinPlaylistSongs)
leftOuterJoin(
playlistSongs,
@ -134,6 +147,9 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
playlistSongs.songId.equalsExp(songs.id),
),
])
..addColumns([
albums.coverArt,
])
..where(filter)
..orderBy(
q.sort
@ -144,6 +160,9 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
SongsColumn.starred => songs.starred,
SongsColumn.disc => songs.disc,
SongsColumn.track => songs.track,
SongsColumn.album => songs.album,
SongsColumn.artist => songs.artist,
SongsColumn.albumArtist => albums.albumArtist,
},
mode: sort.dir.toMode(),
),
@ -153,7 +172,14 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
_limitQuery(query: query, limit: q.limit, offset: q.offset);
return query.map((row) => (row.readTable(songs))).get();
return query
.map(
(row) => (
song: row.readTable(songs),
albumCoverArt: row.read(albums.coverArt),
),
)
.get();
}
Selectable<models.Album> getAlbum(int sourceId, String id) {

View File

@ -27,6 +27,9 @@ enum SongsColumn {
starred,
disc,
track,
album,
artist,
albumArtist,
}
@freezed