ente/lib/services/hidden_service.dart

143 lines
5.1 KiB
Dart
Raw Normal View History

2022-10-27 06:05:39 +00:00
import 'dart:convert';
import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sodium/flutter_sodium.dart';
import 'package:logging/logging.dart';
2022-10-27 07:23:45 +00:00
import 'package:photos/core/event_bus.dart';
import 'package:photos/events/files_updated_event.dart';
import 'package:photos/events/local_photos_updated_event.dart';
2022-10-27 06:05:39 +00:00
import 'package:photos/models/api/collection/create_request.dart';
import 'package:photos/models/collection.dart';
import 'package:photos/models/file.dart';
import 'package:photos/models/magic_metadata.dart';
import 'package:photos/services/collections_service.dart';
import 'package:photos/services/file_magic_service.dart';
import 'package:photos/utils/crypto_util.dart';
import 'package:photos/utils/dialog_util.dart';
extension HiddenService on CollectionsService {
static final _logger = Logger("HiddenCollectionService");
// getDefaultHiddenCollection will return null if there's no default
// collection
Future<Collection> getDefaultHiddenCollection() async {
if (cachedDefaultHiddenCollection != null) {
return cachedDefaultHiddenCollection;
}
final int userID = config.getUserID()!;
final Collection? defaultHidden =
collectionIDToCollections.values.firstWhereOrNull(
(element) => element.isDefaultHidden() && element.owner!.id == userID,
);
if (defaultHidden != null) {
cachedDefaultHiddenCollection = defaultHidden;
return cachedDefaultHiddenCollection;
}
final Collection createdHiddenCollection =
await _createDefaultHiddenAlbum();
2022-10-31 10:54:30 +00:00
cachedDefaultHiddenCollection = createdHiddenCollection;
return cachedDefaultHiddenCollection;
2022-10-27 06:05:39 +00:00
}
Future<bool> hideFiles(
BuildContext context,
List<File> filesToHide, {
bool forceHide = false,
}) async {
final int userID = config.getUserID()!;
final List<int> uploadedIDs = <int>[];
final dialog = createProgressDialog(
context,
2022-10-31 09:56:59 +00:00
"Hiding...",
2022-10-27 06:05:39 +00:00
);
await dialog.show();
try {
for (File file in filesToHide) {
if (file.uploadedFileID == null) {
throw AssertionError("Can only hide uploaded files");
}
if (file.ownerID != userID) {
throw AssertionError("Can only hide files owned by user");
}
uploadedIDs.add(file.uploadedFileID!);
}
final defaultHiddenCollection = await getDefaultHiddenCollection();
2022-10-27 06:05:39 +00:00
final Map<int, List<File>> collectionToFilesMap =
await filesDB.getAllFilesGroupByCollectionID(uploadedIDs);
for (MapEntry<int, List<File>> entry in collectionToFilesMap.entries) {
if (entry.key == defaultHiddenCollection.id) {
_logger.finest('file already part of hidden collection');
continue;
}
2022-10-27 06:05:39 +00:00
await move(defaultHiddenCollection.id, entry.key, entry.value);
}
2022-10-27 07:23:45 +00:00
Bus.instance.fire(
LocalPhotosUpdatedEvent(
filesToHide,
type: EventType.hide,
source: "hideFiles",
),
2022-10-31 09:56:59 +00:00
);
2022-10-27 06:05:39 +00:00
await dialog.hide();
} on AssertionError catch (e) {
await dialog.hide();
showErrorDialog(context, "Oops", e.message as String);
2022-12-15 04:01:51 +00:00
return false;
2022-10-27 06:05:39 +00:00
} catch (e, s) {
_logger.severe("Could not hide", e, s);
await dialog.hide();
showGenericErrorDialog(context);
return false;
} finally {
await dialog.hide();
}
return true;
}
Future<Collection> _createDefaultHiddenAlbum() async {
final key = CryptoUtil.generateKey();
final encryptedKeyData = CryptoUtil.encryptSync(key, config.getKey()!);
final encryptedName = CryptoUtil.encryptSync(
2022-10-31 09:56:59 +00:00
utf8.encode(".Hidden") as Uint8List,
2022-10-27 06:05:39 +00:00
key,
);
final jsonToUpdate = CollectionMagicMetadata(
visibility: visibilityHidden,
subType: subTypeDefaultHidden,
).toJson();
2022-10-31 10:12:04 +00:00
assert(jsonToUpdate.length == 2, "metadata should have two keys");
2022-10-27 06:05:39 +00:00
final encryptedMMd = await CryptoUtil.encryptChaCha(
utf8.encode(jsonEncode(jsonToUpdate)) as Uint8List,
key,
);
final MetadataRequest metadataRequest = MetadataRequest(
version: 1,
count: jsonToUpdate.length,
data: Sodium.bin2base64(encryptedMMd.encryptedData!),
header: Sodium.bin2base64(encryptedMMd.header!),
);
final CreateRequest createRequest = CreateRequest(
encryptedKey: Sodium.bin2base64(encryptedKeyData.encryptedData!),
keyDecryptionNonce: Sodium.bin2base64(encryptedKeyData.nonce!),
encryptedName: Sodium.bin2base64(encryptedName.encryptedData!),
nameDecryptionNonce: Sodium.bin2base64(encryptedName.nonce!),
2022-12-20 09:36:54 +00:00
type: CollectionType.album,
2022-10-27 06:05:39 +00:00
attributes: CollectionAttributes(),
magicMetadata: metadataRequest,
);
_logger.info("Creating Hidden Collection");
final collection =
await createAndCacheCollection(null, createRequest: createRequest);
_logger.info("Creating Hidden Collection Created Successfully");
final Collection collectionFromServer =
await fetchCollectionByID(collection.id);
_logger.info("Fetched Created Hidden Collection Successfully");
return collectionFromServer;
}
}