mirror of
https://github.com/immich-app/immich.git
synced 2026-06-22 14:52:17 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e716448148 |
@@ -1,10 +1,10 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/app_metadata_key.dart';
|
||||
import 'package:immich_mobile/domain/models/session.model.dart';
|
||||
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/main.dart' as app;
|
||||
@@ -34,14 +34,14 @@ void main() {
|
||||
server = await FakeImmichServer.start();
|
||||
await ApiService().resolveAndSetEndpoint(server.endpoint);
|
||||
await drift.delete(drift.userEntity).go();
|
||||
await Store.delete(StoreKey.legacySyncMigrationStatus);
|
||||
await (drift.appMetadataEntity.delete()..where((t) => t.key.equals(AppMetadataKey.syncMigrationStatus.name))).go();
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await workerManagerPatch.dispose();
|
||||
await server.close();
|
||||
await Store.delete(StoreKey.legacyServerEndpoint);
|
||||
await Store.delete(StoreKey.legacySyncMigrationStatus);
|
||||
await (drift.sessionEntity.delete()..where((t) => t.key.equals(SessionKey.serverEndpoint.name))).go();
|
||||
await (drift.appMetadataEntity.delete()..where((t) => t.key.equals(AppMetadataKey.syncMigrationStatus.name))).go();
|
||||
});
|
||||
|
||||
void sendUser(SyncStream stream, String id, String name) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'dart:async';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/main.dart' as app;
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:immich_mobile/utils/bootstrap.dart';
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/// Key for each possible value in the `Store`.
|
||||
/// Defines the data type for each value
|
||||
enum StoreKey<T> {
|
||||
deviceId<String>._(4),
|
||||
|
||||
// Legacy keys that have been migrated to the new metadata store
|
||||
legacyVersion<int>._(0),
|
||||
legacyManageLocalMediaAndroid<bool>._(137),
|
||||
legacySyncMigrationStatus<String>._(1013),
|
||||
legacyAdvancedTroubleshooting<bool>._(114),
|
||||
legacyEnableHapticFeedback<bool>._(126),
|
||||
legacyReadonlyModeEnabled<bool>._(138),
|
||||
legacyServerUrl<String>._(10),
|
||||
legacyAccessToken<String>._(11),
|
||||
legacyServerEndpoint<String>._(12),
|
||||
legacyBackupRequireCharging<bool>._(7),
|
||||
legacyBackupTriggerDelay<int>._(8),
|
||||
legacySyncAlbums<bool>._(131),
|
||||
legacyEnableBackup<bool>._(1003),
|
||||
legacyUseWifiForUploadVideos<bool>._(1004),
|
||||
legacyUseWifiForUploadPhotos<bool>._(1005),
|
||||
legacySelectedAlbumSortOrder<int>._(113),
|
||||
legacySelectedAlbumSortReverse<bool>._(123),
|
||||
legacyAlbumGridView<bool>._(140),
|
||||
legacyAutoEndpointSwitching<bool>._(132),
|
||||
legacyPreferredWifiName<String>._(133),
|
||||
legacyLocalEndpoint<String>._(134),
|
||||
legacyExternalEndpointList<String>._(135),
|
||||
legacyCustomHeaders<String>._(127),
|
||||
legacyLoopVideo<bool>._(117),
|
||||
legacyLoadOriginalVideo<bool>._(136),
|
||||
legacyAutoPlayVideo<bool>._(139),
|
||||
legacyTapToNavigate<bool>._(141),
|
||||
legacyPreferRemoteImage<bool>._(116),
|
||||
legacyLoadOriginal<bool>._(101),
|
||||
legacyPrimaryColor<String>._(128),
|
||||
legacyDynamicTheme<bool>._(129),
|
||||
legacyColorfulInterface<bool>._(130),
|
||||
legacyThemeMode<String>._(102),
|
||||
legacyCleanupKeepFavorites<bool>._(1008),
|
||||
legacyCleanupKeepMediaType<int>._(1009),
|
||||
legacyCleanupKeepAlbumIds<String>._(1010),
|
||||
legacyCleanupCutoffDaysAgo<int>._(1011),
|
||||
legacyCleanupDefaultsInitialized<bool>._(1012),
|
||||
legacyTilesPerRow<int>._(103),
|
||||
legacyGroupAssetsBy<int>._(105),
|
||||
legacyStorageIndicator<bool>._(109),
|
||||
legacyMapRelativeDate<int>._(119),
|
||||
legacyMapShowFavoriteOnly<bool>._(118),
|
||||
legacyMapIncludeArchived<bool>._(121),
|
||||
legacyMapThemeMode<int>._(124),
|
||||
legacyMapwithPartners<bool>._(125),
|
||||
legacyLogLevel<int>._(115);
|
||||
|
||||
const StoreKey._(this.id);
|
||||
final int id;
|
||||
Type get type => T;
|
||||
}
|
||||
|
||||
class StoreDto<T> {
|
||||
final StoreKey<T> key;
|
||||
final T? value;
|
||||
|
||||
const StoreDto(this.key, this.value);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '''
|
||||
StoreDto: {
|
||||
key: $key,
|
||||
value: ${value ?? '<NA>'},
|
||||
}''';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant StoreDto<T> other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other.key == key && other.value == value;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ value.hashCode;
|
||||
}
|
||||
@@ -7,11 +7,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/platform/background_worker_api.g.dart';
|
||||
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
|
||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||
@@ -314,6 +314,6 @@ Future<void> backgroundSyncNativeEntrypoint() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
|
||||
final (drift, logDB) = await Bootstrap.initDomain(shouldBufferLogs: false, listenStoreUpdates: false);
|
||||
final (drift, logDB) = await Bootstrap.initDomain(shouldBufferLogs: false);
|
||||
await BackgroundWorkerBgService(drift: drift, driftLogger: logDB).init();
|
||||
}
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/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 DriftStoreRepository _storeRepository;
|
||||
|
||||
/// In-memory cache. Keys are [StoreKey.id]
|
||||
final Map<int, Object?> _cache = {};
|
||||
StreamSubscription<List<StoreDto>>? _storeUpdateSubscription;
|
||||
|
||||
StoreService._({required DriftStoreRepository isarStoreRepository}) : _storeRepository = isarStoreRepository;
|
||||
|
||||
// TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider
|
||||
static StoreService? _instance;
|
||||
static StoreService get I {
|
||||
if (_instance == null) {
|
||||
throw UnsupportedError("StoreService not initialized. Call init() first");
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
// TODO: Replace the implementation with the one from create after removing the typedef
|
||||
static Future<StoreService> init({required DriftStoreRepository storeRepository, bool listenUpdates = true}) async {
|
||||
_instance ??= await create(storeRepository: storeRepository, listenUpdates: listenUpdates);
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
static Future<StoreService> create({required DriftStoreRepository storeRepository, bool listenUpdates = true}) async {
|
||||
final instance = StoreService._(isarStoreRepository: storeRepository);
|
||||
await instance.populateCache();
|
||||
if (listenUpdates) {
|
||||
instance._storeUpdateSubscription = instance._listenForChange();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
Future<void> populateCache() async {
|
||||
final storeValues = await _storeRepository.getAll();
|
||||
for (StoreDto storeValue in storeValues) {
|
||||
_cache[storeValue.key.id] = storeValue.value;
|
||||
}
|
||||
}
|
||||
|
||||
StreamSubscription<List<StoreDto>> _listenForChange() => _storeRepository.watchAll().listen((events) {
|
||||
for (final event in events) {
|
||||
_cache[event.key.id] = event.value;
|
||||
}
|
||||
});
|
||||
|
||||
/// Disposes the store and cancels the subscription. To reuse the store call init() again
|
||||
Future<void> dispose() async {
|
||||
await _storeUpdateSubscription?.cancel();
|
||||
_storeUpdateSubscription = null;
|
||||
_cache.clear();
|
||||
// Allow a subsequent init() (e.g. when a worker isolate is reused) to
|
||||
// create a fresh instance instead of returning this disposed one.
|
||||
if (identical(_instance, this)) {
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the cached value for [key], or `null`
|
||||
T? tryGet<T>(StoreKey<T> key) => _cache[key.id] as T?;
|
||||
|
||||
/// Returns the stored value for [key] or [defaultValue].
|
||||
/// Throws [StoreKeyNotFoundException] if value and [defaultValue] are null.
|
||||
T get<T>(StoreKey<T> key, [T? defaultValue]) {
|
||||
final value = tryGet(key) ?? defaultValue;
|
||||
if (value == null) {
|
||||
throw StoreKeyNotFoundException(key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Stores the [value] for the [key]. Skips write if value hasn't changed.
|
||||
Future<void> put<U extends StoreKey<T>, T>(U key, T value) async {
|
||||
if (_cache[key.id] == value) {
|
||||
return;
|
||||
}
|
||||
await _storeRepository.upsert(key, value);
|
||||
_cache[key.id] = value;
|
||||
}
|
||||
|
||||
/// Returns a stream that emits the value for [key] on change.
|
||||
Stream<T?> watch<T>(StoreKey<T> key) => _storeRepository.watch(key);
|
||||
|
||||
/// Removes the value for [key]
|
||||
Future<void> delete<T>(StoreKey<T> key) async {
|
||||
await _storeRepository.delete(key);
|
||||
_cache.remove(key.id);
|
||||
}
|
||||
|
||||
/// Clears all values from the store (cache and DB)
|
||||
Future<void> clear() async {
|
||||
await _storeRepository.deleteAll();
|
||||
_cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class StoreKeyNotFoundException implements Exception {
|
||||
final StoreKey key;
|
||||
const StoreKeyNotFoundException(this.key);
|
||||
|
||||
@override
|
||||
String toString() => "Key - <${key.name}> not available in Store";
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
final Store = StoreService.I;
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/app_metadata_key.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/app_metadata.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
@@ -20,8 +19,8 @@ class AppMetadataRepository {
|
||||
.insertOnConflictUpdate(
|
||||
AppMetadataEntityCompanion.insert(
|
||||
key: key.name,
|
||||
value: Value(key.encode(value)),
|
||||
updatedAt: Value(DateTime.now()),
|
||||
value: .new(key.encode(value)),
|
||||
updatedAt: .new(DateTime.now()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
|
||||
class DriftStoreRepository extends DriftDatabaseRepository {
|
||||
final Drift _db;
|
||||
final validStoreKeys = StoreKey.values.map((e) => e.id).toSet();
|
||||
|
||||
DriftStoreRepository(super.db) : _db = db;
|
||||
|
||||
Future<bool> deleteAll() async {
|
||||
await _db.storeEntity.deleteAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<List<StoreDto<Object>>> getAll() async {
|
||||
final query = _db.storeEntity.select()..where((entity) => entity.id.isIn(validStoreKeys));
|
||||
return query.asyncMap((entity) => _toUpdateEvent(entity)).get();
|
||||
}
|
||||
|
||||
Stream<List<StoreDto<Object>>> watchAll() {
|
||||
final query = _db.storeEntity.select()..where((entity) => entity.id.isIn(validStoreKeys));
|
||||
|
||||
return query.asyncMap((entity) => _toUpdateEvent(entity)).watch();
|
||||
}
|
||||
|
||||
Future<void> delete<T>(StoreKey<T> key) async {
|
||||
await _db.storeEntity.deleteWhere((entity) => entity.id.equals(key.id));
|
||||
return;
|
||||
}
|
||||
|
||||
Future<bool> upsert<T>(StoreKey<T> key, T value) async {
|
||||
await _db.storeEntity.insertOnConflictUpdate(await _fromValue(key, value));
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<T?> tryGet<T>(StoreKey<T> key) async {
|
||||
final entity = await _db.managers.storeEntity.filter((entity) => entity.id.equals(key.id)).getSingleOrNull();
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
return await _toValue(key, entity);
|
||||
}
|
||||
|
||||
Stream<T?> watch<T>(StoreKey<T> key) async* {
|
||||
final query = _db.storeEntity.select()..where((entity) => entity.id.equals(key.id));
|
||||
|
||||
yield* query.watchSingleOrNull().asyncMap((e) async => e == null ? null : await _toValue(key, e));
|
||||
}
|
||||
|
||||
Future<StoreDto<Object>> _toUpdateEvent(StoreEntityData entity) async {
|
||||
final key = StoreKey.values.firstWhere((e) => e.id == entity.id) as StoreKey<Object>;
|
||||
final value = await _toValue(key, entity);
|
||||
return StoreDto(key, value);
|
||||
}
|
||||
|
||||
Future<T?> _toValue<T>(StoreKey<T> key, StoreEntityData entity) async =>
|
||||
switch (key.type) {
|
||||
const (int) => entity.intValue,
|
||||
const (String) => entity.stringValue,
|
||||
const (bool) => entity.intValue == 1,
|
||||
const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!),
|
||||
_ => null,
|
||||
}
|
||||
as T?;
|
||||
|
||||
Future<StoreEntityCompanion> _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),
|
||||
_ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"),
|
||||
};
|
||||
return StoreEntityCompanion(id: Value(key.id), intValue: Value(intValue), stringValue: Value(strValue));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
|
||||
enum LegacyStoreKey {
|
||||
deviceId(4),
|
||||
legacyVersion(0),
|
||||
legacyManageLocalMediaAndroid(137),
|
||||
legacySyncMigrationStatus(1013),
|
||||
legacyAdvancedTroubleshooting(114),
|
||||
legacyEnableHapticFeedback(126),
|
||||
legacyReadonlyModeEnabled(138),
|
||||
legacyServerUrl(10),
|
||||
legacyAccessToken(11),
|
||||
legacyServerEndpoint(12),
|
||||
legacyBackupRequireCharging(7),
|
||||
legacyBackupTriggerDelay(8),
|
||||
legacySyncAlbums(131),
|
||||
legacyEnableBackup(1003),
|
||||
legacyUseWifiForUploadVideos(1004),
|
||||
legacyUseWifiForUploadPhotos(1005),
|
||||
legacySelectedAlbumSortOrder(113),
|
||||
legacySelectedAlbumSortReverse(123),
|
||||
legacyAlbumGridView(140),
|
||||
legacyAutoEndpointSwitching(132),
|
||||
legacyPreferredWifiName(133),
|
||||
legacyLocalEndpoint(134),
|
||||
legacyExternalEndpointList(135),
|
||||
legacyCustomHeaders(127),
|
||||
legacyLoopVideo(117),
|
||||
legacyLoadOriginalVideo(136),
|
||||
legacyAutoPlayVideo(139),
|
||||
legacyTapToNavigate(141),
|
||||
legacyPreferRemoteImage(116),
|
||||
legacyLoadOriginal(101),
|
||||
legacyPrimaryColor(128),
|
||||
legacyDynamicTheme(129),
|
||||
legacyColorfulInterface(130),
|
||||
legacyThemeMode(102),
|
||||
legacyCleanupKeepFavorites(1008),
|
||||
legacyCleanupKeepMediaType(1009),
|
||||
legacyCleanupKeepAlbumIds(1010),
|
||||
legacyCleanupCutoffDaysAgo(1011),
|
||||
legacyCleanupDefaultsInitialized(1012),
|
||||
legacyTilesPerRow(103),
|
||||
legacyGroupAssetsBy(105),
|
||||
legacyStorageIndicator(109),
|
||||
legacyMapRelativeDate(119),
|
||||
legacyMapShowFavoriteOnly(118),
|
||||
legacyMapIncludeArchived(121),
|
||||
legacyMapThemeMode(124),
|
||||
legacyMapwithPartners(125),
|
||||
legacyLogLevel(115);
|
||||
|
||||
const LegacyStoreKey(this.id);
|
||||
|
||||
final int id;
|
||||
}
|
||||
|
||||
class DeviceIdStore {
|
||||
DeviceIdStore._(this._db);
|
||||
|
||||
final Drift _db;
|
||||
String? _deviceId;
|
||||
|
||||
static DeviceIdStore? _instance;
|
||||
static DeviceIdStore get I => _instance ?? (throw StateError('DeviceIdStore not initialized. Call init() first'));
|
||||
|
||||
static Future<DeviceIdStore> init(Drift db) async {
|
||||
final instance = DeviceIdStore._(db);
|
||||
final row = await (db.storeEntity.select()..where((t) => t.id.equals(LegacyStoreKey.deviceId.id)))
|
||||
.getSingleOrNull();
|
||||
instance._deviceId = row?.stringValue;
|
||||
return _instance = instance;
|
||||
}
|
||||
|
||||
String? get deviceId => _deviceId;
|
||||
|
||||
String get requireDeviceId => _deviceId ?? (throw StateError('deviceId not set'));
|
||||
|
||||
Future<void> setDeviceId(String value) async {
|
||||
if (_deviceId == value) {
|
||||
return;
|
||||
}
|
||||
await _db.storeEntity.insertOnConflictUpdate(
|
||||
StoreEntityCompanion(id: Value(LegacyStoreKey.deviceId.id), stringValue: Value(value)),
|
||||
);
|
||||
_deviceId = value;
|
||||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
await _db.storeEntity.deleteAll();
|
||||
_deviceId = null;
|
||||
}
|
||||
|
||||
Future<void> dispose() async {
|
||||
_deviceId = null;
|
||||
if (identical(_instance, this)) {
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
DeviceIdStore get Store => DeviceIdStore.I;
|
||||
@@ -4,10 +4,9 @@ import 'package:flutter_udid/flutter_udid.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/session.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||
import 'package:immich_mobile/domain/services/user.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/models/auth/auth_state.model.dart';
|
||||
import 'package:immich_mobile/models/auth/login_response.model.dart';
|
||||
import 'package:immich_mobile/providers/api.provider.dart';
|
||||
@@ -136,7 +135,7 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
||||
await _widgetService.writeCredentials(serverEndpoint, accessToken, customHeaders);
|
||||
|
||||
// Get the deviceid from the store if it exists, otherwise generate a new one
|
||||
String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid;
|
||||
String deviceId = Store.deviceId ?? await FlutterUdid.consistentUdid;
|
||||
|
||||
UserDto? user = await _userService.tryGetMyUser();
|
||||
|
||||
@@ -148,7 +147,7 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
||||
// If the user information is successfully retrieved, update the store
|
||||
// Due to the flow of the code, this will always happen on first login
|
||||
user = serverUser;
|
||||
await Store.put(StoreKey.deviceId, deviceId);
|
||||
await Store.setDeviceId(deviceId);
|
||||
}
|
||||
} on ApiException catch (error, stackTrace) {
|
||||
if (error.code == 401) {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
|
||||
final storeServiceProvider = Provider((_) => StoreService.I);
|
||||
@@ -8,14 +8,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/session.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||
@@ -390,7 +389,7 @@ class BackgroundUploadService {
|
||||
final serverEndpoint = SessionRepository.instance.session.serverEndpoint!;
|
||||
final url = Uri.parse('$serverEndpoint/assets').toString();
|
||||
final headers = ApiService.getRequestHeaders();
|
||||
final deviceId = Store.get(StoreKey.deviceId);
|
||||
final deviceId = Store.requireDeviceId;
|
||||
final (baseDirectory, directory, filename) = await Task.split(filePath: file.path);
|
||||
final fieldsMap = {
|
||||
'filename': originalFileName ?? filename,
|
||||
|
||||
@@ -5,14 +5,13 @@ import 'dart:io';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/extensions/network_capability_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/platform/connectivity_api.g.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
||||
@@ -317,7 +316,7 @@ class ForegroundUploadService {
|
||||
}
|
||||
|
||||
final originalFileName = entity.isLivePhoto ? p.setExtension(fileName, p.extension(file.path)) : fileName;
|
||||
final deviceId = Store.get(StoreKey.deviceId);
|
||||
final deviceId = Store.requireDeviceId;
|
||||
|
||||
final fields = {
|
||||
// deviceAssetId/deviceId required by server v2.7.5 and below (drop in v4.0 per #27818).
|
||||
@@ -430,7 +429,7 @@ class ForegroundUploadService {
|
||||
final fields = {
|
||||
// deviceAssetId/deviceId required by server v2.7.5 and below (drop in v4.0 per #27818).
|
||||
'deviceAssetId': deviceAssetId,
|
||||
'deviceId': Store.get(StoreKey.deviceId),
|
||||
'deviceId': Store.requireDeviceId,
|
||||
'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(),
|
||||
'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(),
|
||||
'isFavorite': 'false',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:background_downloader/background_downloader.dart';
|
||||
import 'package:immich_mobile/constants/constants.dart';
|
||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
|
||||
@@ -9,7 +8,7 @@ import 'package:immich_mobile/infrastructure/repositories/logger_db.repository.d
|
||||
import 'package:immich_mobile/infrastructure/repositories/session.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
|
||||
void configureFileDownloaderNotifications() {
|
||||
@@ -43,14 +42,13 @@ void configureFileDownloaderNotifications() {
|
||||
}
|
||||
|
||||
abstract final class Bootstrap {
|
||||
static Future<(Drift, DriftLogger)> initDomain({bool listenStoreUpdates = true, bool shouldBufferLogs = true}) async {
|
||||
static Future<(Drift, DriftLogger)> initDomain({bool shouldBufferLogs = true}) async {
|
||||
await configureSqliteCache();
|
||||
final (db, updatePool) = await openSqliteConnectionWithUpdatePool(name: 'immich');
|
||||
final drift = Drift.sqlite(db, updatePool);
|
||||
final logDb = DriftLogger.sqlite(await openSqliteConnection(name: 'immich_logs'));
|
||||
final DriftStoreRepository storeRepo = DriftStoreRepository(drift);
|
||||
|
||||
await StoreService.init(storeRepository: storeRepo, listenUpdates: listenStoreUpdates);
|
||||
await DeviceIdStore.init(drift);
|
||||
|
||||
await SessionRepository.ensureInitialized(drift);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'dart:ui';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart';
|
||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||
import 'package:immich_mobile/utils/bootstrap.dart';
|
||||
@@ -34,7 +34,7 @@ Cancelable<T?> runInIsolateGentle<T>({
|
||||
DartPluginRegistrant.ensureInitialized();
|
||||
|
||||
final log = Logger("IsolateLogger");
|
||||
final (drift, logDb) = await Bootstrap.initDomain(shouldBufferLogs: false, listenStoreUpdates: false);
|
||||
final (drift, logDb) = await Bootstrap.initDomain(shouldBufferLogs: false);
|
||||
final ref = ProviderContainer(
|
||||
overrides: [cancellationProvider.overrideWithValue(onCancel), driftProvider.overrideWith(driftOverride(drift))],
|
||||
);
|
||||
|
||||
@@ -11,9 +11,7 @@ import 'package:immich_mobile/domain/models/config/app_config.dart';
|
||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||
import 'package:immich_mobile/domain/models/session.model.dart';
|
||||
import 'package:immich_mobile/domain/models/settings_key.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/models/timeline.model.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/app_metadata.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/session.entity.drift.dart';
|
||||
import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart';
|
||||
@@ -22,54 +20,59 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/session.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
||||
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
|
||||
|
||||
Future<void> migrateDatabaseIfNeeded(Drift drift) async {
|
||||
final metadataRepository = AppMetadataRepository(drift);
|
||||
|
||||
final int version = await metadataRepository.get(AppMetadataKey.version);
|
||||
|
||||
if (version < 25) {
|
||||
await _migrateTo25();
|
||||
}
|
||||
|
||||
if (version < 26) {
|
||||
await _migrateTo26(drift);
|
||||
}
|
||||
|
||||
if (version < 27) {
|
||||
await _migrateTo27(drift);
|
||||
}
|
||||
|
||||
if (version < 28) {
|
||||
await _migrateTo28(drift);
|
||||
}
|
||||
|
||||
if (version < 29) {
|
||||
await _migrateTo29(drift);
|
||||
final legacyStore = await _readLegacyStore(drift);
|
||||
|
||||
if (version < 25) {
|
||||
await _migrateTo25(drift, legacyStore);
|
||||
}
|
||||
if (version < 26) {
|
||||
await _migrateTo26(drift, legacyStore);
|
||||
}
|
||||
if (version < 27) {
|
||||
await _migrateTo27(drift, legacyStore);
|
||||
}
|
||||
if (version < 28) {
|
||||
await _migrateTo28(drift, legacyStore);
|
||||
}
|
||||
await _migrateTo29(drift, legacyStore);
|
||||
}
|
||||
|
||||
await metadataRepository.set(AppMetadataKey.version, kCurrentVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> _migrateTo25() async {
|
||||
final accessToken = Store.tryGet(.legacyAccessToken);
|
||||
Future<Map<int, Object?>> _readLegacyStore(Drift drift) async {
|
||||
final rows = await drift.storeEntity.select().get();
|
||||
return {for (final row in rows) row.id: row.stringValue ?? row.intValue};
|
||||
}
|
||||
|
||||
Future<void> _migrateTo25(Drift drift, Map<int, Object?> legacyStore) async {
|
||||
final migrator = _StoreMigrator.settings(drift, legacyStore);
|
||||
|
||||
final accessToken = migrator.readLegacyStoreString(.legacyAccessToken);
|
||||
if (accessToken == null || accessToken.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final urls = <String>[];
|
||||
final serverEndpoint = Store.tryGet(.legacyServerEndpoint);
|
||||
final serverEndpoint = migrator.readLegacyStoreString(.legacyServerEndpoint);
|
||||
if (serverEndpoint != null && serverEndpoint.isNotEmpty) {
|
||||
urls.add(serverEndpoint);
|
||||
}
|
||||
final localEndpoint = Store.tryGet(.legacyLocalEndpoint);
|
||||
final localEndpoint = migrator.readLegacyStoreString(.legacyLocalEndpoint);
|
||||
if (localEndpoint != null && localEndpoint.isNotEmpty) {
|
||||
urls.add(localEndpoint);
|
||||
}
|
||||
final externalJson = Store.tryGet(.legacyExternalEndpointList);
|
||||
final externalJson = migrator.readLegacyStoreString(.legacyExternalEndpointList);
|
||||
if (externalJson != null) {
|
||||
final List<dynamic> list = jsonDecode(externalJson);
|
||||
for (final entry in list) {
|
||||
@@ -83,7 +86,7 @@ Future<void> _migrateTo25() async {
|
||||
return;
|
||||
}
|
||||
|
||||
final customHeadersStr = Store.get(.legacyCustomHeaders, "");
|
||||
final customHeadersStr = migrator.readLegacyStoreString(.legacyCustomHeaders) ?? "";
|
||||
final headers = customHeadersStr.isEmpty
|
||||
? const <String, String>{}
|
||||
: (jsonDecode(customHeadersStr) as Map).cast<String, String>();
|
||||
@@ -91,8 +94,8 @@ Future<void> _migrateTo25() async {
|
||||
await NetworkRepository.setHeaders(headers, urls, token: accessToken);
|
||||
}
|
||||
|
||||
Future<void> _migrateTo26(Drift drift) async {
|
||||
final migrator = _StoreMigrator.settings(drift);
|
||||
Future<void> _migrateTo26(Drift drift, Map<int, Object?> legacyStore) async {
|
||||
final migrator = _StoreMigrator.settings(drift, legacyStore);
|
||||
await migrator.migrateEnumIndex(.legacyLogLevel, .logLevel, LogLevel.values);
|
||||
// Theme
|
||||
await migrator.migrateEnumName(.legacyThemeMode, .themeMode, ThemeMode.values);
|
||||
@@ -100,7 +103,7 @@ Future<void> _migrateTo26(Drift drift) async {
|
||||
await migrator.migrateBool(.legacyDynamicTheme, .themeDynamic);
|
||||
await migrator.migrateBool(.legacyColorfulInterface, .themeColorfulInterface);
|
||||
// Cleanup
|
||||
final cleanupKeepAlbumIds = await migrator.readLegacyStoreString(.legacyCleanupKeepAlbumIds);
|
||||
final cleanupKeepAlbumIds = migrator.readLegacyStoreString(.legacyCleanupKeepAlbumIds);
|
||||
if (cleanupKeepAlbumIds != null) {
|
||||
final ids = cleanupKeepAlbumIds.split(',').where((id) => id.isNotEmpty).toList();
|
||||
migrator.stage(.legacyCleanupKeepAlbumIds, .cleanupKeepAlbumIds, ids);
|
||||
@@ -129,9 +132,9 @@ Future<void> _migrateTo26(Drift drift) async {
|
||||
await migrator.migrateBool(.legacyTapToNavigate, .viewerTapToNavigate);
|
||||
// Network
|
||||
await migrator.migrateBool(.legacyAutoEndpointSwitching, .networkAutoEndpointSwitching);
|
||||
final preferredWifiName = await migrator.readLegacyStoreString(.legacyPreferredWifiName);
|
||||
final preferredWifiName = migrator.readLegacyStoreString(.legacyPreferredWifiName);
|
||||
migrator.stage(.legacyPreferredWifiName, .networkPreferredWifiName, preferredWifiName);
|
||||
final localEndpoint = await migrator.readLegacyStoreString(.legacyLocalEndpoint);
|
||||
final localEndpoint = migrator.readLegacyStoreString(.legacyLocalEndpoint);
|
||||
migrator.stage(.legacyLocalEndpoint, .networkLocalEndpoint, localEndpoint);
|
||||
await _migrateExternalEndpointList(migrator);
|
||||
await _migrateCustomHeaders(migrator);
|
||||
@@ -149,8 +152,8 @@ Future<void> _migrateTo26(Drift drift) async {
|
||||
await migrator.complete();
|
||||
}
|
||||
|
||||
Future<void> _migrateTo27(Drift drift) async {
|
||||
final migrator = _StoreMigrator.session(drift);
|
||||
Future<void> _migrateTo27(Drift drift, Map<int, Object?> legacyStore) async {
|
||||
final migrator = _StoreMigrator.session(drift, legacyStore);
|
||||
await migrator.migrateString(.legacyServerUrl, .serverUrl);
|
||||
await migrator.migrateString(.legacyAccessToken, .accessToken);
|
||||
await migrator.migrateString(.legacyServerEndpoint, .serverEndpoint);
|
||||
@@ -159,8 +162,8 @@ Future<void> _migrateTo27(Drift drift) async {
|
||||
await SessionRepository.instance.refresh();
|
||||
}
|
||||
|
||||
Future<void> _migrateTo28(Drift drift) async {
|
||||
final migrator = _StoreMigrator.settings(drift);
|
||||
Future<void> _migrateTo28(Drift drift, Map<int, Object?> legacyStore) async {
|
||||
final migrator = _StoreMigrator.settings(drift, legacyStore);
|
||||
await migrator.migrateBool(.legacyAdvancedTroubleshooting, .advancedTroubleshooting);
|
||||
await migrator.migrateBool(.legacyEnableHapticFeedback, .advancedEnableHapticFeedback);
|
||||
await migrator.migrateBool(.legacyReadonlyModeEnabled, .advancedReadonlyModeEnabled);
|
||||
@@ -169,10 +172,10 @@ Future<void> _migrateTo28(Drift drift) async {
|
||||
await SettingsRepository.instance.refresh();
|
||||
}
|
||||
|
||||
Future<void> _migrateTo29(Drift drift) async {
|
||||
final migrator = _StoreMigrator.appMetadata(drift);
|
||||
Future<void> _migrateTo29(Drift drift, Map<int, Object?> legacyStore) async {
|
||||
final migrator = _StoreMigrator.appMetadata(drift, legacyStore);
|
||||
|
||||
final rawStatus = await migrator.readLegacyStoreString(.legacySyncMigrationStatus);
|
||||
final rawStatus = migrator.readLegacyStoreString(.legacySyncMigrationStatus);
|
||||
if (rawStatus != null) {
|
||||
final decoded = jsonDecode(rawStatus);
|
||||
final migrations = decoded is List ? decoded.whereType<String>().toList() : <String>[];
|
||||
@@ -184,7 +187,7 @@ Future<void> _migrateTo29(Drift drift) async {
|
||||
}
|
||||
|
||||
Future<void> _migrateAlbumSortMode(_StoreMigrator<SettingsKey> migrator) async {
|
||||
final raw = await migrator.readLegacyStoreInt(.legacySelectedAlbumSortOrder);
|
||||
final raw = migrator.readLegacyStoreInt(.legacySelectedAlbumSortOrder);
|
||||
final mode = AlbumSortMode.values.firstWhereOrNull((e) => raw != null && e.storeIndex == raw);
|
||||
if (mode == null) {
|
||||
return;
|
||||
@@ -194,7 +197,7 @@ Future<void> _migrateAlbumSortMode(_StoreMigrator<SettingsKey> migrator) async {
|
||||
}
|
||||
|
||||
Future<void> _migrateExternalEndpointList(_StoreMigrator<SettingsKey> migrator) async {
|
||||
final raw = await migrator.readLegacyStoreString(.legacyExternalEndpointList);
|
||||
final raw = migrator.readLegacyStoreString(.legacyExternalEndpointList);
|
||||
if (raw == null) {
|
||||
return;
|
||||
}
|
||||
@@ -214,11 +217,11 @@ Future<void> _migrateExternalEndpointList(_StoreMigrator<SettingsKey> migrator)
|
||||
// ignore invalid entries
|
||||
}
|
||||
|
||||
migrator.stage(StoreKey.legacyExternalEndpointList, SettingsKey.networkExternalEndpointList, urls);
|
||||
migrator.stage(.legacyExternalEndpointList, SettingsKey.networkExternalEndpointList, urls);
|
||||
}
|
||||
|
||||
Future<void> _migrateCustomHeaders(_StoreMigrator<SettingsKey> migrator) async {
|
||||
final raw = await migrator.readLegacyStoreString(.legacyCustomHeaders);
|
||||
final raw = migrator.readLegacyStoreString(.legacyCustomHeaders);
|
||||
if (raw == null) {
|
||||
return;
|
||||
}
|
||||
@@ -237,14 +240,21 @@ Future<void> _migrateCustomHeaders(_StoreMigrator<SettingsKey> migrator) async {
|
||||
// ignore invalid entries
|
||||
}
|
||||
|
||||
migrator.stage(StoreKey.legacyCustomHeaders, SettingsKey.networkCustomHeaders, headers);
|
||||
migrator.stage(.legacyCustomHeaders, SettingsKey.networkCustomHeaders, headers);
|
||||
}
|
||||
|
||||
class _StoreMigrator<K extends Enum> {
|
||||
_StoreMigrator._(this._db, {required this.encode, required this.readDefault, required this.insertRow});
|
||||
_StoreMigrator._(
|
||||
this._db,
|
||||
this._legacyStore, {
|
||||
required this.encode,
|
||||
required this.readDefault,
|
||||
required this.insertRow,
|
||||
});
|
||||
|
||||
static _StoreMigrator<SettingsKey> settings(Drift db) => _StoreMigrator<SettingsKey>._(
|
||||
static _StoreMigrator<SettingsKey> settings(Drift db, Map<int, Object?> legacyStore) => _StoreMigrator<SettingsKey>._(
|
||||
db,
|
||||
legacyStore,
|
||||
encode: (key, value) => key.encode(value),
|
||||
readDefault: (key) => defaultConfig.read(key),
|
||||
insertRow: (batch, name, value) => batch.insert(
|
||||
@@ -254,8 +264,9 @@ class _StoreMigrator<K extends Enum> {
|
||||
),
|
||||
);
|
||||
|
||||
static _StoreMigrator<SessionKey> session(Drift db) => _StoreMigrator<SessionKey>._(
|
||||
static _StoreMigrator<SessionKey> session(Drift db, Map<int, Object?> legacyStore) => _StoreMigrator<SessionKey>._(
|
||||
db,
|
||||
legacyStore,
|
||||
encode: (key, value) => key.encode(value),
|
||||
readDefault: (key) => defaultSession.read(key),
|
||||
insertRow: (batch, name, value) => batch.insert(
|
||||
@@ -265,26 +276,29 @@ class _StoreMigrator<K extends Enum> {
|
||||
),
|
||||
);
|
||||
|
||||
static _StoreMigrator<AppMetadataKey> appMetadata(Drift db) => _StoreMigrator<AppMetadataKey>._(
|
||||
db,
|
||||
encode: (key, value) => key.encode(value),
|
||||
readDefault: (_) => null,
|
||||
insertRow: (batch, name, value) => batch.insert(
|
||||
db.appMetadataEntity,
|
||||
AppMetadataEntityCompanion(key: Value(name), value: Value(value)),
|
||||
mode: InsertMode.insertOrReplace,
|
||||
),
|
||||
);
|
||||
static _StoreMigrator<AppMetadataKey> appMetadata(Drift db, Map<int, Object?> legacyStore) =>
|
||||
_StoreMigrator<AppMetadataKey>._(
|
||||
db,
|
||||
legacyStore,
|
||||
encode: (key, value) => key.encode(value),
|
||||
readDefault: (_) => null,
|
||||
insertRow: (batch, name, value) => batch.insert(
|
||||
db.appMetadataEntity,
|
||||
AppMetadataEntityCompanion(key: Value(name), value: Value(value)),
|
||||
mode: InsertMode.insertOrReplace,
|
||||
),
|
||||
);
|
||||
|
||||
final Drift _db;
|
||||
final Map<int, Object?> _legacyStore;
|
||||
final String Function(K key, Object value) encode;
|
||||
final Object? Function(K key) readDefault;
|
||||
final void Function(Batch batch, String name, String? value) insertRow;
|
||||
final Map<K, Object?> _cache = {};
|
||||
final List<int> _migratedStoreIds = [];
|
||||
|
||||
Future<void> migrateEnumIndex<T extends Enum>(StoreKey<int> legacyKey, K newKey, List<T> values) async {
|
||||
final index = await readLegacyStoreInt(legacyKey);
|
||||
Future<void> migrateEnumIndex<T extends Enum>(LegacyStoreKey legacyKey, K newKey, List<T> values) async {
|
||||
final index = readLegacyStoreInt(legacyKey);
|
||||
if (index == null) {
|
||||
return;
|
||||
}
|
||||
@@ -298,8 +312,8 @@ class _StoreMigrator<K extends Enum> {
|
||||
_migratedStoreIds.add(legacyKey.id);
|
||||
}
|
||||
|
||||
Future<void> migrateEnumName<T extends Enum>(StoreKey<String> legacyKey, K newKey, List<T> values) async {
|
||||
final name = await readLegacyStoreString(legacyKey);
|
||||
Future<void> migrateEnumName<T extends Enum>(LegacyStoreKey legacyKey, K newKey, List<T> values) async {
|
||||
final name = readLegacyStoreString(legacyKey);
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
@@ -313,8 +327,8 @@ class _StoreMigrator<K extends Enum> {
|
||||
_migratedStoreIds.add(legacyKey.id);
|
||||
}
|
||||
|
||||
Future<void> migrateBool(StoreKey<bool> legacyKey, K newKey) async {
|
||||
final intValue = await readLegacyStoreInt(legacyKey);
|
||||
Future<void> migrateBool(LegacyStoreKey legacyKey, K newKey) async {
|
||||
final intValue = readLegacyStoreInt(legacyKey);
|
||||
if (intValue == null) {
|
||||
return;
|
||||
}
|
||||
@@ -323,8 +337,8 @@ class _StoreMigrator<K extends Enum> {
|
||||
_migratedStoreIds.add(legacyKey.id);
|
||||
}
|
||||
|
||||
Future<void> migrateInt(StoreKey<int> legacyKey, K newKey) async {
|
||||
final intValue = await readLegacyStoreInt(legacyKey);
|
||||
Future<void> migrateInt(LegacyStoreKey legacyKey, K newKey) async {
|
||||
final intValue = readLegacyStoreInt(legacyKey);
|
||||
if (intValue == null) {
|
||||
return;
|
||||
}
|
||||
@@ -333,8 +347,8 @@ class _StoreMigrator<K extends Enum> {
|
||||
_migratedStoreIds.add(legacyKey.id);
|
||||
}
|
||||
|
||||
Future<void> migrateString(StoreKey<String> legacyKey, K newKey) async {
|
||||
final value = await readLegacyStoreString(legacyKey);
|
||||
Future<void> migrateString(LegacyStoreKey legacyKey, K newKey) async {
|
||||
final value = readLegacyStoreString(legacyKey);
|
||||
if (value == null || value.isEmpty) {
|
||||
return;
|
||||
}
|
||||
@@ -343,12 +357,12 @@ class _StoreMigrator<K extends Enum> {
|
||||
_migratedStoreIds.add(legacyKey.id);
|
||||
}
|
||||
|
||||
Future<void> migrateNullableString(StoreKey<String> legacyKey, K newKey) async {
|
||||
_cache[newKey] = await readLegacyStoreString(legacyKey);
|
||||
Future<void> migrateNullableString(LegacyStoreKey legacyKey, K newKey) async {
|
||||
_cache[newKey] = readLegacyStoreString(legacyKey);
|
||||
_migratedStoreIds.add(legacyKey.id);
|
||||
}
|
||||
|
||||
void stage(StoreKey legacyKey, K newKey, Object? value) {
|
||||
void stage(LegacyStoreKey legacyKey, K newKey, Object? value) {
|
||||
_cache[newKey] = value;
|
||||
_migratedStoreIds.add(legacyKey.id);
|
||||
}
|
||||
@@ -367,15 +381,9 @@ class _StoreMigrator<K extends Enum> {
|
||||
await deleteLegacyStoreRows(_migratedStoreIds);
|
||||
}
|
||||
|
||||
Future<String?> readLegacyStoreString(StoreKey key) async {
|
||||
final row = await (_db.storeEntity.select()..where((t) => t.id.equals(key.id))).getSingleOrNull();
|
||||
return row?.stringValue;
|
||||
}
|
||||
String? readLegacyStoreString(LegacyStoreKey key) => _legacyStore[key.id] as String?;
|
||||
|
||||
Future<int?> readLegacyStoreInt(StoreKey key) async {
|
||||
final row = await (_db.storeEntity.select()..where((t) => t.id.equals(key.id))).getSingleOrNull();
|
||||
return row?.intValue;
|
||||
}
|
||||
int? readLegacyStoreInt(LegacyStoreKey key) => _legacyStore[key.id] as int?;
|
||||
|
||||
Future<void> deleteLegacyStoreRows(List<int> ids) async {
|
||||
if (ids.isEmpty) {
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import 'package:immich_mobile/domain/services/partner.service.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockStoreService extends Mock implements StoreService {}
|
||||
|
||||
class MockBackgroundSyncManager extends Mock implements BackgroundSyncManager {}
|
||||
|
||||
class MockNativeSyncApi extends Mock implements NativeSyncApi {}
|
||||
|
||||
@@ -5,12 +5,10 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/models/app_metadata_key.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/services/local_sync.service.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||
@@ -37,7 +35,7 @@ void main() {
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.android;
|
||||
|
||||
db = Drift(drift.DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
|
||||
await StoreService.init(storeRepository: DriftStoreRepository(db));
|
||||
await DeviceIdStore.init(db);
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import '../../infrastructure/repository.mock.dart';
|
||||
|
||||
const _kAccessToken = '#ThisIsAToken';
|
||||
const _kAdvancedTroubleshooting = false;
|
||||
const _kVersion = 2;
|
||||
|
||||
void main() {
|
||||
late StoreService sut;
|
||||
late DriftStoreRepository mockDriftStoreRepo;
|
||||
late StreamController<List<StoreDto<Object>>> controller;
|
||||
|
||||
setUp(() async {
|
||||
controller = StreamController<List<StoreDto<Object>>>.broadcast();
|
||||
mockDriftStoreRepo = MockDriftStoreRepository();
|
||||
// For generics, we need to provide fallback to each concrete type to avoid runtime errors
|
||||
registerFallbackValue(StoreKey.legacyAccessToken);
|
||||
registerFallbackValue(StoreKey.legacyVersion);
|
||||
registerFallbackValue(StoreKey.legacyAdvancedTroubleshooting);
|
||||
|
||||
when(() => mockDriftStoreRepo.getAll()).thenAnswer(
|
||||
(_) async => [
|
||||
const StoreDto(StoreKey.legacyAccessToken, _kAccessToken),
|
||||
const StoreDto(StoreKey.legacyAdvancedTroubleshooting, _kAdvancedTroubleshooting),
|
||||
const StoreDto(StoreKey.legacyVersion, _kVersion),
|
||||
],
|
||||
);
|
||||
when(() => mockDriftStoreRepo.watchAll()).thenAnswer((_) => controller.stream);
|
||||
|
||||
sut = await StoreService.create(storeRepository: mockDriftStoreRepo);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
unawaited(sut.dispose());
|
||||
await controller.close();
|
||||
});
|
||||
|
||||
group("Store Service Init:", () {
|
||||
test('Populates the internal cache on init', () {
|
||||
verify(() => mockDriftStoreRepo.getAll()).called(1);
|
||||
expect(sut.tryGet(StoreKey.legacyAccessToken), _kAccessToken);
|
||||
expect(sut.tryGet(StoreKey.legacyAdvancedTroubleshooting), _kAdvancedTroubleshooting);
|
||||
expect(sut.tryGet(StoreKey.legacyVersion), _kVersion);
|
||||
// Other keys should be null
|
||||
expect(sut.tryGet(StoreKey.deviceId), isNull);
|
||||
});
|
||||
|
||||
test('Listens to stream of store updates', () async {
|
||||
final event = StoreDto(StoreKey.legacyAccessToken, _kAccessToken.toUpperCase());
|
||||
controller.add([event]);
|
||||
|
||||
await pumpEventQueue();
|
||||
|
||||
verify(() => mockDriftStoreRepo.watchAll()).called(1);
|
||||
expect(sut.tryGet(StoreKey.legacyAccessToken), _kAccessToken.toUpperCase());
|
||||
});
|
||||
});
|
||||
|
||||
group('Store Service get:', () {
|
||||
test('Returns the stored value for the given key', () {
|
||||
expect(sut.get(StoreKey.legacyAccessToken), _kAccessToken);
|
||||
});
|
||||
|
||||
test('Throws StoreKeyNotFoundException for nonexistent keys', () {
|
||||
expect(() => sut.get(StoreKey.deviceId), throwsA(isA<StoreKeyNotFoundException>()));
|
||||
});
|
||||
|
||||
test('Returns the stored value for the given key or the defaultValue', () {
|
||||
expect(sut.get(StoreKey.legacyBackupTriggerDelay, 5), 5);
|
||||
});
|
||||
});
|
||||
|
||||
group('Store Service put:', () {
|
||||
setUp(() {
|
||||
when(() => mockDriftStoreRepo.upsert<String>(any<StoreKey<String>>(), any())).thenAnswer((_) async => true);
|
||||
});
|
||||
|
||||
test('Skip insert when value is not modified', () async {
|
||||
await sut.put(StoreKey.legacyAccessToken, _kAccessToken);
|
||||
verifyNever(() => mockDriftStoreRepo.upsert<String>(StoreKey.legacyAccessToken, any()));
|
||||
});
|
||||
|
||||
test('Insert value when modified', () async {
|
||||
final newAccessToken = _kAccessToken.toUpperCase();
|
||||
await sut.put(StoreKey.legacyAccessToken, newAccessToken);
|
||||
verify(() => mockDriftStoreRepo.upsert<String>(StoreKey.legacyAccessToken, newAccessToken)).called(1);
|
||||
expect(sut.tryGet(StoreKey.legacyAccessToken), newAccessToken);
|
||||
});
|
||||
});
|
||||
|
||||
group('Store Service watch:', () {
|
||||
late StreamController<String?> valueController;
|
||||
|
||||
setUp(() {
|
||||
valueController = StreamController<String?>.broadcast();
|
||||
when(() => mockDriftStoreRepo.watch<String>(any<StoreKey<String>>())).thenAnswer((_) => valueController.stream);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await valueController.close();
|
||||
});
|
||||
|
||||
test('Watches a specific key for changes', () async {
|
||||
final stream = sut.watch(StoreKey.legacyAccessToken);
|
||||
final events = <String?>[_kAccessToken, _kAccessToken.toUpperCase(), null, _kAccessToken.toLowerCase()];
|
||||
|
||||
unawaited(expectLater(stream, emitsInOrder(events)));
|
||||
|
||||
for (final event in events) {
|
||||
valueController.add(event);
|
||||
}
|
||||
|
||||
await pumpEventQueue();
|
||||
verify(() => mockDriftStoreRepo.watch<String>(StoreKey.legacyAccessToken)).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('Store Service delete:', () {
|
||||
setUp(() {
|
||||
when(() => mockDriftStoreRepo.delete<String>(any<StoreKey<String>>())).thenAnswer((_) async => true);
|
||||
});
|
||||
|
||||
test('Removes the value from the DB', () async {
|
||||
await sut.delete(StoreKey.legacyAccessToken);
|
||||
verify(() => mockDriftStoreRepo.delete<String>(StoreKey.legacyAccessToken)).called(1);
|
||||
});
|
||||
|
||||
test('Removes the value from the cache', () async {
|
||||
await sut.delete(StoreKey.legacyAccessToken);
|
||||
expect(sut.tryGet(StoreKey.legacyAccessToken), isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('Store Service clear:', () {
|
||||
setUp(() {
|
||||
when(() => mockDriftStoreRepo.deleteAll()).thenAnswer((_) async => true);
|
||||
});
|
||||
|
||||
test('Clears all values from the store', () async {
|
||||
await sut.clear();
|
||||
verify(() => mockDriftStoreRepo.deleteAll()).called(1);
|
||||
expect(sut.tryGet(StoreKey.legacyAccessToken), isNull);
|
||||
expect(sut.tryGet(StoreKey.legacyAdvancedTroubleshooting), isNull);
|
||||
expect(sut.tryGet(StoreKey.legacyVersion), isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -7,12 +7,10 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/models/app_metadata_key.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/domain/services/sync_stream.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
||||
@@ -63,7 +61,7 @@ void main() {
|
||||
registerFallbackValue(const <String>[]);
|
||||
|
||||
db = Drift(drift.DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
|
||||
await StoreService.init(storeRepository: DriftStoreRepository(db));
|
||||
await DeviceIdStore.init(db);
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:drift/drift.dart' hide isNull;
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/models/store.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/store.repository.dart';
|
||||
|
||||
const _kTestAccessToken = "#TestToken";
|
||||
const _kTestVersion = 10;
|
||||
const _kTestAdvancedTroubleshooting = false;
|
||||
|
||||
Future<void> _populateStore(Drift db) async {
|
||||
await db.batch((batch) async {
|
||||
batch.insert(
|
||||
db.storeEntity,
|
||||
StoreEntityCompanion(
|
||||
id: Value(StoreKey.legacyAdvancedTroubleshooting.id),
|
||||
intValue: const Value(_kTestAdvancedTroubleshooting ? 1 : 0),
|
||||
stringValue: const Value(null),
|
||||
),
|
||||
);
|
||||
batch.insert(
|
||||
db.storeEntity,
|
||||
StoreEntityCompanion(
|
||||
id: Value(StoreKey.legacyAccessToken.id),
|
||||
intValue: const Value(null),
|
||||
stringValue: const Value(_kTestAccessToken),
|
||||
),
|
||||
);
|
||||
batch.insert(
|
||||
db.storeEntity,
|
||||
StoreEntityCompanion(
|
||||
id: Value(StoreKey.legacyVersion.id),
|
||||
intValue: const Value(_kTestVersion),
|
||||
stringValue: const Value(null),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void main() {
|
||||
late Drift db;
|
||||
late DriftStoreRepository sut;
|
||||
|
||||
setUp(() async {
|
||||
db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
|
||||
sut = DriftStoreRepository(db);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await db.close();
|
||||
});
|
||||
|
||||
group('Store Repository converters:', () {
|
||||
test('converts int', () async {
|
||||
int? version = await sut.tryGet(StoreKey.legacyVersion);
|
||||
expect(version, isNull);
|
||||
await sut.upsert(StoreKey.legacyVersion, _kTestVersion);
|
||||
version = await sut.tryGet(StoreKey.legacyVersion);
|
||||
expect(version, _kTestVersion);
|
||||
});
|
||||
|
||||
test('converts string', () async {
|
||||
String? accessToken = await sut.tryGet(StoreKey.legacyAccessToken);
|
||||
expect(accessToken, isNull);
|
||||
await sut.upsert(StoreKey.legacyAccessToken, _kTestAccessToken);
|
||||
accessToken = await sut.tryGet(StoreKey.legacyAccessToken);
|
||||
expect(accessToken, _kTestAccessToken);
|
||||
});
|
||||
|
||||
test('converts bool', () async {
|
||||
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.legacyAdvancedTroubleshooting);
|
||||
expect(advancedTroubleshooting, isNull);
|
||||
await sut.upsert(StoreKey.legacyAdvancedTroubleshooting, _kTestAdvancedTroubleshooting);
|
||||
advancedTroubleshooting = await sut.tryGet(StoreKey.legacyAdvancedTroubleshooting);
|
||||
expect(advancedTroubleshooting, _kTestAdvancedTroubleshooting);
|
||||
});
|
||||
});
|
||||
|
||||
group('Store Repository Deletes:', () {
|
||||
setUp(() async {
|
||||
await _populateStore(db);
|
||||
});
|
||||
|
||||
test('delete()', () async {
|
||||
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.legacyAdvancedTroubleshooting);
|
||||
expect(advancedTroubleshooting, isFalse);
|
||||
await sut.delete(StoreKey.legacyAdvancedTroubleshooting);
|
||||
advancedTroubleshooting = await sut.tryGet(StoreKey.legacyAdvancedTroubleshooting);
|
||||
expect(advancedTroubleshooting, isNull);
|
||||
});
|
||||
|
||||
test('deleteAll()', () async {
|
||||
final count = await db.storeEntity.count().getSingle();
|
||||
expect(count, isNot(isZero));
|
||||
await sut.deleteAll();
|
||||
unawaited(expectLater(await db.storeEntity.count().getSingle(), isZero));
|
||||
});
|
||||
});
|
||||
|
||||
group('Store Repository Updates:', () {
|
||||
setUp(() async {
|
||||
await _populateStore(db);
|
||||
});
|
||||
|
||||
test('upsert()', () async {
|
||||
int? version = await sut.tryGet(StoreKey.legacyVersion);
|
||||
expect(version, _kTestVersion);
|
||||
await sut.upsert(StoreKey.legacyVersion, _kTestVersion + 10);
|
||||
version = await sut.tryGet(StoreKey.legacyVersion);
|
||||
expect(version, _kTestVersion + 10);
|
||||
});
|
||||
});
|
||||
|
||||
group('Store Repository Watchers:', () {
|
||||
setUp(() async {
|
||||
await _populateStore(db);
|
||||
});
|
||||
|
||||
test('watch()', () async {
|
||||
final stream = sut.watch(StoreKey.legacyVersion);
|
||||
unawaited(expectLater(stream, emitsInOrder([_kTestVersion, _kTestVersion + 10])));
|
||||
await pumpEventQueue();
|
||||
await sut.upsert(StoreKey.legacyVersion, _kTestVersion + 10);
|
||||
});
|
||||
|
||||
test('watchAll()', () async {
|
||||
final stream = sut.watchAll();
|
||||
unawaited(
|
||||
expectLater(
|
||||
stream,
|
||||
emitsInOrder([
|
||||
[
|
||||
const StoreDto<Object>(StoreKey.legacyVersion, _kTestVersion),
|
||||
const StoreDto<Object>(StoreKey.legacyAccessToken, _kTestAccessToken),
|
||||
const StoreDto<Object>(StoreKey.legacyAdvancedTroubleshooting, _kTestAdvancedTroubleshooting),
|
||||
],
|
||||
[
|
||||
const StoreDto<Object>(StoreKey.legacyVersion, _kTestVersion + 10),
|
||||
const StoreDto<Object>(StoreKey.legacyAccessToken, _kTestAccessToken),
|
||||
const StoreDto<Object>(StoreKey.legacyAdvancedTroubleshooting, _kTestAdvancedTroubleshooting),
|
||||
],
|
||||
]),
|
||||
),
|
||||
);
|
||||
await sut.upsert(StoreKey.legacyVersion, _kTestVersion + 10);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -6,9 +6,8 @@ import 'package:drift/native.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:immich_mobile/domain/models/sync_event.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart';
|
||||
import 'package:immich_mobile/utils/semver.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
@@ -41,7 +40,7 @@ void main() {
|
||||
|
||||
setUpAll(() async {
|
||||
final db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
|
||||
await StoreService.init(storeRepository: DriftStoreRepository(db));
|
||||
await DeviceIdStore.init(db);
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:immich_mobile/infrastructure/repositories/settings.repository.da
|
||||
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_api.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_migration.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/sync_stream.repository.dart';
|
||||
@@ -19,8 +18,6 @@ import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
||||
import 'package:immich_mobile/repositories/upload.repository.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockDriftStoreRepository extends Mock implements DriftStoreRepository {}
|
||||
|
||||
class MockSettingsRepository extends Mock implements SettingsRepository {}
|
||||
|
||||
class MockAppMetadataRepository extends Mock implements AppMetadataRepository {}
|
||||
|
||||
@@ -7,9 +7,8 @@ import 'package:drift/native.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/models/map/map_state.model.dart';
|
||||
import 'package:immich_mobile/providers/locale_provider.dart';
|
||||
import 'package:immich_mobile/providers/map/map_state.provider.dart';
|
||||
@@ -33,7 +32,7 @@ void main() {
|
||||
setUp(() async {
|
||||
mapState = const MapState(themeMode: ThemeMode.dark);
|
||||
mapStateNotifier = MockMapStateNotifier(mapState);
|
||||
await StoreService.init(storeRepository: DriftStoreRepository(db));
|
||||
await DeviceIdStore.init(db);
|
||||
overrides = [
|
||||
mapStateNotifierProvider.overrideWith(() => mapStateNotifier),
|
||||
localeProvider.overrideWithValue(const Locale("en")),
|
||||
|
||||
@@ -2,10 +2,8 @@ import 'package:drift/drift.dart' as drift;
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/repositories/download.repository.dart';
|
||||
import 'package:immich_mobile/services/action.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
@@ -36,7 +34,7 @@ void main() {
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.android;
|
||||
|
||||
db = Drift(drift.DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
|
||||
await StoreService.init(storeRepository: DriftStoreRepository(db));
|
||||
await DeviceIdStore.init(db);
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
|
||||
@@ -2,10 +2,9 @@ import 'package:drift/drift.dart' hide isNull;
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/session.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
||||
import 'package:immich_mobile/services/auth.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
@@ -44,7 +43,7 @@ void main() {
|
||||
setUpAll(() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
|
||||
await StoreService.init(storeRepository: DriftStoreRepository(db));
|
||||
await DeviceIdStore.init(db);
|
||||
await SessionRepository.ensureInitialized(db);
|
||||
});
|
||||
|
||||
|
||||
@@ -8,13 +8,10 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||
import 'package:immich_mobile/domain/models/session.model.dart';
|
||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/entities/store.entity.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/session.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
import 'package:immich_mobile/services/background_upload.service.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
@@ -39,11 +36,11 @@ void main() {
|
||||
(MethodCall methodCall) async => 'test',
|
||||
);
|
||||
db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
|
||||
await StoreService.init(storeRepository: DriftStoreRepository(db));
|
||||
await DeviceIdStore.init(db);
|
||||
await SettingsRepository.ensureInitialized(db);
|
||||
await SessionRepository.ensureInitialized(db);
|
||||
await SessionRepository.instance.write(SessionKey.serverEndpoint, 'https://demo.immich.app');
|
||||
await Store.put(StoreKey.deviceId, 'test-device-id');
|
||||
await Store.setDeviceId('test-device-id');
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
|
||||
@@ -6,11 +6,10 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/locales.dart';
|
||||
import 'package:immich_mobile/domain/models/session.model.dart';
|
||||
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||
import 'package:immich_mobile/generated/codegen_loader.g.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/session.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||
import 'package:immich_mobile/infrastructure/store.dart';
|
||||
|
||||
import '../test_utils.dart';
|
||||
|
||||
@@ -25,7 +24,7 @@ class PresentationContext {
|
||||
TestUtils.init();
|
||||
if (_db == null) {
|
||||
final db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true));
|
||||
await StoreService.init(storeRepository: DriftStoreRepository(db), listenUpdates: false);
|
||||
await DeviceIdStore.init(db);
|
||||
await SessionRepository.ensureInitialized(db);
|
||||
await SessionRepository.instance.write(SessionKey.serverEndpoint, serverEndpoint);
|
||||
_db = db;
|
||||
|
||||
Reference in New Issue
Block a user