diff --git a/mobile/lib/services/remote/fetch/collection_files.dart b/mobile/lib/services/remote/fetch/collection_files.dart index a45d776217..d97b29f84f 100644 --- a/mobile/lib/services/remote/fetch/collection_files.dart +++ b/mobile/lib/services/remote/fetch/collection_files.dart @@ -55,6 +55,40 @@ class CollectionFilesService { } } + Future getPublicCollectionDiff( + int collectionID, + int sinceTime, + Uint8List collectionKey, + Map headers, + ) async { + final response = await _enteDio.get( + "/public-collection/diff", + options: Options(headers: headers), + queryParameters: {"sinceTime": sinceTime}, + ); + final List diff = response.data["diff"] as List; + if (diff.isEmpty) { + return DiffResult([], [], response.data["hasMore"] as bool, 0); + } + final String encodedKey = base64Encode(Uint8List.fromList(collectionKey)); + final startTime = DateTime.now(); + final DiffResult result = + await Computer.shared().compute, DiffResult>( + _parseDiff, + param: { + "collectionKey": encodedKey, + "diff": diff, + "hasMore": response.data["hasMore"] as bool, + }, + taskName: "parseDiff", + ); + devLog( + '[Collection-$collectionID] $result in ${DateTime.now().difference(startTime).inMilliseconds} ms', + name: "CollectionFilesService.getCollectionItemsDiff", + ); + return result; + } + DiffResult _parseDiff(Map args) { final Uint8List collectionKey = base64Decode(args['collectionKey']); final List diff = args['diff'] as List; diff --git a/mobile/lib/services/sync/diff_fetcher.dart b/mobile/lib/services/sync/diff_fetcher.dart deleted file mode 100644 index 8f71b6a608..0000000000 --- a/mobile/lib/services/sync/diff_fetcher.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'dart:convert'; - -import 'package:dio/dio.dart'; -import 'package:ente_crypto/ente_crypto.dart'; -import 'package:flutter/material.dart'; -import 'package:logging/logging.dart'; -import 'package:photos/core/network/network.dart'; -import 'package:photos/generated/l10n.dart'; -import 'package:photos/models/file/file.dart'; -import "package:photos/models/metadata/file_magic.dart"; -import "package:photos/services/collections_service.dart"; -import "package:photos/utils/dialog_util.dart"; -import "package:photos/utils/file_key.dart"; - -class DiffFetcher { - final _logger = Logger("DiffFetcher"); - final _enteDio = NetworkClient.instance.enteDio; - - Future> getPublicFiles( - BuildContext context, - int collectionID, - bool sortAsc, - ) async { - try { - bool hasMore = false; - final sharedFiles = []; - final headers = - CollectionsService.instance.publicCollectionHeaders(collectionID); - int sinceTime = 0; - - do { - final response = await _enteDio.get( - "/public-collection/diff", - options: Options(headers: headers), - queryParameters: {"sinceTime": sinceTime}, - ); - - final diff = response.data["diff"] as List; - hasMore = response.data["hasMore"] as bool; - - for (final item in diff) { - final file = EnteFile(); - if (item["isDeleted"]) { - continue; - } - file.uploadedFileID = item["id"]; - file.collectionID = item["collectionID"]; - file.ownerID = item["ownerID"]; - file.encryptedKey = item["encryptedKey"]; - file.keyDecryptionNonce = item["keyDecryptionNonce"]; - file.fileDecryptionHeader = item["file"]["decryptionHeader"]; - file.thumbnailDecryptionHeader = - item["thumbnail"]["decryptionHeader"]; - file.metadataDecryptionHeader = item["metadata"]["decryptionHeader"]; - if (item["info"] != null) { - file.fileSize = item["info"]["fileSize"]; - } - final fileKey = getFileKey(file); - final encodedMetadata = await CryptoUtil.decryptChaCha( - CryptoUtil.base642bin(item["metadata"]["encryptedData"]), - fileKey, - CryptoUtil.base642bin(file.metadataDecryptionHeader!), - ); - final Map metadata = - jsonDecode(utf8.decode(encodedMetadata)); - file.applyMetadata(metadata); - if (item['pubMagicMetadata'] != null) { - final utfEncodedMmd = await CryptoUtil.decryptChaCha( - CryptoUtil.base642bin(item['pubMagicMetadata']['data']), - fileKey, - CryptoUtil.base642bin(item['pubMagicMetadata']['header']), - ); - file.pubMmdEncodedJson = utf8.decode(utfEncodedMmd); - file.pubMmdVersion = item['pubMagicMetadata']['version']; - file.pubMagicMetadata = - PubMagicMetadata.fromEncodedJson(file.pubMmdEncodedJson!); - } - - // To avoid local file to be used as thumbnail or full file. - file.localID = null; - - sharedFiles.add(file); - } - - if (diff.isNotEmpty) { - sinceTime = diff.last["updationTime"]; - } - } while (hasMore); - if (sortAsc) { - sharedFiles.sort((a, b) => a.creationTime!.compareTo(b.creationTime!)); - } - return sharedFiles; - } catch (e, s) { - _logger.severe("Failed to decrypt collection ", e, s); - await showErrorDialog( - context, - S.of(context).somethingWentWrong, - e.toString(), - ); - rethrow; - } - } -} diff --git a/mobile/lib/services/sync/sync_public_collection.dart b/mobile/lib/services/sync/sync_public_collection.dart new file mode 100644 index 0000000000..1586d29d00 --- /dev/null +++ b/mobile/lib/services/sync/sync_public_collection.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:photos/core/network/network.dart'; +import 'package:photos/generated/l10n.dart'; +import 'package:photos/models/file/file.dart'; +import "package:photos/models/file/remote/asset.dart"; +import "package:photos/models/file/remote/file_entry.dart"; +import "package:photos/services/collections_service.dart"; +import "package:photos/services/remote/fetch/collection_files.dart"; +import "package:photos/utils/dialog_util.dart"; + +Future> getPublicFiles( + BuildContext context, + int collectionID, + bool sortAsc, +) async { + try { + final collectionFilService = + CollectionFilesService(NetworkClient.instance.enteDio); + bool hasMore = false; + final sharedFiles = []; + final headers = + CollectionsService.instance.publicCollectionHeaders(collectionID); + int sinceTime = 0; + final collectionKey = + CollectionsService.instance.getCollectionKey(collectionID); + do { + final diffResult = await collectionFilService.getPublicCollectionDiff( + collectionID, + sinceTime, + collectionKey, + headers, + ); + for (final item in diffResult.updatedItems) { + final cf = CollectionFileEntry( + collectionID: item.collectionID, + fileID: item.fileID, + encFileKey: item.encFileKey!, + encFileKeyNonce: item.encFileKeyNonce!, + updatedAt: item.updatedAt, + createdAt: item.createdAt ?? 0, + ); + final rAsset = RemoteAsset( + id: item.fileItem.fileID, + ownerID: item.fileItem.ownerID, + fileHeader: item.fileItem.fileDecryotionHeader!, + thumbHeader: item.fileItem.thumnailDecryptionHeader!, + metadata: item.fileItem.metadata!, + publicMetadata: item.fileItem.pubMagicMetadata, + privateMetadata: item.fileItem.magicMetadata, + info: item.fileItem.info, + ); + sharedFiles.add(EnteFile.fromRemoteAsset(rAsset, cf)); + } + sinceTime = diffResult.maxUpdatedAtTime; + hasMore = diffResult.hasMore; + } while (hasMore); + if (sortAsc) { + sharedFiles.sort((a, b) => a.creationTime!.compareTo(b.creationTime!)); + } + return sharedFiles; + } catch (e, s) { + Logger("getPublicFiles").severe("Failed to decrypt collection ", e, s); + await showErrorDialog( + context, + S.of(context).somethingWentWrong, + e.toString(), + ); + rethrow; + } +} diff --git a/mobile/lib/ui/tabs/home_widget.dart b/mobile/lib/ui/tabs/home_widget.dart index 78d93d0138..5a6694f0a6 100644 --- a/mobile/lib/ui/tabs/home_widget.dart +++ b/mobile/lib/ui/tabs/home_widget.dart @@ -44,8 +44,8 @@ import "package:photos/services/machine_learning/face_ml/person/person_service.d import "package:photos/services/memory_home_widget_service.dart"; import "package:photos/services/notification_service.dart"; import "package:photos/services/people_home_widget_service.dart"; -import "package:photos/services/sync/diff_fetcher.dart"; import "package:photos/services/sync/remote_sync_service.dart"; +import "package:photos/services/sync/sync_public_collection.dart"; import 'package:photos/states/user_details_state.dart'; import 'package:photos/theme/colors.dart'; import "package:photos/theme/effects.dart"; @@ -120,8 +120,6 @@ class _HomeWidgetState extends State { late StreamSubscription _collectionUpdatedEvent; late StreamSubscription _publicAlbumLinkSubscription; - final DiffFetcher _diffFetcher = DiffFetcher(); - @override void initState() { _logger.info("Building initstate"); @@ -337,8 +335,7 @@ class _HomeWidgetState extends State { if (result) { await dialog.show(); - final List sharedFiles = - await _diffFetcher.getPublicFiles( + final sharedFiles = await getPublicFiles( context, collection.id, collection.pubMagicMetadata.asc ?? false, @@ -369,7 +366,7 @@ class _HomeWidgetState extends State { } else { await dialog.show(); - final List sharedFiles = await _diffFetcher.getPublicFiles( + final List sharedFiles = await getPublicFiles( context, collection.id, collection.pubMagicMetadata.asc ?? false,