import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/entity/album.dart'; import 'package:nc_photos/entity/album/cover_provider.dart'; import 'package:nc_photos/entity/album/item.dart'; import 'package:nc_photos/entity/album/provider.dart'; import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/iterable_extension.dart'; import 'package:nc_photos/use_case/preprocess_album.dart'; import 'package:nc_photos/use_case/unshare_file_from_album.dart'; import 'package:nc_photos/use_case/update_album.dart'; import 'package:nc_photos/use_case/update_album_with_actual_items.dart'; class RemoveFromAlbum { RemoveFromAlbum(this._c) : assert(require(_c)), assert(UnshareFileFromAlbum.require(_c)); static bool require(DiContainer c) => DiContainer.has(c, DiType.albumRepo) && DiContainer.has(c, DiType.appDb); /// Remove a list of AlbumItems from [album] /// /// The items are compared with [identical], so it must come from [album] for /// it to work /// /// If [shouldUnshare] is false, files will not be unshared after removing /// from the album Future call( Account account, Album album, List items, { bool shouldUnshare = true, }) async { _log.info("[call] Remove ${items.length} items from album '${album.name}'"); assert(album.provider is AlbumStaticProvider); final provider = album.provider as AlbumStaticProvider; final newItems = provider.items .where((element) => !items.containsIdentical(element)) .toList(); var newAlbum = album.copyWith( provider: AlbumStaticProvider.of(album).copyWith( items: newItems, ), ); newAlbum = await _fixAlbumPostRemove(account, newAlbum, items); await UpdateAlbum(_c.albumRepo)(account, newAlbum); if (!shouldUnshare) { _log.info("[call] Skip unsharing files"); } else { if (album.shares?.isNotEmpty == true) { final removeFiles = items.whereType().map((e) => e.file).toList(); if (removeFiles.isNotEmpty) { await _unshareFiles(account, newAlbum, removeFiles); } } } return newAlbum; } /// Update the album accordingly if any of the removed items is interesting /// (e.g., cover, latest item, etc) Future _fixAlbumPostRemove( Account account, Album newAlbum, List items) async { bool isNeedUpdate = false; for (final fileItem in items.whereType()) { if (newAlbum.coverProvider .getCover(newAlbum) ?.compareServerIdentity(fileItem.file) == true) { // revert to auto cover so [UpdateAutoAlbumCover] can do its work newAlbum = newAlbum.copyWith( coverProvider: AlbumAutoCoverProvider(), ); isNeedUpdate = true; break; } if (fileItem.file.bestDateTime == newAlbum.provider.latestItemTime) { isNeedUpdate = true; break; } } if (!isNeedUpdate) { return newAlbum; } _log.info( "[_fixAlbumPostRemove] Resync as interesting item is being removed"); // need to update the album properties final newItemsSynced = await PreProcessAlbum(_c.appDb)(account, newAlbum); newAlbum = await UpdateAlbumWithActualItems(null)( account, newAlbum, newItemsSynced, ); return newAlbum; } Future _unshareFiles( Account account, Album album, List files) async { final albumShares = (album.shares!.map((e) => e.userId).toList() ..add(album.albumFile!.ownerId ?? account.username)) .where((element) => element != account.username) .toList(); if (albumShares.isNotEmpty) { await UnshareFileFromAlbum(_c)(account, album, files, albumShares); } } final DiContainer _c; static final _log = Logger("use_case.remove_from_album.RemoveFromAlbum"); }