refactor(mobile): split store into repo and service (#16199)

* refactor(mobile): migrate store

* refactor(mobile): expand abbreviations

* chore(mobile): fix lint

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
shenlong
2025-02-20 00:35:24 +05:30
committed by GitHub
parent 8634c59850
commit aeb3e0a84f
33 changed files with 582 additions and 287 deletions
@@ -0,0 +1,19 @@
import 'dart:async';
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
import 'package:isar/isar.dart';
// #zoneTxn is the symbol used by Isar to mark a transaction within the current zone
// ref: isar/isar_common.dart
const Symbol _kzoneTxn = #zoneTxn;
class IsarDatabaseRepository implements IDatabaseRepository {
final Isar _db;
const IsarDatabaseRepository(Isar db) : _db = db;
// Isar do not support nested transactions. This is a workaround to prevent us from making nested transactions
// Reuse the current transaction if it is already active, else start a new transaction
@override
Future<T> transaction<T>(Future<T> Function() callback) =>
Zone.current[_kzoneTxn] == null ? _db.writeTxn(callback) : callback();
}
@@ -0,0 +1,107 @@
import 'package:immich_mobile/domain/interfaces/store.interface.dart';
import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/repositories/user.repository.dart';
import 'package:isar/isar.dart';
class IsarStoreRepository extends IsarDatabaseRepository
implements IStoreRepository {
final Isar _db;
const IsarStoreRepository(super.db) : _db = db;
@override
Future<bool> deleteAll() async {
return await transaction(() async {
await _db.storeValues.clear();
return true;
});
}
@override
Stream<StoreUpdateEvent> watchAll() {
return _db.storeValues.where().watch().asyncExpand(
(entities) =>
Stream.fromFutures(entities.map((e) async => _toUpdateEvent(e))),
);
}
@override
Future<void> delete<T>(StoreKey<T> key) async {
return await transaction(() async => await _db.storeValues.delete(key.id));
}
@override
Future<bool> insert<T>(StoreKey<T> key, T value) async {
return await transaction(() async {
await _db.storeValues.put(await _fromValue(key, value));
return true;
});
}
@override
Future<T?> tryGet<T>(StoreKey<T> key) async {
final entity = (await _db.storeValues.get(key.id));
if (entity == null) {
return null;
}
return await _toValue(key, entity);
}
@override
Future<bool> update<T>(StoreKey<T> key, T value) async {
return await transaction(() async {
await _db.storeValues.put(await _fromValue(key, value));
return true;
});
}
@override
Stream<T?> watch<T>(StoreKey<T> key) async* {
yield* _db.storeValues
.watchObject(key.id, fireImmediately: true)
.asyncMap((e) async => e == null ? null : await _toValue(key, e));
}
Future<StoreUpdateEvent> _toUpdateEvent(StoreValue entity) async {
final key = StoreKey.values.firstWhere((e) => e.id == entity.id);
final value = await _toValue(key, entity);
return StoreUpdateEvent(key, value);
}
Future<T?> _toValue<T>(StoreKey<T> key, StoreValue 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 (User) => await UserRepository(_db).getByDbId(entity.intValue!),
_ => null,
} as T?;
Future<StoreValue> _fromValue<T>(StoreKey<T> 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 (User) => (
(await UserRepository(_db).update(value as User)).isarId,
null
),
_ => throw UnsupportedError(
"Unsupported primitive type: ${key.type} for key: ${key.name}",
),
};
return StoreValue(key.id, intValue: intValue, strValue: strValue);
}
}