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:
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
|
||||||
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);
|
||||||
});
|
});
|
||||||
|
@ -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…
Reference in New Issue
Block a user