mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-02 18:59:15 -05:00 
			
		
		
		
	fix(mobile): asset deletion state management (#4568)
This commit is contained in:
		
							parent
							
								
									aefd052888
								
							
						
					
					
						commit
						52e09b4857
					
				@ -2,9 +2,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/ui/asset_grid/asset_grid_data_structure.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/trash/services/trash.service.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/models/asset.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/models/exif_info.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/providers/db.provider.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/providers/user.provider.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/services/sync.service.dart';
 | 
			
		||||
import 'package:isar/isar.dart';
 | 
			
		||||
import 'package:logging/logging.dart';
 | 
			
		||||
 | 
			
		||||
@ -28,19 +28,18 @@ class TrashNotifier extends StateNotifier<bool> {
 | 
			
		||||
      }
 | 
			
		||||
      await _trashService.emptyTrash();
 | 
			
		||||
 | 
			
		||||
      final dbIds = await _db.assets
 | 
			
		||||
      final idsToRemove = await _db.assets
 | 
			
		||||
          .where()
 | 
			
		||||
          .remoteIdIsNotNull()
 | 
			
		||||
          .filter()
 | 
			
		||||
          .ownerIdEqualTo(user.isarId)
 | 
			
		||||
          .isTrashedEqualTo(true)
 | 
			
		||||
          .idProperty()
 | 
			
		||||
          .remoteIdProperty()
 | 
			
		||||
          .findAll();
 | 
			
		||||
 | 
			
		||||
      await _db.writeTxn(() async {
 | 
			
		||||
        await _db.exifInfos.deleteAll(dbIds);
 | 
			
		||||
        await _db.assets.deleteAll(dbIds);
 | 
			
		||||
      });
 | 
			
		||||
      _ref
 | 
			
		||||
          .read(syncServiceProvider)
 | 
			
		||||
          .handleRemoteAssetRemoval(idsToRemove.cast<String>().toList());
 | 
			
		||||
    } catch (error, stack) {
 | 
			
		||||
      _log.severe("Cannot empty trash ${error.toString()}", error, stack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -94,7 +94,7 @@ class AssetNotifier extends StateNotifier<bool> {
 | 
			
		||||
 | 
			
		||||
  Future<bool> deleteAssets(
 | 
			
		||||
    Iterable<Asset> deleteAssets, {
 | 
			
		||||
    bool? force = false,
 | 
			
		||||
    bool force = false,
 | 
			
		||||
  }) async {
 | 
			
		||||
    _deleteInProgress = true;
 | 
			
		||||
    state = true;
 | 
			
		||||
@ -102,25 +102,69 @@ class AssetNotifier extends StateNotifier<bool> {
 | 
			
		||||
      final localDeleted = await _deleteLocalAssets(deleteAssets);
 | 
			
		||||
      final remoteDeleted = await _deleteRemoteAssets(deleteAssets, force);
 | 
			
		||||
      if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) {
 | 
			
		||||
        List<Asset>? assetsToUpdate;
 | 
			
		||||
        // Local only assets are permanently deleted for now. So always remove them from db
 | 
			
		||||
        final dbIds = deleteAssets
 | 
			
		||||
            .where((a) => a.isLocal && !a.isRemote)
 | 
			
		||||
            .map((e) => e.id)
 | 
			
		||||
            .toList();
 | 
			
		||||
        if (force == null || !force) {
 | 
			
		||||
          assetsToUpdate = remoteDeleted.map((e) {
 | 
			
		||||
        final dbIds = <int>[];
 | 
			
		||||
        final dbUpdates = <Asset>[];
 | 
			
		||||
 | 
			
		||||
        // Local assets are removed
 | 
			
		||||
        if (localDeleted.isNotEmpty) {
 | 
			
		||||
          // Permanently remove local only assets from isar
 | 
			
		||||
          dbIds.addAll(
 | 
			
		||||
            deleteAssets
 | 
			
		||||
                .where((a) => a.storage == AssetState.local)
 | 
			
		||||
                .map((e) => e.id),
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          if (remoteDeleted.any((e) => e.isLocal)) {
 | 
			
		||||
            // Force delete: Add all local assets including merged assets
 | 
			
		||||
            if (force) {
 | 
			
		||||
              dbIds.addAll(remoteDeleted.map((e) => e.id));
 | 
			
		||||
              // Soft delete: Remove local Id from asset and trash it
 | 
			
		||||
            } else {
 | 
			
		||||
              dbUpdates.addAll(
 | 
			
		||||
                remoteDeleted.map((e) {
 | 
			
		||||
                  e.localId = null;
 | 
			
		||||
                  e.isTrashed = true;
 | 
			
		||||
                  return e;
 | 
			
		||||
          }).toList();
 | 
			
		||||
                }),
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Handle remote deletion
 | 
			
		||||
        if (remoteDeleted.isNotEmpty) {
 | 
			
		||||
          if (force) {
 | 
			
		||||
            // Remove remote only assets
 | 
			
		||||
            dbIds.addAll(
 | 
			
		||||
              deleteAssets
 | 
			
		||||
                  .where((a) => a.storage == AssetState.remote)
 | 
			
		||||
                  .map((e) => e.id),
 | 
			
		||||
            );
 | 
			
		||||
            // Local assets are not removed and there are merged assets
 | 
			
		||||
            final hasLocal = remoteDeleted.any((e) => e.isLocal);
 | 
			
		||||
            if (localDeleted.isEmpty && hasLocal) {
 | 
			
		||||
              // Remove remote Id from local assets
 | 
			
		||||
              dbUpdates.addAll(
 | 
			
		||||
                remoteDeleted.map((e) {
 | 
			
		||||
                  e.remoteId = null;
 | 
			
		||||
                  // Remove from trashed if remote asset is removed
 | 
			
		||||
                  e.isTrashed = false;
 | 
			
		||||
                  return e;
 | 
			
		||||
                }),
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
          // Add all remote assets to be deleted from isar as since they are permanently deleted
 | 
			
		||||
          dbIds.addAll(remoteDeleted.map((e) => e.id));
 | 
			
		||||
            dbUpdates.addAll(
 | 
			
		||||
              remoteDeleted.map((e) {
 | 
			
		||||
                e.isTrashed = true;
 | 
			
		||||
                return e;
 | 
			
		||||
              }),
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await _db.writeTxn(() async {
 | 
			
		||||
          if (assetsToUpdate != null) {
 | 
			
		||||
            await _db.assets.putAll(assetsToUpdate);
 | 
			
		||||
          }
 | 
			
		||||
          await _db.assets.putAll(dbUpdates);
 | 
			
		||||
          await _db.exifInfos.deleteAll(dbIds);
 | 
			
		||||
          await _db.assets.deleteAll(dbIds);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -173,7 +173,14 @@ class SyncService {
 | 
			
		||||
  /// Deletes remote-only assets, updates merged assets to be local-only
 | 
			
		||||
  Future<void> handleRemoteAssetRemoval(List<String> idsToDelete) {
 | 
			
		||||
    return _db.writeTxn(() async {
 | 
			
		||||
      await _db.assets.remote(idsToDelete).filter().localIdIsNull().deleteAll();
 | 
			
		||||
      final idsToRemove = await _db.assets
 | 
			
		||||
          .remote(idsToDelete)
 | 
			
		||||
          .filter()
 | 
			
		||||
          .localIdIsNull()
 | 
			
		||||
          .idProperty()
 | 
			
		||||
          .findAll();
 | 
			
		||||
      await _db.assets.deleteAll(idsToRemove);
 | 
			
		||||
      await _db.exifInfos.deleteAll(idsToRemove);
 | 
			
		||||
      final onlyLocal = await _db.assets.remote(idsToDelete).findAll();
 | 
			
		||||
      if (onlyLocal.isNotEmpty) {
 | 
			
		||||
        for (final Asset a in onlyLocal) {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user