diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index c52665f093..67591da4d1 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -5,7 +5,7 @@ import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/log.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; -import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/drift_store.repository.dart'; import 'package:logging/logging.dart'; /// Service responsible for handling application logging. @@ -15,7 +15,7 @@ import 'package:logging/logging.dart'; /// via [IStoreRepository] class LogService { final IsarLogRepository _logRepository; - final IsarStoreRepository _storeRepository; + final IStoreRepository _storeRepository; final List _msgBuffer = []; @@ -38,7 +38,7 @@ class LogService { static Future init({ required IsarLogRepository logRepository, - required IsarStoreRepository storeRepository, + required IStoreRepository storeRepository, bool shouldBuffer = true, }) async { _instance ??= await create( @@ -51,7 +51,7 @@ class LogService { static Future create({ required IsarLogRepository logRepository, - required IsarStoreRepository storeRepository, + required IStoreRepository storeRepository, bool shouldBuffer = true, }) async { final instance = LogService._(logRepository, storeRepository, shouldBuffer); diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index 359ce8cf60..e2e5130c02 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -1,18 +1,18 @@ import 'dart:async'; import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/drift_store.repository.dart'; /// Provides access to a persistent key-value store with an in-memory cache. /// Listens for repository changes to keep the cache updated. class StoreService { - final IsarStoreRepository _storeRepository; + final IStoreRepository _storeRepository; /// In-memory cache. Keys are [StoreKey.id] final Map _cache = {}; late final StreamSubscription _storeUpdateSubscription; - StoreService._({required IsarStoreRepository storeRepository}) + StoreService._({required IStoreRepository storeRepository}) : _storeRepository = storeRepository; // TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider @@ -26,14 +26,14 @@ class StoreService { // TODO: Replace the implementation with the one from create after removing the typedef static Future init({ - required IsarStoreRepository storeRepository, + required IStoreRepository storeRepository, }) async { _instance ??= await create(storeRepository: storeRepository); return _instance!; } static Future create({ - required IsarStoreRepository storeRepository, + required IStoreRepository storeRepository, }) async { final instance = StoreService._(storeRepository: storeRepository); await instance._populateCache(); diff --git a/mobile/lib/infrastructure/entities/isar_store.entity.dart b/mobile/lib/infrastructure/entities/isar_store.entity.dart new file mode 100644 index 0000000000..f4328476b8 --- /dev/null +++ b/mobile/lib/infrastructure/entities/isar_store.entity.dart @@ -0,0 +1,13 @@ +import 'package:isar/isar.dart'; + +part 'isar_store.entity.g.dart'; + +/// Internal class for `Store`, do not use elsewhere. +@Collection(inheritance: false) +class StoreValue { + final Id id; + final int? intValue; + final String? strValue; + + const StoreValue(this.id, {this.intValue, this.strValue}); +} diff --git a/mobile/lib/infrastructure/entities/store.entity.g.dart b/mobile/lib/infrastructure/entities/isar_store.entity.g.dart similarity index 99% rename from mobile/lib/infrastructure/entities/store.entity.g.dart rename to mobile/lib/infrastructure/entities/isar_store.entity.g.dart index b97b5b0a28..c270b20090 100644 --- a/mobile/lib/infrastructure/entities/store.entity.g.dart +++ b/mobile/lib/infrastructure/entities/isar_store.entity.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'store.entity.dart'; +part of 'isar_store.entity.dart'; // ************************************************************************** // IsarCollectionGenerator diff --git a/mobile/lib/infrastructure/entities/store.entity.dart b/mobile/lib/infrastructure/entities/store.entity.dart index 8d6d9a7d16..abd32e0f3e 100644 --- a/mobile/lib/infrastructure/entities/store.entity.dart +++ b/mobile/lib/infrastructure/entities/store.entity.dart @@ -1,13 +1,13 @@ -import 'package:isar/isar.dart'; +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -part 'store.entity.g.dart'; +class StoreEntity extends Table with DriftDefaultsMixin { + const StoreEntity(); -/// Internal class for `Store`, do not use elsewhere. -@Collection(inheritance: false) -class StoreValue { - final Id id; - final int? intValue; - final String? strValue; + IntColumn get id => integer()(); + IntColumn get intValue => integer().nullable()(); + TextColumn get strValue => text().nullable()(); - const StoreValue(this.id, {this.intValue, this.strValue}); + @override + Set get primaryKey => {id}; } diff --git a/mobile/lib/infrastructure/entities/store.entity.drift.dart b/mobile/lib/infrastructure/entities/store.entity.drift.dart new file mode 100644 index 0000000000..c8b9a9bae3 --- /dev/null +++ b/mobile/lib/infrastructure/entities/store.entity.drift.dart @@ -0,0 +1,364 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/store.entity.dart' as i2; + +typedef $$StoreEntityTableCreateCompanionBuilder = i1.StoreEntityCompanion + Function({ + required int id, + i0.Value intValue, + i0.Value strValue, +}); +typedef $$StoreEntityTableUpdateCompanionBuilder = i1.StoreEntityCompanion + Function({ + i0.Value id, + i0.Value intValue, + i0.Value strValue, +}); + +class $$StoreEntityTableFilterComposer + extends i0.Composer { + $$StoreEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get intValue => $composableBuilder( + column: $table.intValue, builder: (column) => i0.ColumnFilters(column)); + + i0.ColumnFilters get strValue => $composableBuilder( + column: $table.strValue, builder: (column) => i0.ColumnFilters(column)); +} + +class $$StoreEntityTableOrderingComposer + extends i0.Composer { + $$StoreEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get intValue => $composableBuilder( + column: $table.intValue, builder: (column) => i0.ColumnOrderings(column)); + + i0.ColumnOrderings get strValue => $composableBuilder( + column: $table.strValue, builder: (column) => i0.ColumnOrderings(column)); +} + +class $$StoreEntityTableAnnotationComposer + extends i0.Composer { + $$StoreEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get intValue => + $composableBuilder(column: $table.intValue, builder: (column) => column); + + i0.GeneratedColumn get strValue => + $composableBuilder(column: $table.strValue, builder: (column) => column); +} + +class $$StoreEntityTableTableManager extends i0.RootTableManager< + i0.GeneratedDatabase, + i1.$StoreEntityTable, + i1.StoreEntityData, + i1.$$StoreEntityTableFilterComposer, + i1.$$StoreEntityTableOrderingComposer, + i1.$$StoreEntityTableAnnotationComposer, + $$StoreEntityTableCreateCompanionBuilder, + $$StoreEntityTableUpdateCompanionBuilder, + ( + i1.StoreEntityData, + i0.BaseReferences + ), + i1.StoreEntityData, + i0.PrefetchHooks Function()> { + $$StoreEntityTableTableManager( + i0.GeneratedDatabase db, i1.$StoreEntityTable table) + : super(i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$StoreEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$StoreEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$StoreEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: ({ + i0.Value id = const i0.Value.absent(), + i0.Value intValue = const i0.Value.absent(), + i0.Value strValue = const i0.Value.absent(), + }) => + i1.StoreEntityCompanion( + id: id, + intValue: intValue, + strValue: strValue, + ), + createCompanionCallback: ({ + required int id, + i0.Value intValue = const i0.Value.absent(), + i0.Value strValue = const i0.Value.absent(), + }) => + i1.StoreEntityCompanion.insert( + id: id, + intValue: intValue, + strValue: strValue, + ), + withReferenceMapper: (p0) => p0 + .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) + .toList(), + prefetchHooksCallback: null, + )); +} + +typedef $$StoreEntityTableProcessedTableManager = i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$StoreEntityTable, + i1.StoreEntityData, + i1.$$StoreEntityTableFilterComposer, + i1.$$StoreEntityTableOrderingComposer, + i1.$$StoreEntityTableAnnotationComposer, + $$StoreEntityTableCreateCompanionBuilder, + $$StoreEntityTableUpdateCompanionBuilder, + ( + i1.StoreEntityData, + i0.BaseReferences + ), + i1.StoreEntityData, + i0.PrefetchHooks Function()>; + +class $StoreEntityTable extends i2.StoreEntity + with i0.TableInfo<$StoreEntityTable, i1.StoreEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $StoreEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', aliasedName, false, + type: i0.DriftSqlType.int, requiredDuringInsert: true); + static const i0.VerificationMeta _intValueMeta = + const i0.VerificationMeta('intValue'); + @override + late final i0.GeneratedColumn intValue = i0.GeneratedColumn( + 'int_value', aliasedName, true, + type: i0.DriftSqlType.int, requiredDuringInsert: false); + static const i0.VerificationMeta _strValueMeta = + const i0.VerificationMeta('strValue'); + @override + late final i0.GeneratedColumn strValue = i0.GeneratedColumn( + 'str_value', aliasedName, true, + type: i0.DriftSqlType.string, requiredDuringInsert: false); + @override + List get $columns => [id, intValue, strValue]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'store_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, + {bool isInserting = false}) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('int_value')) { + context.handle(_intValueMeta, + intValue.isAcceptableOrUnknown(data['int_value']!, _intValueMeta)); + } + if (data.containsKey('str_value')) { + context.handle(_strValueMeta, + strValue.isAcceptableOrUnknown(data['str_value']!, _strValueMeta)); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.StoreEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.StoreEntityData( + id: attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}id'])!, + intValue: attachedDatabase.typeMapping + .read(i0.DriftSqlType.int, data['${effectivePrefix}int_value']), + strValue: attachedDatabase.typeMapping + .read(i0.DriftSqlType.string, data['${effectivePrefix}str_value']), + ); + } + + @override + $StoreEntityTable createAlias(String alias) { + return $StoreEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StoreEntityData extends i0.DataClass + implements i0.Insertable { + final int id; + final int? intValue; + final String? strValue; + const StoreEntityData({required this.id, this.intValue, this.strValue}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + if (!nullToAbsent || intValue != null) { + map['int_value'] = i0.Variable(intValue); + } + if (!nullToAbsent || strValue != null) { + map['str_value'] = i0.Variable(strValue); + } + return map; + } + + factory StoreEntityData.fromJson(Map json, + {i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return StoreEntityData( + id: serializer.fromJson(json['id']), + intValue: serializer.fromJson(json['intValue']), + strValue: serializer.fromJson(json['strValue']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'intValue': serializer.toJson(intValue), + 'strValue': serializer.toJson(strValue), + }; + } + + i1.StoreEntityData copyWith( + {int? id, + i0.Value intValue = const i0.Value.absent(), + i0.Value strValue = const i0.Value.absent()}) => + i1.StoreEntityData( + id: id ?? this.id, + intValue: intValue.present ? intValue.value : this.intValue, + strValue: strValue.present ? strValue.value : this.strValue, + ); + StoreEntityData copyWithCompanion(i1.StoreEntityCompanion data) { + return StoreEntityData( + id: data.id.present ? data.id.value : this.id, + intValue: data.intValue.present ? data.intValue.value : this.intValue, + strValue: data.strValue.present ? data.strValue.value : this.strValue, + ); + } + + @override + String toString() { + return (StringBuffer('StoreEntityData(') + ..write('id: $id, ') + ..write('intValue: $intValue, ') + ..write('strValue: $strValue') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, intValue, strValue); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.StoreEntityData && + other.id == this.id && + other.intValue == this.intValue && + other.strValue == this.strValue); +} + +class StoreEntityCompanion extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value intValue; + final i0.Value strValue; + const StoreEntityCompanion({ + this.id = const i0.Value.absent(), + this.intValue = const i0.Value.absent(), + this.strValue = const i0.Value.absent(), + }); + StoreEntityCompanion.insert({ + required int id, + this.intValue = const i0.Value.absent(), + this.strValue = const i0.Value.absent(), + }) : id = i0.Value(id); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? intValue, + i0.Expression? strValue, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (intValue != null) 'int_value': intValue, + if (strValue != null) 'str_value': strValue, + }); + } + + i1.StoreEntityCompanion copyWith( + {i0.Value? id, + i0.Value? intValue, + i0.Value? strValue}) { + return i1.StoreEntityCompanion( + id: id ?? this.id, + intValue: intValue ?? this.intValue, + strValue: strValue ?? this.strValue, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (intValue.present) { + map['int_value'] = i0.Variable(intValue.value); + } + if (strValue.present) { + map['str_value'] = i0.Variable(strValue.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StoreEntityCompanion(') + ..write('id: $id, ') + ..write('intValue: $intValue, ') + ..write('strValue: $strValue') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index dbe491b035..983ce76cf8 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:isar/isar.dart'; @@ -46,6 +47,7 @@ class IsarDatabaseRepository implements IDatabaseRepository { RemoteAlbumEntity, RemoteAlbumAssetEntity, RemoteAlbumUserEntity, + StoreEntity, ], include: { 'package:immich_mobile/infrastructure/entities/merged_asset.drift', diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 69fd84b79a..1ac93d04a6 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -23,9 +23,11 @@ import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity. as i10; import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart' as i11; -import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' +import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart' as i12; -import 'package:drift/internal/modular.dart' as i13; +import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' + as i13; +import 'package:drift/internal/modular.dart' as i14; abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); @@ -51,8 +53,9 @@ abstract class $Drift extends i0.GeneratedDatabase { i10.$RemoteAlbumAssetEntityTable(this); late final i11.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i11.$RemoteAlbumUserEntityTable(this); - i12.MergedAssetDrift get mergedAssetDrift => i13.ReadDatabaseContainer(this) - .accessor(i12.MergedAssetDrift.new); + late final i12.$StoreEntityTable storeEntity = i12.$StoreEntityTable(this); + i13.MergedAssetDrift get mergedAssetDrift => i14.ReadDatabaseContainer(this) + .accessor(i13.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -71,7 +74,8 @@ abstract class $Drift extends i0.GeneratedDatabase { remoteExifEntity, remoteAlbumEntity, remoteAlbumAssetEntity, - remoteAlbumUserEntity + remoteAlbumUserEntity, + storeEntity ]; @override i0.StreamQueryUpdateRules get streamUpdateRules => @@ -208,4 +212,6 @@ class $DriftManager { _db, _db.remoteAlbumAssetEntity); i11.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i11 .$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity); + i12.$$StoreEntityTableTableManager get storeEntity => + i12.$$StoreEntityTableTableManager(_db, _db.storeEntity); } diff --git a/mobile/lib/infrastructure/repositories/drift_store.repository.dart b/mobile/lib/infrastructure/repositories/drift_store.repository.dart new file mode 100644 index 0000000000..41a89f089d --- /dev/null +++ b/mobile/lib/infrastructure/repositories/drift_store.repository.dart @@ -0,0 +1,145 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/drift_user.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; + +final driftStoreRepositoryProvider = Provider( + (ref) => DriftStoreRepository(ref.watch(driftProvider)), +); + +class DriftStoreRepository implements IStoreRepository { + final Drift _db; + final validStoreKeys = StoreKey.values.map((e) => e.id).toSet(); + + DriftStoreRepository(this._db); + + @override + Future deleteAll() async { + return await _db.transaction(() async { + await _db.delete(_db.storeEntity).go(); + return true; + }); + } + + @override + Stream> watchAll() { + return (_db.select(_db.storeEntity) + ..where((tbl) => tbl.id.isIn(validStoreKeys))) + .watch() + .asyncExpand( + (entities) => Stream.fromFutures( + entities.map((e) async => _toUpdateEvent(e)), + ), + ); + } + + @override + Future delete(StoreKey key) async { + return await _db.transaction(() async { + await (_db.delete(_db.storeEntity)..where((tbl) => tbl.id.equals(key.id))) + .go(); + }); + } + + @override + Future insert(StoreKey key, T value) async { + return await _db.transaction(() async { + await _db + .into(_db.storeEntity) + .insertOnConflictUpdate(await _fromValue(key, value)); + return true; + }); + } + + @override + Future tryGet(StoreKey key) async { + final entity = await (_db.select(_db.storeEntity) + ..where((tbl) => tbl.id.equals(key.id))) + .getSingleOrNull(); + if (entity == null) { + return null; + } + return await _toValue(key, entity); + } + + @override + Future update(StoreKey key, T value) async { + return await _db.transaction(() async { + await _db + .into(_db.storeEntity) + .insertOnConflictUpdate(await _fromValue(key, value)); + return true; + }); + } + + @override + Stream watch(StoreKey key) async* { + yield* (_db.select(_db.storeEntity)..where((tbl) => tbl.id.equals(key.id))) + .watchSingleOrNull() + .asyncMap((e) async => e == null ? null : await _toValue(key, e)); + } + + Future> _toUpdateEvent(StoreEntityData entity) async { + final key = StoreKey.values.firstWhere((e) => e.id == entity.id) + as StoreKey; + final value = await _toValue(key, entity); + return StoreDto(key, value); + } + + Future _toValue(StoreKey key, StoreEntityData entity) async => + switch (key.type) { + const (int) => entity.intValue, + const (String) => entity.strValue, + const (bool) => entity.intValue == 1, + const (DateTime) => entity.intValue == null + ? null + : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), + const (UserDto) => entity.strValue == null + ? null + : await DriftUserRepository(_db).getByUserId(entity.strValue!), + _ => null, + } as T?; + + Future _fromValue(StoreKey key, T value) async { + final (int? intValue, String? strValue) = switch (key.type) { + const (int) => (value as int, null), + const (String) => (null, value as String), + const (bool) => ((value as bool) ? 1 : 0, null), + const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), + const (UserDto) => ( + null, + (await DriftUserRepository(_db).update(value as UserDto)).id, + ), + _ => throw UnsupportedError( + "Unsupported primitive type: ${key.type} for key: ${key.name}", + ), + }; + return StoreEntityData( + id: key.id, + intValue: intValue, + strValue: strValue, + ); + } + + @override + Future>> getAll() async { + final entities = await (_db.select(_db.storeEntity) + ..where((tbl) => tbl.id.isIn(validStoreKeys))) + .get(); + return Future.wait(entities.map((e) => _toUpdateEvent(e)).toList()); + } +} + +abstract class IStoreRepository { + Future deleteAll(); + Stream> watchAll(); + Future delete(StoreKey key); + Future insert(StoreKey key, T value); + Future tryGet(StoreKey key); + Future update(StoreKey key, T value); + Stream watch(StoreKey key); + Future>> getAll(); +} diff --git a/mobile/lib/infrastructure/repositories/drift_user.repository.dart b/mobile/lib/infrastructure/repositories/drift_user.repository.dart new file mode 100644 index 0000000000..448444bd30 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/drift_user.repository.dart @@ -0,0 +1,117 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'; +import 'package:immich_mobile/domain/models/user_metadata.model.dart'; + +class DriftUserRepository { + final Drift _db; + const DriftUserRepository(this._db); + + Future delete(List ids) async { + await _db.transaction(() async { + await (_db.delete(_db.userEntity)..where((tbl) => tbl.id.isIn(ids))).go(); + }); + } + + Future deleteAll() async { + await _db.transaction(() async { + await _db.delete(_db.userEntity).go(); + }); + } + + Future> getAll({SortUserBy? sortBy}) async { + var query = _db.select(_db.userEntity); + + if (sortBy != null) { + switch (sortBy) { + case SortUserBy.id: + query = query..orderBy([(u) => OrderingTerm.asc(u.id)]); + } + } + + final users = await query.get(); + return users.map((u) => _toDto(u)).toList(); + } + + Future getByUserId(String id) async { + final user = await (_db.select(_db.userEntity) + ..where((tbl) => tbl.id.equals(id))) + .getSingleOrNull(); + return user != null ? _toDto(user) : null; + } + + Future> getByUserIds(List ids) async { + final users = await (_db.select(_db.userEntity) + ..where((tbl) => tbl.id.isIn(ids))) + .get(); + + // Create a map for quick lookup + final userMap = {for (var user in users) user.id: _toDto(user)}; + + // Return results in the same order as input ids + return ids.map((id) => userMap[id]).toList(); + } + + Future insert(UserDto user) async { + await _db.transaction(() async { + await _db.into(_db.userEntity).insertOnConflictUpdate(_fromDto(user)); + }); + return true; + } + + Future update(UserDto user) async { + await _db.transaction(() async { + await _db.into(_db.userEntity).insertOnConflictUpdate(_fromDto(user)); + }); + return user; + } + + Future updateAll(List users) async { + await _db.transaction(() async { + await _db.batch((batch) { + for (final user in users) { + batch.insert(_db.userEntity, _fromDto(user), + mode: InsertMode.insertOrReplace); + } + }); + }); + return true; + } + + UserDto _toDto(UserEntityData entity) { + return UserDto( + id: entity.id, + updatedAt: entity.updatedAt, + email: entity.email, + name: entity.name, + isAdmin: entity.isAdmin, + profileImagePath: entity.profileImagePath ?? '', + // Note: These fields are not in the current UserEntity table but are in UserDto + // You may need to add them to the table or provide defaults + isPartnerSharedBy: false, + isPartnerSharedWith: false, + avatarColor: AvatarColor.primary, + memoryEnabled: true, + inTimeline: false, + quotaUsageInBytes: entity.quotaUsageInBytes, + quotaSizeInBytes: entity.quotaSizeInBytes ?? 0, + ); + } + + UserEntityCompanion _fromDto(UserDto dto) { + return UserEntityCompanion( + id: Value(dto.id), + name: Value(dto.name), + isAdmin: Value(dto.isAdmin), + email: Value(dto.email), + profileImagePath: Value.absentIfNull( + dto.profileImagePath?.isEmpty == true ? null : dto.profileImagePath), + updatedAt: Value(dto.updatedAt), + quotaSizeInBytes: Value.absentIfNull( + dto.quotaSizeInBytes == 0 ? null : dto.quotaSizeInBytes), + quotaUsageInBytes: Value(dto.quotaUsageInBytes), + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index 400c8ccf7d..24ba76d3a5 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -1,16 +1,19 @@ import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/isar_store.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/drift_store.repository.dart'; import 'package:isar/isar.dart'; -class IsarStoreRepository extends IsarDatabaseRepository { +class IsarStoreRepository extends IsarDatabaseRepository + implements IStoreRepository { final Isar _db; final validStoreKeys = StoreKey.values.map((e) => e.id).toSet(); IsarStoreRepository(super.db) : _db = db; + @override Future deleteAll() async { return await transaction(() async { await _db.storeValues.clear(); @@ -18,6 +21,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { }); } + @override Stream> watchAll() { return _db.storeValues .filter() @@ -30,10 +34,12 @@ class IsarStoreRepository extends IsarDatabaseRepository { ); } + @override Future delete(StoreKey key) async { return await transaction(() async => await _db.storeValues.delete(key.id)); } + @override Future insert(StoreKey key, T value) async { return await transaction(() async { await _db.storeValues.put(await _fromValue(key, value)); @@ -41,6 +47,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { }); } + @override Future tryGet(StoreKey key) async { final entity = (await _db.storeValues.get(key.id)); if (entity == null) { @@ -49,6 +56,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { return await _toValue(key, entity); } + @override Future update(StoreKey key, T value) async { return await transaction(() async { await _db.storeValues.put(await _fromValue(key, value)); @@ -56,6 +64,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { }); } + @override Stream watch(StoreKey key) async* { yield* _db.storeValues .watchObject(key.id, fireImmediately: true) @@ -100,6 +109,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { return StoreValue(key.id, intValue: intValue, strValue: strValue); } + @override Future>> getAll() async { final entities = await _db.storeValues .filter() diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index f10c042e10..6d05d3a05c 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -5,8 +5,11 @@ import 'package:drift/drift.dart' hide Column; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/infrastructure/repositories/drift_store.repository.dart'; import 'package:immich_mobile/presentation/pages/dev/dev_logger.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; @@ -14,6 +17,32 @@ import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/routing/router.dart'; final _features = [ + _Feature( + name: 'test', + icon: Icons.abc, + onTap: (_, ref) { + final UserDto value = UserDto( + id: "1234", + email: "alex@email.com", + name: "alex", + isAdmin: true, + updatedAt: DateTime.now(), + ); + + ref + .read(driftStoreRepositoryProvider) + .insert(StoreKey.serverUrl, "https://example.com"); + + final readback = + ref.read(driftStoreRepositoryProvider).tryGet(StoreKey.serverUrl); + + readback.then((value) { + print("Read back: $value"); + }); + + return Future.value(); + }, + ), _Feature( name: 'Sync Local', icon: Icons.photo_album_rounded, diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index a5c2c58614..438917f582 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -14,7 +14,7 @@ part of 'router.dart'; /// [ActivitiesPage] class ActivitiesRoute extends PageRouteInfo { const ActivitiesRoute({List? children}) - : super(ActivitiesRoute.name, initialChildren: children); + : super(ActivitiesRoute.name, initialChildren: children); static const String name = 'ActivitiesRoute'; @@ -35,13 +35,13 @@ class AlbumAdditionalSharedUserSelectionRoute required Album album, List? children, }) : super( - AlbumAdditionalSharedUserSelectionRoute.name, - args: AlbumAdditionalSharedUserSelectionRouteArgs( - key: key, - album: album, - ), - initialChildren: children, - ); + AlbumAdditionalSharedUserSelectionRoute.name, + args: AlbumAdditionalSharedUserSelectionRouteArgs( + key: key, + album: album, + ), + initialChildren: children, + ); static const String name = 'AlbumAdditionalSharedUserSelectionRoute'; @@ -83,14 +83,14 @@ class AlbumAssetSelectionRoute bool canDeselect = false, List? children, }) : super( - AlbumAssetSelectionRoute.name, - args: AlbumAssetSelectionRouteArgs( - key: key, - existingAssets: existingAssets, - canDeselect: canDeselect, - ), - initialChildren: children, - ); + AlbumAssetSelectionRoute.name, + args: AlbumAssetSelectionRouteArgs( + key: key, + existingAssets: existingAssets, + canDeselect: canDeselect, + ), + initialChildren: children, + ); static const String name = 'AlbumAssetSelectionRoute'; @@ -130,7 +130,7 @@ class AlbumAssetSelectionRouteArgs { /// [AlbumOptionsPage] class AlbumOptionsRoute extends PageRouteInfo { const AlbumOptionsRoute({List? children}) - : super(AlbumOptionsRoute.name, initialChildren: children); + : super(AlbumOptionsRoute.name, initialChildren: children); static const String name = 'AlbumOptionsRoute'; @@ -150,10 +150,10 @@ class AlbumPreviewRoute extends PageRouteInfo { required Album album, List? children, }) : super( - AlbumPreviewRoute.name, - args: AlbumPreviewRouteArgs(key: key, album: album), - initialChildren: children, - ); + AlbumPreviewRoute.name, + args: AlbumPreviewRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'AlbumPreviewRoute'; @@ -188,10 +188,10 @@ class AlbumSharedUserSelectionRoute required Set assets, List? children, }) : super( - AlbumSharedUserSelectionRoute.name, - args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + AlbumSharedUserSelectionRoute.name, + args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'AlbumSharedUserSelectionRoute'; @@ -225,10 +225,10 @@ class AlbumViewerRoute extends PageRouteInfo { required int albumId, List? children, }) : super( - AlbumViewerRoute.name, - args: AlbumViewerRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + AlbumViewerRoute.name, + args: AlbumViewerRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'AlbumViewerRoute'; @@ -258,7 +258,7 @@ class AlbumViewerRouteArgs { /// [AlbumsPage] class AlbumsRoute extends PageRouteInfo { const AlbumsRoute({List? children}) - : super(AlbumsRoute.name, initialChildren: children); + : super(AlbumsRoute.name, initialChildren: children); static const String name = 'AlbumsRoute'; @@ -274,7 +274,7 @@ class AlbumsRoute extends PageRouteInfo { /// [AllMotionPhotosPage] class AllMotionPhotosRoute extends PageRouteInfo { const AllMotionPhotosRoute({List? children}) - : super(AllMotionPhotosRoute.name, initialChildren: children); + : super(AllMotionPhotosRoute.name, initialChildren: children); static const String name = 'AllMotionPhotosRoute'; @@ -290,7 +290,7 @@ class AllMotionPhotosRoute extends PageRouteInfo { /// [AllPeoplePage] class AllPeopleRoute extends PageRouteInfo { const AllPeopleRoute({List? children}) - : super(AllPeopleRoute.name, initialChildren: children); + : super(AllPeopleRoute.name, initialChildren: children); static const String name = 'AllPeopleRoute'; @@ -306,7 +306,7 @@ class AllPeopleRoute extends PageRouteInfo { /// [AllPlacesPage] class AllPlacesRoute extends PageRouteInfo { const AllPlacesRoute({List? children}) - : super(AllPlacesRoute.name, initialChildren: children); + : super(AllPlacesRoute.name, initialChildren: children); static const String name = 'AllPlacesRoute'; @@ -322,7 +322,7 @@ class AllPlacesRoute extends PageRouteInfo { /// [AllVideosPage] class AllVideosRoute extends PageRouteInfo { const AllVideosRoute({List? children}) - : super(AllVideosRoute.name, initialChildren: children); + : super(AllVideosRoute.name, initialChildren: children); static const String name = 'AllVideosRoute'; @@ -342,10 +342,10 @@ class AppLogDetailRoute extends PageRouteInfo { required LogMessage logMessage, List? children, }) : super( - AppLogDetailRoute.name, - args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), - initialChildren: children, - ); + AppLogDetailRoute.name, + args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), + initialChildren: children, + ); static const String name = 'AppLogDetailRoute'; @@ -375,7 +375,7 @@ class AppLogDetailRouteArgs { /// [AppLogPage] class AppLogRoute extends PageRouteInfo { const AppLogRoute({List? children}) - : super(AppLogRoute.name, initialChildren: children); + : super(AppLogRoute.name, initialChildren: children); static const String name = 'AppLogRoute'; @@ -391,7 +391,7 @@ class AppLogRoute extends PageRouteInfo { /// [ArchivePage] class ArchiveRoute extends PageRouteInfo { const ArchiveRoute({List? children}) - : super(ArchiveRoute.name, initialChildren: children); + : super(ArchiveRoute.name, initialChildren: children); static const String name = 'ArchiveRoute'; @@ -407,7 +407,7 @@ class ArchiveRoute extends PageRouteInfo { /// [BackupAlbumSelectionPage] class BackupAlbumSelectionRoute extends PageRouteInfo { const BackupAlbumSelectionRoute({List? children}) - : super(BackupAlbumSelectionRoute.name, initialChildren: children); + : super(BackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'BackupAlbumSelectionRoute'; @@ -423,7 +423,7 @@ class BackupAlbumSelectionRoute extends PageRouteInfo { /// [BackupControllerPage] class BackupControllerRoute extends PageRouteInfo { const BackupControllerRoute({List? children}) - : super(BackupControllerRoute.name, initialChildren: children); + : super(BackupControllerRoute.name, initialChildren: children); static const String name = 'BackupControllerRoute'; @@ -439,7 +439,7 @@ class BackupControllerRoute extends PageRouteInfo { /// [BackupOptionsPage] class BackupOptionsRoute extends PageRouteInfo { const BackupOptionsRoute({List? children}) - : super(BackupOptionsRoute.name, initialChildren: children); + : super(BackupOptionsRoute.name, initialChildren: children); static const String name = 'BackupOptionsRoute'; @@ -455,7 +455,7 @@ class BackupOptionsRoute extends PageRouteInfo { /// [ChangePasswordPage] class ChangePasswordRoute extends PageRouteInfo { const ChangePasswordRoute({List? children}) - : super(ChangePasswordRoute.name, initialChildren: children); + : super(ChangePasswordRoute.name, initialChildren: children); static const String name = 'ChangePasswordRoute'; @@ -475,10 +475,10 @@ class CreateAlbumRoute extends PageRouteInfo { List? assets, List? children, }) : super( - CreateAlbumRoute.name, - args: CreateAlbumRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + CreateAlbumRoute.name, + args: CreateAlbumRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'CreateAlbumRoute'; @@ -515,10 +515,10 @@ class CropImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - CropImageRoute.name, - args: CropImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + CropImageRoute.name, + args: CropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'CropImageRoute'; @@ -560,15 +560,15 @@ class EditImageRoute extends PageRouteInfo { required bool isEdited, List? children, }) : super( - EditImageRoute.name, - args: EditImageRouteArgs( - key: key, - asset: asset, - image: image, - isEdited: isEdited, - ), - initialChildren: children, - ); + EditImageRoute.name, + args: EditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); static const String name = 'EditImageRoute'; @@ -612,7 +612,7 @@ class EditImageRouteArgs { /// [FailedBackupStatusPage] class FailedBackupStatusRoute extends PageRouteInfo { const FailedBackupStatusRoute({List? children}) - : super(FailedBackupStatusRoute.name, initialChildren: children); + : super(FailedBackupStatusRoute.name, initialChildren: children); static const String name = 'FailedBackupStatusRoute'; @@ -628,7 +628,7 @@ class FailedBackupStatusRoute extends PageRouteInfo { /// [FavoritesPage] class FavoritesRoute extends PageRouteInfo { const FavoritesRoute({List? children}) - : super(FavoritesRoute.name, initialChildren: children); + : super(FavoritesRoute.name, initialChildren: children); static const String name = 'FavoritesRoute'; @@ -644,7 +644,7 @@ class FavoritesRoute extends PageRouteInfo { /// [FeatInDevPage] class FeatInDevRoute extends PageRouteInfo { const FeatInDevRoute({List? children}) - : super(FeatInDevRoute.name, initialChildren: children); + : super(FeatInDevRoute.name, initialChildren: children); static const String name = 'FeatInDevRoute'; @@ -665,10 +665,10 @@ class FilterImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - FilterImageRoute.name, - args: FilterImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + FilterImageRoute.name, + args: FilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'FilterImageRoute'; @@ -712,10 +712,10 @@ class FolderRoute extends PageRouteInfo { RecursiveFolder? folder, List? children, }) : super( - FolderRoute.name, - args: FolderRouteArgs(key: key, folder: folder), - initialChildren: children, - ); + FolderRoute.name, + args: FolderRouteArgs(key: key, folder: folder), + initialChildren: children, + ); static const String name = 'FolderRoute'; @@ -754,16 +754,16 @@ class GalleryViewerRoute extends PageRouteInfo { bool showStack = false, List? children, }) : super( - GalleryViewerRoute.name, - args: GalleryViewerRouteArgs( - key: key, - renderList: renderList, - initialIndex: initialIndex, - heroOffset: heroOffset, - showStack: showStack, - ), - initialChildren: children, - ); + GalleryViewerRoute.name, + args: GalleryViewerRouteArgs( + key: key, + renderList: renderList, + initialIndex: initialIndex, + heroOffset: heroOffset, + showStack: showStack, + ), + initialChildren: children, + ); static const String name = 'GalleryViewerRoute'; @@ -811,7 +811,7 @@ class GalleryViewerRouteArgs { /// [HeaderSettingsPage] class HeaderSettingsRoute extends PageRouteInfo { const HeaderSettingsRoute({List? children}) - : super(HeaderSettingsRoute.name, initialChildren: children); + : super(HeaderSettingsRoute.name, initialChildren: children); static const String name = 'HeaderSettingsRoute'; @@ -827,7 +827,7 @@ class HeaderSettingsRoute extends PageRouteInfo { /// [LibraryPage] class LibraryRoute extends PageRouteInfo { const LibraryRoute({List? children}) - : super(LibraryRoute.name, initialChildren: children); + : super(LibraryRoute.name, initialChildren: children); static const String name = 'LibraryRoute'; @@ -843,7 +843,7 @@ class LibraryRoute extends PageRouteInfo { /// [LocalAlbumsPage] class LocalAlbumsRoute extends PageRouteInfo { const LocalAlbumsRoute({List? children}) - : super(LocalAlbumsRoute.name, initialChildren: children); + : super(LocalAlbumsRoute.name, initialChildren: children); static const String name = 'LocalAlbumsRoute'; @@ -859,7 +859,7 @@ class LocalAlbumsRoute extends PageRouteInfo { /// [LocalMediaSummaryPage] class LocalMediaSummaryRoute extends PageRouteInfo { const LocalMediaSummaryRoute({List? children}) - : super(LocalMediaSummaryRoute.name, initialChildren: children); + : super(LocalMediaSummaryRoute.name, initialChildren: children); static const String name = 'LocalMediaSummaryRoute'; @@ -879,10 +879,10 @@ class LocalTimelineRoute extends PageRouteInfo { required String albumId, List? children, }) : super( - LocalTimelineRoute.name, - args: LocalTimelineRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + LocalTimelineRoute.name, + args: LocalTimelineRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'LocalTimelineRoute'; @@ -912,7 +912,7 @@ class LocalTimelineRouteArgs { /// [LockedPage] class LockedRoute extends PageRouteInfo { const LockedRoute({List? children}) - : super(LockedRoute.name, initialChildren: children); + : super(LockedRoute.name, initialChildren: children); static const String name = 'LockedRoute'; @@ -928,7 +928,7 @@ class LockedRoute extends PageRouteInfo { /// [LoginPage] class LoginRoute extends PageRouteInfo { const LoginRoute({List? children}) - : super(LoginRoute.name, initialChildren: children); + : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; @@ -944,7 +944,7 @@ class LoginRoute extends PageRouteInfo { /// [MainTimelinePage] class MainTimelineRoute extends PageRouteInfo { const MainTimelineRoute({List? children}) - : super(MainTimelineRoute.name, initialChildren: children); + : super(MainTimelineRoute.name, initialChildren: children); static const String name = 'MainTimelineRoute'; @@ -964,13 +964,13 @@ class MapLocationPickerRoute extends PageRouteInfo { LatLng initialLatLng = const LatLng(0, 0), List? children, }) : super( - MapLocationPickerRoute.name, - args: MapLocationPickerRouteArgs( - key: key, - initialLatLng: initialLatLng, - ), - initialChildren: children, - ); + MapLocationPickerRoute.name, + args: MapLocationPickerRouteArgs( + key: key, + initialLatLng: initialLatLng, + ), + initialChildren: children, + ); static const String name = 'MapLocationPickerRoute'; @@ -1008,11 +1008,11 @@ class MapLocationPickerRouteArgs { /// [MapPage] class MapRoute extends PageRouteInfo { MapRoute({Key? key, LatLng? initialLocation, List? children}) - : super( - MapRoute.name, - args: MapRouteArgs(key: key, initialLocation: initialLocation), - initialChildren: children, - ); + : super( + MapRoute.name, + args: MapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); static const String name = 'MapRoute'; @@ -1049,14 +1049,14 @@ class MemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - MemoryRoute.name, - args: MemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + MemoryRoute.name, + args: MemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'MemoryRoute'; @@ -1103,16 +1103,16 @@ class NativeVideoViewerRoute extends PageRouteInfo { int playbackDelayFactor = 1, List? children, }) : super( - NativeVideoViewerRoute.name, - args: NativeVideoViewerRouteArgs( - key: key, - asset: asset, - image: image, - showControls: showControls, - playbackDelayFactor: playbackDelayFactor, - ), - initialChildren: children, - ); + NativeVideoViewerRoute.name, + args: NativeVideoViewerRouteArgs( + key: key, + asset: asset, + image: image, + showControls: showControls, + playbackDelayFactor: playbackDelayFactor, + ), + initialChildren: children, + ); static const String name = 'NativeVideoViewerRoute'; @@ -1164,10 +1164,10 @@ class PartnerDetailRoute extends PageRouteInfo { required UserDto partner, List? children, }) : super( - PartnerDetailRoute.name, - args: PartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + PartnerDetailRoute.name, + args: PartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'PartnerDetailRoute'; @@ -1197,7 +1197,7 @@ class PartnerDetailRouteArgs { /// [PartnerPage] class PartnerRoute extends PageRouteInfo { const PartnerRoute({List? children}) - : super(PartnerRoute.name, initialChildren: children); + : super(PartnerRoute.name, initialChildren: children); static const String name = 'PartnerRoute'; @@ -1213,7 +1213,7 @@ class PartnerRoute extends PageRouteInfo { /// [PeopleCollectionPage] class PeopleCollectionRoute extends PageRouteInfo { const PeopleCollectionRoute({List? children}) - : super(PeopleCollectionRoute.name, initialChildren: children); + : super(PeopleCollectionRoute.name, initialChildren: children); static const String name = 'PeopleCollectionRoute'; @@ -1229,7 +1229,7 @@ class PeopleCollectionRoute extends PageRouteInfo { /// [PermissionOnboardingPage] class PermissionOnboardingRoute extends PageRouteInfo { const PermissionOnboardingRoute({List? children}) - : super(PermissionOnboardingRoute.name, initialChildren: children); + : super(PermissionOnboardingRoute.name, initialChildren: children); static const String name = 'PermissionOnboardingRoute'; @@ -1250,14 +1250,14 @@ class PersonResultRoute extends PageRouteInfo { required String personName, List? children, }) : super( - PersonResultRoute.name, - args: PersonResultRouteArgs( - key: key, - personId: personId, - personName: personName, - ), - initialChildren: children, - ); + PersonResultRoute.name, + args: PersonResultRouteArgs( + key: key, + personId: personId, + personName: personName, + ), + initialChildren: children, + ); static const String name = 'PersonResultRoute'; @@ -1297,7 +1297,7 @@ class PersonResultRouteArgs { /// [PhotosPage] class PhotosRoute extends PageRouteInfo { const PhotosRoute({List? children}) - : super(PhotosRoute.name, initialChildren: children); + : super(PhotosRoute.name, initialChildren: children); static const String name = 'PhotosRoute'; @@ -1317,10 +1317,10 @@ class PinAuthRoute extends PageRouteInfo { bool createPinCode = false, List? children, }) : super( - PinAuthRoute.name, - args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), - initialChildren: children, - ); + PinAuthRoute.name, + args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), + initialChildren: children, + ); static const String name = 'PinAuthRoute'; @@ -1356,13 +1356,13 @@ class PlacesCollectionRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - PlacesCollectionRoute.name, - args: PlacesCollectionRouteArgs( - key: key, - currentLocation: currentLocation, - ), - initialChildren: children, - ); + PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), + initialChildren: children, + ); static const String name = 'PlacesCollectionRoute'; @@ -1397,7 +1397,7 @@ class PlacesCollectionRouteArgs { /// [RecentlyTakenPage] class RecentlyTakenRoute extends PageRouteInfo { const RecentlyTakenRoute({List? children}) - : super(RecentlyTakenRoute.name, initialChildren: children); + : super(RecentlyTakenRoute.name, initialChildren: children); static const String name = 'RecentlyTakenRoute'; @@ -1413,7 +1413,7 @@ class RecentlyTakenRoute extends PageRouteInfo { /// [RemoteMediaSummaryPage] class RemoteMediaSummaryRoute extends PageRouteInfo { const RemoteMediaSummaryRoute({List? children}) - : super(RemoteMediaSummaryRoute.name, initialChildren: children); + : super(RemoteMediaSummaryRoute.name, initialChildren: children); static const String name = 'RemoteMediaSummaryRoute'; @@ -1433,10 +1433,10 @@ class RemoteTimelineRoute extends PageRouteInfo { required String albumId, List? children, }) : super( - RemoteTimelineRoute.name, - args: RemoteTimelineRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + RemoteTimelineRoute.name, + args: RemoteTimelineRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'RemoteTimelineRoute'; @@ -1470,10 +1470,10 @@ class SearchRoute extends PageRouteInfo { SearchFilter? prefilter, List? children, }) : super( - SearchRoute.name, - args: SearchRouteArgs(key: key, prefilter: prefilter), - initialChildren: children, - ); + SearchRoute.name, + args: SearchRouteArgs(key: key, prefilter: prefilter), + initialChildren: children, + ); static const String name = 'SearchRoute'; @@ -1505,7 +1505,7 @@ class SearchRouteArgs { /// [SettingsPage] class SettingsRoute extends PageRouteInfo { const SettingsRoute({List? children}) - : super(SettingsRoute.name, initialChildren: children); + : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; @@ -1525,10 +1525,10 @@ class SettingsSubRoute extends PageRouteInfo { Key? key, List? children, }) : super( - SettingsSubRoute.name, - args: SettingsSubRouteArgs(section: section, key: key), - initialChildren: children, - ); + SettingsSubRoute.name, + args: SettingsSubRouteArgs(section: section, key: key), + initialChildren: children, + ); static const String name = 'SettingsSubRoute'; @@ -1562,10 +1562,10 @@ class ShareIntentRoute extends PageRouteInfo { required List attachments, List? children, }) : super( - ShareIntentRoute.name, - args: ShareIntentRouteArgs(key: key, attachments: attachments), - initialChildren: children, - ); + ShareIntentRoute.name, + args: ShareIntentRouteArgs(key: key, attachments: attachments), + initialChildren: children, + ); static const String name = 'ShareIntentRoute'; @@ -1601,15 +1601,15 @@ class SharedLinkEditRoute extends PageRouteInfo { String? albumId, List? children, }) : super( - SharedLinkEditRoute.name, - args: SharedLinkEditRouteArgs( - key: key, - existingLink: existingLink, - assetsList: assetsList, - albumId: albumId, - ), - initialChildren: children, - ); + SharedLinkEditRoute.name, + args: SharedLinkEditRouteArgs( + key: key, + existingLink: existingLink, + assetsList: assetsList, + albumId: albumId, + ), + initialChildren: children, + ); static const String name = 'SharedLinkEditRoute'; @@ -1655,7 +1655,7 @@ class SharedLinkEditRouteArgs { /// [SharedLinkPage] class SharedLinkRoute extends PageRouteInfo { const SharedLinkRoute({List? children}) - : super(SharedLinkRoute.name, initialChildren: children); + : super(SharedLinkRoute.name, initialChildren: children); static const String name = 'SharedLinkRoute'; @@ -1671,7 +1671,7 @@ class SharedLinkRoute extends PageRouteInfo { /// [SplashScreenPage] class SplashScreenRoute extends PageRouteInfo { const SplashScreenRoute({List? children}) - : super(SplashScreenRoute.name, initialChildren: children); + : super(SplashScreenRoute.name, initialChildren: children); static const String name = 'SplashScreenRoute'; @@ -1687,7 +1687,7 @@ class SplashScreenRoute extends PageRouteInfo { /// [TabControllerPage] class TabControllerRoute extends PageRouteInfo { const TabControllerRoute({List? children}) - : super(TabControllerRoute.name, initialChildren: children); + : super(TabControllerRoute.name, initialChildren: children); static const String name = 'TabControllerRoute'; @@ -1703,7 +1703,7 @@ class TabControllerRoute extends PageRouteInfo { /// [TabShellPage] class TabShellRoute extends PageRouteInfo { const TabShellRoute({List? children}) - : super(TabShellRoute.name, initialChildren: children); + : super(TabShellRoute.name, initialChildren: children); static const String name = 'TabShellRoute'; @@ -1719,7 +1719,7 @@ class TabShellRoute extends PageRouteInfo { /// [TrashPage] class TrashRoute extends PageRouteInfo { const TrashRoute({List? children}) - : super(TrashRoute.name, initialChildren: children); + : super(TrashRoute.name, initialChildren: children); static const String name = 'TrashRoute'; diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 26f3b49242..6e34f6b4ab 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -13,10 +13,12 @@ import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/log.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/isar_store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/log.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/drift_store.repository.dart'; import 'package:isar/isar.dart'; import 'package:path_provider/path_provider.dart'; @@ -48,6 +50,10 @@ abstract final class Bootstrap { ); } + static Future initDrift() async { + return Drift(); + } + static Future initDomain( Isar db, { bool shouldBufferLogs = true, @@ -59,4 +65,17 @@ abstract final class Bootstrap { shouldBuffer: shouldBufferLogs, ); } + + static Future initDomainWithDrift( + Drift db, { + bool shouldBufferLogs = true, + }) async { + await StoreService.init(storeRepository: DriftStoreRepository(db)); + final isarDb = await initIsar(); + await LogService.init( + logRepository: IsarLogRepository(isarDb), + storeRepository: DriftStoreRepository(db), + shouldBuffer: shouldBufferLogs, + ); + } } diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 3ec8ce5bbc..984261a9a0 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -14,8 +14,8 @@ import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/isar_store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; -import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/utils/diff.dart';