mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
feat(mobile): enhance album sorting functionality with order handling (#24816)
* feat: enhance album sorting functionality with effective order handling * mobile: formatting * test: align album sorting order in unit tests with defaultSortOrder * test(mobile): add reverse order validation for album sorting * chore(PR): remove OppositeSortOrder Extension and move it directly into SortOrder enum * refactor: return sorted list directly in album sorting function * refactor: remove sort_order_extensions.dart
This commit is contained in:
parent
57483a1e7f
commit
354dd3cc3c
5 changed files with 47 additions and 17 deletions
|
|
@ -1,4 +1,11 @@
|
||||||
enum SortOrder { asc, desc }
|
enum SortOrder {
|
||||||
|
asc,
|
||||||
|
desc;
|
||||||
|
|
||||||
|
SortOrder reverse() {
|
||||||
|
return this == SortOrder.asc ? SortOrder.desc : SortOrder.asc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum TextSearchType { context, filename, description, ocr }
|
enum TextSearchType { context, filename, description, ocr }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
|
|
@ -36,6 +37,7 @@ class RemoteAlbumService {
|
||||||
AlbumSortMode sortMode, {
|
AlbumSortMode sortMode, {
|
||||||
bool isReverse = false,
|
bool isReverse = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
// list of albums sorted ascendingly according to the selected sort mode
|
||||||
final List<RemoteAlbum> sorted = switch (sortMode) {
|
final List<RemoteAlbum> sorted = switch (sortMode) {
|
||||||
AlbumSortMode.created => albums.sortedBy((album) => album.createdAt),
|
AlbumSortMode.created => albums.sortedBy((album) => album.createdAt),
|
||||||
AlbumSortMode.title => albums.sortedBy((album) => album.name),
|
AlbumSortMode.title => albums.sortedBy((album) => album.name),
|
||||||
|
|
@ -44,8 +46,9 @@ class RemoteAlbumService {
|
||||||
AlbumSortMode.mostRecent => await _sortByNewestAsset(albums),
|
AlbumSortMode.mostRecent => await _sortByNewestAsset(albums),
|
||||||
AlbumSortMode.mostOldest => await _sortByOldestAsset(albums),
|
AlbumSortMode.mostOldest => await _sortByOldestAsset(albums),
|
||||||
};
|
};
|
||||||
|
final effectiveOrder = isReverse ? sortMode.defaultOrder.reverse() : sortMode.defaultOrder;
|
||||||
|
|
||||||
return (isReverse ? sorted.reversed : sorted).toList();
|
return (effectiveOrder == SortOrder.asc ? sorted : sorted.reversed).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RemoteAlbum> searchAlbums(
|
List<RemoteAlbum> searchAlbums(
|
||||||
|
|
@ -209,6 +212,6 @@ class RemoteAlbumService {
|
||||||
return aDate.compareTo(bDate);
|
return aDate.compareTo(bDate);
|
||||||
});
|
});
|
||||||
|
|
||||||
return sorted.reversed.toList();
|
return sorted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
|
@ -281,6 +282,8 @@ class _SortButtonState extends ConsumerState<_SortButton> {
|
||||||
setState(() {
|
setState(() {
|
||||||
albumSortOption = sortMode;
|
albumSortOption = sortMode;
|
||||||
isSorting = true;
|
isSorting = true;
|
||||||
|
// reset sort order to default state when switching option
|
||||||
|
albumSortIsReverse = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,6 +296,7 @@ class _SortButtonState extends ConsumerState<_SortButton> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final effectiveOrder = albumSortOption.effectiveOrder(albumSortIsReverse);
|
||||||
return MenuAnchor(
|
return MenuAnchor(
|
||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
style: MenuStyle(
|
style: MenuStyle(
|
||||||
|
|
@ -307,7 +311,7 @@ class _SortButtonState extends ConsumerState<_SortButton> {
|
||||||
.map(
|
.map(
|
||||||
(sortMode) => MenuItemButton(
|
(sortMode) => MenuItemButton(
|
||||||
leadingIcon: albumSortOption == sortMode
|
leadingIcon: albumSortOption == sortMode
|
||||||
? albumSortIsReverse
|
? effectiveOrder == SortOrder.desc
|
||||||
? Icon(
|
? Icon(
|
||||||
Icons.keyboard_arrow_down,
|
Icons.keyboard_arrow_down,
|
||||||
color: albumSortOption == sortMode
|
color: albumSortOption == sortMode
|
||||||
|
|
@ -355,7 +359,7 @@ class _SortButtonState extends ConsumerState<_SortButton> {
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 5),
|
padding: const EdgeInsets.only(right: 5),
|
||||||
child: albumSortIsReverse
|
child: effectiveOrder == SortOrder.desc
|
||||||
? Icon(Icons.keyboard_arrow_down, color: context.colorScheme.onSurface)
|
? Icon(Icons.keyboard_arrow_down, color: context.colorScheme.onSurface)
|
||||||
: Icon(Icons.keyboard_arrow_up_rounded, color: context.colorScheme.onSurface),
|
: Icon(Icons.keyboard_arrow_up_rounded, color: context.colorScheme.onSurface),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
|
|
@ -73,18 +74,21 @@ class _AlbumSortHandlers {
|
||||||
|
|
||||||
// Store index allows us to re-arrange the values without affecting the saved prefs
|
// Store index allows us to re-arrange the values without affecting the saved prefs
|
||||||
enum AlbumSortMode {
|
enum AlbumSortMode {
|
||||||
title(1, "library_page_sort_title", _AlbumSortHandlers.title),
|
title(1, "library_page_sort_title", _AlbumSortHandlers.title, SortOrder.asc),
|
||||||
assetCount(4, "library_page_sort_asset_count", _AlbumSortHandlers.assetCount),
|
assetCount(4, "library_page_sort_asset_count", _AlbumSortHandlers.assetCount, SortOrder.desc),
|
||||||
lastModified(3, "library_page_sort_last_modified", _AlbumSortHandlers.lastModified),
|
lastModified(3, "library_page_sort_last_modified", _AlbumSortHandlers.lastModified, SortOrder.desc),
|
||||||
created(0, "library_page_sort_created", _AlbumSortHandlers.created),
|
created(0, "library_page_sort_created", _AlbumSortHandlers.created, SortOrder.desc),
|
||||||
mostRecent(2, "sort_recent", _AlbumSortHandlers.mostRecent),
|
mostRecent(2, "sort_recent", _AlbumSortHandlers.mostRecent, SortOrder.desc),
|
||||||
mostOldest(5, "sort_oldest", _AlbumSortHandlers.mostOldest);
|
mostOldest(5, "sort_oldest", _AlbumSortHandlers.mostOldest, SortOrder.asc);
|
||||||
|
|
||||||
final int storeIndex;
|
final int storeIndex;
|
||||||
final String label;
|
final String label;
|
||||||
final AlbumSortFn sortFn;
|
final AlbumSortFn sortFn;
|
||||||
|
final SortOrder defaultOrder;
|
||||||
|
|
||||||
const AlbumSortMode(this.storeIndex, this.label, this.sortFn);
|
const AlbumSortMode(this.storeIndex, this.label, this.sortFn, this.defaultOrder);
|
||||||
|
|
||||||
|
SortOrder effectiveOrder(bool isReverse) => isReverse ? defaultOrder.reverse() : defaultOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
|
|
|
||||||
|
|
@ -85,35 +85,47 @@ void main() {
|
||||||
final albums = [albumB, albumA];
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
final result = await sut.sortAlbums(albums, AlbumSortMode.created);
|
final result = await sut.sortAlbums(albums, AlbumSortMode.created);
|
||||||
expect(result, [albumA, albumB]);
|
expect(result, [albumB, albumA]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should sort correctly based on updatedAt', () async {
|
test('should sort correctly based on updatedAt', () async {
|
||||||
final albums = [albumB, albumA];
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
final result = await sut.sortAlbums(albums, AlbumSortMode.lastModified);
|
final result = await sut.sortAlbums(albums, AlbumSortMode.lastModified);
|
||||||
expect(result, [albumA, albumB]);
|
expect(result, [albumB, albumA]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should sort correctly based on assetCount', () async {
|
test('should sort correctly based on assetCount', () async {
|
||||||
final albums = [albumB, albumA];
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
final result = await sut.sortAlbums(albums, AlbumSortMode.assetCount);
|
final result = await sut.sortAlbums(albums, AlbumSortMode.assetCount);
|
||||||
expect(result, [albumA, albumB]);
|
expect(result, [albumB, albumA]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should sort correctly based on newestAssetTimestamp', () async {
|
test('should sort correctly based on newestAssetTimestamp', () async {
|
||||||
final albums = [albumB, albumA];
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
final result = await sut.sortAlbums(albums, AlbumSortMode.mostRecent);
|
final result = await sut.sortAlbums(albums, AlbumSortMode.mostRecent);
|
||||||
expect(result, [albumA, albumB]);
|
expect(result, [albumB, albumA]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should sort correctly based on oldestAssetTimestamp', () async {
|
test('should sort correctly based on oldestAssetTimestamp', () async {
|
||||||
final albums = [albumB, albumA];
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
final result = await sut.sortAlbums(albums, AlbumSortMode.mostOldest);
|
final result = await sut.sortAlbums(albums, AlbumSortMode.mostOldest);
|
||||||
expect(result, [albumB, albumA]);
|
expect(result, [albumA, albumB]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should flip order when isReverse is true for all modes', () async {
|
||||||
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
|
for (final mode in AlbumSortMode.values) {
|
||||||
|
final normal = await sut.sortAlbums(albums, mode, isReverse: false);
|
||||||
|
final reversed = await sut.sortAlbums(albums, mode, isReverse: true);
|
||||||
|
|
||||||
|
// reversed should be the exact inverse of normal
|
||||||
|
expect(reversed, normal.reversed.toList(), reason: 'Mode: $mode');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue