diff --git a/app/lib/entity/album/data_source.dart b/app/lib/entity/album/data_source.dart index 20b4169d..5c79e268 100644 --- a/app/lib/entity/album/data_source.dart +++ b/app/lib/entity/album/data_source.dart @@ -189,7 +189,8 @@ class AlbumSqliteDbDataSource implements AlbumDataSource { await _c.sqliteDb.use((db) async { final rowIds = await db.accountFileRowIdsOf(album.albumFile!, appAccount: account); - final insert = SqliteAlbumConverter.toSql(album, rowIds.fileRowId); + final insert = SqliteAlbumConverter.toSql( + album, rowIds.fileRowId, album.albumFile!.etag!); var rowId = await _updateCache(db, rowIds.fileRowId, insert.album); if (rowId == null) { // new album, need insert diff --git a/app/lib/entity/sqlite_table.dart b/app/lib/entity/sqlite_table.dart index cec88932..e4b517fe 100644 --- a/app/lib/entity/sqlite_table.dart +++ b/app/lib/entity/sqlite_table.dart @@ -135,6 +135,8 @@ class Albums extends Table { IntColumn get file => integer() .references(Files, #rowId, onDelete: KeyAction.cascade) .unique()(); + // store the etag of the file when the album is cached in the db + TextColumn get fileEtag => text().nullable()(); IntColumn get version => integer()(); DateTimeColumn get lastUpdated => dateTime().map(const _DateTimeConverter())(); @@ -218,7 +220,7 @@ class SqliteDb extends _$SqliteDb { SqliteDb.connect(DatabaseConnection connection) : super.connect(connection); @override - get schemaVersion => 3; + get schemaVersion => 4; @override get migration => MigrationStrategy( @@ -263,6 +265,9 @@ class SqliteDb extends _$SqliteDb { await m.createTable(imageLocations); await _createIndexV3(m); } + if (from < 4) { + await m.addColumn(albums, albums.fileEtag); + } }); } catch (e, stackTrace) { _log.shout("[onUpgrade] Failed upgrading sqlite db", e, stackTrace); diff --git a/app/lib/entity/sqlite_table.g.dart b/app/lib/entity/sqlite_table.g.dart index 32b9cca4..bbe0e43f 100644 --- a/app/lib/entity/sqlite_table.g.dart +++ b/app/lib/entity/sqlite_table.g.dart @@ -2671,6 +2671,7 @@ class $DirFilesTable extends DirFiles with TableInfo<$DirFilesTable, DirFile> { class Album extends DataClass implements Insertable<Album> { final int rowId; final int file; + final String? fileEtag; final int version; final DateTime lastUpdated; final String name; @@ -2683,6 +2684,7 @@ class Album extends DataClass implements Insertable<Album> { Album( {required this.rowId, required this.file, + this.fileEtag, required this.version, required this.lastUpdated, required this.name, @@ -2699,6 +2701,8 @@ class Album extends DataClass implements Insertable<Album> { .mapFromDatabaseResponse(data['${effectivePrefix}row_id'])!, file: const IntType() .mapFromDatabaseResponse(data['${effectivePrefix}file'])!, + fileEtag: const StringType() + .mapFromDatabaseResponse(data['${effectivePrefix}file_etag']), version: const IntType() .mapFromDatabaseResponse(data['${effectivePrefix}version'])!, lastUpdated: $AlbumsTable.$converter0.mapToDart(const DateTimeType() @@ -2724,6 +2728,9 @@ class Album extends DataClass implements Insertable<Album> { final map = <String, Expression>{}; map['row_id'] = Variable<int>(rowId); map['file'] = Variable<int>(file); + if (!nullToAbsent || fileEtag != null) { + map['file_etag'] = Variable<String?>(fileEtag); + } map['version'] = Variable<int>(version); { final converter = $AlbumsTable.$converter0; @@ -2744,6 +2751,9 @@ class Album extends DataClass implements Insertable<Album> { return AlbumsCompanion( rowId: Value(rowId), file: Value(file), + fileEtag: fileEtag == null && nullToAbsent + ? const Value.absent() + : Value(fileEtag), version: Value(version), lastUpdated: Value(lastUpdated), name: Value(name), @@ -2762,6 +2772,7 @@ class Album extends DataClass implements Insertable<Album> { return Album( rowId: serializer.fromJson<int>(json['rowId']), file: serializer.fromJson<int>(json['file']), + fileEtag: serializer.fromJson<String?>(json['fileEtag']), version: serializer.fromJson<int>(json['version']), lastUpdated: serializer.fromJson<DateTime>(json['lastUpdated']), name: serializer.fromJson<String>(json['name']), @@ -2781,6 +2792,7 @@ class Album extends DataClass implements Insertable<Album> { return <String, dynamic>{ 'rowId': serializer.toJson<int>(rowId), 'file': serializer.toJson<int>(file), + 'fileEtag': serializer.toJson<String?>(fileEtag), 'version': serializer.toJson<int>(version), 'lastUpdated': serializer.toJson<DateTime>(lastUpdated), 'name': serializer.toJson<String>(name), @@ -2796,6 +2808,7 @@ class Album extends DataClass implements Insertable<Album> { Album copyWith( {int? rowId, int? file, + Value<String?> fileEtag = const Value.absent(), int? version, DateTime? lastUpdated, String? name, @@ -2808,6 +2821,7 @@ class Album extends DataClass implements Insertable<Album> { Album( rowId: rowId ?? this.rowId, file: file ?? this.file, + fileEtag: fileEtag.present ? fileEtag.value : this.fileEtag, version: version ?? this.version, lastUpdated: lastUpdated ?? this.lastUpdated, name: name ?? this.name, @@ -2823,6 +2837,7 @@ class Album extends DataClass implements Insertable<Album> { return (StringBuffer('Album(') ..write('rowId: $rowId, ') ..write('file: $file, ') + ..write('fileEtag: $fileEtag, ') ..write('version: $version, ') ..write('lastUpdated: $lastUpdated, ') ..write('name: $name, ') @@ -2840,6 +2855,7 @@ class Album extends DataClass implements Insertable<Album> { int get hashCode => Object.hash( rowId, file, + fileEtag, version, lastUpdated, name, @@ -2855,6 +2871,7 @@ class Album extends DataClass implements Insertable<Album> { (other is Album && other.rowId == this.rowId && other.file == this.file && + other.fileEtag == this.fileEtag && other.version == this.version && other.lastUpdated == this.lastUpdated && other.name == this.name && @@ -2869,6 +2886,7 @@ class Album extends DataClass implements Insertable<Album> { class AlbumsCompanion extends UpdateCompanion<Album> { final Value<int> rowId; final Value<int> file; + final Value<String?> fileEtag; final Value<int> version; final Value<DateTime> lastUpdated; final Value<String> name; @@ -2881,6 +2899,7 @@ class AlbumsCompanion extends UpdateCompanion<Album> { const AlbumsCompanion({ this.rowId = const Value.absent(), this.file = const Value.absent(), + this.fileEtag = const Value.absent(), this.version = const Value.absent(), this.lastUpdated = const Value.absent(), this.name = const Value.absent(), @@ -2894,6 +2913,7 @@ class AlbumsCompanion extends UpdateCompanion<Album> { AlbumsCompanion.insert({ this.rowId = const Value.absent(), required int file, + this.fileEtag = const Value.absent(), required int version, required DateTime lastUpdated, required String name, @@ -2916,6 +2936,7 @@ class AlbumsCompanion extends UpdateCompanion<Album> { static Insertable<Album> custom({ Expression<int>? rowId, Expression<int>? file, + Expression<String?>? fileEtag, Expression<int>? version, Expression<DateTime>? lastUpdated, Expression<String>? name, @@ -2929,6 +2950,7 @@ class AlbumsCompanion extends UpdateCompanion<Album> { return RawValuesInsertable({ if (rowId != null) 'row_id': rowId, if (file != null) 'file': file, + if (fileEtag != null) 'file_etag': fileEtag, if (version != null) 'version': version, if (lastUpdated != null) 'last_updated': lastUpdated, if (name != null) 'name': name, @@ -2946,6 +2968,7 @@ class AlbumsCompanion extends UpdateCompanion<Album> { AlbumsCompanion copyWith( {Value<int>? rowId, Value<int>? file, + Value<String?>? fileEtag, Value<int>? version, Value<DateTime>? lastUpdated, Value<String>? name, @@ -2958,6 +2981,7 @@ class AlbumsCompanion extends UpdateCompanion<Album> { return AlbumsCompanion( rowId: rowId ?? this.rowId, file: file ?? this.file, + fileEtag: fileEtag ?? this.fileEtag, version: version ?? this.version, lastUpdated: lastUpdated ?? this.lastUpdated, name: name ?? this.name, @@ -2979,6 +3003,9 @@ class AlbumsCompanion extends UpdateCompanion<Album> { if (file.present) { map['file'] = Variable<int>(file.value); } + if (fileEtag.present) { + map['file_etag'] = Variable<String?>(fileEtag.value); + } if (version.present) { map['version'] = Variable<int>(version.value); } @@ -3018,6 +3045,7 @@ class AlbumsCompanion extends UpdateCompanion<Album> { return (StringBuffer('AlbumsCompanion(') ..write('rowId: $rowId, ') ..write('file: $file, ') + ..write('fileEtag: $fileEtag, ') ..write('version: $version, ') ..write('lastUpdated: $lastUpdated, ') ..write('name: $name, ') @@ -3051,6 +3079,11 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, Album> { type: const IntType(), requiredDuringInsert: true, defaultConstraints: 'UNIQUE REFERENCES files (row_id) ON DELETE CASCADE'); + final VerificationMeta _fileEtagMeta = const VerificationMeta('fileEtag'); + @override + late final GeneratedColumn<String?> fileEtag = GeneratedColumn<String?>( + 'file_etag', aliasedName, true, + type: const StringType(), requiredDuringInsert: false); final VerificationMeta _versionMeta = const VerificationMeta('version'); @override late final GeneratedColumn<int?> version = GeneratedColumn<int?>( @@ -3108,6 +3141,7 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, Album> { List<GeneratedColumn> get $columns => [ rowId, file, + fileEtag, version, lastUpdated, name, @@ -3137,6 +3171,10 @@ class $AlbumsTable extends Albums with TableInfo<$AlbumsTable, Album> { } else if (isInserting) { context.missing(_fileMeta); } + if (data.containsKey('file_etag')) { + context.handle(_fileEtagMeta, + fileEtag.isAcceptableOrUnknown(data['file_etag']!, _fileEtagMeta)); + } if (data.containsKey('version')) { context.handle(_versionMeta, version.isAcceptableOrUnknown(data['version']!, _versionMeta)); diff --git a/app/lib/entity/sqlite_table_converter.dart b/app/lib/entity/sqlite_table_converter.dart index 1f51d9db..213e958c 100644 --- a/app/lib/entity/sqlite_table_converter.dart +++ b/app/lib/entity/sqlite_table_converter.dart @@ -14,6 +14,7 @@ import 'package:nc_photos/entity/sqlite_table_extension.dart' as sql; import 'package:nc_photos/entity/tag.dart'; import 'package:nc_photos/iterable_extension.dart'; import 'package:nc_photos/object_extension.dart'; +import 'package:nc_photos/or_null.dart'; extension SqlTagListExtension on List<sql.Tag> { Future<List<Tag>> convertToAppTag() { @@ -74,17 +75,20 @@ class SqliteAlbumConverter { sharedAt: e.sharedAt.toUtc(), )) .toList(), - albumFile: albumFile, + // replace with the original etag when this album was cached + albumFile: albumFile.copyWith(etag: OrNull(album.fileEtag)), savedVersion: album.version, ); } - static sql.CompleteAlbumCompanion toSql(Album album, int albumFileRowId) { + static sql.CompleteAlbumCompanion toSql( + Album album, int albumFileRowId, String albumFileEtag) { final providerJson = album.provider.toJson(); final coverProviderJson = album.coverProvider.toJson(); final sortProviderJson = album.sortProvider.toJson(); final dbAlbum = sql.AlbumsCompanion.insert( file: albumFileRowId, + fileEtag: Value(albumFileEtag), version: Album.version, lastUpdated: album.lastUpdated, name: album.name,