From c3376b10da3cd8383146efbdccdd606043066700 Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Sun, 1 Aug 2021 03:33:31 +0800 Subject: [PATCH] Throttle album clean ups when removing file --- lib/iterable_extension.dart | 8 ++++ lib/use_case/remove.dart | 76 +++++++++++++++++++++++++++++++------ 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/lib/iterable_extension.dart b/lib/iterable_extension.dart index 3632dc16..d074ce0e 100644 --- a/lib/iterable_extension.dart +++ b/lib/iterable_extension.dart @@ -33,4 +33,12 @@ extension IterableExtension on Iterable { /// Same as [contains] but uses [identical] to compare the objects bool containsIdentical(T element) => containsIf(element, (a, b) => identical(a, b)); + + Iterable>> groupBy({required U Function(T e) key}) { + final map = fold>>( + {}, + (previousValue, element) => + previousValue..putIfAbsent(key(element), () => []).add(element)); + return map.entries.map((e) => Tuple2(e.key, e.value)); + } } diff --git a/lib/use_case/remove.dart b/lib/use_case/remove.dart index e94ab034..d270d74a 100644 --- a/lib/use_case/remove.dart +++ b/lib/use_case/remove.dart @@ -7,6 +7,8 @@ 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/event/event.dart'; +import 'package:nc_photos/iterable_extension.dart'; +import 'package:nc_photos/throttler.dart'; import 'package:nc_photos/use_case/list_album.dart'; import 'package:nc_photos/use_case/update_album.dart'; @@ -18,26 +20,77 @@ class Remove { await fileRepo.remove(account, file); if (albumRepo != null) { _log.info("[call] Skip albums cleanup as albumRepo == null"); - await _cleanUpAlbums(account, file); + _CleanUpAlbums()(_CleanUpAlbumsData(fileRepo, albumRepo!, account, file)); } KiwiContainer().resolve().fire(FileRemovedEvent(account, file)); } - Future _cleanUpAlbums(Account account, File file) async { - final albumRepo = this.albumRepo!; + final FileRepo fileRepo; + final AlbumRepo? albumRepo; + + static final _log = Logger("use_case.remove.Remove"); +} + +class _CleanUpAlbumsData { + _CleanUpAlbumsData(this.fileRepo, this.albumRepo, this.account, this.file); + + final FileRepo fileRepo; + final AlbumRepo albumRepo; + final Account account; + final File file; +} + +class _CleanUpAlbums { + factory _CleanUpAlbums() { + if (_inst == null) { + _inst = _CleanUpAlbums._(); + } + return _inst!; + } + + _CleanUpAlbums._() { + _throttler = Throttler<_CleanUpAlbumsData>( + onTriggered: (data) { + _onTriggered(data); + }, + logTag: "remove._CleanUpAlbums", + ); + } + + void call(_CleanUpAlbumsData data) { + _throttler.trigger( + maxResponceTime: const Duration(seconds: 3), + maxPendingCount: 10, + data: data, + ); + } + + void _onTriggered(List<_CleanUpAlbumsData> data) async { + for (final pair in data.groupBy(key: (e) => e.account)) { + final list = pair.item2; + await _cleanUp(list.first.fileRepo, list.first.albumRepo, + list.first.account, list.map((e) => e.file).toList()); + } + } + + /// Clean up for a single account + Future _cleanUp(FileRepo fileRepo, AlbumRepo albumRepo, Account account, + List removes) async { final albums = (await ListAlbum(fileRepo, albumRepo)(account) - .where((event) => event is Album) - .toList()).cast(); + .where((event) => event is Album) + .toList()) + .cast(); // clean up only make sense for static albums for (final a in albums.where((element) => element.provider is AlbumStaticProvider)) { try { final provider = AlbumStaticProvider.of(a); - if (provider.items.any((element) => - element is AlbumFileItem && element.file.path == file.path)) { + if (provider.items.whereType().any((element) => + removes.containsIf(element.file, (a, b) => a.path == b.path))) { final newItems = provider.items.where((element) { if (element is AlbumFileItem) { - return element.file.path != file.path; + return !removes.containsIf( + element.file, (a, b) => a.path == b.path); } else { return true; } @@ -58,8 +111,9 @@ class Remove { } } - final FileRepo fileRepo; - final AlbumRepo? albumRepo; + late final Throttler<_CleanUpAlbumsData> _throttler; - static final _log = Logger("use_case.remove.Remove"); + static final _log = Logger("use_case.remove"); + + static _CleanUpAlbums? _inst; }