mirror of https://github.com/immich-app/immich.git
fix(mobile): newest/oldest album sort (#20743)
* fix(mobile): newest/oldest album sort * chore: use sqlite to determine album asset timestamps * Fix missing future Co-authored-by: Alex <alex.tran1502@gmail.com> * fix: async handling of sort * chore: tests * chore: code review changes * fix: use created at for newest asset * fix: use localDateTime for sorting * chore: cleanup * chore: use final * feat: loading indicator --------- Co-authored-by: Alex <alex.tran1502@gmail.com>pull/20897/head^2
parent
54960157c0
commit
0d60199514
@ -1,64 +0,0 @@
|
|||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
|
||||||
|
|
||||||
typedef AlbumSortFn = List<RemoteAlbum> Function(List<RemoteAlbum> albums, bool isReverse);
|
|
||||||
|
|
||||||
class _RemoteAlbumSortHandlers {
|
|
||||||
const _RemoteAlbumSortHandlers._();
|
|
||||||
|
|
||||||
static const AlbumSortFn created = _sortByCreated;
|
|
||||||
static List<RemoteAlbum> _sortByCreated(List<RemoteAlbum> albums, bool isReverse) {
|
|
||||||
final sorted = albums.sortedBy((album) => album.createdAt);
|
|
||||||
return (isReverse ? sorted.reversed : sorted).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const AlbumSortFn title = _sortByTitle;
|
|
||||||
static List<RemoteAlbum> _sortByTitle(List<RemoteAlbum> albums, bool isReverse) {
|
|
||||||
final sorted = albums.sortedBy((album) => album.name);
|
|
||||||
return (isReverse ? sorted.reversed : sorted).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const AlbumSortFn lastModified = _sortByLastModified;
|
|
||||||
static List<RemoteAlbum> _sortByLastModified(List<RemoteAlbum> albums, bool isReverse) {
|
|
||||||
final sorted = albums.sortedBy((album) => album.updatedAt);
|
|
||||||
return (isReverse ? sorted.reversed : sorted).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const AlbumSortFn assetCount = _sortByAssetCount;
|
|
||||||
static List<RemoteAlbum> _sortByAssetCount(List<RemoteAlbum> albums, bool isReverse) {
|
|
||||||
final sorted = albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount));
|
|
||||||
return (isReverse ? sorted.reversed : sorted).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const AlbumSortFn mostRecent = _sortByMostRecent;
|
|
||||||
static List<RemoteAlbum> _sortByMostRecent(List<RemoteAlbum> albums, bool isReverse) {
|
|
||||||
final sorted = albums.sorted((a, b) {
|
|
||||||
// For most recent, we sort by updatedAt in descending order
|
|
||||||
return b.updatedAt.compareTo(a.updatedAt);
|
|
||||||
});
|
|
||||||
return (isReverse ? sorted.reversed : sorted).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const AlbumSortFn mostOldest = _sortByMostOldest;
|
|
||||||
static List<RemoteAlbum> _sortByMostOldest(List<RemoteAlbum> albums, bool isReverse) {
|
|
||||||
final sorted = albums.sorted((a, b) {
|
|
||||||
// For oldest, we sort by createdAt in ascending order
|
|
||||||
return a.createdAt.compareTo(b.createdAt);
|
|
||||||
});
|
|
||||||
return (isReverse ? sorted.reversed : sorted).toList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum RemoteAlbumSortMode {
|
|
||||||
title("library_page_sort_title", _RemoteAlbumSortHandlers.title),
|
|
||||||
assetCount("library_page_sort_asset_count", _RemoteAlbumSortHandlers.assetCount),
|
|
||||||
lastModified("library_page_sort_last_modified", _RemoteAlbumSortHandlers.lastModified),
|
|
||||||
created("library_page_sort_created", _RemoteAlbumSortHandlers.created),
|
|
||||||
mostRecent("sort_recent", _RemoteAlbumSortHandlers.mostRecent),
|
|
||||||
mostOldest("sort_oldest", _RemoteAlbumSortHandlers.mostOldest);
|
|
||||||
|
|
||||||
final String key;
|
|
||||||
final AlbumSortFn sortFn;
|
|
||||||
|
|
||||||
const RemoteAlbumSortMode(this.key, this.sortFn);
|
|
||||||
}
|
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/album/album.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/remote_album.service.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
||||||
|
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
|
||||||
|
import '../../infrastructure/repository.mock.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late RemoteAlbumService sut;
|
||||||
|
late DriftRemoteAlbumRepository mockRemoteAlbumRepo;
|
||||||
|
late DriftAlbumApiRepository mockAlbumApiRepo;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockRemoteAlbumRepo = MockRemoteAlbumRepository();
|
||||||
|
mockAlbumApiRepo = MockDriftAlbumApiRepository();
|
||||||
|
sut = RemoteAlbumService(mockRemoteAlbumRepo, mockAlbumApiRepo);
|
||||||
|
|
||||||
|
when(() => mockRemoteAlbumRepo.getNewestAssetTimestamp(any())).thenAnswer((invocation) {
|
||||||
|
// Simulate a timestamp for the newest asset in the album
|
||||||
|
final albumID = invocation.positionalArguments[0] as String;
|
||||||
|
|
||||||
|
if (albumID == '1') {
|
||||||
|
return Future.value(DateTime(2023, 1, 1));
|
||||||
|
} else if (albumID == '2') {
|
||||||
|
return Future.value(DateTime(2023, 2, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Future.value(DateTime.fromMillisecondsSinceEpoch(0));
|
||||||
|
});
|
||||||
|
|
||||||
|
when(() => mockRemoteAlbumRepo.getOldestAssetTimestamp(any())).thenAnswer((invocation) {
|
||||||
|
// Simulate a timestamp for the oldest asset in the album
|
||||||
|
final albumID = invocation.positionalArguments[0] as String;
|
||||||
|
|
||||||
|
if (albumID == '1') {
|
||||||
|
return Future.value(DateTime(2019, 1, 1));
|
||||||
|
} else if (albumID == '2') {
|
||||||
|
return Future.value(DateTime(2019, 2, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Future.value(DateTime.fromMillisecondsSinceEpoch(0));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
final albumA = RemoteAlbum(
|
||||||
|
id: '1',
|
||||||
|
name: 'Album A',
|
||||||
|
description: "",
|
||||||
|
isActivityEnabled: false,
|
||||||
|
order: AlbumAssetOrder.asc,
|
||||||
|
assetCount: 1,
|
||||||
|
createdAt: DateTime(2023, 1, 1),
|
||||||
|
updatedAt: DateTime(2023, 1, 2),
|
||||||
|
ownerId: 'owner1',
|
||||||
|
ownerName: "Test User",
|
||||||
|
);
|
||||||
|
|
||||||
|
final albumB = RemoteAlbum(
|
||||||
|
id: '2',
|
||||||
|
name: 'Album B',
|
||||||
|
description: "",
|
||||||
|
isActivityEnabled: false,
|
||||||
|
order: AlbumAssetOrder.desc,
|
||||||
|
assetCount: 2,
|
||||||
|
createdAt: DateTime(2023, 2, 1),
|
||||||
|
updatedAt: DateTime(2023, 2, 2),
|
||||||
|
ownerId: 'owner2',
|
||||||
|
ownerName: "Test User",
|
||||||
|
);
|
||||||
|
|
||||||
|
group('sortAlbums', () {
|
||||||
|
test('should sort correctly based on name', () async {
|
||||||
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
|
final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.title);
|
||||||
|
expect(result, [albumA, albumB]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should sort correctly based on createdAt', () async {
|
||||||
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
|
final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.created);
|
||||||
|
expect(result, [albumA, albumB]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should sort correctly based on updatedAt', () async {
|
||||||
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
|
final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.lastModified);
|
||||||
|
expect(result, [albumA, albumB]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should sort correctly based on assetCount', () async {
|
||||||
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
|
final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.assetCount);
|
||||||
|
expect(result, [albumA, albumB]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should sort correctly based on newestAssetTimestamp', () async {
|
||||||
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
|
final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.mostRecent);
|
||||||
|
expect(result, [albumA, albumB]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should sort correctly based on oldestAssetTimestamp', () async {
|
||||||
|
final albums = [albumB, albumA];
|
||||||
|
|
||||||
|
final result = await sut.sortAlbums(albums, RemoteAlbumSortMode.mostOldest);
|
||||||
|
expect(result, [albumB, albumA]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue