From e57c9266764e9217eed00e1b3f875d07c4cf36af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Gro=C3=9F?= Date: Thu, 12 Oct 2023 20:18:54 +0200 Subject: [PATCH] feat(mobile): offer the same album sorting options on mobile as on web (#3804) * Add translations for new album sort options * Support additional album sort options like on web * Update generated code * Fix lint --------- Co-authored-by: Alex --- mobile/assets/i18n/en-US.json | 2 ++ .../lib/modules/album/views/library_page.dart | 31 ++++++++++++++++++ mobile/lib/shared/models/album.dart | 13 ++++++++ mobile/lib/shared/models/album.g.dart | Bin 39252 -> 43044 bytes mobile/lib/shared/services/sync.service.dart | 14 +++++++- 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 0fd20a79bd..e83e1e6677 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -173,6 +173,8 @@ "library_page_sharing": "Sharing", "library_page_sort_created": "Most recently created", "library_page_sort_title": "Album title", + "library_page_sort_most_recent_photo": "Most recent photo", + "library_page_sort_last_modified": "Last modified", "login_disabled": "Login has been disabled", "login_form_api_exception": "API exception. Please check the server URL and try again.", "login_form_handshake_exception": "There was an Handshake Exception with the server. Enable self-signed certificate support in the settings if you are using a self-signed certificate.", diff --git a/mobile/lib/modules/album/views/library_page.dart b/mobile/lib/modules/album/views/library_page.dart index c22129a501..0eb44b82c5 100644 --- a/mobile/lib/modules/album/views/library_page.dart +++ b/mobile/lib/modules/album/views/library_page.dart @@ -47,6 +47,7 @@ class LibraryPage extends HookConsumerWidget { useState(settings.getSetting(AppSettingsEnum.selectedAlbumSortOrder)); List sortedAlbums() { + // Created. if (selectedAlbumSortOrder.value == 0) { return albums .where((a) => a.isRemote) @@ -54,6 +55,34 @@ class LibraryPage extends HookConsumerWidget { .reversed .toList(); } + // Album title. + if (selectedAlbumSortOrder.value == 1) { + return albums.where((a) => a.isRemote).sortedBy((album) => album.name); + } + // Most recent photo, if unset (e.g. empty album, use modifiedAt / updatedAt). + if (selectedAlbumSortOrder.value == 2) { + return albums + .where((a) => a.isRemote) + .sorted( + (a, b) => a.lastModifiedAssetTimestamp != null && + b.lastModifiedAssetTimestamp != null + ? a.lastModifiedAssetTimestamp! + .compareTo(b.lastModifiedAssetTimestamp!) + : a.modifiedAt.compareTo(b.modifiedAt), + ) + .reversed + .toList(); + } + // Last modified. + if (selectedAlbumSortOrder.value == 3) { + return albums + .where((a) => a.isRemote) + .sortedBy((album) => album.modifiedAt) + .reversed + .toList(); + } + + // Fallback: Album title. return albums.where((a) => a.isRemote).sortedBy((album) => album.name); } @@ -61,6 +90,8 @@ class LibraryPage extends HookConsumerWidget { final options = [ "library_page_sort_created".tr(), "library_page_sort_title".tr(), + "library_page_sort_most_recent_photo".tr(), + "library_page_sort_last_modified".tr(), ]; return PopupMenuButton( diff --git a/mobile/lib/shared/models/album.dart b/mobile/lib/shared/models/album.dart index 486647ad1b..94afe1d76d 100644 --- a/mobile/lib/shared/models/album.dart +++ b/mobile/lib/shared/models/album.dart @@ -18,6 +18,7 @@ class Album { required this.name, required this.createdAt, required this.modifiedAt, + this.lastModifiedAssetTimestamp, required this.shared, }); @@ -29,6 +30,7 @@ class Album { String name; DateTime createdAt; DateTime modifiedAt; + DateTime? lastModifiedAssetTimestamp; bool shared; final IsarLink owner = IsarLink(); final IsarLink thumbnail = IsarLink(); @@ -83,12 +85,21 @@ class Album { @override bool operator ==(other) { if (other is! Album) return false; + + final lastModifiedAssetTimestampIsSetAndEqual = + lastModifiedAssetTimestamp != null && + other.lastModifiedAssetTimestamp != null + ? lastModifiedAssetTimestamp! + .isAtSameMomentAs(other.lastModifiedAssetTimestamp!) + : true; + return id == other.id && remoteId == other.remoteId && localId == other.localId && name == other.name && createdAt.isAtSameMomentAs(other.createdAt) && modifiedAt.isAtSameMomentAs(other.modifiedAt) && + lastModifiedAssetTimestampIsSetAndEqual && shared == other.shared && owner.value == other.owner.value && thumbnail.value == other.thumbnail.value && @@ -105,6 +116,7 @@ class Album { name.hashCode ^ createdAt.hashCode ^ modifiedAt.hashCode ^ + lastModifiedAssetTimestamp.hashCode ^ shared.hashCode ^ owner.value.hashCode ^ thumbnail.value.hashCode ^ @@ -130,6 +142,7 @@ class Album { name: dto.albumName, createdAt: dto.createdAt, modifiedAt: dto.updatedAt, + lastModifiedAssetTimestamp: dto.lastModifiedAssetTimestamp, shared: dto.shared, ); a.owner.value = await db.users.getById(dto.ownerId); diff --git a/mobile/lib/shared/models/album.g.dart b/mobile/lib/shared/models/album.g.dart index 8d24155657e55a6c01358ca80f8a87adff94fb30..63f71f380f1a2fe1764f2da292893fa018d7e74c 100644 GIT binary patch delta 1297 zcmcbziD}6NrVZLGl8MD7zWFJcX_=`hj>W~PB_Wx)sl_FUxdoFAS@iM9@~}xve#;_0 znV*AevI&dSg_Y7>y=}@)O_3Ea0Gi?_puuP|xm>^qXvQia+ide?0Y%2iHUb>1{zZPJIg>vM ziB1*~Qk=Y2NOf|UkPJ|Ue=@s}(Bz3iN|SR1*eBNm*_VLgKzX18<_Jn}ekbIvs>r1P z26+5pYcUCrTNPt`i7?~5TF_(#8-d9^mS*_PG9u_6*TT}ooREACm&B4(P<-1@7BKFX z!sC8Lvt%M%={N}MWvaA$;q-Y|SdF}C<_zE*dRlPZoW$w+=rK8xVw7}luC-mHR?kF$nN-Ug)g*div&vI%aU z&SuXsS(cw4s8pBVn9*o*BEJcv@#JazdWHr@P5P?2#mzliE&D`A<*(SrPwHH3vGR|DDnB5ady2}@4iA*?hxMp%CG zXJMJm0V3|Ilk>eeCR?ccPo9vbIoZHUYVraf{>du?T_+cKv2Xt9wAOXAZ`WDQ%_pax QVc(oIuZMN>nI#d-05~{ET>t<8 diff --git a/mobile/lib/shared/services/sync.service.dart b/mobile/lib/shared/services/sync.service.dart index a11bb8eefc..d12a68ffde 100644 --- a/mobile/lib/shared/services/sync.service.dart +++ b/mobile/lib/shared/services/sync.service.dart @@ -282,6 +282,9 @@ class SyncService { if (!_hasAlbumResponseDtoChanged(dto, album)) { return false; } + // loadDetails (/api/album/:id) will not include lastModifiedAssetTimestamp, + // i.e. it will always be null. Save it here. + final originalDto = dto; dto = await loadDetails(dto); if (dto.assetCount != dto.assets.length) { return false; @@ -321,6 +324,7 @@ class SyncService { album.name = dto.albumName; album.shared = dto.shared; album.modifiedAt = dto.updatedAt; + album.lastModifiedAssetTimestamp = originalDto.lastModifiedAssetTimestamp; if (album.thumbnail.value?.remoteId != dto.albumThumbnailAssetId) { album.thumbnail.value = await _db.assets .where() @@ -808,5 +812,13 @@ bool _hasAlbumResponseDtoChanged(AlbumResponseDto dto, Album a) { dto.albumThumbnailAssetId != a.thumbnail.value?.remoteId || dto.shared != a.shared || dto.sharedUsers.length != a.sharedUsers.length || - !dto.updatedAt.isAtSameMomentAs(a.modifiedAt); + !dto.updatedAt.isAtSameMomentAs(a.modifiedAt) || + (dto.lastModifiedAssetTimestamp == null && + a.lastModifiedAssetTimestamp != null) || + (dto.lastModifiedAssetTimestamp != null && + a.lastModifiedAssetTimestamp == null) || + (dto.lastModifiedAssetTimestamp != null && + a.lastModifiedAssetTimestamp != null && + !dto.lastModifiedAssetTimestamp! + .isAtSameMomentAs(a.lastModifiedAssetTimestamp!)); }