#27 albums: action to create empty album
This commit is contained in:
parent
bcbb7d5994
commit
2f34c3c48d
14 changed files with 105 additions and 6 deletions
|
@ -59,6 +59,8 @@
|
||||||
"@chipActionRename": {},
|
"@chipActionRename": {},
|
||||||
"chipActionSetCover": "Set cover",
|
"chipActionSetCover": "Set cover",
|
||||||
"@chipActionSetCover": {},
|
"@chipActionSetCover": {},
|
||||||
|
"chipActionCreateAlbum": "Create album",
|
||||||
|
"@chipActionCreateAlbum": {},
|
||||||
|
|
||||||
"entryActionDelete": "Delete",
|
"entryActionDelete": "Delete",
|
||||||
"@entryActionDelete": {},
|
"@entryActionDelete": {},
|
||||||
|
@ -515,6 +517,8 @@
|
||||||
"@createAlbumTooltip": {},
|
"@createAlbumTooltip": {},
|
||||||
"createAlbumButtonLabel": "CREATE",
|
"createAlbumButtonLabel": "CREATE",
|
||||||
"@createAlbumButtonLabel": {},
|
"@createAlbumButtonLabel": {},
|
||||||
|
"newFilterBanner": "new",
|
||||||
|
"@newFilterBanner": {},
|
||||||
|
|
||||||
"countryPageTitle": "Countries",
|
"countryPageTitle": "Countries",
|
||||||
"@countryPageTitle": {},
|
"@countryPageTitle": {},
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
"chipActionUnpin": "고정 해제",
|
"chipActionUnpin": "고정 해제",
|
||||||
"chipActionRename": "이름 변경",
|
"chipActionRename": "이름 변경",
|
||||||
"chipActionSetCover": "대표 이미지 변경",
|
"chipActionSetCover": "대표 이미지 변경",
|
||||||
|
"chipActionCreateAlbum": "앨범 만들기",
|
||||||
|
|
||||||
"entryActionDelete": "삭제",
|
"entryActionDelete": "삭제",
|
||||||
"entryActionExport": "내보내기",
|
"entryActionExport": "내보내기",
|
||||||
|
@ -232,8 +233,9 @@
|
||||||
|
|
||||||
"albumPageTitle": "앨범",
|
"albumPageTitle": "앨범",
|
||||||
"albumEmpty": "앨범이 없습니다",
|
"albumEmpty": "앨범이 없습니다",
|
||||||
"createAlbumTooltip": "새 앨범 만들기",
|
"createAlbumTooltip": "앨범 만들기",
|
||||||
"createAlbumButtonLabel": "추가",
|
"createAlbumButtonLabel": "추가",
|
||||||
|
"newFilterBanner": "신규",
|
||||||
|
|
||||||
"countryPageTitle": "국가",
|
"countryPageTitle": "국가",
|
||||||
"countryEmpty": "국가가 없습니다",
|
"countryEmpty": "국가가 없습니다",
|
||||||
|
|
|
@ -10,6 +10,7 @@ enum ChipSetAction {
|
||||||
selectAll,
|
selectAll,
|
||||||
selectNone,
|
selectNone,
|
||||||
stats,
|
stats,
|
||||||
|
createAlbum,
|
||||||
// single/multiple filters
|
// single/multiple filters
|
||||||
delete,
|
delete,
|
||||||
hide,
|
hide,
|
||||||
|
@ -36,6 +37,8 @@ extension ExtraChipSetAction on ChipSetAction {
|
||||||
return context.l10n.collectionActionSelectNone;
|
return context.l10n.collectionActionSelectNone;
|
||||||
case ChipSetAction.stats:
|
case ChipSetAction.stats:
|
||||||
return context.l10n.menuActionStats;
|
return context.l10n.menuActionStats;
|
||||||
|
case ChipSetAction.createAlbum:
|
||||||
|
return context.l10n.chipActionCreateAlbum;
|
||||||
// single/multiple filters
|
// single/multiple filters
|
||||||
case ChipSetAction.delete:
|
case ChipSetAction.delete:
|
||||||
return context.l10n.chipActionDelete;
|
return context.l10n.chipActionDelete;
|
||||||
|
@ -67,6 +70,8 @@ extension ExtraChipSetAction on ChipSetAction {
|
||||||
return null;
|
return null;
|
||||||
case ChipSetAction.stats:
|
case ChipSetAction.stats:
|
||||||
return AIcons.stats;
|
return AIcons.stats;
|
||||||
|
case ChipSetAction.createAlbum:
|
||||||
|
return AIcons.createAlbum;
|
||||||
// single/multiple filters
|
// single/multiple filters
|
||||||
case ChipSetAction.delete:
|
case ChipSetAction.delete:
|
||||||
return AIcons.delete;
|
return AIcons.delete;
|
||||||
|
|
|
@ -10,9 +10,12 @@ import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
mixin AlbumMixin on SourceBase {
|
mixin AlbumMixin on SourceBase {
|
||||||
final Set<String?> _directories = {};
|
final Set<String?> _directories = {};
|
||||||
|
final Set<String> _newAlbums = {};
|
||||||
|
|
||||||
List<String> get rawAlbums => List.unmodifiable(_directories);
|
List<String> get rawAlbums => List.unmodifiable(_directories);
|
||||||
|
|
||||||
|
Set<AlbumFilter> getNewAlbumFilters(BuildContext context) => Set.unmodifiable(_newAlbums.map((v) => AlbumFilter(v, getAlbumDisplayName(context, v))));
|
||||||
|
|
||||||
int compareAlbumsByName(String a, String b) {
|
int compareAlbumsByName(String a, String b) {
|
||||||
final ua = getAlbumDisplayName(null, a);
|
final ua = getAlbumDisplayName(null, a);
|
||||||
final ub = getAlbumDisplayName(null, b);
|
final ub = getAlbumDisplayName(null, b);
|
||||||
|
@ -109,7 +112,7 @@ mixin AlbumMixin on SourceBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanEmptyAlbums([Set<String?>? albums]) {
|
void cleanEmptyAlbums([Set<String?>? albums]) {
|
||||||
final emptyAlbums = (albums ?? _directories).where(_isEmptyAlbum).toSet();
|
final emptyAlbums = (albums ?? _directories).where((v) => _isEmptyAlbum(v) && !_newAlbums.contains(v)).toSet();
|
||||||
if (emptyAlbums.isNotEmpty) {
|
if (emptyAlbums.isNotEmpty) {
|
||||||
_directories.removeAll(emptyAlbums);
|
_directories.removeAll(emptyAlbums);
|
||||||
_notifyAlbumChange();
|
_notifyAlbumChange();
|
||||||
|
@ -148,6 +151,22 @@ mixin AlbumMixin on SourceBase {
|
||||||
AvesEntry? albumRecentEntry(AlbumFilter filter) {
|
AvesEntry? albumRecentEntry(AlbumFilter filter) {
|
||||||
return _filterRecentEntryMap.putIfAbsent(filter.album, () => sortedEntriesByDate.firstWhereOrNull(filter.test));
|
return _filterRecentEntryMap.putIfAbsent(filter.album, () => sortedEntriesByDate.firstWhereOrNull(filter.test));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void createAlbum(String directory) {
|
||||||
|
_newAlbums.add(directory);
|
||||||
|
addDirectories({directory});
|
||||||
|
}
|
||||||
|
|
||||||
|
void renameNewAlbum(String source, String destination) {
|
||||||
|
if (_newAlbums.remove(source)) {
|
||||||
|
cleanEmptyAlbums({source});
|
||||||
|
createAlbum(destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void forgetNewAlbums(Set<String> directories) {
|
||||||
|
_newAlbums.removeAll(directories);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlbumsChangedEvent {}
|
class AlbumsChangedEvent {}
|
||||||
|
|
|
@ -162,6 +162,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
|
||||||
final pinned = settings.pinnedFilters.contains(oldFilter);
|
final pinned = settings.pinnedFilters.contains(oldFilter);
|
||||||
final oldCoverContentId = covers.coverContentId(oldFilter);
|
final oldCoverContentId = covers.coverContentId(oldFilter);
|
||||||
final coverEntry = oldCoverContentId != null ? todoEntries.firstWhereOrNull((entry) => entry.contentId == oldCoverContentId) : null;
|
final coverEntry = oldCoverContentId != null ? todoEntries.firstWhereOrNull((entry) => entry.contentId == oldCoverContentId) : null;
|
||||||
|
renameNewAlbum(sourceAlbum, destinationAlbum);
|
||||||
await updateAfterMove(
|
await updateAfterMove(
|
||||||
todoEntries: todoEntries,
|
todoEntries: todoEntries,
|
||||||
copy: false,
|
copy: false,
|
||||||
|
|
|
@ -23,6 +23,7 @@ class AvesFilterChip extends StatefulWidget {
|
||||||
final bool removable;
|
final bool removable;
|
||||||
final bool showGenericIcon;
|
final bool showGenericIcon;
|
||||||
final Widget? background;
|
final Widget? background;
|
||||||
|
final String? banner;
|
||||||
final Widget? details;
|
final Widget? details;
|
||||||
final BorderRadius? borderRadius;
|
final BorderRadius? borderRadius;
|
||||||
final double padding;
|
final double padding;
|
||||||
|
@ -43,6 +44,7 @@ class AvesFilterChip extends StatefulWidget {
|
||||||
this.removable = false,
|
this.removable = false,
|
||||||
this.showGenericIcon = true,
|
this.showGenericIcon = true,
|
||||||
this.background,
|
this.background,
|
||||||
|
this.banner,
|
||||||
this.details,
|
this.details,
|
||||||
this.borderRadius,
|
this.borderRadius,
|
||||||
this.padding = 6.0,
|
this.padding = 6.0,
|
||||||
|
@ -195,6 +197,7 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
|
||||||
}
|
}
|
||||||
|
|
||||||
final borderRadius = widget.borderRadius ?? const BorderRadius.all(Radius.circular(AvesFilterChip.defaultRadius));
|
final borderRadius = widget.borderRadius ?? const BorderRadius.all(Radius.circular(AvesFilterChip.defaultRadius));
|
||||||
|
final banner = widget.banner;
|
||||||
Widget chip = Container(
|
Widget chip = Container(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
minWidth: AvesFilterChip.minChipWidth,
|
minWidth: AvesFilterChip.minChipWidth,
|
||||||
|
@ -250,6 +253,21 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (banner != null)
|
||||||
|
LayoutBuilder(builder: (context, constraints) {
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
child: Transform(
|
||||||
|
transform: Matrix4.identity().scaled((constraints.maxHeight / 90 - .4).clamp(.45, 1.0)),
|
||||||
|
child: Banner(
|
||||||
|
message: banner.toUpperCase(),
|
||||||
|
location: BannerLocation.topStart,
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
child: const SizedBox(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -66,6 +66,7 @@ class _AlbumPickPageState extends State<AlbumPickPage> {
|
||||||
),
|
),
|
||||||
appBarHeight: AlbumPickAppBar.preferredHeight,
|
appBarHeight: AlbumPickAppBar.preferredHeight,
|
||||||
sections: AlbumListPage.groupToSections(context, gridItems),
|
sections: AlbumListPage.groupToSections(context, gridItems),
|
||||||
|
newFilters: source.getNewAlbumFilters(context),
|
||||||
sortFactor: settings.albumSortFactor,
|
sortFactor: settings.albumSortFactor,
|
||||||
showHeaders: settings.albumGroupFactor != AlbumChipGroupFactor.none,
|
showHeaders: settings.albumGroupFactor != AlbumChipGroupFactor.none,
|
||||||
selectable: false,
|
selectable: false,
|
||||||
|
|
|
@ -46,6 +46,7 @@ class AlbumListPage extends StatelessWidget {
|
||||||
showHeaders: settings.albumGroupFactor != AlbumChipGroupFactor.none,
|
showHeaders: settings.albumGroupFactor != AlbumChipGroupFactor.none,
|
||||||
actionDelegate: AlbumChipSetActionDelegate(gridItems),
|
actionDelegate: AlbumChipSetActionDelegate(gridItems),
|
||||||
filterSections: groupToSections(context, gridItems),
|
filterSections: groupToSections(context, gridItems),
|
||||||
|
newFilters: source.getNewAlbumFilters(context),
|
||||||
emptyBuilder: () => EmptyContent(
|
emptyBuilder: () => EmptyContent(
|
||||||
icon: AIcons.album,
|
icon: AIcons.album,
|
||||||
text: context.l10n.albumEmpty,
|
text: context.l10n.albumEmpty,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/actions/chip_set_actions.dart';
|
||||||
import 'package:aves/model/actions/move_type.dart';
|
import 'package:aves/model/actions/move_type.dart';
|
||||||
import 'package:aves/model/filters/album.dart';
|
import 'package:aves/model/filters/album.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
|
import 'package:aves/model/highlight.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/model/source/collection_source.dart';
|
import 'package:aves/model/source/collection_source.dart';
|
||||||
import 'package:aves/model/source/enums.dart';
|
import 'package:aves/model/source/enums.dart';
|
||||||
|
@ -14,8 +15,10 @@ import 'package:aves/utils/android_file_utils.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/create_album_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/rename_album_dialog.dart';
|
import 'package:aves/widgets/dialogs/rename_album_dialog.dart';
|
||||||
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
|
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
@ -67,6 +70,9 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> {
|
||||||
case ChipSetAction.group:
|
case ChipSetAction.group:
|
||||||
_showGroupDialog(context);
|
_showGroupDialog(context);
|
||||||
break;
|
break;
|
||||||
|
case ChipSetAction.createAlbum:
|
||||||
|
_createAlbum(context);
|
||||||
|
break;
|
||||||
// single/multiple filters
|
// single/multiple filters
|
||||||
case ChipSetAction.delete:
|
case ChipSetAction.delete:
|
||||||
_showDeleteDialog(context, filters);
|
_showDeleteDialog(context, filters);
|
||||||
|
@ -101,13 +107,35 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _createAlbum(BuildContext context) async {
|
||||||
|
final newAlbum = await showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const CreateAlbumDialog(),
|
||||||
|
);
|
||||||
|
if (newAlbum != null && newAlbum.isNotEmpty) {
|
||||||
|
final source = context.read<CollectionSource>();
|
||||||
|
source.createAlbum(newAlbum);
|
||||||
|
|
||||||
|
final showAction = SnackBarAction(
|
||||||
|
label: context.l10n.showButtonLabel,
|
||||||
|
onPressed: () async {
|
||||||
|
final filter = AlbumFilter(newAlbum, source.getAlbumDisplayName(context, newAlbum));
|
||||||
|
context.read<HighlightInfo>().trackItem(FilterGridItem(filter, null), highlightItem: filter);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
showFeedback(context, context.l10n.genericSuccessFeedback, showAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _showDeleteDialog(BuildContext context, Set<AlbumFilter> filters) async {
|
Future<void> _showDeleteDialog(BuildContext context, Set<AlbumFilter> filters) async {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final messenger = ScaffoldMessenger.of(context);
|
final messenger = ScaffoldMessenger.of(context);
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
final albums = filters.map((v) => v.album).toSet();
|
|
||||||
final todoEntries = source.visibleEntries.where((entry) => filters.any((f) => f.test(entry))).toSet();
|
final todoEntries = source.visibleEntries.where((entry) => filters.any((f) => f.test(entry))).toSet();
|
||||||
final todoCount = todoEntries.length;
|
final todoCount = todoEntries.length;
|
||||||
|
final todoAlbums = filters.map((v) => v.album).toSet();
|
||||||
|
final filledAlbums = todoEntries.map((e) => e.directory).whereNotNull().toSet();
|
||||||
|
final emptyAlbums = todoAlbums.whereNot(filledAlbums.contains).toSet();
|
||||||
|
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -130,7 +158,10 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> {
|
||||||
);
|
);
|
||||||
if (confirmed == null || !confirmed) return;
|
if (confirmed == null || !confirmed) return;
|
||||||
|
|
||||||
if (!await checkStoragePermissionForAlbums(context, albums)) return;
|
source.forgetNewAlbums(todoAlbums);
|
||||||
|
source.cleanEmptyAlbums(emptyAlbums);
|
||||||
|
|
||||||
|
if (!await checkStoragePermissionForAlbums(context, filledAlbums)) return;
|
||||||
|
|
||||||
source.pauseMonitoring();
|
source.pauseMonitoring();
|
||||||
showOpReport<ImageOpEvent>(
|
showOpReport<ImageOpEvent>(
|
||||||
|
@ -149,7 +180,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
await storageService.deleteEmptyDirectories(albums);
|
await storageService.deleteEmptyDirectories(filledAlbums);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
case ChipSetAction.selectAll:
|
case ChipSetAction.selectAll:
|
||||||
case ChipSetAction.selectNone:
|
case ChipSetAction.selectNone:
|
||||||
case ChipSetAction.stats:
|
case ChipSetAction.stats:
|
||||||
|
case ChipSetAction.createAlbum:
|
||||||
return true;
|
return true;
|
||||||
// single/multiple filters
|
// single/multiple filters
|
||||||
case ChipSetAction.delete:
|
case ChipSetAction.delete:
|
||||||
|
|
|
@ -205,6 +205,7 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
|
||||||
enabled: !widget.isEmpty,
|
enabled: !widget.isEmpty,
|
||||||
),
|
),
|
||||||
toMenuItem(ChipSetAction.stats),
|
toMenuItem(ChipSetAction.stats),
|
||||||
|
toMenuItem(ChipSetAction.createAlbum),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ class CoveredFilterChip<T extends CollectionFilter> extends StatelessWidget {
|
||||||
final double extent, thumbnailExtent;
|
final double extent, thumbnailExtent;
|
||||||
final AvesEntry? coverEntry;
|
final AvesEntry? coverEntry;
|
||||||
final bool pinned;
|
final bool pinned;
|
||||||
|
final String? banner;
|
||||||
final FilterCallback? onTap;
|
final FilterCallback? onTap;
|
||||||
|
|
||||||
const CoveredFilterChip({
|
const CoveredFilterChip({
|
||||||
|
@ -34,6 +35,7 @@ class CoveredFilterChip<T extends CollectionFilter> extends StatelessWidget {
|
||||||
double? thumbnailExtent,
|
double? thumbnailExtent,
|
||||||
this.coverEntry,
|
this.coverEntry,
|
||||||
this.pinned = false,
|
this.pinned = false,
|
||||||
|
this.banner,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
}) : thumbnailExtent = thumbnailExtent ?? extent,
|
}) : thumbnailExtent = thumbnailExtent ?? extent,
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
@ -42,7 +44,7 @@ class CoveredFilterChip<T extends CollectionFilter> extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Consumer<CollectionSource>(
|
return Consumer<CollectionSource>(
|
||||||
builder: (context, source, child) {
|
builder: (context, source, child) {
|
||||||
switch (filter.runtimeType) {
|
switch (T) {
|
||||||
case AlbumFilter:
|
case AlbumFilter:
|
||||||
{
|
{
|
||||||
final album = (filter as AlbumFilter).album;
|
final album = (filter as AlbumFilter).album;
|
||||||
|
@ -92,6 +94,7 @@ class CoveredFilterChip<T extends CollectionFilter> extends StatelessWidget {
|
||||||
filter: filter,
|
filter: filter,
|
||||||
showGenericIcon: false,
|
showGenericIcon: false,
|
||||||
background: backgroundImage,
|
background: backgroundImage,
|
||||||
|
banner: banner,
|
||||||
details: _buildDetails(source, filter),
|
details: _buildDetails(source, filter),
|
||||||
borderRadius: BorderRadius.all(radius(extent)),
|
borderRadius: BorderRadius.all(radius(extent)),
|
||||||
padding: titlePadding,
|
padding: titlePadding,
|
||||||
|
|
|
@ -44,6 +44,7 @@ class FilterGridPage<T extends CollectionFilter> extends StatelessWidget {
|
||||||
final Widget appBar;
|
final Widget appBar;
|
||||||
final double appBarHeight;
|
final double appBarHeight;
|
||||||
final Map<ChipSectionKey, List<FilterGridItem<T>>> sections;
|
final Map<ChipSectionKey, List<FilterGridItem<T>>> sections;
|
||||||
|
final Set<T> newFilters;
|
||||||
final ChipSortFactor sortFactor;
|
final ChipSortFactor sortFactor;
|
||||||
final bool showHeaders, selectable;
|
final bool showHeaders, selectable;
|
||||||
final ValueNotifier<String> queryNotifier;
|
final ValueNotifier<String> queryNotifier;
|
||||||
|
@ -57,6 +58,7 @@ class FilterGridPage<T extends CollectionFilter> extends StatelessWidget {
|
||||||
required this.appBar,
|
required this.appBar,
|
||||||
this.appBarHeight = kToolbarHeight,
|
this.appBarHeight = kToolbarHeight,
|
||||||
required this.sections,
|
required this.sections,
|
||||||
|
required this.newFilters,
|
||||||
required this.sortFactor,
|
required this.sortFactor,
|
||||||
required this.showHeaders,
|
required this.showHeaders,
|
||||||
required this.selectable,
|
required this.selectable,
|
||||||
|
@ -92,6 +94,7 @@ class FilterGridPage<T extends CollectionFilter> extends StatelessWidget {
|
||||||
appBar: appBar,
|
appBar: appBar,
|
||||||
appBarHeight: appBarHeight,
|
appBarHeight: appBarHeight,
|
||||||
sections: sections,
|
sections: sections,
|
||||||
|
newFilters: newFilters,
|
||||||
sortFactor: sortFactor,
|
sortFactor: sortFactor,
|
||||||
showHeaders: showHeaders,
|
showHeaders: showHeaders,
|
||||||
selectable: selectable,
|
selectable: selectable,
|
||||||
|
@ -117,6 +120,7 @@ class FilterGrid<T extends CollectionFilter> extends StatefulWidget {
|
||||||
final Widget appBar;
|
final Widget appBar;
|
||||||
final double appBarHeight;
|
final double appBarHeight;
|
||||||
final Map<ChipSectionKey, List<FilterGridItem<T>>> sections;
|
final Map<ChipSectionKey, List<FilterGridItem<T>>> sections;
|
||||||
|
final Set<T> newFilters;
|
||||||
final ChipSortFactor sortFactor;
|
final ChipSortFactor sortFactor;
|
||||||
final bool showHeaders, selectable;
|
final bool showHeaders, selectable;
|
||||||
final ValueNotifier<String> queryNotifier;
|
final ValueNotifier<String> queryNotifier;
|
||||||
|
@ -130,6 +134,7 @@ class FilterGrid<T extends CollectionFilter> extends StatefulWidget {
|
||||||
required this.appBar,
|
required this.appBar,
|
||||||
required this.appBarHeight,
|
required this.appBarHeight,
|
||||||
required this.sections,
|
required this.sections,
|
||||||
|
required this.newFilters,
|
||||||
required this.sortFactor,
|
required this.sortFactor,
|
||||||
required this.showHeaders,
|
required this.showHeaders,
|
||||||
required this.selectable,
|
required this.selectable,
|
||||||
|
@ -166,6 +171,7 @@ class _FilterGridState<T extends CollectionFilter> extends State<FilterGrid<T>>
|
||||||
appBar: widget.appBar,
|
appBar: widget.appBar,
|
||||||
appBarHeight: widget.appBarHeight,
|
appBarHeight: widget.appBarHeight,
|
||||||
sections: widget.sections,
|
sections: widget.sections,
|
||||||
|
newFilters: widget.newFilters,
|
||||||
sortFactor: widget.sortFactor,
|
sortFactor: widget.sortFactor,
|
||||||
showHeaders: widget.showHeaders,
|
showHeaders: widget.showHeaders,
|
||||||
selectable: widget.selectable,
|
selectable: widget.selectable,
|
||||||
|
@ -181,6 +187,7 @@ class _FilterGridState<T extends CollectionFilter> extends State<FilterGrid<T>>
|
||||||
class _FilterGridContent<T extends CollectionFilter> extends StatelessWidget {
|
class _FilterGridContent<T extends CollectionFilter> extends StatelessWidget {
|
||||||
final Widget appBar;
|
final Widget appBar;
|
||||||
final Map<ChipSectionKey, List<FilterGridItem<T>>> sections;
|
final Map<ChipSectionKey, List<FilterGridItem<T>>> sections;
|
||||||
|
final Set<T> newFilters;
|
||||||
final ChipSortFactor sortFactor;
|
final ChipSortFactor sortFactor;
|
||||||
final bool showHeaders, selectable;
|
final bool showHeaders, selectable;
|
||||||
final ValueNotifier<String> queryNotifier;
|
final ValueNotifier<String> queryNotifier;
|
||||||
|
@ -195,6 +202,7 @@ class _FilterGridContent<T extends CollectionFilter> extends StatelessWidget {
|
||||||
required this.appBar,
|
required this.appBar,
|
||||||
required double appBarHeight,
|
required double appBarHeight,
|
||||||
required this.sections,
|
required this.sections,
|
||||||
|
required this.newFilters,
|
||||||
required this.sortFactor,
|
required this.sortFactor,
|
||||||
required this.showHeaders,
|
required this.showHeaders,
|
||||||
required this.selectable,
|
required this.selectable,
|
||||||
|
@ -258,6 +266,7 @@ class _FilterGridContent<T extends CollectionFilter> extends StatelessWidget {
|
||||||
filter: filter,
|
filter: filter,
|
||||||
extent: tileExtent,
|
extent: tileExtent,
|
||||||
pinned: pinnedFilters.contains(filter),
|
pinned: pinnedFilters.contains(filter),
|
||||||
|
banner: newFilters.contains(filter) ? context.l10n.newFilterBanner : null,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -18,6 +18,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
|
||||||
final bool groupable, showHeaders;
|
final bool groupable, showHeaders;
|
||||||
final ChipSetActionDelegate actionDelegate;
|
final ChipSetActionDelegate actionDelegate;
|
||||||
final Map<ChipSectionKey, List<FilterGridItem<T>>> filterSections;
|
final Map<ChipSectionKey, List<FilterGridItem<T>>> filterSections;
|
||||||
|
final Set<T>? newFilters;
|
||||||
final Widget Function() emptyBuilder;
|
final Widget Function() emptyBuilder;
|
||||||
|
|
||||||
const FilterNavigationPage({
|
const FilterNavigationPage({
|
||||||
|
@ -29,6 +30,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
|
||||||
this.showHeaders = false,
|
this.showHeaders = false,
|
||||||
required this.actionDelegate,
|
required this.actionDelegate,
|
||||||
required this.filterSections,
|
required this.filterSections,
|
||||||
|
this.newFilters,
|
||||||
required this.emptyBuilder,
|
required this.emptyBuilder,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@ -46,6 +48,7 @@ class FilterNavigationPage<T extends CollectionFilter> extends StatelessWidget {
|
||||||
isEmpty: filterSections.isEmpty,
|
isEmpty: filterSections.isEmpty,
|
||||||
),
|
),
|
||||||
sections: filterSections,
|
sections: filterSections,
|
||||||
|
newFilters: newFilters ?? {},
|
||||||
sortFactor: sortFactor,
|
sortFactor: sortFactor,
|
||||||
showHeaders: showHeaders,
|
showHeaders: showHeaders,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
|
|
Loading…
Reference in a new issue