import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:immich_mobile/services/trash.service.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/sync.service.dart'; import 'package:immich_mobile/utils/renderlist_generator.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; class TrashNotifier extends StateNotifier { final Isar _db; final Ref _ref; final TrashService _trashService; final _log = Logger('TrashNotifier'); TrashNotifier( this._trashService, this._db, this._ref, ) : super(false); Future emptyTrash() async { try { final user = _ref.read(currentUserProvider); if (user == null) { return; } await _trashService.emptyTrash(); final idsToRemove = await _db.assets .where() .remoteIdIsNotNull() .filter() .ownerIdEqualTo(user.isarId) .isTrashedEqualTo(true) .remoteIdProperty() .findAll(); // TODO: handle local asset removal on emptyTrash _ref .read(syncServiceProvider) .handleRemoteAssetRemoval(idsToRemove.cast().toList()); } catch (error, stack) { _log.severe("Cannot empty trash", error, stack); } } Future removeAssets(Iterable assetList) async { try { final user = _ref.read(currentUserProvider); if (user == null) { return false; } final isRemoved = await _ref .read(assetProvider.notifier) .deleteRemoteOnlyAssets(assetList, force: true); if (isRemoved) { final idsToRemove = assetList.where((a) => a.isRemote).map((a) => a.remoteId!).toList(); _ref .read(syncServiceProvider) .handleRemoteAssetRemoval(idsToRemove.cast().toList()); } return isRemoved; } catch (error, stack) { _log.severe("Cannot remove assets", error, stack); } return false; } Future restoreAsset(Asset asset) async { try { final result = await _trashService.restoreAsset(asset); if (result) { final remoteAsset = asset.isRemote; asset.isTrashed = false; if (remoteAsset) { await _db.writeTxn(() async { await _db.assets.put(asset); }); } return true; } } catch (error, stack) { _log.severe("Cannot restore asset", error, stack); } return false; } Future restoreAssets(Iterable assetList) async { try { final result = await _trashService.restoreAssets(assetList); if (result) { final remoteAssets = assetList.where((a) => a.isRemote).toList(); final updatedAssets = remoteAssets.map((e) { e.isTrashed = false; return e; }).toList(); await _db.writeTxn(() async { await _db.assets.putAll(updatedAssets); }); return true; } } catch (error, stack) { _log.severe("Cannot restore assets", error, stack); } return false; } Future restoreTrash() async { try { final user = _ref.read(currentUserProvider); if (user == null) { return; } await _trashService.restoreTrash(); final assets = await _db.assets .where() .remoteIdIsNotNull() .filter() .ownerIdEqualTo(user.isarId) .isTrashedEqualTo(true) .findAll(); final updatedAssets = assets.map((e) { e.isTrashed = false; return e; }).toList(); await _db.writeTxn(() async { await _db.assets.putAll(updatedAssets); }); } catch (error, stack) { _log.severe("Cannot restore trash", error, stack); } } } final trashProvider = StateNotifierProvider((ref) { return TrashNotifier( ref.watch(trashServiceProvider), ref.watch(dbProvider), ref, ); }); final trashedAssetsProvider = StreamProvider((ref) { final user = ref.read(currentUserProvider); if (user == null) return const Stream.empty(); final query = ref .watch(dbProvider) .assets .filter() .ownerIdEqualTo(user.isarId) .isTrashedEqualTo(true) .sortByFileCreatedAtDesc(); return renderListGeneratorWithGroupBy(query, GroupAssetsBy.none); });