From 08122d687128e9b5b60baf6d861fa4683bdf0b0d Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:43:15 +0530 Subject: [PATCH] fix: show only local assets from albums selected for backup (#20050) * show only local assets from albums selected for backup # Conflicts: # mobile/lib/infrastructure/repositories/db.repository.drift.dart * ignore backup selection * fix: backup album ownerId * fix: backup album ownerId * only show local only assets that are selected for backup * add index on visibility and backup selection * fix: video not playing in search view * remove safe area from bottom bar * refactor stack count with a CTE and local asset with a SELECT * fix lint * remove redundant COALESCE * remove stack count from main timeline query --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex --- .../drift_schemas/main/drift_schema_v4.json | Bin 32634 -> 32634 bytes .../models/asset/remote_asset.model.dart | 11 +- .../entities/merged_asset.drift | 143 +++++++++--------- .../entities/merged_asset.drift.dart | Bin 6681 -> 7392 bytes .../repositories/backup.repository.dart | 11 +- .../repositories/db.repository.dart | 4 +- .../repositories/db.repository.drift.dart | Bin 12714 -> 12714 bytes .../repositories/db.repository.steps.dart | Bin 62056 -> 62056 bytes .../repositories/remote_asset.repository.dart | 30 +--- .../repositories/timeline.repository.dart | 3 +- .../lib/pages/backup/drift_backup.page.dart | 27 +++- .../drift_backup_album_selection.page.dart | 18 ++- mobile/lib/pages/common/tab_shell.page.dart | 10 +- .../asset_viewer/asset_stack.provider.dart | 6 +- .../widgets/images/thumbnail_tile.widget.dart | 40 +---- .../providers/app_life_cycle.provider.dart | 10 +- .../backup/drift_backup.provider.dart | 14 +- mobile/lib/services/drift_backup.service.dart | 11 +- .../test/drift/main/generated/schema_v4.dart | Bin 206970 -> 206970 bytes 19 files changed, 152 insertions(+), 186 deletions(-) diff --git a/mobile/drift_schemas/main/drift_schema_v4.json b/mobile/drift_schemas/main/drift_schema_v4.json index 82ef30adae0890ca19151b41a2dc3b982d7b2984..2488319a5e6f5a9720b17c2d442e2e5f8785be54 100644 GIT binary patch delta 126 zcmezMkMY+(#tpB;Cfmd*ZQi56%sRPCL16M71C7l>;uV~e_p1tQzGJY1ZL+tT0JE`< z$>f4E#mRpScs5@(FJYeiTup${c=82RIUxC7HD+>&y1?XUwH`)`$v$cplNHoMCZ917 a0Gqr_U591z6mvl^hehWE%Vtk&Ll*!$l`cL2 delta 134 zcmezMkMY+(#tpB;Hp|N_;GE1aBQVKGX)}*FGwbC2ssfBglYI>2fMm8o%;a_*fyq(U z+>`4JPqsFIaf>D&HxSrlxF-EzbY| diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index 760a16170..f96fa18a7 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -15,7 +15,6 @@ class RemoteAsset extends BaseAsset { final AssetVisibility visibility; final String ownerId; final String? stackId; - final int stackCount; const RemoteAsset({ required this.id, @@ -34,7 +33,6 @@ class RemoteAsset extends BaseAsset { this.visibility = AssetVisibility.timeline, super.livePhotoVideoId, this.stackId, - this.stackCount = 0, }); @override @@ -61,7 +59,6 @@ class RemoteAsset extends BaseAsset { thumbHash: ${thumbHash ?? ""}, visibility: $visibility, stackId: ${stackId ?? ""}, - stackCount: $stackCount, checksum: $checksum, livePhotoVideoId: ${livePhotoVideoId ?? ""}, }'''; @@ -77,8 +74,7 @@ class RemoteAsset extends BaseAsset { ownerId == other.ownerId && thumbHash == other.thumbHash && visibility == other.visibility && - stackId == other.stackId && - stackCount == other.stackCount; + stackId == other.stackId; } @override @@ -89,8 +85,7 @@ class RemoteAsset extends BaseAsset { localId.hashCode ^ thumbHash.hashCode ^ visibility.hashCode ^ - stackId.hashCode ^ - stackCount.hashCode; + stackId.hashCode; RemoteAsset copyWith({ String? id, @@ -109,7 +104,6 @@ class RemoteAsset extends BaseAsset { AssetVisibility? visibility, String? livePhotoVideoId, String? stackId, - int? stackCount, }) { return RemoteAsset( id: id ?? this.id, @@ -128,7 +122,6 @@ class RemoteAsset extends BaseAsset { visibility: visibility ?? this.visibility, livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, stackId: stackId ?? this.stackId, - stackCount: stackCount ?? this.stackCount, ); } } diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 3dc7221c1..9778ba723 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -1,76 +1,68 @@ import 'remote_asset.entity.dart'; -import 'local_asset.entity.dart'; import 'stack.entity.dart'; +import 'local_asset.entity.dart'; +import 'local_album.entity.dart'; +import 'local_album_asset.entity.dart'; -mergedAsset: SELECT * FROM -( - SELECT - rae.id as remote_id, - lae.id as local_id, - rae.name, - rae."type", - rae.created_at, - rae.updated_at, - rae.width, - rae.height, - rae.duration_in_seconds, - rae.is_favorite, - rae.thumb_hash, - rae.checksum, - rae.owner_id, - rae.live_photo_video_id, - 0 as orientation, - rae.stack_id, - COALESCE(stack_count.total_count, 0) AS stack_count - FROM - remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum - LEFT JOIN - stack_entity se ON rae.stack_id = se.id - LEFT JOIN - (SELECT - stack_id, - COUNT(*) AS total_count - FROM remote_asset_entity - WHERE deleted_at IS NULL - AND visibility = 0 - AND stack_id IS NOT NULL - GROUP BY stack_id - ) AS stack_count ON rae.stack_id = stack_count.stack_id - WHERE - rae.deleted_at IS NULL - AND rae.visibility = 0 - AND rae.owner_id in ? - AND ( - rae.stack_id IS NULL - OR rae.id = se.primary_asset_id - ) - UNION ALL - SELECT - NULL as remote_id, - lae.id as local_id, - lae.name, - lae."type", - lae.created_at, - lae.updated_at, - lae.width, - lae.height, - lae.duration_in_seconds, - lae.is_favorite, - NULL as thumb_hash, - lae.checksum, - NULL as owner_id, - NULL as live_photo_video_id, - lae.orientation, - NULL as stack_id, - 0 AS stack_count - FROM - local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - WHERE - rae.id IS NULL +mergedAsset: +SELECT + rae.id as remote_id, + (SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id, + rae.name, + rae."type", + rae.created_at as created_at, + rae.updated_at, + rae.width, + rae.height, + rae.duration_in_seconds, + rae.is_favorite, + rae.thumb_hash, + rae.checksum, + rae.owner_id, + rae.live_photo_video_id, + 0 as orientation, + rae.stack_id +FROM + remote_asset_entity rae +LEFT JOIN + stack_entity se ON rae.stack_id = se.id +WHERE + rae.deleted_at IS NULL + AND rae.visibility = 0 -- timeline visibility + AND rae.owner_id in ? + AND ( + rae.stack_id IS NULL + OR rae.id = se.primary_asset_id + ) + +UNION ALL + +SELECT + NULL as remote_id, + lae.id as local_id, + lae.name, + lae."type", + lae.created_at as created_at, + lae.updated_at, + lae.width, + lae.height, + lae.duration_in_seconds, + lae.is_favorite, + NULL as thumb_hash, + lae.checksum, + NULL as owner_id, + NULL as live_photo_video_id, + lae.orientation, + NULL as stack_id +FROM + local_asset_entity lae +WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum +) +AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected ) ORDER BY created_at DESC LIMIT $limit; @@ -85,17 +77,14 @@ SELECT FROM ( SELECT - rae.name, rae.created_at FROM remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL - AND rae.visibility = 0 + AND rae.visibility = 0 -- timeline visibility AND rae.owner_id in ? AND ( rae.stack_id IS NULL @@ -103,14 +92,18 @@ FROM ) UNION ALL SELECT - lae.name, lae.created_at FROM local_asset_entity lae LEFT JOIN remote_asset_entity rae ON rae.checksum = lae.checksum + LEFT JOIN + local_album_asset_entity laa ON laa.asset_id = lae.id + LEFT JOIN + local_album_entity la ON la.id = laa.album_id WHERE rae.id IS NULL + AND la.backup_selection = 0 -- selected ) GROUP BY bucket_date ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index ac3db868e1ee858da3b4cd14d5b66521b5e98498..7f56b25d4e1dcfdfbc0576edb24e2ce299bef644 100644 GIT binary patch delta 1081 zcma)*O=uHA6vvr%W81Biq)A94u|6f3jn*ZlwXwtmgKjjE4@iP~P{Jfx+ojvIG#e#C zNkKeIc_Mi5;8_qQc&;bGlX~^wCmsX^y?AzZwh3Eet9uyO`M-H@e*byz!R*KRr+$LB zNC=Z;9{ohZN95Y(m$^}WrAqZO)ho?qJVcI=xy?_fc5Hqifq`fYe|g>L9C+f(s%r~s zE!k+OMo`z5H3RsH@5cbY51=ejU84=jSkxLa&&@-Xx}h1ZDG2FQ4TZx~B3K`84Bp0Z z!8;}um5g$>1WcE;8g9AZ*yRgKLE#(KMYX!rXf8t>)|lS=&7`yG5=@8jgb)!RSp+N! zFHmN&Lt(lha zE3vQ&tu8fJ%MDdmtA@5xXNM!5-@_6?aPJfNGyWX$^?o}0ML!|4NIVmr#HcXXxiR*c zV8wZoS@ag4yXyGHDYpLw)xB`vr7}vY^gpmBfJteCwqVWo)bPjfNMJ8OjE;Z9({3+L zx+i;RXy?SFdy8YXELcR(LA>g{Cje+O_{?*Yn5z%Zcmq>&axi0debRKx)}AF=(N$Pb zSxfh140B$Lf+LIN8q$bA;X-@8D(>C^Tbqi7O^7r&p&18iF=l17H1}9=46&sD%dI*C^-7Lz|Bzb3|8>-4^i+7 z_3=?~5AqKUP;iQb%VIb#P*6k=$tw_dPfiz%n9R*SZSs2dqk=#$q55dDq)@5|#4$yQ zsd||yU@L*vZJy7m!8lo+OK7qf_g6;a$!kR=HmmTkFl#74>~{u*mX$(Lez{&zYGR5_ zW?qS%hB{JEZIP`H|?9&GRJoF^Z&R<|XDR0G)#5#?28@os5&G ZO9=>r9S3%|4wr&Lt)?|s?POD#)d0JpzkC1y diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart index 99df206db..aed7118e5 100644 --- a/mobile/lib/infrastructure/repositories/backup.repository.dart +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -50,7 +50,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future getRemainderCount() async { + Future getRemainderCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ @@ -74,7 +74,8 @@ class DriftBackupRepository extends DriftDatabaseRepository { ..where( _db.localAlbumEntity.backupSelection .equalsValue(BackupSelection.selected) & - _db.remoteAssetEntity.id.isNull() & + (_db.remoteAssetEntity.id.isNull() | + _db.remoteAssetEntity.ownerId.equals(userId).not()) & _db.localAlbumAssetEntity.assetId .isNotInQuery(_getExcludedSubquery()), ); @@ -82,7 +83,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future getBackupCount() async { + Future getBackupCount(String userId) async { final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) ..addColumns( [_db.localAlbumAssetEntity.assetId], @@ -109,6 +110,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { _db.localAlbumEntity.backupSelection .equalsValue(BackupSelection.selected) & _db.remoteAssetEntity.id.isNotNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & _db.localAlbumAssetEntity.assetId .isNotInQuery(_getExcludedSubquery()), ); @@ -116,7 +118,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { return query.get().then((rows) => rows.length); } - Future> getCandidates() async { + Future> getCandidates(String userId) async { final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) ..addColumns([_db.localAlbumEntity.id]) ..where( @@ -141,6 +143,7 @@ class DriftBackupRepository extends DriftDatabaseRepository { ..addColumns([_db.remoteAssetEntity.checksum]) ..where( _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & + _db.remoteAssetEntity.ownerId.equals(userId) & lae.checksum.isNotNull(), ), ) & diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index ced148f85..5cbbb3b4f 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -4,8 +4,8 @@ import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; @@ -97,7 +97,9 @@ class Drift extends $Drift implements IDatabaseRepository { await m.alterTable(TableMigration(v3.stackEntity)); }, from3To4: (m, v4) async { + // Thumbnail path column got removed from person_entity await m.alterTable(TableMigration(v4.personEntity)); + // asset_face_entity is added await m.create(v4.assetFaceEntity); }, ), diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 7b722dfff60a4fb37195a794f190d15f8b9e8805..f5962f09ab09c5ed9a62e1186922d7a026c4f875 100644 GIT binary patch delta 344 zcmX|*Jxjw-7=^hvk>ti96>Y3dim451Z(>Sozme+DRZ~Paap@r9;HD`40TDzzbKOMf zV&U$U{s6^U`~y0;xqFS&?j{9?sS)|;z?hohLM@?J15PK|pNRWk|4OykpR(8Dq|&7R05MhcEonpmGbphzUo#s172 zz0F*TLi*Xnh&#B+PvC)Da^8?PNf|Bf#cqMyach$bwt_!b3hcPGm1^jL88!xiFDJeS myGl(Om!+^)oPy_P{`;OE%8^%oE52ED@&>;BX;EA`Q-1(y#9;gY delta 351 zcmYjMJ4*vW7-aV%WKZD?F=w7)l7o1A$;FU&6j89z6cN-if}{$9F9gxTQm_*TY$Rf1 zVJcyNLHZNLGNstqX{VKL4iTGqFnq&&-B!1C-ypanDeRIe8YGS<$&b{;G_4}1BylO3 z(G)%`Y|C51L%HK=Rn(23AZJFMKfS%dD}2Z5XQuo2@PMcpOdcQ;N2M4;I91+Wymy zeB>$}R7x_7tA31dANv3NAfL$iNf~pNfpc>R2`l2dMWrHdmRSThmL*?staXvg9C>~K Dxszw^ diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index d8c35707ed0edce515340059806f01c3a18863e9..7f179fb5f00609d742b7601e0ff462fb176d1970 100644 GIT binary patch delta 67 zcmV-J0KEU`s delta 39 xcmV+?0NDTN watchAsset(String id) { - final stackCountRef = _db.stackEntity.id.count(); - final query = _db.remoteAssetEntity.select().addColumns([ _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - stackCountRef, ]).join([ leftOuterJoin( _db.localAssetEntity, _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), useColumns: false, ), - leftOuterJoin( - _db.stackEntity, - _db.stackEntity.primaryAssetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity.createAlias('stacked_assets'), - _db.stackEntity.id.equalsExp( - _db.remoteAssetEntity.createAlias('stacked_assets').stackId, - ), - useColumns: false, - ), ]) ..where(_db.remoteAssetEntity.id.equals(id)) - ..groupBy([ - _db.remoteAssetEntity.id, - _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - ]) ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); - final primaryAssetId = row.read(_db.stackEntity.primaryAssetId); - final stackCount = - primaryAssetId == id ? (row.read(stackCountRef) ?? 0) : 0; - - return asset.copyWith( - localId: row.read(_db.localAssetEntity.id), - stackCount: stackCount, - ); + return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); }).watchSingleOrNull(); } diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 7bbae9a80..fe9ae1e60 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -71,7 +71,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { required int count, }) { return _db.mergedAssetDrift - .mergedAsset(userIds, limit: Limit(count, offset)) + .mergedAsset(userIds, limit: (_) => Limit(count, offset)) .map( (row) => row.remoteId != null && row.ownerId != null ? RemoteAsset( @@ -90,7 +90,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository { durationInSeconds: row.durationInSeconds, livePhotoVideoId: row.livePhotoVideoId, stackId: row.stackId, - stackCount: row.stackCount, ) : LocalAsset( id: row.localId!, diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart index 778041339..08b09250e 100644 --- a/mobile/lib/pages/backup/drift_backup.page.dart +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; @@ -24,12 +25,24 @@ class _DriftBackupPageState extends ConsumerState { @override void initState() { super.initState(); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); } Future startBackup() async { - await ref.read(driftBackupProvider.notifier).getBackupStatus(); - await ref.read(driftBackupProvider.notifier).backup(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); + await ref.read(driftBackupProvider.notifier).backup(currentUser.id); } Future stopBackup() async { @@ -207,7 +220,13 @@ class _BackupAlbumSelectionCard extends ConsumerWidget { trailing: ElevatedButton( onPressed: () async { await context.pushRoute(const DriftBackupAlbumSelectionRoute()); - ref.read(driftBackupProvider.notifier).getBackupStatus(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); }, child: const Text( "select", diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart index 18d3ee115..f0f3bedaa 100644 --- a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -4,17 +4,17 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart'; - import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; -import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/widgets/common/search_field.dart'; +import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; @RoutePage() class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { @@ -92,7 +92,15 @@ class _DriftBackupAlbumSelectionPageState if (didPop && !_hasPopped) { _hasPopped = true; - await ref.read(driftBackupProvider.notifier).getBackupStatus(); + super.initState(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .getBackupStatus(currentUser.id); final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount)); @@ -107,7 +115,7 @@ class _DriftBackupAlbumSelectionPageState final backupNotifier = ref.read(driftBackupProvider.notifier); backupNotifier.cancel().then((_) { - backupNotifier.backup(); + backupNotifier.backup(currentUser.id); }); } } diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index b0be136a1..418c8b9e3 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; @@ -38,7 +39,14 @@ class _TabShellPageState extends ConsumerState { await runNewSync(ref, full: true).then((_) async { if (isEnableBackup) { - await ref.read(driftBackupProvider.notifier).handleBackupResume(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref + .read(driftBackupProvider.notifier) + .handleBackupResume(currentUser.id); } }); }); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart index cb4e02b56..5b86258e7 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart @@ -6,11 +6,7 @@ class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier, BaseAsset?> { @override Future> build(BaseAsset? asset) async { - if (asset == null || - asset is! RemoteAsset || - asset.stackId == null || - // The stackCount check is to ensure we only fetch stacks for timelines that have stacks - asset.stackCount == 0) { + if (asset == null || asset is! RemoteAsset || asset.stackId == null) { return const []; } diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index ce3d39629..b059f5fea 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -54,7 +54,7 @@ class ThumbnailTile extends ConsumerWidget { : const BoxDecoration(); final hasStack = - asset is RemoteAsset && (asset as RemoteAsset).stackCount > 0; + asset is RemoteAsset && (asset as RemoteAsset).stackId != null; return Stack( children: [ @@ -86,9 +86,7 @@ class ThumbnailTile extends ConsumerWidget { right: 10.0, top: asset.isVideo ? 24.0 : 6.0, ), - child: _StackIndicator( - stackCount: (asset as RemoteAsset).stackCount, - ), + child: const _TileOverlayIcon(Icons.burst_mode_rounded), ), ), if (asset.isVideo) @@ -198,40 +196,6 @@ class _SelectionIndicator extends StatelessWidget { } } -class _StackIndicator extends StatelessWidget { - final int stackCount; - - const _StackIndicator({required this.stackCount}); - - @override - Widget build(BuildContext context) { - return Row( - spacing: 3, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - // CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - stackCount.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], - ), - ), - const _TileOverlayIcon(Icons.burst_mode_rounded), - ], - ); - } -} - class _VideoIndicator extends StatelessWidget { final Duration duration; const _VideoIndicator(this.duration); diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 3be46d2fb..647ada4a6 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -19,6 +19,7 @@ import 'package:immich_mobile/providers/memory.provider.dart'; import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/background.service.dart'; @@ -110,7 +111,14 @@ class AppLifeCycleNotifier extends StateNotifier { .getSetting(AppSettingsEnum.enableBackup); if (isEnableBackup) { - await _ref.read(driftBackupProvider.notifier).handleBackupResume(); + final currentUser = _ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _ref + .read(driftBackupProvider.notifier) + .handleBackupResume(currentUser.id); } }); } catch (e, stackTrace) { diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart index 3a2c7fd9c..45941639a 100644 --- a/mobile/lib/providers/backup/drift_backup.provider.dart +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -329,11 +329,11 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future getBackupStatus() async { + Future getBackupStatus(String userId) async { final [totalCount, backupCount, remainderCount] = await Future.wait([ _backupService.getTotalCount(), - _backupService.getBackupCount(), - _backupService.getRemainderCount(), + _backupService.getBackupCount(userId), + _backupService.getRemainderCount(userId), ]); state = state.copyWith( @@ -343,8 +343,8 @@ class ExpBackupNotifier extends StateNotifier { ); } - Future backup() { - return _backupService.backup(_updateEnqueueCount); + Future backup(String userId) { + return _backupService.backup(userId, _updateEnqueueCount); } void _updateEnqueueCount(EnqueueStatus status) { @@ -379,11 +379,11 @@ class ExpBackupNotifier extends StateNotifier { } } - Future handleBackupResume() async { + Future handleBackupResume(String userId) async { final tasks = await FileDownloader().allTasks(group: kBackupGroup); if (tasks.isEmpty) { // Start a new backup queue - await backup(); + await backup(userId); } debugPrint("Tasks to resume: ${tasks.length}"); diff --git a/mobile/lib/services/drift_backup.service.dart b/mobile/lib/services/drift_backup.service.dart index 2f51c261f..7373643c9 100644 --- a/mobile/lib/services/drift_backup.service.dart +++ b/mobile/lib/services/drift_backup.service.dart @@ -48,20 +48,21 @@ class DriftBackupService { return _backupRepository.getTotalCount(); } - Future getRemainderCount() { - return _backupRepository.getRemainderCount(); + Future getRemainderCount(String userId) { + return _backupRepository.getRemainderCount(userId); } - Future getBackupCount() { - return _backupRepository.getBackupCount(); + Future getBackupCount(String userId) { + return _backupRepository.getBackupCount(userId); } Future backup( + String userId, void Function(EnqueueStatus status) onEnqueueTasks, ) async { shouldCancel = false; - final candidates = await _backupRepository.getCandidates(); + final candidates = await _backupRepository.getCandidates(userId); if (candidates.isEmpty) { return; } diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart index d02e2ff9c49e711967d38cc501323341faf2f879..9cf72a098bb06d5501e0e1ede4bf054454fd6c3f 100644 GIT binary patch delta 154 zcmex$f#=r+o(&79PUe2cJv}^&k#FJxfz6+iKBd$|BeKxy;kso(9Vmw7NkxZ6c^ z7>(^VANZkUJYD|@BU`&~7UOo`ET(l^rn|^9TTPEsVrHApD9`K+VsTBES7M&Ky-uEa zI@9#^a?EVo4=FKwGf!7nV-}sxtHvxf{f!z>(H%8r{pmJp%+sbjr~}mqsxxOY0sxyQ BHH!cM delta 127 zcmV-_0D%Ac&Q9H#Y$gGqb?>Wj>cL|`yK;2my00-3AZjF1C9chejfv;m#-cJ2Dic@14{#! hA|?YGmklNZ6_<4;1D2OOCIdN_KqmtRw-F};X#qA=Eb9OO