fix: asset count not populated for local albums

This commit is contained in:
shenlong-tanwen 2025-04-17 01:02:51 +05:30
parent c4047fd9b2
commit 24e80e667b
7 changed files with 77 additions and 10 deletions

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:immich_mobile/domain/interfaces/album_media.interface.dart'; import 'package:immich_mobile/domain/interfaces/album_media.interface.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart'; import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
@ -30,7 +31,8 @@ class SyncService {
// The deviceAlbums will not have the updatedAt field // The deviceAlbums will not have the updatedAt field
// and the assetCount will be 0. They are refreshed later // and the assetCount will be 0. They are refreshed later
// after the comparison // after the comparison
final deviceAlbums = await _albumMediaRepository.getAll(); final deviceAlbums =
(await _albumMediaRepository.getAll()).sortedBy((a) => a.id);
final dbAlbums = final dbAlbums =
await _localAlbumRepository.getAll(sortBy: SortLocalAlbumsBy.id); await _localAlbumRepository.getAll(sortBy: SortLocalAlbumsBy.id);

View File

@ -2,12 +2,13 @@
import 'dart:async'; import 'dart:async';
import 'package:immich_mobile/providers/infrastructure/sync_stream.provider.dart'; import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
import 'package:immich_mobile/utils/isolate.dart'; import 'package:immich_mobile/utils/isolate.dart';
import 'package:worker_manager/worker_manager.dart'; import 'package:worker_manager/worker_manager.dart';
class BackgroundSyncManager { class BackgroundSyncManager {
Cancelable<void>? _userSyncTask; Cancelable<void>? _userSyncTask;
Cancelable<void>? _deviceAlbumSyncTask;
BackgroundSyncManager(); BackgroundSyncManager();
@ -21,6 +22,20 @@ class BackgroundSyncManager {
return Future.wait(futures); return Future.wait(futures);
} }
// No need to cancel the task, as it can also be run when the user logs out
Future<void> syncDeviceAlbums() {
if (_deviceAlbumSyncTask != null) {
return _deviceAlbumSyncTask!.future;
}
_deviceAlbumSyncTask = runInIsolateGentle(
computation: (ref) => ref.read(syncServiceProvider).syncLocalAlbums(),
);
return _deviceAlbumSyncTask!.whenComplete(() {
_deviceAlbumSyncTask = null;
});
}
Future<void> syncUsers() { Future<void> syncUsers() {
if (_userSyncTask != null) { if (_userSyncTask != null) {
return _userSyncTask!.future; return _userSyncTask!.future;
@ -29,9 +44,9 @@ class BackgroundSyncManager {
_userSyncTask = runInIsolateGentle( _userSyncTask = runInIsolateGentle(
computation: (ref) => ref.read(syncStreamServiceProvider).syncUsers(), computation: (ref) => ref.read(syncStreamServiceProvider).syncUsers(),
); );
_userSyncTask!.whenComplete(() { return _userSyncTask!
_userSyncTask = null; ..whenComplete(() {
}); _userSyncTask = null;
return _userSyncTask!.future; });
} }
} }

View File

@ -22,11 +22,12 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin {
} }
extension LocalAlbumEntityX on LocalAlbumEntityData { extension LocalAlbumEntityX on LocalAlbumEntityData {
LocalAlbum toDto() { LocalAlbum toDto({int assetCount = 0}) {
return LocalAlbum( return LocalAlbum(
id: id, id: id,
name: name, name: name,
updatedAt: updatedAt, updatedAt: updatedAt,
assetCount: assetCount,
thumbnailId: thumbnailId, thumbnailId: thumbnailId,
backupSelection: backupSelection, backupSelection: backupSelection,
isAll: isAll, isAll: isAll,

View File

@ -20,11 +20,28 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository
@override @override
Future<List<LocalAlbum>> getAll({SortLocalAlbumsBy? sortBy}) { Future<List<LocalAlbum>> getAll({SortLocalAlbumsBy? sortBy}) {
final query = _db.localAlbumEntity.select(); final assetCount = _db.localAlbumAssetEntity.assetId.count();
final query = _db.localAlbumEntity.select().join([
innerJoin(
_db.localAlbumAssetEntity,
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
useColumns: false,
),
]);
query
..addColumns([assetCount])
..groupBy([_db.localAlbumEntity.id]);
if (sortBy == SortLocalAlbumsBy.id) { if (sortBy == SortLocalAlbumsBy.id) {
query.orderBy([(a) => OrderingTerm.asc(a.id)]); query.orderBy([OrderingTerm.asc(_db.localAlbumEntity.id)]);
} }
return query.map((a) => a.toDto()).get(); return query
.map(
(row) => row
.readTable(_db.localAlbumEntity)
.toDto(assetCount: row.read(assetCount) ?? 0),
)
.get();
} }
@override @override

View File

@ -0,0 +1,13 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/album_media.interface.dart';
import 'package:immich_mobile/domain/interfaces/local_album.interface.dart';
import 'package:immich_mobile/infrastructure/repositories/album_media.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
final albumMediaRepositoryProvider =
Provider<IAlbumMediaRepository>((ref) => const AlbumMediaRepository());
final localAlbumRepository = Provider<ILocalAlbumRepository>(
(ref) => DriftLocalAlbumRepository(ref.watch(driftProvider)),
);

View File

@ -0,0 +1,8 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/interfaces/local_asset.interface.dart';
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
final localAssetProvider = Provider<ILocalAssetRepository>(
(ref) => DriftLocalAssetRepository(ref.watch(driftProvider)),
);

View File

@ -1,11 +1,22 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/services/sync.service.dart';
import 'package:immich_mobile/domain/services/sync_stream.service.dart'; import 'package:immich_mobile/domain/services/sync_stream.service.dart';
import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/api.provider.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
final syncServiceProvider = Provider(
(ref) => SyncService(
albumMediaRepository: ref.watch(albumMediaRepositoryProvider),
localAlbumRepository: ref.watch(localAlbumRepository),
localAssetRepository: ref.watch(localAssetProvider),
),
);
final syncStreamServiceProvider = Provider( final syncStreamServiceProvider = Provider(
(ref) => SyncStreamService( (ref) => SyncStreamService(
syncApiRepository: ref.watch(syncApiRepositoryProvider), syncApiRepository: ref.watch(syncApiRepositoryProvider),