mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-09 22:42:44 +01:00
context menu base and move query to state
This commit is contained in:
@@ -1,19 +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/lists.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/playlists_list.dart';
|
||||
import '../ui/lists/songs_list.dart';
|
||||
import '../ui/menus.dart';
|
||||
import '../util/custom_scroll_fix.dart';
|
||||
|
||||
const kIconSize = 26.0;
|
||||
@@ -75,25 +74,7 @@ class LibraryTabBarView extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sourceId = ref.watch(sourceIdProvider);
|
||||
|
||||
final albumsQuery = AlbumsQuery(
|
||||
sourceId: sourceId,
|
||||
sort: IList([
|
||||
SortingTerm.albumsDesc(AlbumsColumn.created),
|
||||
]),
|
||||
);
|
||||
|
||||
final songsQuery = SongsQuery(
|
||||
sourceId: sourceId,
|
||||
sort: IList([
|
||||
SortingTerm.songsAsc(SongsColumn.albumArtist),
|
||||
SortingTerm.songsAsc(SongsColumn.album),
|
||||
SortingTerm.songsAsc(SongsColumn.disc),
|
||||
SortingTerm.songsAsc(SongsColumn.track),
|
||||
SortingTerm.songsAsc(SongsColumn.title),
|
||||
]),
|
||||
);
|
||||
final songsQuery = ref.watch(songsQueryProvider);
|
||||
|
||||
return TabBarView(
|
||||
controller: tabController,
|
||||
@@ -102,7 +83,7 @@ class LibraryTabBarView extends HookConsumerWidget {
|
||||
(tab) => TabScrollView(
|
||||
index: LibraryTab.values.indexOf(tab),
|
||||
sliver: switch (tab) {
|
||||
LibraryTab.albums => AlbumsGrid(query: albumsQuery),
|
||||
LibraryTab.albums => AlbumsGrid(),
|
||||
LibraryTab.artists => ArtistsList(),
|
||||
LibraryTab.playlists => PlaylistsList(),
|
||||
LibraryTab.songs => SongsList(
|
||||
@@ -116,6 +97,13 @@ class LibraryTabBarView extends HookConsumerWidget {
|
||||
),
|
||||
// _ => SliverToBoxAdapter(child: Container()),
|
||||
},
|
||||
menuBuilder: switch (tab) {
|
||||
LibraryTab.albums => (_) => AlbumsGridFilters(),
|
||||
// LibraryTab.artists => (_) => AlbumsGridFilters(),
|
||||
// LibraryTab.playlists => (_) => AlbumsGridFilters(),
|
||||
// LibraryTab.songs => (_) => AlbumsGridFilters(),
|
||||
_ => null,
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
@@ -240,10 +228,12 @@ class TabScrollView extends HookConsumerWidget {
|
||||
super.key,
|
||||
required this.index,
|
||||
required this.sliver,
|
||||
this.menuBuilder,
|
||||
});
|
||||
|
||||
final int index;
|
||||
final Widget sliver;
|
||||
final WidgetBuilder? menuBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -251,14 +241,24 @@ class TabScrollView extends HookConsumerWidget {
|
||||
|
||||
final scrollProvider = CustomScrollProviderData.of(context);
|
||||
|
||||
return CustomScrollView(
|
||||
controller: scrollProvider.scrollControllers[index],
|
||||
slivers: <Widget>[
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
sliver,
|
||||
],
|
||||
final listBuilder = menuBuilder;
|
||||
final floatingActionButton = listBuilder != null
|
||||
? FabFilter(
|
||||
listBuilder: listBuilder,
|
||||
)
|
||||
: null;
|
||||
|
||||
return Scaffold(
|
||||
floatingActionButton: floatingActionButton,
|
||||
body: CustomScrollView(
|
||||
controller: scrollProvider.scrollControllers[index],
|
||||
slivers: <Widget>[
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
sliver,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import '../../database/database.dart';
|
||||
import '../../l10n/generated/app_localizations.dart';
|
||||
import '../../util/logger.dart';
|
||||
import '../state/database.dart';
|
||||
import '../util/padding.dart';
|
||||
import '../ui/menus.dart';
|
||||
|
||||
class SettingsSourceScreen extends HookConsumerWidget {
|
||||
const SettingsSourceScreen({
|
||||
|
||||
31
lib/app/state/lists.dart
Normal file
31
lib/app/state/lists.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
import '../../database/query.dart';
|
||||
import 'source.dart';
|
||||
|
||||
final albumsQueryProvider = Provider<AlbumsQuery>((ref) {
|
||||
final sourceId = ref.watch(sourceIdProvider);
|
||||
|
||||
return AlbumsQuery(
|
||||
sourceId: sourceId,
|
||||
sort: IList([
|
||||
SortingTerm.albumsDesc(AlbumsColumn.created),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
final songsQueryProvider = Provider<SongsQuery>((ref) {
|
||||
final sourceId = ref.watch(sourceIdProvider);
|
||||
|
||||
return SongsQuery(
|
||||
sourceId: sourceId,
|
||||
sort: IList([
|
||||
SortingTerm.songsAsc(SongsColumn.albumArtist),
|
||||
SortingTerm.songsAsc(SongsColumn.album),
|
||||
SortingTerm.songsAsc(SongsColumn.disc),
|
||||
SortingTerm.songsAsc(SongsColumn.track),
|
||||
SortingTerm.songsAsc(SongsColumn.title),
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -1,29 +1,28 @@
|
||||
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:infinite_scroll_pagination/infinite_scroll_pagination.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 '../../state/lists.dart';
|
||||
import '../../state/source.dart';
|
||||
import '../menus.dart';
|
||||
import 'items.dart';
|
||||
|
||||
const kPageSize = 60;
|
||||
|
||||
class AlbumsGrid extends HookConsumerWidget {
|
||||
const AlbumsGrid({
|
||||
super.key,
|
||||
required this.query,
|
||||
});
|
||||
|
||||
final AlbumsQuery query;
|
||||
const AlbumsGrid({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final db = ref.watch(databaseProvider);
|
||||
final query = ref.watch(albumsQueryProvider);
|
||||
|
||||
final controller = usePagingController<int, Album>(
|
||||
getNextPageKey: (state) =>
|
||||
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
||||
@@ -36,8 +35,8 @@ class AlbumsGrid extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
|
||||
useOnSourceChange(ref, (_) => controller.refresh());
|
||||
useOnSourceSync(ref, controller.refresh);
|
||||
useValueChanged(query, (_, _) => controller.refresh());
|
||||
|
||||
return PagingListener(
|
||||
controller: controller,
|
||||
@@ -50,7 +49,9 @@ class AlbumsGrid extends HookConsumerWidget {
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
),
|
||||
showNoMoreItemsIndicatorAsGridChild: false,
|
||||
builderDelegate: PagedChildBuilderDelegate<Album>(
|
||||
noMoreItemsIndicatorBuilder: (context) => FabPadding(),
|
||||
itemBuilder: (context, item, index) => AlbumGridTile(
|
||||
album: item,
|
||||
onTap: () async {
|
||||
@@ -64,3 +65,14 @@ class AlbumsGrid extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AlbumsGridFilters extends HookConsumerWidget {
|
||||
const AlbumsGridFilters({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return ListView(
|
||||
children: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import '../../hooks/use_on_source.dart';
|
||||
import '../../hooks/use_paging_controller.dart';
|
||||
import '../../state/database.dart';
|
||||
import '../../state/source.dart';
|
||||
import '../menus.dart';
|
||||
import 'items.dart';
|
||||
|
||||
const kPageSize = 30;
|
||||
@@ -46,6 +47,7 @@ class AlbumsList extends HookConsumerWidget {
|
||||
state: state,
|
||||
fetchNextPage: fetchNextPage,
|
||||
builderDelegate: PagedChildBuilderDelegate<Album>(
|
||||
noMoreItemsIndicatorBuilder: (context) => FabPadding(),
|
||||
itemBuilder: (context, item, index) {
|
||||
final tile = AlbumListTile(
|
||||
album: item,
|
||||
|
||||
@@ -10,6 +10,7 @@ import '../../hooks/use_on_source.dart';
|
||||
import '../../hooks/use_paging_controller.dart';
|
||||
import '../../state/database.dart';
|
||||
import '../../state/source.dart';
|
||||
import '../menus.dart';
|
||||
import 'items.dart';
|
||||
|
||||
const kPageSize = 30;
|
||||
@@ -45,6 +46,7 @@ class ArtistsList extends HookConsumerWidget {
|
||||
state: state,
|
||||
fetchNextPage: fetchNextPage,
|
||||
builderDelegate: PagedChildBuilderDelegate<AristListItem>(
|
||||
noMoreItemsIndicatorBuilder: (context) => FabPadding(),
|
||||
itemBuilder: (context, item, index) {
|
||||
final (:artist, :albumCount) = item;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import '../../hooks/use_on_source.dart';
|
||||
import '../../hooks/use_paging_controller.dart';
|
||||
import '../../state/database.dart';
|
||||
import '../../state/source.dart';
|
||||
import '../menus.dart';
|
||||
import 'items.dart';
|
||||
|
||||
const kPageSize = 30;
|
||||
@@ -45,6 +46,7 @@ class PlaylistsList extends HookConsumerWidget {
|
||||
state: state,
|
||||
fetchNextPage: fetchNextPage,
|
||||
builderDelegate: PagedChildBuilderDelegate<Playlist>(
|
||||
noMoreItemsIndicatorBuilder: (context) => FabPadding(),
|
||||
itemBuilder: (context, item, index) {
|
||||
return PlaylistListTile(
|
||||
playlist: item,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
|
||||
@@ -8,6 +9,7 @@ import '../../hooks/use_on_source.dart';
|
||||
import '../../hooks/use_paging_controller.dart';
|
||||
import '../../state/database.dart';
|
||||
import '../../state/source.dart';
|
||||
import '../menus.dart';
|
||||
|
||||
const kPageSize = 30;
|
||||
|
||||
@@ -37,8 +39,8 @@ class SongsList extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
|
||||
useOnSourceChange(ref, (_) => controller.refresh());
|
||||
useOnSourceSync(ref, controller.refresh);
|
||||
useValueChanged(query, (_, _) => controller.refresh());
|
||||
|
||||
return PagingListener(
|
||||
controller: controller,
|
||||
@@ -47,6 +49,7 @@ class SongsList extends HookConsumerWidget {
|
||||
state: state,
|
||||
fetchNextPage: fetchNextPage,
|
||||
builderDelegate: PagedChildBuilderDelegate<SongListItem>(
|
||||
noMoreItemsIndicatorBuilder: (context) => FabPadding(),
|
||||
itemBuilder: itemBuilder,
|
||||
),
|
||||
);
|
||||
|
||||
76
lib/app/ui/menus.dart
Normal file
76
lib/app/ui/menus.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
Future<void> showContextMenu({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder listBuilder,
|
||||
}) => showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => DraggableScrollableSheet(
|
||||
expand: false,
|
||||
snap: true,
|
||||
initialChildSize: 0.3,
|
||||
minChildSize: 0.3,
|
||||
maxChildSize: 0.4,
|
||||
builder: (context, scrollController) => PrimaryScrollController(
|
||||
controller: scrollController,
|
||||
child: listBuilder(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
class ContextMenuList extends StatelessWidget {
|
||||
const ContextMenuList({
|
||||
super.key,
|
||||
required this.children,
|
||||
});
|
||||
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FabFilter extends StatelessWidget {
|
||||
const FabFilter({
|
||||
super.key,
|
||||
required this.listBuilder,
|
||||
});
|
||||
|
||||
final WidgetBuilder listBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FloatingActionButton(
|
||||
onPressed: () {
|
||||
showContextMenu(
|
||||
context: context,
|
||||
listBuilder: listBuilder,
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
Symbols.filter_list_rounded,
|
||||
weight: 500,
|
||||
opticalSize: 28,
|
||||
size: 28,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FabPadding extends StatelessWidget {
|
||||
const FabPadding({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const SizedBox(height: 86);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FabPadding extends StatelessWidget {
|
||||
const FabPadding({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const SizedBox(height: 86);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user