From a6c1fd52a62ede641b6e3dfdcc5b642fd18957d8 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Mon, 12 May 2025 23:04:14 +0200 Subject: [PATCH] #1554 Collection: sort by storage path --- CHANGELOG.md | 1 + lib/model/entry/sort.dart | 6 ++++++ lib/model/source/album.dart | 2 ++ lib/model/source/collection_lens.dart | 7 +++++++ lib/theme/icons.dart | 2 +- lib/view/src/source/sort.dart | 3 +++ lib/widgets/collection/app_bar.dart | 1 + lib/widgets/collection/collection_grid.dart | 1 + .../collection/draggable_thumb_label.dart | 19 ++++++++++++++----- lib/widgets/collection/grid/headers/any.dart | 1 + plugins/aves_model/lib/src/source/enums.dart | 2 +- 11 files changed, 38 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb0238492..efc10f654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Albums: groups +- Collection: sort by storage path - Search: week day filters ### Changed diff --git a/lib/model/entry/sort.dart b/lib/model/entry/sort.dart index 5cf6aa267..e0e557336 100644 --- a/lib/model/entry/sort.dart +++ b/lib/model/entry/sort.dart @@ -43,4 +43,10 @@ class AvesEntrySort { final c = (b.durationMillis ?? 0).compareTo(a.durationMillis ?? 0); return c != 0 ? c : compareByDate(a, b); } + + // compare by: + // 1) path ascending + static int compareByPath(AvesEntry a, AvesEntry b) { + return compareAsciiUpperCase(a.path ?? '', b.path ?? ''); + } } diff --git a/lib/model/source/album.dart b/lib/model/source/album.dart index 65602d441..a3fea5a60 100644 --- a/lib/model/source/album.dart +++ b/lib/model/source/album.dart @@ -21,6 +21,8 @@ mixin AlbumMixin on SourceBase { Set getNewAlbumFilters(BuildContext context) => Set.unmodifiable(_newAlbums.map((v) => StoredAlbumFilter(v, getStoredAlbumDisplayName(context, v)))); + int compareAlbumsByPath(String? a, String? b) => compareAsciiUpperCase(a ??= '', b ??= ''); + int compareAlbumsByName(String? a, String? b) { a ??= ''; b ??= ''; diff --git a/lib/model/source/collection_lens.dart b/lib/model/source/collection_lens.dart index 8de5ee3bb..97b29fc9d 100644 --- a/lib/model/source/collection_lens.dart +++ b/lib/model/source/collection_lens.dart @@ -157,6 +157,7 @@ class CollectionLens with ChangeNotifier { return true; } case EntrySortFactor.name: + case EntrySortFactor.path: return showAlbumHeaders(); case EntrySortFactor.rating: return !filters.any((f) => f is RatingFilter); @@ -266,6 +267,8 @@ class CollectionLens with ChangeNotifier { _filteredSortedEntries.sort(AvesEntrySort.compareBySize); case EntrySortFactor.duration: _filteredSortedEntries.sort(AvesEntrySort.compareByDuration); + case EntrySortFactor.path: + _filteredSortedEntries.sort(AvesEntrySort.compareByPath); } if (sortReverse) { _filteredSortedEntries = _filteredSortedEntries.reversed.toList(); @@ -303,6 +306,10 @@ class CollectionLens with ChangeNotifier { sections = Map.fromEntries([ MapEntry(const SectionKey(), _filteredSortedEntries), ]); + case EntrySortFactor.path: + final byAlbum = groupBy(_filteredSortedEntries, (entry) => EntryAlbumSectionKey(entry.directory)); + final int Function(EntryAlbumSectionKey, EntryAlbumSectionKey) compare = sortReverse ? (a, b) => source.compareAlbumsByPath(b.directory, a.directory) : (a, b) => source.compareAlbumsByPath(a.directory, b.directory); + sections = SplayTreeMap>.of(byAlbum, compare); } } sections = Map.unmodifiable(sections); diff --git a/lib/theme/icons.dart b/lib/theme/icons.dart index fef92dac7..b0a98aaa8 100644 --- a/lib/theme/icons.dart +++ b/lib/theme/icons.dart @@ -81,7 +81,7 @@ class AIcons { static const place = Symbols.place; // view - static const section = Symbols.group_work; + static const section = Symbols.subheader; static const layout = Symbols.grid_view; static const layoutMosaic = Symbols.view_comfy; static const layoutGrid = Symbols.view_compact; diff --git a/lib/view/src/source/sort.dart b/lib/view/src/source/sort.dart index cd621455e..de67dbe47 100644 --- a/lib/view/src/source/sort.dart +++ b/lib/view/src/source/sort.dart @@ -12,6 +12,7 @@ extension ExtraEntrySortFactorView on EntrySortFactor { EntrySortFactor.rating => l10n.sortByRating, EntrySortFactor.size => l10n.sortBySize, EntrySortFactor.duration => l10n.sortByDuration, + EntrySortFactor.path => l10n.sortByPath, }; } @@ -22,6 +23,7 @@ extension ExtraEntrySortFactorView on EntrySortFactor { EntrySortFactor.rating => AIcons.rating, EntrySortFactor.size => AIcons.size, EntrySortFactor.duration => AIcons.duration, + EntrySortFactor.path => AIcons.path, }; } @@ -33,6 +35,7 @@ extension ExtraEntrySortFactorView on EntrySortFactor { EntrySortFactor.rating => reverse ? l10n.sortOrderLowestFirst : l10n.sortOrderHighestFirst, EntrySortFactor.size => reverse ? l10n.sortOrderSmallestFirst : l10n.sortOrderLargestFirst, EntrySortFactor.duration => reverse ? l10n.sortOrderShortestFirst : l10n.sortOrderLongestFirst, + EntrySortFactor.path => reverse ? l10n.sortOrderZtoA : l10n.sortOrderAtoZ, }; } } diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index 6ed144edf..f85689966 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -81,6 +81,7 @@ class _CollectionAppBarState extends State with SingleTickerPr EntrySortFactor.name, EntrySortFactor.rating, EntrySortFactor.duration, + EntrySortFactor.path, ]; static const _sectionOptions = [ diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index da8bb3ddb..aa3ef6363 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -726,6 +726,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge break; } case EntrySortFactor.name: + case EntrySortFactor.path: addAlbums(collection, sectionLayouts, crumbs); case EntrySortFactor.rating: case EntrySortFactor.size: diff --git a/lib/widgets/collection/draggable_thumb_label.dart b/lib/widgets/collection/draggable_thumb_label.dart index 7f10a2730..1361316df 100644 --- a/lib/widgets/collection/draggable_thumb_label.dart +++ b/lib/widgets/collection/draggable_thumb_label.dart @@ -27,26 +27,28 @@ class CollectionDraggableThumbLabel extends StatelessWidget { lineBuilder: (context, entry) { switch (collection.sortFactor) { case EntrySortFactor.date: + final date = entry.bestDate; switch (collection.sectionFactor) { case EntrySectionFactor.album: return [ - DraggableThumbLabel.formatMonthThumbLabel(context, entry.bestDate), + DraggableThumbLabel.formatMonthThumbLabel(context, date), if (_showAlbumName(context, entry)) _getAlbumName(context, entry), ]; case EntrySectionFactor.month: case EntrySectionFactor.none: return [ - DraggableThumbLabel.formatMonthThumbLabel(context, entry.bestDate), + DraggableThumbLabel.formatMonthThumbLabel(context, date), ]; case EntrySectionFactor.day: return [ - DraggableThumbLabel.formatDayThumbLabel(context, entry.bestDate), + DraggableThumbLabel.formatDayThumbLabel(context, date), ]; } case EntrySortFactor.name: + final entryTitle = entry.bestTitle; return [ if (_showAlbumName(context, entry)) _getAlbumName(context, entry), - if (entry.bestTitle != null) entry.bestTitle!, + if (entryTitle != null) entryTitle, ]; case EntrySortFactor.rating: return [ @@ -54,13 +56,20 @@ class CollectionDraggableThumbLabel extends StatelessWidget { DraggableThumbLabel.formatMonthThumbLabel(context, entry.bestDate), ]; case EntrySortFactor.size: + final sizeBytes = entry.sizeBytes; return [ - if (entry.sizeBytes != null) formatFileSize(context.locale, entry.sizeBytes!, round: 0), + if (sizeBytes != null) formatFileSize(context.locale, sizeBytes, round: 0), ]; case EntrySortFactor.duration: return [ if (entry.durationMillis != null) entry.durationText, ]; + case EntrySortFactor.path: + final entryFilename = entry.filenameWithoutExtension; + return [ + if (_showAlbumName(context, entry)) _getAlbumName(context, entry), + if (entryFilename != null) entryFilename, + ]; } }, ); diff --git a/lib/widgets/collection/grid/headers/any.dart b/lib/widgets/collection/grid/headers/any.dart index 5872b196d..0360041b2 100644 --- a/lib/widgets/collection/grid/headers/any.dart +++ b/lib/widgets/collection/grid/headers/any.dart @@ -58,6 +58,7 @@ class CollectionSectionHeader extends StatelessWidget { break; } case EntrySortFactor.name: + case EntrySortFactor.path: return _buildAlbumHeader(context); case EntrySortFactor.rating: return RatingSectionHeader( diff --git a/plugins/aves_model/lib/src/source/enums.dart b/plugins/aves_model/lib/src/source/enums.dart index 9b574264e..e029534f2 100644 --- a/plugins/aves_model/lib/src/source/enums.dart +++ b/plugins/aves_model/lib/src/source/enums.dart @@ -4,7 +4,7 @@ enum ChipSortFactor { date, name, count, size, path } enum AlbumChipSectionFactor { none, importance, mimeType, volume } -enum EntrySortFactor { date, name, rating, size, duration } +enum EntrySortFactor { date, name, rating, size, duration, path } enum EntrySectionFactor { none, album, month, day }