1
0
mirror of https://github.com/immich-app/immich.git synced 2025-07-04 05:50:38 +02:00
Files
immich/mobile/lib/providers/asset.provider.dart

192 lines
5.8 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
2022-02-03 10:06:44 -06:00
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/user.service.dart';
import 'package:immich_mobile/entities/asset.entity.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
import 'package:immich_mobile/providers/memory.provider.dart';
import 'package:immich_mobile/services/album.service.dart';
import 'package:immich_mobile/services/asset.service.dart';
import 'package:immich_mobile/services/etag.service.dart';
import 'package:immich_mobile/services/exif.service.dart';
import 'package:immich_mobile/services/sync.service.dart';
import 'package:logging/logging.dart';
2022-02-03 10:06:44 -06:00
final assetProvider = StateNotifierProvider<AssetNotifier, bool>((ref) {
return AssetNotifier(
ref.watch(assetServiceProvider),
ref.watch(albumServiceProvider),
ref.watch(userServiceProvider),
ref.watch(syncServiceProvider),
ref.watch(etagServiceProvider),
ref.watch(exifServiceProvider),
ref,
);
});
class AssetNotifier extends StateNotifier<bool> {
final AssetService _assetService;
final AlbumService _albumService;
final UserService _userService;
final SyncService _syncService;
final ETagService _etagService;
final ExifService _exifService;
final Ref _ref;
final log = Logger('AssetNotifier');
bool _getAllAssetInProgress = false;
bool _deleteInProgress = false;
2022-02-03 10:06:44 -06:00
AssetNotifier(
this._assetService,
this._albumService,
this._userService,
this._syncService,
this._etagService,
this._exifService,
this._ref,
) : super(false);
2022-10-15 23:20:15 +02:00
Future<void> getAllAsset({bool clear = false}) async {
if (_getAllAssetInProgress || _deleteInProgress) {
// guard against multiple calls to this method while it's still working
return;
}
final stopwatch = Stopwatch()..start();
try {
_getAllAssetInProgress = true;
state = true;
if (clear) {
await clearAllAssets();
log.info("Manual refresh requested, cleared assets and albums from db");
}
final users = await _syncService.getUsersFromServer();
bool changedUsers = false;
if (users != null) {
changedUsers = await _syncService.syncUsersFromServer(users);
}
final bool newRemote = await _assetService.refreshRemoteAssets();
final bool newLocal = await _albumService.refreshDeviceAlbums();
debugPrint(
"changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal",
);
if (newRemote) {
_ref.invalidate(memoryFutureProvider);
}
log.info("Load assets: ${stopwatch.elapsedMilliseconds}ms");
} catch (error) {
// If there is error in getting the remote assets, still showing the new local assets
await _albumService.refreshDeviceAlbums();
} finally {
_getAllAssetInProgress = false;
state = false;
2022-10-14 23:57:55 +02:00
}
}
Future<void> clearAllAssets() async {
await Store.delete(StoreKey.assetETag);
await Future.wait([
_assetService.clearTable(),
_exifService.clearTable(),
_albumService.clearTable(),
_userService.deleteAll(),
_etagService.clearTable(),
]);
}
2022-02-03 10:06:44 -06:00
Future<void> onNewAssetUploaded(Asset newAsset) async {
// eTag on device is not valid after partially modifying the assets
Store.delete(StoreKey.assetETag);
await _syncService.syncNewAssetToDb(newAsset);
}
Future<bool> deleteLocalAssets(List<Asset> assets) async {
_deleteInProgress = true;
state = true;
try {
await _assetService.deleteLocalAssets(assets);
return true;
} catch (error) {
log.severe("Failed to delete local assets", error);
return false;
} finally {
_deleteInProgress = false;
state = false;
}
}
/// Delete remote asset only
///
/// Default behavior is trashing the asset
Future<bool> deleteRemoteAssets(
Iterable<Asset> deleteAssets, {
bool shouldDeletePermanently = false,
}) async {
_deleteInProgress = true;
state = true;
try {
await _assetService.deleteRemoteAssets(
deleteAssets,
shouldDeletePermanently: shouldDeletePermanently,
);
return true;
} catch (error) {
log.severe("Failed to delete remote assets", error);
return false;
} finally {
_deleteInProgress = false;
state = false;
}
}
feat(server): trash asset (#4015) * refactor(server): delete assets endpoint * fix: formatting * chore: cleanup * chore: open api * chore(mobile): replace DeleteAssetDTO with BulkIdsDTOs * feat: trash an asset * chore(server): formatting * chore: open api * chore: wording * chore: open-api * feat(server): add withDeleted to getAssets queries * WIP: mobile-recycle-bin * feat(server): recycle-bin to system config * feat(web): use recycle-bin system config * chore(server): domain assetcore removed * chore(server): rename recycle-bin to trash * chore(web): rename recycle-bin to trash * chore(server): always send soft deleted assets for getAllByUserId * chore(web): formatting * feat(server): permanent delete assets older than trashed period * feat(web): trash empty placeholder image * feat(server): empty trash * feat(web): empty trash * WIP: mobile-recycle-bin * refactor(server): empty / restore trash to separate endpoint * test(server): handle failures * test(server): fix e2e server-info test * test(server): deletion test refactor * feat(mobile): use map settings from server-config to enable / disable map * feat(mobile): trash asset * fix(server): operations on assets in trash * feat(web): show trash statistics * fix(web): handle trash enabled * fix(mobile): restore updates from trash * fix(server): ignore trashed assets for person * fix(server): add / remove search index when trashed / restored * chore(web): format * fix(server): asset service test * fix(server): include trashed assts for duplicates from uploads * feat(mobile): no dialog for trash, always dialog for permanent delete * refactor(mobile): use isar where instead of dart filter * refactor(mobile): asset provide - handle deletes in single db txn * chore(mobile): review changes * feat(web): confirmation before empty trash * server: review changes * fix(server): handle library changes * fix: filter external assets from getting trashed / deleted * fix(server): empty-bin * feat: broadcast config update events through ws * change order of trash button on mobile * styling * fix(mobile): do not show trashed toast for local only assets --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-10-06 07:01:14 +00:00
Future<bool> deleteAssets(
Iterable<Asset> deleteAssets, {
bool force = false,
feat(server): trash asset (#4015) * refactor(server): delete assets endpoint * fix: formatting * chore: cleanup * chore: open api * chore(mobile): replace DeleteAssetDTO with BulkIdsDTOs * feat: trash an asset * chore(server): formatting * chore: open api * chore: wording * chore: open-api * feat(server): add withDeleted to getAssets queries * WIP: mobile-recycle-bin * feat(server): recycle-bin to system config * feat(web): use recycle-bin system config * chore(server): domain assetcore removed * chore(server): rename recycle-bin to trash * chore(web): rename recycle-bin to trash * chore(server): always send soft deleted assets for getAllByUserId * chore(web): formatting * feat(server): permanent delete assets older than trashed period * feat(web): trash empty placeholder image * feat(server): empty trash * feat(web): empty trash * WIP: mobile-recycle-bin * refactor(server): empty / restore trash to separate endpoint * test(server): handle failures * test(server): fix e2e server-info test * test(server): deletion test refactor * feat(mobile): use map settings from server-config to enable / disable map * feat(mobile): trash asset * fix(server): operations on assets in trash * feat(web): show trash statistics * fix(web): handle trash enabled * fix(mobile): restore updates from trash * fix(server): ignore trashed assets for person * fix(server): add / remove search index when trashed / restored * chore(web): format * fix(server): asset service test * fix(server): include trashed assts for duplicates from uploads * feat(mobile): no dialog for trash, always dialog for permanent delete * refactor(mobile): use isar where instead of dart filter * refactor(mobile): asset provide - handle deletes in single db txn * chore(mobile): review changes * feat(web): confirmation before empty trash * server: review changes * fix(server): handle library changes * fix: filter external assets from getting trashed / deleted * fix(server): empty-bin * feat: broadcast config update events through ws * change order of trash button on mobile * styling * fix(mobile): do not show trashed toast for local only assets --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-10-06 07:01:14 +00:00
}) async {
_deleteInProgress = true;
state = true;
try {
await _assetService.deleteAssets(
deleteAssets,
shouldDeletePermanently: force,
);
return true;
} catch (error) {
log.severe("Failed to delete assets", error);
return false;
} finally {
_deleteInProgress = false;
state = false;
}
}
Future<void> toggleFavorite(List<Asset> assets, [bool? status]) {
status ??= !assets.every((a) => a.isFavorite);
return _assetService.changeFavoriteStatus(assets, status);
}
Future<void> toggleArchive(List<Asset> assets, [bool? status]) {
status ??= !assets.every((a) => a.isArchived);
return _assetService.changeArchiveStatus(assets, status);
}
2022-02-03 10:06:44 -06:00
}
final assetDetailProvider =
StreamProvider.autoDispose.family<Asset, Asset>((ref, asset) async* {
final assetService = ref.watch(assetServiceProvider);
yield await assetService.loadExif(asset);
await for (final asset in assetService.watchAsset(asset.id)) {
if (asset != null) {
yield await ref.watch(assetServiceProvider).loadExif(asset);
}
}
});
final assetWatcher =
StreamProvider.autoDispose.family<Asset?, Asset>((ref, asset) {
final assetService = ref.watch(assetServiceProvider);
return assetService.watchAsset(asset.id, fireImmediately: true);
});