diff --git a/lib/db/collections_db.dart b/lib/db/collections_db.dart new file mode 100644 index 000000000..311e7096b --- /dev/null +++ b/lib/db/collections_db.dart @@ -0,0 +1,108 @@ +import 'dart:io'; + +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:photos/models/collection.dart'; +import 'package:sqflite/sqflite.dart'; + +class CollectionsDB { + static final _databaseName = "ente.collections.db"; + static final _databaseVersion = 1; + + static final table = 'collections'; + + static final columnID = 'collection_id'; + static final columnOwnerID = 'owner_id'; + static final columnEncryptedKey = 'encrypted_key'; + static final columnKeyDecryptionNonce = 'key_decryption_nonce'; + static final columnName = 'name'; + static final columnType = 'type'; + static final columnEncryptedPath = 'path'; + static final columnCreationTime = 'creation_time'; + + CollectionsDB._privateConstructor(); + static final CollectionsDB instance = CollectionsDB._privateConstructor(); + + static Database _database; + Future get database async { + if (_database != null) return _database; + _database = await _initDatabase(); + return _database; + } + + _initDatabase() async { + Directory documentsDirectory = await getApplicationDocumentsDirectory(); + String path = join(documentsDirectory.path, _databaseName); + return await openDatabase( + path, + version: _databaseVersion, + onCreate: _onCreate, + ); + } + + Future _onCreate(Database db, int version) async { + await db.execute(''' + CREATE TABLE $table ( + $columnID INTEGER PRIMARY KEY NOT NULL, + $columnOwnerID TEXT NOT NULL, + $columnEncryptedKey TEXT NOT NULL, + $columnKeyDecryptionNonce TEXT NOT NULL, + $columnName TEXT NOT NULL, + $columnType TEXT NOT NULL, + $columnEncryptedPath TEXT, + $columnCreationTime TEXT NOT NULL, + ) + '''); + } + + Future> insert(List collections) async { + final db = await instance.database; + var batch = db.batch(); + for (final collection in collections) { + batch.insert(table, _getRowForCollection(collection), + conflictAlgorithm: ConflictAlgorithm.replace); + } + return await batch.commit(); + } + + Future getLastCollectionCreationTime() async { + final db = await instance.database; + final rows = await db.query( + table, + orderBy: '$columnCreationTime DESC', + limit: 1, + ); + if (rows.isNotEmpty) { + return int.parse(rows[0][columnCreationTime]); + } else { + return null; + } + } + + Map _getRowForCollection(Collection collection) { + var row = new Map(); + row[columnID] = collection.id; + row[columnOwnerID] = collection.ownerID; + row[columnEncryptedKey] = collection.encryptedKey; + row[columnKeyDecryptionNonce] = collection.keyDecryptionNonce; + row[columnName] = collection.name; + row[columnType] = collection.type; + row[columnEncryptedPath] = collection.encryptedPath; + row[columnCreationTime] = collection.creationTime; + return row; + } + + Collection _convertToCollection(Map row) { + return Collection( + row[columnID], + row[columnOwnerID], + row[columnEncryptedKey], + row[columnKeyDecryptionNonce], + row[columnName], + row[columnType], + row[columnEncryptedPath], + int.parse(row[columnCreationTime]), + null, + ); + } +} diff --git a/lib/models/collection.dart b/lib/models/collection.dart index f7514b5f9..9a918bcfe 100644 --- a/lib/models/collection.dart +++ b/lib/models/collection.dart @@ -9,7 +9,8 @@ class Collection { final String keyDecryptionNonce; final String name; final CollectionType type; - final Map attributes; + final String encryptedPath; + final int creationTime; final List sharees; Collection( @@ -19,12 +20,13 @@ class Collection { this.keyDecryptionNonce, this.name, this.type, - this.attributes, + this.encryptedPath, + this.creationTime, this.sharees, ); static Collection emptyCollection() { - return Collection(null, null, null, null, null, null, null, List()); + return Collection(null, null, null, null, null, null, null, null, List()); } Collection copyWith({ @@ -34,7 +36,8 @@ class Collection { String keyDecryptionNonce, String name, CollectionType type, - Map attributes, + String encryptedPath, + int creationTime, List sharees, }) { return Collection( @@ -44,7 +47,8 @@ class Collection { keyDecryptionNonce ?? this.keyDecryptionNonce, name ?? this.name, type ?? this.type, - attributes ?? this.attributes, + encryptedPath ?? this.encryptedPath, + creationTime ?? this.creationTime, sharees ?? this.sharees, ); } @@ -57,7 +61,8 @@ class Collection { 'keyDecryptionNonce': keyDecryptionNonce, 'name': name, 'type': type.toString(), - 'attributes': attributes, + 'creationTime': creationTime, + 'encryptedPath': encryptedPath, 'sharees': sharees, }; } @@ -72,8 +77,9 @@ class Collection { map['keyDecryptionNonce'], map['name'], fromString(map['type']), - Map.from(map['attributes']), - List.from(map['sharees']), + map['encryptedPath'], + map['creationTime'], + map['sharees'] == null ? null : List.from(map['sharees']), ); } @@ -84,7 +90,7 @@ class Collection { @override String toString() { - return 'Collection(id: $id, ownerID: $ownerID, encryptedKey: $encryptedKey, keyDecryptionNonce: $keyDecryptionNonce, name: $name, type: $type, attributes: $attributes, sharees: $sharees)'; + return 'Collection(id: $id, ownerID: $ownerID, encryptedKey: $encryptedKey, keyDecryptionNonce: $keyDecryptionNonce, name: $name, type: $type, encryptedPath: $encryptedPath, creationTime: $creationTime, sharees: $sharees)'; } @override @@ -98,7 +104,9 @@ class Collection { o.keyDecryptionNonce == keyDecryptionNonce && o.name == name && o.type == type && - mapEquals(o.attributes, attributes); + o.encryptedPath == encryptedPath && + o.creationTime == creationTime && + listEquals(o.sharees, sharees); } @override @@ -109,7 +117,9 @@ class Collection { keyDecryptionNonce.hashCode ^ name.hashCode ^ type.hashCode ^ - attributes.hashCode; + encryptedPath.hashCode ^ + creationTime.hashCode ^ + sharees.hashCode; } }