diff --git a/lib/use_case/unshare_file_from_album.dart b/lib/use_case/unshare_file_from_album.dart index 7e427328..e5975ec1 100644 --- a/lib/use_case/unshare_file_from_album.dart +++ b/lib/use_case/unshare_file_from_album.dart @@ -28,14 +28,14 @@ class UnshareFileFromAlbum { _log.info( "[call] Unshare ${files.length} files from album '${album.name}' with ${unshareWith.length} users"); // list albums with shares identical to any element in [unshareWith] - final otherAlbums = (await ListAlbum(fileRepo, albumRepo)(account) + final otherAlbums = await ListAlbum(fileRepo, albumRepo)(account) .where((event) => event is Album) .cast() - .where((album) => - !album.albumFile!.compareServerIdentity(album.albumFile!) && - album.provider is AlbumStaticProvider && - album.shares?.any((s) => unshareWith.contains(s.userId)) == true) - .toList()); + .where((a) => + !a.albumFile!.compareServerIdentity(album.albumFile!) && + a.provider is AlbumStaticProvider && + a.shares?.any((s) => unshareWith.contains(s.userId)) == true) + .toList(); // look for shares that are exclusive to this album final exclusiveShares = []; @@ -49,10 +49,12 @@ class UnshareFileFromAlbum { e, stackTrace); } } + _log.fine("[call] Pre-filter shares: $exclusiveShares"); for (final a in otherAlbums) { // check if the album is shared with the same users - if (!a.shares! - .any((as) => exclusiveShares.any((s) => s.shareWith == as.userId))) { + final sharesOfInterest = + a.shares?.where((as) => unshareWith.contains(as.userId)).toList(); + if (sharesOfInterest == null || sharesOfInterest.isEmpty) { continue; } final albumFiles = AlbumStaticProvider.of(a) @@ -60,9 +62,12 @@ class UnshareFileFromAlbum { .whereType() .map((e) => e.file) .toList(); - exclusiveShares.removeWhere( - (s) => albumFiles.any((element) => element.fileId == s.itemSource)); + // remove files shared as part of this other shared album + exclusiveShares.removeWhere((s) => + sharesOfInterest.any((i) => i.userId == s.shareWith) && + albumFiles.any((f) => f.fileId == s.itemSource)); } + _log.fine("[call] Post-filter shares: $exclusiveShares"); // unshare them await _unshare(account, exclusiveShares, onUnshareFileFailed); diff --git a/test/use_case/remove_from_album_test.dart b/test/use_case/remove_from_album_test.dart index fabe6248..6bd0552b 100644 --- a/test/use_case/remove_from_album_test.dart +++ b/test/use_case/remove_from_album_test.dart @@ -27,6 +27,8 @@ void main() { test("file w/ shares managed by others", _removeFromSharedAlbumOwnedWithOtherShare); test("file w/ extra share", _removeFromSharedAlbumOwnedLeaveExtraShare); + test("file w/ share in other album", + _removeFromSharedAlbumOwnedFileInOtherAlbum); }); group("shared album (not owned)", () { test("file", _removeFromSharedAlbumNotOwned); @@ -396,6 +398,58 @@ Future _removeFromSharedAlbumOwnedLeaveExtraShare() async { ); } +/// Remove a file from a shared album (admin -> user1, user2) where the file is +/// also shared in other album (admin -> user1) +/// +/// Expect: share (admin -> user2) for the file deleted; +/// share (admin -> user1) for the file unchanged +Future _removeFromSharedAlbumOwnedFileInOtherAlbum() async { + final account = util.buildAccount(); + final pref = Pref.scoped(PrefMemoryProvider({ + "isLabEnableSharedAlbum": true, + })); + final files = + (util.FilesBuilder(initialFileId: 2)..addJpeg("admin/test1.jpg")).build(); + final album1 = (util.AlbumBuilder() + ..addFileItem(files[0]) + ..addShare("user1") + ..addShare("user2")) + .build(); + final album2 = (util.AlbumBuilder.ofId(albumId: 1) + ..addFileItem(files[0]) + ..addShare("user1")) + .build(); + final album1fileItems = util.AlbumBuilder.fileItemsOf(album1); + final album1File = album1.albumFile!; + final album2File = album2.albumFile!; + final appDb = MockAppDb(); + await appDb.use((db) async { + final transaction = db.transaction(AppDb.fileDbStoreName, idbModeReadWrite); + final store = transaction.objectStore(AppDb.fileDbStoreName); + await store.put(AppDbFileDbEntry.fromFile(account, files[0]).toJson(), + AppDbFileDbEntry.toPrimaryKey(account, files[0])); + }); + final albumRepo = MockAlbumMemoryRepo([album1, album2]); + final shareRepo = MockShareMemoryRepo([ + util.buildShare(id: "0", file: album1File, shareWith: "user1"), + util.buildShare(id: "1", file: files[0], shareWith: "user1"), + util.buildShare(id: "2", file: files[0], shareWith: "user2"), + util.buildShare(id: "3", file: album2File, shareWith: "user1"), + ]); + final fileRepo = MockFileMemoryRepo([album1File, album2File, ...files]); + + await RemoveFromAlbum(albumRepo, shareRepo, fileRepo, appDb, pref)(account, + albumRepo.findAlbumByPath(album1File.path), [album1fileItems[0]]); + expect( + shareRepo.shares, + [ + util.buildShare(id: "0", file: album1File, shareWith: "user1"), + util.buildShare(id: "1", file: files[0], shareWith: "user1"), + util.buildShare(id: "3", file: album2File, shareWith: "user1"), + ], + ); +} + /// Remove a file from a shared album (user1 -> admin, user2) /// /// Expect: shares (admin -> user1, user2) for the file deleted