1
0
mirror of https://github.com/immich-app/immich.git synced 2025-01-12 15:32:36 +02:00

fix(mobile): asset deletion state management (#4568)

This commit is contained in:
shenlong 2023-10-25 16:02:59 +00:00 committed by GitHub
parent aefd052888
commit 52e09b4857
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 27 deletions

View File

@ -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);
} }

View File

@ -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
e.isTrashed = true; .where((a) => a.storage == AssetState.local)
return e; .map((e) => e.id),
}).toList(); );
} else {
// Add all remote assets to be deleted from isar as since they are permanently deleted if (remoteDeleted.any((e) => e.isLocal)) {
dbIds.addAll(remoteDeleted.map((e) => e.id)); // Force delete: Add all local assets including merged assets
} if (force) {
await _db.writeTxn(() async { dbIds.addAll(remoteDeleted.map((e) => e.id));
if (assetsToUpdate != null) { // Soft delete: Remove local Id from asset and trash it
await _db.assets.putAll(assetsToUpdate); } else {
dbUpdates.addAll(
remoteDeleted.map((e) {
e.localId = null;
e.isTrashed = true;
return e;
}),
);
}
} }
}
// 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 {
dbUpdates.addAll(
remoteDeleted.map((e) {
e.isTrashed = true;
return e;
}),
);
}
}
await _db.writeTxn(() async {
await _db.assets.putAll(dbUpdates);
await _db.exifInfos.deleteAll(dbIds); await _db.exifInfos.deleteAll(dbIds);
await _db.assets.deleteAll(dbIds); await _db.assets.deleteAll(dbIds);
}); });

View File

@ -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) {