[mob] Add new collection model for remote

This commit is contained in:
Neeraj Gupta
2025-02-14 11:26:20 +05:30
parent b5a9bab5c6
commit 0bcc676e44
4 changed files with 413 additions and 113 deletions

View File

@@ -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],

View File

@@ -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,
);
}
}

View 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,
);
}
}

View File

@@ -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(