[mob] Add new collection model for remote
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
import "package:photos/models/api/collection/public_url.dart";
|
||||
import "package:photos/models/api/collection/user.dart";
|
||||
import 'package:photos/models/collection/collection.dart';
|
||||
import "package:photos/models/collection/collection_old.dart";
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:sqflite_migration/sqflite_migration.dart';
|
||||
|
||||
@@ -192,6 +193,10 @@ class CollectionsDB {
|
||||
}
|
||||
|
||||
Future<void> insert(List<Collection> collections) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<void> insertOld(List<CollectionV2> collections) async {
|
||||
final db = await instance.database;
|
||||
var batch = db.batch();
|
||||
int batchCounter = 0;
|
||||
@@ -211,10 +216,10 @@ class CollectionsDB {
|
||||
await batch.commit(noResult: true);
|
||||
}
|
||||
|
||||
Future<List<Collection>> getAllCollections() async {
|
||||
Future<List<CollectionV2>> getAllCollections() async {
|
||||
final db = await instance.database;
|
||||
final rows = await db.query(table);
|
||||
final collections = <Collection>[];
|
||||
final collections = <CollectionV2>[];
|
||||
for (final row in rows) {
|
||||
collections.add(_convertToCollection(row));
|
||||
}
|
||||
@@ -248,7 +253,7 @@ class CollectionsDB {
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _getRowForCollection(Collection collection) {
|
||||
Map<String, dynamic> _getRowForCollection(CollectionV2 collection) {
|
||||
final row = <String, dynamic>{};
|
||||
row[columnID] = collection.id;
|
||||
row[columnOwner] = collection.owner.toJson();
|
||||
@@ -281,8 +286,8 @@ class CollectionsDB {
|
||||
return row;
|
||||
}
|
||||
|
||||
Collection _convertToCollection(Map<String, dynamic> row) {
|
||||
final Collection result = Collection(
|
||||
CollectionV2 _convertToCollection(Map<String, dynamic> row) {
|
||||
final CollectionV2 result = CollectionV2(
|
||||
row[columnID],
|
||||
User.fromJson(row[columnOwner]),
|
||||
row[columnEncryptedKey],
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:core';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import "package:photos/models/api/collection/public_url.dart";
|
||||
import "package:photos/models/api/collection/user.dart";
|
||||
import "package:photos/models/collection/collection_old.dart";
|
||||
import "package:photos/models/metadata/collection_magic.dart";
|
||||
import "package:photos/models/metadata/common_keys.dart";
|
||||
|
||||
@@ -10,33 +11,17 @@ class Collection {
|
||||
final int id;
|
||||
final User owner;
|
||||
final String encryptedKey;
|
||||
final String? keyDecryptionNonce;
|
||||
@Deprecated("Use collectionName instead")
|
||||
final String keyDecryptionNonce;
|
||||
String? name;
|
||||
|
||||
// encryptedName & nameDecryptionNonce will be null for collections
|
||||
// created before we started encrypting collection name
|
||||
final String? encryptedName;
|
||||
final String? nameDecryptionNonce;
|
||||
final CollectionType type;
|
||||
final CollectionAttributes attributes;
|
||||
final List<User> sharees;
|
||||
final List<PublicURL> publicURLs;
|
||||
final int updationTime;
|
||||
final bool isDeleted;
|
||||
|
||||
// In early days before public launch, we used to store collection name
|
||||
// un-encrypted. decryptName will be value either decrypted value for
|
||||
// encryptedName or name itself.
|
||||
String? decryptedName;
|
||||
|
||||
// decryptedPath will be null for collections now owned by user, deleted
|
||||
// collections, && collections which don't have a path. The path is used
|
||||
// to map local on-device album on mobile to remote collection on ente.
|
||||
String? decryptedPath;
|
||||
String? mMdEncodedJson;
|
||||
String? mMdPubEncodedJson;
|
||||
String? sharedMmdJson;
|
||||
final String? localPath;
|
||||
String mMdEncodedJson;
|
||||
String mMdPubEncodedJson;
|
||||
String sharedMmdJson;
|
||||
int mMdVersion = 0;
|
||||
int mMbPubVersion = 0;
|
||||
int sharedMmdVersion = 0;
|
||||
@@ -45,14 +30,13 @@ class Collection {
|
||||
ShareeMagicMetadata? _sharedMmd;
|
||||
|
||||
CollectionMagicMetadata get magicMetadata =>
|
||||
_mmd ?? CollectionMagicMetadata.fromEncodedJson(mMdEncodedJson ?? '{}');
|
||||
_mmd ?? CollectionMagicMetadata.fromEncodedJson(mMdEncodedJson);
|
||||
|
||||
CollectionPubMagicMetadata get pubMagicMetadata =>
|
||||
_pubMmd ??
|
||||
CollectionPubMagicMetadata.fromEncodedJson(mMdPubEncodedJson ?? '{}');
|
||||
_pubMmd ?? CollectionPubMagicMetadata.fromEncodedJson(mMdPubEncodedJson);
|
||||
|
||||
ShareeMagicMetadata get sharedMagicMetadata =>
|
||||
_sharedMmd ?? ShareeMagicMetadata.fromEncodedJson(sharedMmdJson ?? '{}');
|
||||
_sharedMmd ?? ShareeMagicMetadata.fromEncodedJson(sharedMmdJson);
|
||||
|
||||
set magicMetadata(CollectionMagicMetadata? val) => _mmd = val;
|
||||
|
||||
@@ -60,30 +44,55 @@ class Collection {
|
||||
|
||||
set sharedMagicMetadata(ShareeMagicMetadata? val) => _sharedMmd = val;
|
||||
|
||||
String get displayName => decryptedName ?? name ?? "Unnamed Album";
|
||||
String get displayName =>
|
||||
name ?? (isDeleted == true ? 'Delete album' : "Unnamed Album");
|
||||
|
||||
// set the value for both name and decryptedName till we finish migration
|
||||
void setName(String newName) {
|
||||
name = newName;
|
||||
decryptedName = newName;
|
||||
}
|
||||
|
||||
Collection(
|
||||
this.id,
|
||||
this.owner,
|
||||
this.encryptedKey,
|
||||
this.keyDecryptionNonce,
|
||||
this.name,
|
||||
this.encryptedName,
|
||||
this.nameDecryptionNonce,
|
||||
this.type,
|
||||
this.attributes,
|
||||
this.sharees,
|
||||
this.publicURLs,
|
||||
this.updationTime, {
|
||||
Collection({
|
||||
required this.id,
|
||||
required this.owner,
|
||||
required this.encryptedKey,
|
||||
required this.keyDecryptionNonce,
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.sharees,
|
||||
required this.publicURLs,
|
||||
required this.updationTime,
|
||||
required this.localPath,
|
||||
this.isDeleted = false,
|
||||
this.mMdEncodedJson = '{}',
|
||||
this.mMdPubEncodedJson = '{}',
|
||||
this.sharedMmdJson = '{}',
|
||||
this.mMdVersion = 0,
|
||||
this.mMbPubVersion = 0,
|
||||
this.sharedMmdVersion = 0,
|
||||
});
|
||||
|
||||
factory Collection.fromOldCollection(CollectionV2 collection) {
|
||||
return Collection(
|
||||
id: collection.id,
|
||||
owner: collection.owner,
|
||||
encryptedKey: collection.encryptedKey,
|
||||
keyDecryptionNonce: collection.keyDecryptionNonce!,
|
||||
name: collection.displayName,
|
||||
type: collection.type,
|
||||
sharees: collection.sharees,
|
||||
publicURLs: collection.publicURLs,
|
||||
updationTime: collection.updationTime,
|
||||
localPath: collection.decryptedPath,
|
||||
isDeleted: collection.isDeleted,
|
||||
mMbPubVersion: collection.mMbPubVersion,
|
||||
mMdPubEncodedJson: collection.mMdPubEncodedJson ?? '{}',
|
||||
mMdVersion: collection.mMdVersion,
|
||||
mMdEncodedJson: collection.mMdEncodedJson ?? '{}',
|
||||
sharedMmdJson: collection.sharedMmdJson ?? '{}',
|
||||
sharedMmdVersion: collection.sharedMmdVersion,
|
||||
);
|
||||
}
|
||||
|
||||
bool isArchived() {
|
||||
return mMdVersion > 0 && magicMetadata.visibility == archiveVisibility;
|
||||
}
|
||||
@@ -172,7 +181,10 @@ class Collection {
|
||||
// device album based on path. The path is nothing but the name of the device
|
||||
// album.
|
||||
bool canLinkToDevicePath(int userID) {
|
||||
return isOwner(userID) && !isDeleted && attributes.encryptedPath != null;
|
||||
return isOwner(userID) &&
|
||||
!isDeleted &&
|
||||
localPath != null &&
|
||||
localPath != '';
|
||||
}
|
||||
|
||||
void updateSharees(List<User> newSharees) {
|
||||
@@ -186,47 +198,42 @@ class Collection {
|
||||
String? encryptedKey,
|
||||
String? keyDecryptionNonce,
|
||||
String? name,
|
||||
String? encryptedName,
|
||||
String? nameDecryptionNonce,
|
||||
CollectionType? type,
|
||||
CollectionAttributes? attributes,
|
||||
List<User>? sharees,
|
||||
List<PublicURL>? publicURLs,
|
||||
int? updationTime,
|
||||
bool? isDeleted,
|
||||
String? localPath,
|
||||
String? mMdEncodedJson,
|
||||
int? mMdVersion,
|
||||
String? decryptedName,
|
||||
String? decryptedPath,
|
||||
String? mMdPubEncodedJson,
|
||||
int? mMbPubVersion,
|
||||
String? sharedMmdJson,
|
||||
int? sharedMmdVersion,
|
||||
}) {
|
||||
final Collection result = Collection(
|
||||
id ?? this.id,
|
||||
owner ?? this.owner,
|
||||
encryptedKey ?? this.encryptedKey,
|
||||
keyDecryptionNonce ?? this.keyDecryptionNonce,
|
||||
name ?? this.name,
|
||||
encryptedName ?? this.encryptedName,
|
||||
nameDecryptionNonce ?? this.nameDecryptionNonce,
|
||||
type ?? this.type,
|
||||
attributes ?? this.attributes,
|
||||
sharees ?? this.sharees,
|
||||
publicURLs ?? this.publicURLs,
|
||||
updationTime ?? this.updationTime,
|
||||
id: id ?? this.id,
|
||||
owner: owner ?? this.owner,
|
||||
encryptedKey: encryptedKey ?? this.encryptedKey,
|
||||
keyDecryptionNonce: keyDecryptionNonce ?? this.keyDecryptionNonce,
|
||||
name: name ?? this.name,
|
||||
type: type ?? this.type,
|
||||
sharees: sharees ?? this.sharees,
|
||||
publicURLs: publicURLs ?? this.publicURLs,
|
||||
updationTime: updationTime ?? this.updationTime,
|
||||
localPath: localPath ?? this.localPath,
|
||||
isDeleted: isDeleted ?? this.isDeleted,
|
||||
mMdEncodedJson: mMdEncodedJson ?? this.mMdEncodedJson,
|
||||
mMdVersion: mMdVersion ?? this.mMdVersion,
|
||||
mMdPubEncodedJson: mMdPubEncodedJson ?? this.mMdPubEncodedJson,
|
||||
mMbPubVersion: mMbPubVersion ?? this.mMbPubVersion,
|
||||
sharedMmdJson: sharedMmdJson ?? this.sharedMmdJson,
|
||||
sharedMmdVersion: sharedMmdVersion ?? this.sharedMmdVersion,
|
||||
);
|
||||
result.mMdVersion = mMdVersion ?? this.mMdVersion;
|
||||
result.mMdEncodedJson = mMdEncodedJson ?? this.mMdEncodedJson;
|
||||
result.decryptedName = decryptedName ?? this.decryptedName;
|
||||
result.decryptedPath = decryptedPath ?? this.decryptedPath;
|
||||
result.mMbPubVersion = mMbPubVersion;
|
||||
result.mMdPubEncodedJson = mMdPubEncodedJson;
|
||||
result.sharedMmdVersion = sharedMmdVersion;
|
||||
result.sharedMmdJson = sharedMmdJson;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Collection fromMap(Map<String, dynamic> map) {
|
||||
|
||||
final sharees = (map['sharees'] == null || map['sharees'].length == 0)
|
||||
? <User>[]
|
||||
: List<User>.from(map['sharees'].map((x) => User.fromMap(x)));
|
||||
@@ -237,19 +244,23 @@ class Collection {
|
||||
map['publicURLs'].map((x) => PublicURL.fromMap(x)),
|
||||
);
|
||||
return Collection(
|
||||
map['id'],
|
||||
User.fromMap(map['owner']),
|
||||
map['encryptedKey'],
|
||||
map['keyDecryptionNonce'],
|
||||
map['name'],
|
||||
map['encryptedName'],
|
||||
map['nameDecryptionNonce'],
|
||||
typeFromString(map['type']),
|
||||
CollectionAttributes.fromMap(map['attributes']),
|
||||
sharees,
|
||||
publicURLs,
|
||||
map['updationTime'],
|
||||
id: map['id'],
|
||||
owner: User.fromMap(map['owner']),
|
||||
encryptedKey: map['encryptedKey'],
|
||||
keyDecryptionNonce: map['keyDecryptionNonce'],
|
||||
name: map['name'],
|
||||
type: typeFromString(map['type']),
|
||||
sharees: sharees,
|
||||
publicURLs: publicURLs,
|
||||
updationTime: map['updationTime'],
|
||||
localPath: map['localPath'],
|
||||
isDeleted: map['isDeleted'] ?? false,
|
||||
mMdEncodedJson: map['mMdEncodedJson'] ?? '{}',
|
||||
mMdPubEncodedJson: map['mMdPubEncodedJson'] ?? '{}',
|
||||
sharedMmdJson: map['sharedMmdJson'] ?? '{}',
|
||||
mMdVersion: map['mMdVersion'] ?? 0,
|
||||
mMbPubVersion: map['mMbPubVersion'] ?? 0,
|
||||
sharedMmdVersion: map['sharedMmdVersion'] ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
252
mobile/lib/models/collection/collection_old.dart
Normal file
252
mobile/lib/models/collection/collection_old.dart
Normal file
@@ -0,0 +1,252 @@
|
||||
import "package:photos/models/api/collection/public_url.dart";
|
||||
import "package:photos/models/api/collection/user.dart";
|
||||
import "package:photos/models/collection/collection.dart";
|
||||
import "package:photos/models/metadata/collection_magic.dart";
|
||||
import "package:photos/models/metadata/common_keys.dart";
|
||||
|
||||
class CollectionV2 {
|
||||
final int id;
|
||||
final User owner;
|
||||
final String encryptedKey;
|
||||
final String? keyDecryptionNonce;
|
||||
@Deprecated("Use collectionName instead")
|
||||
String? name;
|
||||
|
||||
// encryptedName & nameDecryptionNonce will be null for collections
|
||||
// created before we started encrypting collection name
|
||||
final String? encryptedName;
|
||||
final String? nameDecryptionNonce;
|
||||
final CollectionType type;
|
||||
final CollectionAttributes attributes;
|
||||
final List<User> sharees;
|
||||
final List<PublicURL> publicURLs;
|
||||
final int updationTime;
|
||||
final bool isDeleted;
|
||||
|
||||
// In early days before public launch, we used to store collection name
|
||||
// un-encrypted. decryptName will be value either decrypted value for
|
||||
// encryptedName or name itself.
|
||||
String? decryptedName;
|
||||
|
||||
// decryptedPath will be null for collections now owned by user, deleted
|
||||
// collections, && collections which don't have a path. The path is used
|
||||
// to map local on-device album on mobile to remote collection on ente.
|
||||
String? decryptedPath;
|
||||
String? mMdEncodedJson;
|
||||
String? mMdPubEncodedJson;
|
||||
String? sharedMmdJson;
|
||||
int mMdVersion = 0;
|
||||
int mMbPubVersion = 0;
|
||||
int sharedMmdVersion = 0;
|
||||
CollectionMagicMetadata? _mmd;
|
||||
CollectionPubMagicMetadata? _pubMmd;
|
||||
ShareeMagicMetadata? _sharedMmd;
|
||||
|
||||
CollectionMagicMetadata get magicMetadata =>
|
||||
_mmd ?? CollectionMagicMetadata.fromEncodedJson(mMdEncodedJson ?? '{}');
|
||||
|
||||
CollectionPubMagicMetadata get pubMagicMetadata =>
|
||||
_pubMmd ??
|
||||
CollectionPubMagicMetadata.fromEncodedJson(mMdPubEncodedJson ?? '{}');
|
||||
|
||||
ShareeMagicMetadata get sharedMagicMetadata =>
|
||||
_sharedMmd ?? ShareeMagicMetadata.fromEncodedJson(sharedMmdJson ?? '{}');
|
||||
|
||||
set magicMetadata(CollectionMagicMetadata? val) => _mmd = val;
|
||||
|
||||
set pubMagicMetadata(CollectionPubMagicMetadata? val) => _pubMmd = val;
|
||||
|
||||
set sharedMagicMetadata(ShareeMagicMetadata? val) => _sharedMmd = val;
|
||||
|
||||
String get displayName => decryptedName ?? name ?? "Unnamed Album";
|
||||
|
||||
// set the value for both name and decryptedName till we finish migration
|
||||
void setName(String newName) {
|
||||
name = newName;
|
||||
decryptedName = newName;
|
||||
}
|
||||
|
||||
CollectionV2(
|
||||
this.id,
|
||||
this.owner,
|
||||
this.encryptedKey,
|
||||
this.keyDecryptionNonce,
|
||||
this.name,
|
||||
this.encryptedName,
|
||||
this.nameDecryptionNonce,
|
||||
this.type,
|
||||
this.attributes,
|
||||
this.sharees,
|
||||
this.publicURLs,
|
||||
this.updationTime, {
|
||||
this.isDeleted = false,
|
||||
});
|
||||
|
||||
bool isArchived() {
|
||||
return mMdVersion > 0 && magicMetadata.visibility == archiveVisibility;
|
||||
}
|
||||
|
||||
bool hasShareeArchived() {
|
||||
return sharedMmdVersion > 0 &&
|
||||
sharedMagicMetadata.visibility == archiveVisibility;
|
||||
}
|
||||
|
||||
// hasLink returns true if there's any link attached to the collection
|
||||
// including expired links
|
||||
bool get hasLink => publicURLs.isNotEmpty;
|
||||
|
||||
bool get hasCover => (pubMagicMetadata.coverID ?? 0) > 0;
|
||||
|
||||
// hasSharees returns true if the collection is shared with other ente users
|
||||
bool get hasSharees => sharees.isNotEmpty;
|
||||
|
||||
bool get isPinned => (magicMetadata.order ?? 0) != 0;
|
||||
|
||||
bool isHidden() {
|
||||
if (isDefaultHidden()) {
|
||||
return true;
|
||||
}
|
||||
return mMdVersion > 0 && (magicMetadata.visibility == hiddenVisibility);
|
||||
}
|
||||
|
||||
bool isDefaultHidden() {
|
||||
return (magicMetadata.subType ?? 0) == subTypeDefaultHidden;
|
||||
}
|
||||
|
||||
bool isQuickLinkCollection() {
|
||||
return (magicMetadata.subType ?? 0) == subTypeSharedFilesCollection &&
|
||||
!hasSharees;
|
||||
}
|
||||
|
||||
List<User> getSharees() {
|
||||
return sharees;
|
||||
}
|
||||
|
||||
bool isOwner(int userID) {
|
||||
return (owner.id ?? -100) == userID;
|
||||
}
|
||||
|
||||
bool isDownloadEnabledForPublicLink() {
|
||||
if (publicURLs.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return publicURLs.first.enableDownload;
|
||||
}
|
||||
|
||||
bool isCollectEnabledForPublicLink() {
|
||||
if (publicURLs.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return publicURLs.first.enableCollect;
|
||||
}
|
||||
|
||||
bool get isJoinEnabled {
|
||||
if (publicURLs.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return publicURLs.first.enableJoin;
|
||||
}
|
||||
|
||||
CollectionParticipantRole getRole(int userID) {
|
||||
if (isOwner(userID)) {
|
||||
return CollectionParticipantRole.owner;
|
||||
}
|
||||
if (sharees.isEmpty) {
|
||||
return CollectionParticipantRole.unknown;
|
||||
}
|
||||
for (final User u in sharees) {
|
||||
if (u.id == userID) {
|
||||
if (u.isViewer) {
|
||||
return CollectionParticipantRole.viewer;
|
||||
} else if (u.isCollaborator) {
|
||||
return CollectionParticipantRole.collaborator;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CollectionParticipantRole.unknown;
|
||||
}
|
||||
|
||||
// canLinkToDevicePath returns true if the collection can be linked to local
|
||||
// device album based on path. The path is nothing but the name of the device
|
||||
// album.
|
||||
bool canLinkToDevicePath(int userID) {
|
||||
return isOwner(userID) && !isDeleted && attributes.encryptedPath != null;
|
||||
}
|
||||
|
||||
void updateSharees(List<User> newSharees) {
|
||||
sharees.clear();
|
||||
sharees.addAll(newSharees);
|
||||
}
|
||||
|
||||
CollectionV2 copyWith({
|
||||
int? id,
|
||||
User? owner,
|
||||
String? encryptedKey,
|
||||
String? keyDecryptionNonce,
|
||||
String? name,
|
||||
String? encryptedName,
|
||||
String? nameDecryptionNonce,
|
||||
CollectionType? type,
|
||||
CollectionAttributes? attributes,
|
||||
List<User>? sharees,
|
||||
List<PublicURL>? publicURLs,
|
||||
int? updationTime,
|
||||
bool? isDeleted,
|
||||
String? mMdEncodedJson,
|
||||
int? mMdVersion,
|
||||
String? decryptedName,
|
||||
String? decryptedPath,
|
||||
}) {
|
||||
final CollectionV2 result = CollectionV2(
|
||||
id ?? this.id,
|
||||
owner ?? this.owner,
|
||||
encryptedKey ?? this.encryptedKey,
|
||||
keyDecryptionNonce ?? this.keyDecryptionNonce,
|
||||
name ?? this.name,
|
||||
encryptedName ?? this.encryptedName,
|
||||
nameDecryptionNonce ?? this.nameDecryptionNonce,
|
||||
type ?? this.type,
|
||||
attributes ?? this.attributes,
|
||||
sharees ?? this.sharees,
|
||||
publicURLs ?? this.publicURLs,
|
||||
updationTime ?? this.updationTime,
|
||||
isDeleted: isDeleted ?? this.isDeleted,
|
||||
);
|
||||
result.mMdVersion = mMdVersion ?? this.mMdVersion;
|
||||
result.mMdEncodedJson = mMdEncodedJson ?? this.mMdEncodedJson;
|
||||
result.decryptedName = decryptedName ?? this.decryptedName;
|
||||
result.decryptedPath = decryptedPath ?? this.decryptedPath;
|
||||
result.mMbPubVersion = mMbPubVersion;
|
||||
result.mMdPubEncodedJson = mMdPubEncodedJson;
|
||||
result.sharedMmdVersion = sharedMmdVersion;
|
||||
result.sharedMmdJson = sharedMmdJson;
|
||||
return result;
|
||||
}
|
||||
|
||||
static CollectionV2 fromMap(Map<String, dynamic> map) {
|
||||
final sharees = (map['sharees'] == null || map['sharees'].length == 0)
|
||||
? <User>[]
|
||||
: List<User>.from(map['sharees'].map((x) => User.fromMap(x)));
|
||||
final publicURLs =
|
||||
(map['publicURLs'] == null || map['publicURLs'].length == 0)
|
||||
? <PublicURL>[]
|
||||
: List<PublicURL>.from(
|
||||
map['publicURLs'].map((x) => PublicURL.fromMap(x)),
|
||||
);
|
||||
return CollectionV2(
|
||||
map['id'],
|
||||
User.fromMap(map['owner']),
|
||||
map['encryptedKey'],
|
||||
map['keyDecryptionNonce'],
|
||||
map['name'],
|
||||
map['encryptedName'],
|
||||
map['nameDecryptionNonce'],
|
||||
typeFromString(map['type']),
|
||||
CollectionAttributes.fromMap(map['attributes']),
|
||||
sharees,
|
||||
publicURLs,
|
||||
map['updationTime'],
|
||||
isDeleted: map['isDeleted'] ?? false,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import "package:photos/models/api/collection/user.dart";
|
||||
import 'package:photos/models/collection/collection.dart';
|
||||
import 'package:photos/models/collection/collection_file_item.dart';
|
||||
import 'package:photos/models/collection/collection_items.dart';
|
||||
import "package:photos/models/collection/collection_old.dart";
|
||||
import 'package:photos/models/file/file.dart';
|
||||
import "package:photos/models/files_split.dart";
|
||||
import "package:photos/models/metadata/collection_magic.dart";
|
||||
@@ -679,26 +680,34 @@ class CollectionsService {
|
||||
fetchCollectionByID(collectionID);
|
||||
throw AssertionError('collectionID $collectionID is not cached');
|
||||
}
|
||||
_cachedKeys[collectionID] =
|
||||
_getAndCacheDecryptedKey(collection, source: "getCollectionKey");
|
||||
_cachedKeys[collectionID] = _getAndCacheDecryptedKey(
|
||||
collection.id,
|
||||
collection.isOwner(_config.getUserID()!),
|
||||
encKey: collection.encryptedKey,
|
||||
encKeyNonce: collection.keyDecryptionNonce,
|
||||
source: "getCollectionKey",
|
||||
);
|
||||
}
|
||||
return _cachedKeys[collectionID]!;
|
||||
}
|
||||
|
||||
Uint8List _getAndCacheDecryptedKey(
|
||||
Collection collection, {
|
||||
int id,
|
||||
bool isOwner, {
|
||||
required String encKey,
|
||||
required String encKeyNonce,
|
||||
String source = "",
|
||||
}) {
|
||||
if (_cachedKeys.containsKey(collection.id)) {
|
||||
return _cachedKeys[collection.id]!;
|
||||
if (_cachedKeys.containsKey(id)) {
|
||||
return _cachedKeys[id]!;
|
||||
}
|
||||
debugPrint(
|
||||
"Compute collection decryption key for ${collection.id} source"
|
||||
"Compute collection decryption key for $id source"
|
||||
" $source",
|
||||
);
|
||||
final encryptedKey = CryptoUtil.base642bin(collection.encryptedKey);
|
||||
final encryptedKey = CryptoUtil.base642bin(encKey);
|
||||
Uint8List? collectionKey;
|
||||
if (collection.owner.id == _config.getUserID()) {
|
||||
if (isOwner) {
|
||||
// If the collection is owned by the user, decrypt with the master key
|
||||
if (_config.getKey() == null) {
|
||||
// Possible during AppStore account migration, where SecureStorage
|
||||
@@ -708,7 +717,7 @@ class CollectionsService {
|
||||
collectionKey = CryptoUtil.decryptSync(
|
||||
encryptedKey,
|
||||
_config.getKey()!,
|
||||
CryptoUtil.base642bin(collection.keyDecryptionNonce!),
|
||||
CryptoUtil.base642bin(encKeyNonce),
|
||||
);
|
||||
} else {
|
||||
// If owned by a different user, decrypt with the public key
|
||||
@@ -718,7 +727,7 @@ class CollectionsService {
|
||||
_config.getSecretKey()!,
|
||||
);
|
||||
}
|
||||
_cachedKeys[collection.id] = collectionKey;
|
||||
_cachedKeys[id] = collectionKey;
|
||||
return collectionKey;
|
||||
}
|
||||
|
||||
@@ -730,7 +739,7 @@ class CollectionsService {
|
||||
await updateMagicMetadata(collection, {"subType": 0});
|
||||
}
|
||||
final encryptedName = CryptoUtil.encryptSync(
|
||||
utf8.encode(newName) as Uint8List,
|
||||
utf8.encode(newName),
|
||||
getCollectionKey(collection.id),
|
||||
);
|
||||
await _enteDio.post(
|
||||
@@ -1060,7 +1069,7 @@ class CollectionsService {
|
||||
);
|
||||
|
||||
final collectionData = response.data["collection"];
|
||||
final Collection collection = Collection.fromMap(collectionData);
|
||||
final CollectionV2 collection = CollectionV2.fromMap(collectionData);
|
||||
final Uint8List collectionKey =
|
||||
Uint8List.fromList(Base58Decode(albumKey));
|
||||
|
||||
@@ -1087,7 +1096,22 @@ class CollectionsService {
|
||||
}
|
||||
|
||||
collection.setName(_getDecryptedCollectionName(collection));
|
||||
return collection;
|
||||
final Collection result = Collection(
|
||||
id: collection.id,
|
||||
owner: collection.owner,
|
||||
name: collection.displayName,
|
||||
type: collection.type,
|
||||
updationTime: collection.updationTime,
|
||||
encryptedKey: collection.encryptedKey,
|
||||
keyDecryptionNonce: collection.keyDecryptionNonce!,
|
||||
sharees: collection.sharees,
|
||||
publicURLs: collection.publicURLs,
|
||||
localPath: null,
|
||||
isDeleted: collection.isDeleted,
|
||||
mMbPubVersion: collection.mMbPubVersion,
|
||||
mMdPubEncodedJson: collection.mMdPubEncodedJson ?? '{}',
|
||||
);
|
||||
return result;
|
||||
} catch (e, s) {
|
||||
_logger.warning(e, s);
|
||||
_logger.severe("Failed to fetch public collection");
|
||||
@@ -1204,10 +1228,15 @@ class CollectionsService {
|
||||
Future<Collection> _fromRemoteCollection(
|
||||
Map<String, dynamic> collectionData,
|
||||
) async {
|
||||
final Collection collection = Collection.fromMap(collectionData);
|
||||
final CollectionV2 collection = CollectionV2.fromMap(collectionData);
|
||||
if (!collection.isDeleted) {
|
||||
final collectionKey =
|
||||
_getAndCacheDecryptedKey(collection, source: "fetchDecryptMeta");
|
||||
final collectionKey = _getAndCacheDecryptedKey(
|
||||
collection.id,
|
||||
collection.isOwner(_config.getUserID()!),
|
||||
encKey: collection.encryptedKey,
|
||||
encKeyNonce: collection.keyDecryptionNonce!,
|
||||
source: "fetchDecryptMeta",
|
||||
);
|
||||
if (collectionData['magicMetadata'] != null) {
|
||||
final utfEncodedMmd = await CryptoUtil.decryptChaCha(
|
||||
CryptoUtil.base642bin(collectionData['magicMetadata']['data']),
|
||||
@@ -1259,7 +1288,7 @@ class CollectionsService {
|
||||
if (collection.canLinkToDevicePath(_config.getUserID()!)) {
|
||||
collection.decryptedPath = (_decryptCollectionPath(collection));
|
||||
}
|
||||
return collection;
|
||||
return Collection.fromOldCollection(collection);
|
||||
}
|
||||
|
||||
Collection? getCollectionByID(int collectionID) {
|
||||
@@ -1874,31 +1903,31 @@ class CollectionsService {
|
||||
}
|
||||
|
||||
@Deprecated("Use _cacheLocalPathAndCollection instead")
|
||||
Collection _cacheCollectionAttributes(Collection collection) {
|
||||
CollectionV2 _cacheCollectionAttributes(CollectionV2 collection) {
|
||||
final String decryptedName = _getDecryptedCollectionName(collection);
|
||||
collection.setName(decryptedName);
|
||||
if (collection.canLinkToDevicePath(_config.getUserID()!)) {
|
||||
_localPathToCollectionID[_decryptCollectionPath(collection)] =
|
||||
collection.id;
|
||||
collection.decryptedPath = _decryptCollectionPath(collection);
|
||||
_localPathToCollectionID[collection.decryptedPath!] = collection.id;
|
||||
}
|
||||
_collectionIDToCollections[collection.id] = collection;
|
||||
final Collection c = Collection.fromOldCollection(collection);
|
||||
_collectionIDToCollections[collection.id] = c;
|
||||
return collection;
|
||||
}
|
||||
|
||||
Collection _cacheLocalPathAndCollection(Collection collection) {
|
||||
assert(
|
||||
collection.decryptedName != null,
|
||||
collection.name != null,
|
||||
"decryptedName should be already set",
|
||||
);
|
||||
if (collection.canLinkToDevicePath(_config.getUserID()!) &&
|
||||
(collection.decryptedPath ?? '').isNotEmpty) {
|
||||
_localPathToCollectionID[collection.decryptedPath!] = collection.id;
|
||||
if (collection.canLinkToDevicePath(_config.getUserID()!)) {
|
||||
_localPathToCollectionID[collection.localPath!] = collection.id;
|
||||
}
|
||||
_collectionIDToCollections[collection.id] = collection;
|
||||
return collection;
|
||||
}
|
||||
|
||||
String _decryptCollectionPath(Collection collection) {
|
||||
String _decryptCollectionPath(CollectionV2 collection) {
|
||||
final existingPath = collection.decryptedPath;
|
||||
if (existingPath != null && existingPath.isNotEmpty) {
|
||||
debugPrint("Using cached decrypted path for collection ${collection.id}");
|
||||
@@ -1925,7 +1954,7 @@ class CollectionsService {
|
||||
return _prefs.containsKey(_collectionsSyncTimeKey);
|
||||
}
|
||||
|
||||
String _getDecryptedCollectionName(Collection collection) {
|
||||
String _getDecryptedCollectionName(CollectionV2 collection) {
|
||||
if (collection.isDeleted) {
|
||||
return "Deleted Album";
|
||||
}
|
||||
@@ -1933,7 +1962,10 @@ class CollectionsService {
|
||||
collection.encryptedName!.isNotEmpty) {
|
||||
try {
|
||||
final collectionKey = _getAndCacheDecryptedKey(
|
||||
collection,
|
||||
collection.id,
|
||||
collection.isOwner(_config.getUserID()!),
|
||||
encKey: collection.encryptedKey,
|
||||
encKeyNonce: collection.keyDecryptionNonce!,
|
||||
source: "Name",
|
||||
);
|
||||
final result = CryptoUtil.decryptSync(
|
||||
|
||||
Reference in New Issue
Block a user