Fix API to add a file to favorites
This commit is contained in:
parent
9f88eb413d
commit
26f3e62c0e
4 changed files with 230 additions and 116 deletions
|
@ -148,8 +148,8 @@ class CollectionsDB {
|
|||
row[columnKeyDecryptionNonce] = collection.keyDecryptionNonce;
|
||||
row[columnName] = collection.name;
|
||||
row[columnType] = Collection.typeToString(collection.type);
|
||||
row[columnEncryptedPath] = collection.encryptedPath;
|
||||
row[columnPathDecryptionNonce] = collection.pathDecryptionNonce;
|
||||
row[columnEncryptedPath] = collection.attributes.encryptedPath;
|
||||
row[columnPathDecryptionNonce] = collection.attributes.pathDecryptionNonce;
|
||||
row[columnCreationTime] = collection.creationTime;
|
||||
return row;
|
||||
}
|
||||
|
@ -162,8 +162,9 @@ class CollectionsDB {
|
|||
row[columnKeyDecryptionNonce],
|
||||
row[columnName],
|
||||
Collection.typeFromString(row[columnType]),
|
||||
row[columnEncryptedPath],
|
||||
row[columnPathDecryptionNonce],
|
||||
CollectionAttributes(
|
||||
encryptedPath: row[columnEncryptedPath],
|
||||
pathDecryptionNonce: row[columnPathDecryptionNonce]),
|
||||
int.parse(row[columnCreationTime]),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ class Collection {
|
|||
final String keyDecryptionNonce;
|
||||
final String name;
|
||||
final CollectionType type;
|
||||
final String encryptedPath;
|
||||
final String pathDecryptionNonce;
|
||||
final CollectionAttributes attributes;
|
||||
final int creationTime;
|
||||
|
||||
Collection(
|
||||
|
@ -18,105 +17,10 @@ class Collection {
|
|||
this.keyDecryptionNonce,
|
||||
this.name,
|
||||
this.type,
|
||||
this.encryptedPath,
|
||||
this.pathDecryptionNonce,
|
||||
this.attributes,
|
||||
this.creationTime,
|
||||
);
|
||||
|
||||
Collection copyWith({
|
||||
int id,
|
||||
int ownerID,
|
||||
String encryptedKey,
|
||||
String keyDecryptionNonce,
|
||||
String name,
|
||||
CollectionType type,
|
||||
String encryptedPath,
|
||||
String pathDecryptionNonce,
|
||||
int creationTime,
|
||||
List<String> sharees,
|
||||
}) {
|
||||
return Collection(
|
||||
id ?? this.id,
|
||||
ownerID ?? this.ownerID,
|
||||
encryptedKey ?? this.encryptedKey,
|
||||
keyDecryptionNonce ?? this.keyDecryptionNonce,
|
||||
name ?? this.name,
|
||||
type ?? this.type,
|
||||
encryptedPath ?? this.encryptedPath,
|
||||
encryptedPath ?? this.pathDecryptionNonce,
|
||||
creationTime ?? this.creationTime,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'id': id,
|
||||
'ownerID': ownerID,
|
||||
'encryptedKey': encryptedKey,
|
||||
'keyDecryptionNonce': keyDecryptionNonce,
|
||||
'name': name,
|
||||
'type': typeToString(type),
|
||||
'creationTime': creationTime,
|
||||
'encryptedPath': encryptedPath,
|
||||
'pathDecryptionNonce': pathDecryptionNonce,
|
||||
};
|
||||
}
|
||||
|
||||
factory Collection.fromMap(Map<String, dynamic> map) {
|
||||
if (map == null) return null;
|
||||
|
||||
return Collection(
|
||||
map['id'],
|
||||
map['ownerID'],
|
||||
map['encryptedKey'],
|
||||
map['keyDecryptionNonce'],
|
||||
map['name'],
|
||||
typeFromString(map['type']),
|
||||
map['encryptedPath'],
|
||||
map['pathDecryptionNonce'],
|
||||
map['creationTime'],
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory Collection.fromJson(String source) =>
|
||||
Collection.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Collection(id: $id, ownerID: $ownerID, encryptedKey: $encryptedKey, keyDecryptionNonce: $keyDecryptionNonce, name: $name, type: $type, encryptedPath: $encryptedPath, pathDecryptionNonce: $pathDecryptionNonce, creationTime: $creationTime)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
if (identical(this, o)) return true;
|
||||
|
||||
return o is Collection &&
|
||||
o.id == id &&
|
||||
o.ownerID == ownerID &&
|
||||
o.encryptedKey == encryptedKey &&
|
||||
o.keyDecryptionNonce == keyDecryptionNonce &&
|
||||
o.name == name &&
|
||||
o.type == type &&
|
||||
o.encryptedPath == encryptedPath &&
|
||||
o.pathDecryptionNonce == pathDecryptionNonce &&
|
||||
o.creationTime == creationTime;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^
|
||||
ownerID.hashCode ^
|
||||
encryptedKey.hashCode ^
|
||||
keyDecryptionNonce.hashCode ^
|
||||
name.hashCode ^
|
||||
type.hashCode ^
|
||||
encryptedPath.hashCode ^
|
||||
pathDecryptionNonce.hashCode ^
|
||||
creationTime.hashCode;
|
||||
}
|
||||
|
||||
static CollectionType typeFromString(String type) {
|
||||
switch (type) {
|
||||
case "folder":
|
||||
|
@ -137,6 +41,93 @@ class Collection {
|
|||
return "album";
|
||||
}
|
||||
}
|
||||
|
||||
Collection copyWith({
|
||||
int id,
|
||||
int ownerID,
|
||||
String encryptedKey,
|
||||
String keyDecryptionNonce,
|
||||
String name,
|
||||
CollectionType type,
|
||||
CollectionAttributes attributes,
|
||||
int creationTime,
|
||||
}) {
|
||||
return Collection(
|
||||
id ?? this.id,
|
||||
ownerID ?? this.ownerID,
|
||||
encryptedKey ?? this.encryptedKey,
|
||||
keyDecryptionNonce ?? this.keyDecryptionNonce,
|
||||
name ?? this.name,
|
||||
type ?? this.type,
|
||||
attributes ?? this.attributes,
|
||||
creationTime ?? this.creationTime,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'id': id,
|
||||
'ownerID': ownerID,
|
||||
'encryptedKey': encryptedKey,
|
||||
'keyDecryptionNonce': keyDecryptionNonce,
|
||||
'name': name,
|
||||
'type': typeToString(type),
|
||||
'attributes': attributes?.toMap(),
|
||||
'creationTime': creationTime,
|
||||
};
|
||||
}
|
||||
|
||||
factory Collection.fromMap(Map<String, dynamic> map) {
|
||||
if (map == null) return null;
|
||||
|
||||
return Collection(
|
||||
map['id'],
|
||||
map['ownerID'],
|
||||
map['encryptedKey'],
|
||||
map['keyDecryptionNonce'],
|
||||
map['name'],
|
||||
typeFromString(map['type']),
|
||||
CollectionAttributes.fromMap(map['attributes']),
|
||||
map['creationTime'],
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory Collection.fromJson(String source) =>
|
||||
Collection.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Collection(id: $id, ownerID: $ownerID, encryptedKey: $encryptedKey, keyDecryptionNonce: $keyDecryptionNonce, name: $name, type: $type, attributes: $attributes, creationTime: $creationTime)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
if (identical(this, o)) return true;
|
||||
|
||||
return o is Collection &&
|
||||
o.id == id &&
|
||||
o.ownerID == ownerID &&
|
||||
o.encryptedKey == encryptedKey &&
|
||||
o.keyDecryptionNonce == keyDecryptionNonce &&
|
||||
o.name == name &&
|
||||
o.type == type &&
|
||||
o.attributes == attributes &&
|
||||
o.creationTime == creationTime;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return id.hashCode ^
|
||||
ownerID.hashCode ^
|
||||
encryptedKey.hashCode ^
|
||||
keyDecryptionNonce.hashCode ^
|
||||
name.hashCode ^
|
||||
type.hashCode ^
|
||||
attributes.hashCode ^
|
||||
creationTime.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
enum CollectionType {
|
||||
|
@ -144,3 +135,64 @@ enum CollectionType {
|
|||
favorites,
|
||||
album,
|
||||
}
|
||||
|
||||
class CollectionAttributes {
|
||||
final String encryptedPath;
|
||||
final String pathDecryptionNonce;
|
||||
|
||||
CollectionAttributes({
|
||||
this.encryptedPath,
|
||||
this.pathDecryptionNonce,
|
||||
});
|
||||
|
||||
CollectionAttributes copyWith({
|
||||
String encryptedPath,
|
||||
String pathDecryptionNonce,
|
||||
}) {
|
||||
return CollectionAttributes(
|
||||
encryptedPath: encryptedPath ?? this.encryptedPath,
|
||||
pathDecryptionNonce: pathDecryptionNonce ?? this.pathDecryptionNonce,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
final map = Map<String, dynamic>();
|
||||
if (encryptedPath != null) {
|
||||
map['encryptedPath'] = encryptedPath;
|
||||
}
|
||||
if (pathDecryptionNonce != null) {
|
||||
map['pathDecryptionNonce'] = pathDecryptionNonce;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
factory CollectionAttributes.fromMap(Map<String, dynamic> map) {
|
||||
if (map == null) return null;
|
||||
|
||||
return CollectionAttributes(
|
||||
encryptedPath: map['encryptedPath'],
|
||||
pathDecryptionNonce: map['pathDecryptionNonce'],
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory CollectionAttributes.fromJson(String source) =>
|
||||
CollectionAttributes.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'CollectionAttributes(encryptedPath: $encryptedPath, pathDecryptionNonce: $pathDecryptionNonce)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
if (identical(this, o)) return true;
|
||||
|
||||
return o is CollectionAttributes &&
|
||||
o.encryptedPath == encryptedPath &&
|
||||
o.pathDecryptionNonce == pathDecryptionNonce;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => encryptedPath.hashCode ^ pathDecryptionNonce.hashCode;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ import 'dart:convert';
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_sodium/flutter_sodium.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'package:photos/core/configuration.dart';
|
||||
import 'package:photos/db/collections_db.dart';
|
||||
import 'package:photos/models/collection.dart';
|
||||
|
@ -172,31 +174,33 @@ class CollectionsService {
|
|||
Sodium.bin2base64(encryptedKeyData.nonce),
|
||||
path,
|
||||
CollectionType.folder,
|
||||
Sodium.bin2base64(encryptedPath.encryptedData),
|
||||
Sodium.bin2base64(encryptedPath.nonce),
|
||||
CollectionAttributes(
|
||||
encryptedPath: Sodium.bin2base64(encryptedPath.encryptedData),
|
||||
pathDecryptionNonce: Sodium.bin2base64(encryptedPath.nonce)),
|
||||
null,
|
||||
));
|
||||
return collection;
|
||||
}
|
||||
|
||||
Future<void> addToCollection(int collectionID, List<File> files) {
|
||||
final items = List<CollectionFileItem>();
|
||||
final params = Map<String, dynamic>();
|
||||
params["collectionID"] = collectionID;
|
||||
for (final file in files) {
|
||||
final key = decryptFileKey(file);
|
||||
final encryptedKeyData =
|
||||
CryptoUtil.encryptSync(key, getCollectionKey(collectionID));
|
||||
items.add(CollectionFileItem(
|
||||
if (params["files"] == null) {
|
||||
params["files"] = [];
|
||||
}
|
||||
params["files"].add(CollectionFileItem(
|
||||
file.uploadedFileID,
|
||||
Sodium.bin2base64(encryptedKeyData.encryptedData),
|
||||
Sodium.bin2base64(encryptedKeyData.nonce),
|
||||
));
|
||||
).toMap());
|
||||
}
|
||||
return Dio().post(
|
||||
Configuration.instance.getHttpEndpoint() + "/collections/add-files",
|
||||
data: {
|
||||
"id": collectionID,
|
||||
"files": items,
|
||||
},
|
||||
data: params,
|
||||
options:
|
||||
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
);
|
||||
|
@ -218,14 +222,72 @@ class CollectionsService {
|
|||
}
|
||||
|
||||
void _cacheOwnedCollectionAttributes(Collection collection) {
|
||||
if (collection.encryptedPath != null) {
|
||||
if (collection.attributes.encryptedPath != null) {
|
||||
var path = utf8.decode(CryptoUtil.decryptSync(
|
||||
Sodium.base642bin(collection.encryptedPath),
|
||||
Sodium.base642bin(collection.attributes.encryptedPath),
|
||||
_config.getKey(),
|
||||
Sodium.base642bin(collection.pathDecryptionNonce)));
|
||||
Sodium.base642bin(collection.attributes.pathDecryptionNonce)));
|
||||
_localCollections[path] = collection;
|
||||
}
|
||||
_collectionIDToOwnedCollections[collection.id] = collection;
|
||||
getCollectionKey(collection.id);
|
||||
}
|
||||
}
|
||||
|
||||
class AddFilesRequest {
|
||||
final int collectionID;
|
||||
final List<CollectionFileItem> files;
|
||||
|
||||
AddFilesRequest(
|
||||
this.collectionID,
|
||||
this.files,
|
||||
);
|
||||
|
||||
AddFilesRequest copyWith({
|
||||
int collectionID,
|
||||
List<CollectionFileItem> files,
|
||||
}) {
|
||||
return AddFilesRequest(
|
||||
collectionID ?? this.collectionID,
|
||||
files ?? this.files,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'collectionID': collectionID,
|
||||
'files': files?.map((x) => x?.toMap())?.toList(),
|
||||
};
|
||||
}
|
||||
|
||||
factory AddFilesRequest.fromMap(Map<String, dynamic> map) {
|
||||
if (map == null) return null;
|
||||
|
||||
return AddFilesRequest(
|
||||
map['collectionID'],
|
||||
List<CollectionFileItem>.from(
|
||||
map['files']?.map((x) => CollectionFileItem.fromMap(x))),
|
||||
);
|
||||
}
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory AddFilesRequest.fromJson(String source) =>
|
||||
AddFilesRequest.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'AddFilesRequest(collectionID: $collectionID, files: $files)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
if (identical(this, o)) return true;
|
||||
|
||||
return o is AddFilesRequest &&
|
||||
o.collectionID == collectionID &&
|
||||
listEquals(o.files, files);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => collectionID.hashCode ^ files.hashCode;
|
||||
}
|
||||
|
|
|
@ -84,8 +84,7 @@ class FavoritesService {
|
|||
Sodium.bin2base64(encryptedKeyData.nonce),
|
||||
"Favorites",
|
||||
CollectionType.favorites,
|
||||
null,
|
||||
null,
|
||||
CollectionAttributes(),
|
||||
null,
|
||||
));
|
||||
await _preferences.setInt(_favoritesCollectionIDKey, collection.id);
|
||||
|
|
Loading…
Add table
Reference in a new issue