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/home/ui/asset_grid/asset_grid_data_structure.dart';
 | 
				
			||||||
import 'package:immich_mobile/modules/trash/services/trash.service.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/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/db.provider.dart';
 | 
				
			||||||
import 'package:immich_mobile/shared/providers/user.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:isar/isar.dart';
 | 
				
			||||||
import 'package:logging/logging.dart';
 | 
					import 'package:logging/logging.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,19 +28,18 @@ class TrashNotifier extends StateNotifier<bool> {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      await _trashService.emptyTrash();
 | 
					      await _trashService.emptyTrash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      final dbIds = await _db.assets
 | 
					      final idsToRemove = await _db.assets
 | 
				
			||||||
          .where()
 | 
					          .where()
 | 
				
			||||||
          .remoteIdIsNotNull()
 | 
					          .remoteIdIsNotNull()
 | 
				
			||||||
          .filter()
 | 
					          .filter()
 | 
				
			||||||
          .ownerIdEqualTo(user.isarId)
 | 
					          .ownerIdEqualTo(user.isarId)
 | 
				
			||||||
          .isTrashedEqualTo(true)
 | 
					          .isTrashedEqualTo(true)
 | 
				
			||||||
          .idProperty()
 | 
					          .remoteIdProperty()
 | 
				
			||||||
          .findAll();
 | 
					          .findAll();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await _db.writeTxn(() async {
 | 
					      _ref
 | 
				
			||||||
        await _db.exifInfos.deleteAll(dbIds);
 | 
					          .read(syncServiceProvider)
 | 
				
			||||||
        await _db.assets.deleteAll(dbIds);
 | 
					          .handleRemoteAssetRemoval(idsToRemove.cast<String>().toList());
 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    } catch (error, stack) {
 | 
					    } catch (error, stack) {
 | 
				
			||||||
      _log.severe("Cannot empty trash ${error.toString()}", error, stack);
 | 
					      _log.severe("Cannot empty trash ${error.toString()}", error, stack);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -94,7 +94,7 @@ class AssetNotifier extends StateNotifier<bool> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  Future<bool> deleteAssets(
 | 
					  Future<bool> deleteAssets(
 | 
				
			||||||
    Iterable<Asset> deleteAssets, {
 | 
					    Iterable<Asset> deleteAssets, {
 | 
				
			||||||
    bool? force = false,
 | 
					    bool force = false,
 | 
				
			||||||
  }) async {
 | 
					  }) async {
 | 
				
			||||||
    _deleteInProgress = true;
 | 
					    _deleteInProgress = true;
 | 
				
			||||||
    state = true;
 | 
					    state = true;
 | 
				
			||||||
@ -102,25 +102,69 @@ class AssetNotifier extends StateNotifier<bool> {
 | 
				
			|||||||
      final localDeleted = await _deleteLocalAssets(deleteAssets);
 | 
					      final localDeleted = await _deleteLocalAssets(deleteAssets);
 | 
				
			||||||
      final remoteDeleted = await _deleteRemoteAssets(deleteAssets, force);
 | 
					      final remoteDeleted = await _deleteRemoteAssets(deleteAssets, force);
 | 
				
			||||||
      if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) {
 | 
					      if (localDeleted.isNotEmpty || remoteDeleted.isNotEmpty) {
 | 
				
			||||||
        List<Asset>? assetsToUpdate;
 | 
					        final dbIds = <int>[];
 | 
				
			||||||
        // Local only assets are permanently deleted for now. So always remove them from db
 | 
					        final dbUpdates = <Asset>[];
 | 
				
			||||||
        final dbIds = deleteAssets
 | 
					
 | 
				
			||||||
            .where((a) => a.isLocal && !a.isRemote)
 | 
					        // Local assets are removed
 | 
				
			||||||
            .map((e) => e.id)
 | 
					        if (localDeleted.isNotEmpty) {
 | 
				
			||||||
            .toList();
 | 
					          // Permanently remove local only assets from isar
 | 
				
			||||||
        if (force == null || !force) {
 | 
					          dbIds.addAll(
 | 
				
			||||||
          assetsToUpdate = remoteDeleted.map((e) {
 | 
					            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;
 | 
					                  e.isTrashed = true;
 | 
				
			||||||
                  return e;
 | 
					                  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 {
 | 
					          } else {
 | 
				
			||||||
          // Add all remote assets to be deleted from isar as since they are permanently deleted
 | 
					            dbUpdates.addAll(
 | 
				
			||||||
          dbIds.addAll(remoteDeleted.map((e) => e.id));
 | 
					              remoteDeleted.map((e) {
 | 
				
			||||||
 | 
					                e.isTrashed = true;
 | 
				
			||||||
 | 
					                return e;
 | 
				
			||||||
 | 
					              }),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await _db.writeTxn(() async {
 | 
					        await _db.writeTxn(() async {
 | 
				
			||||||
          if (assetsToUpdate != null) {
 | 
					          await _db.assets.putAll(dbUpdates);
 | 
				
			||||||
            await _db.assets.putAll(assetsToUpdate);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          await _db.exifInfos.deleteAll(dbIds);
 | 
					          await _db.exifInfos.deleteAll(dbIds);
 | 
				
			||||||
          await _db.assets.deleteAll(dbIds);
 | 
					          await _db.assets.deleteAll(dbIds);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
				
			|||||||
@ -173,7 +173,14 @@ class SyncService {
 | 
				
			|||||||
  /// Deletes remote-only assets, updates merged assets to be local-only
 | 
					  /// Deletes remote-only assets, updates merged assets to be local-only
 | 
				
			||||||
  Future<void> handleRemoteAssetRemoval(List<String> idsToDelete) {
 | 
					  Future<void> handleRemoteAssetRemoval(List<String> idsToDelete) {
 | 
				
			||||||
    return _db.writeTxn(() async {
 | 
					    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();
 | 
					      final onlyLocal = await _db.assets.remote(idsToDelete).findAll();
 | 
				
			||||||
      if (onlyLocal.isNotEmpty) {
 | 
					      if (onlyLocal.isNotEmpty) {
 | 
				
			||||||
        for (final Asset a in onlyLocal) {
 | 
					        for (final Asset a in onlyLocal) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user