瀏覽代碼

Expose API to add a photo to the favorites-collection

Vishnu Mohandas 4 年之前
父節點
當前提交
65a431f366
共有 3 個文件被更改,包括 150 次插入10 次删除
  1. 66 0
      lib/models/collection_file_item.dart
  2. 34 7
      lib/services/collections_service.dart
  3. 50 3
      lib/services/favorites_service.dart

+ 66 - 0
lib/models/collection_file_item.dart

@@ -0,0 +1,66 @@
+import 'dart:convert';
+
+class CollectionFileItem {
+  final int id;
+  final String encryptedKey;
+  final String keyDecryptionNonce;
+
+  CollectionFileItem(
+    this.id,
+    this.encryptedKey,
+    this.keyDecryptionNonce,
+  );
+
+  CollectionFileItem copyWith({
+    int id,
+    String encryptedKey,
+    String keyDecryptionNonce,
+  }) {
+    return CollectionFileItem(
+      id ?? this.id,
+      encryptedKey ?? this.encryptedKey,
+      keyDecryptionNonce ?? this.keyDecryptionNonce,
+    );
+  }
+
+  Map<String, dynamic> toMap() {
+    return {
+      'id': id,
+      'encryptedKey': encryptedKey,
+      'keyDecryptionNonce': keyDecryptionNonce,
+    };
+  }
+
+  factory CollectionFileItem.fromMap(Map<String, dynamic> map) {
+    if (map == null) return null;
+
+    return CollectionFileItem(
+      map['id'],
+      map['encryptedKey'],
+      map['keyDecryptionNonce'],
+    );
+  }
+
+  String toJson() => json.encode(toMap());
+
+  factory CollectionFileItem.fromJson(String source) =>
+      CollectionFileItem.fromMap(json.decode(source));
+
+  @override
+  String toString() =>
+      'CollectionFileItem(id: $id, encryptedKey: $encryptedKey, keyDecryptionNonce: $keyDecryptionNonce)';
+
+  @override
+  bool operator ==(Object o) {
+    if (identical(this, o)) return true;
+
+    return o is CollectionFileItem &&
+        o.id == id &&
+        o.encryptedKey == encryptedKey &&
+        o.keyDecryptionNonce == keyDecryptionNonce;
+  }
+
+  @override
+  int get hashCode =>
+      id.hashCode ^ encryptedKey.hashCode ^ keyDecryptionNonce.hashCode;
+}

+ 34 - 7
lib/services/collections_service.dart

@@ -7,8 +7,11 @@ import 'package:logging/logging.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/db/collections_db.dart';
 import 'package:photos/models/collection.dart';
+import 'package:photos/models/collection_file_item.dart';
+import 'package:photos/models/file.dart';
 import 'package:photos/models/shared_collection.dart';
 import 'package:photos/utils/crypto_util.dart';
+import 'package:photos/utils/file_util.dart';
 
 class CollectionsService {
   final _logger = Logger("CollectionsService");
@@ -36,7 +39,7 @@ class CollectionsService {
     await _db.insert(collections);
     collections = await _db.getAllCollections();
     for (final collection in collections) {
-      _cacheCollectionAttributes(collection);
+      _cacheOwnedCollectionAttributes(collection);
     }
 
     final lastSharedCollectionCreationTime =
@@ -162,7 +165,7 @@ class CollectionsService {
     final encryptedKeyData = CryptoUtil.encryptSync(key, _config.getKey());
     final encryptedPath =
         CryptoUtil.encryptSync(utf8.encode(path), _config.getKey());
-    final collection = await createCollection(Collection(
+    final collection = await createAndCacheCollection(Collection(
       null,
       null,
       Sodium.bin2base64(encryptedKeyData.encryptedData),
@@ -173,11 +176,33 @@ class CollectionsService {
       Sodium.bin2base64(encryptedPath.nonce),
       null,
     ));
-    _cacheCollectionAttributes(collection);
     return collection;
   }
 
-  Future<Collection> createCollection(Collection collection) async {
+  Future<void> addToCollection(int collectionID, List<File> files) {
+    final items = List<CollectionFileItem>();
+    for (final file in files) {
+      final key = decryptFileKey(file);
+      final encryptedKeyData =
+          CryptoUtil.encryptSync(key, getCollectionKey(collectionID));
+      items.add(CollectionFileItem(
+        file.uploadedFileID,
+        Sodium.bin2base64(encryptedKeyData.encryptedData),
+        Sodium.bin2base64(encryptedKeyData.nonce),
+      ));
+    }
+    return Dio().post(
+      Configuration.instance.getHttpEndpoint() + "/collections/add-files",
+      data: {
+        "id": collectionID,
+        "files": items,
+      },
+      options:
+          Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
+    );
+  }
+
+  Future<Collection> createAndCacheCollection(Collection collection) async {
     return Dio()
         .post(
       Configuration.instance.getHttpEndpoint() + "/collections/",
@@ -186,12 +211,14 @@ class CollectionsService {
           Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
     )
         .then((response) {
-      return Collection.fromMap(response.data["collection"]);
+      final collection = Collection.fromMap(response.data["collection"]);
+      _cacheOwnedCollectionAttributes(collection);
+      return collection;
     });
   }
 
-  void _cacheCollectionAttributes(Collection collection) {
-    if (collection.ownerID == _config.getUserID()) {
+  void _cacheOwnedCollectionAttributes(Collection collection) {
+    if (collection.encryptedPath != null) {
       var path = utf8.decode(CryptoUtil.decryptSync(
           Sodium.base642bin(collection.encryptedPath),
           _config.getKey(),

+ 50 - 3
lib/services/favorites_service.dart

@@ -1,13 +1,28 @@
+import 'package:flutter_sodium/flutter_sodium.dart';
+import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
+import 'package:photos/models/collection.dart';
 import 'package:photos/models/file.dart';
+import 'package:photos/services/collections_service.dart';
+import 'package:photos/utils/crypto_util.dart';
+import 'package:photos/utils/file_uploader.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 
 class FavoritesService {
   static final _favoritePhotoIdsKey = "favorite_photo_ids";
-  FavoritesService._privateConstructor();
-  static FavoritesService instance =
-      FavoritesService._privateConstructor();
+  static final _favoritesCollectionIDKey = "favorites_collection_id";
+
+  Configuration _config;
+  CollectionsService _collectionsService;
+  FileUploader _fileUploader;
+
+  FavoritesService._privateConstructor() {
+    _config = Configuration.instance;
+    _collectionsService = CollectionsService.instance;
+    _fileUploader = FileUploader.instance;
+  }
+  static FavoritesService instance = FavoritesService._privateConstructor();
 
   SharedPreferences _preferences;
 
@@ -44,4 +59,36 @@ class FavoritesService {
       return value.toSet();
     }
   }
+
+  Future<void> addToFavorites(File file) async {
+    final collectionID = await getOrCreateFavoriteCollectionID();
+    var fileID = file.uploadedFileID;
+    if (fileID == null) {
+      file.collectionID = collectionID;
+      fileID = (await _fileUploader.encryptAndUploadFile(file)).uploadedFileID;
+    }
+    return _collectionsService.addToCollection(collectionID, [file]);
+  }
+
+  Future<int> getOrCreateFavoriteCollectionID() async {
+    if (_preferences.containsKey(_favoritesCollectionIDKey)) {
+      return _preferences.getInt(_favoritesCollectionIDKey);
+    }
+    final key = CryptoUtil.generateKey();
+    final encryptedKeyData = CryptoUtil.encryptSync(key, _config.getKey());
+    final collection =
+        await _collectionsService.createAndCacheCollection(Collection(
+      null,
+      null,
+      Sodium.bin2base64(encryptedKeyData.encryptedData),
+      Sodium.bin2base64(encryptedKeyData.nonce),
+      "Favorites",
+      CollectionType.favorites,
+      null,
+      null,
+      null,
+    ));
+    await _preferences.setInt(_favoritesCollectionIDKey, collection.id);
+    return collection.id;
+  }
 }