mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
Found via `codespell -q 3 -S "*.svg,./i18n,./docs/package-lock.json,./readme_i18n,./mobile/assets/i18n" -L afterall,devlop,finaly,inout,nd,optin,renderd,sade`
104 lines
3.6 KiB
Dart
104 lines
3.6 KiB
Dart
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 IStoreRepository _storeRepository;
|
|
|
|
/// In-memory cache. Keys are [StoreKey.id]
|
|
final Map<int, Object?> _cache = {};
|
|
StreamSubscription<List<StoreDto>>? _storeUpdateSubscription;
|
|
|
|
StoreService._({required IStoreRepository 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 IStoreRepository storeRepository, bool listenUpdates = true}) async {
|
|
_instance ??= await create(storeRepository: storeRepository, listenUpdates: listenUpdates);
|
|
return _instance!;
|
|
}
|
|
|
|
static Future<StoreService> create({required IStoreRepository 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();
|
|
_cache.clear();
|
|
}
|
|
|
|
/// 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();
|
|
}
|
|
|
|
bool get isBetaTimelineEnabled => tryGet(StoreKey.betaTimeline) ?? true;
|
|
}
|
|
|
|
class StoreKeyNotFoundException implements Exception {
|
|
final StoreKey key;
|
|
const StoreKeyNotFoundException(this.key);
|
|
|
|
@override
|
|
String toString() => "Key - <${key.name}> not available in Store";
|
|
}
|