refactor: app metadata

This commit is contained in:
shenlong-tanwen
2026-04-24 19:23:21 +05:30
parent 8cff5883b5
commit cc56f86333
33 changed files with 14221 additions and 85 deletions
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/domain/models/config/theme_config.dart';
import 'package:immich_mobile/domain/models/metadata_value.dart';
import 'package:immich_mobile/extensions/json_extensions.dart';
class AppConfig implements MetadataValue {
static const String name = 'app-config';
final ThemeConfig theme;
const AppConfig({this.theme = const ThemeConfig()});
factory AppConfig.fromJson(Map<String, Object?> json) {
final themeJson = json.nested(ThemeConfig.name);
return AppConfig(
theme: ThemeConfig(
mode: ThemeMode.values.firstWhere(
(e) => e.name == themeJson[ThemeConfig.keys.mode],
orElse: () => ThemeMode.system,
),
),
);
}
@override
Map<String, Object?> toJson() => {
ThemeConfig.name: {ThemeConfig.keys.mode: theme.mode.name},
};
AppConfig copyWith({ThemeMode? themeMode}) => AppConfig(theme: ThemeConfig(mode: themeMode ?? theme.mode));
@override
bool operator ==(Object other) => identical(this, other) || (other is AppConfig && other.theme == theme);
@override
int get hashCode => theme.hashCode;
@override
String toString() => '$name: { $theme }';
}
@@ -0,0 +1,27 @@
import 'package:immich_mobile/domain/models/log.model.dart';
class _Keys {
const _Keys();
final level = 'level';
}
class LogConfig {
static const String name = 'log';
// ignore: library_private_types_in_public_api
static const _Keys keys = _Keys();
final LogLevel level;
const LogConfig({this.level = LogLevel.info});
@override
bool operator ==(Object other) => identical(this, other) || (other is LogConfig && other.level == level);
@override
int get hashCode => level.hashCode;
@override
String toString() => '$name: {${keys.level}: $level}';
}
@@ -0,0 +1,37 @@
import 'package:immich_mobile/domain/models/config/log_config.dart';
import 'package:immich_mobile/domain/models/log.model.dart';
import 'package:immich_mobile/domain/models/metadata_value.dart';
import 'package:immich_mobile/extensions/json_extensions.dart';
class SystemConfig implements MetadataValue {
static const String name = 'system-config';
final LogConfig log;
const SystemConfig({this.log = const LogConfig()});
factory SystemConfig.fromJson(Map<String, Object?> json) {
final logJson = json.nested(LogConfig.name);
return SystemConfig(
log: LogConfig(
level: LogLevel.values.firstWhere((e) => e.name == logJson[LogConfig.keys.level], orElse: () => LogLevel.info),
),
);
}
@override
Map<String, Object?> toJson() => {
LogConfig.name: {LogConfig.keys.level: log.level.name},
};
SystemConfig copyWith({LogLevel? logLevel}) => SystemConfig(log: LogConfig(level: logLevel ?? log.level));
@override
bool operator ==(Object other) => identical(this, other) || (other is SystemConfig && other.log == log);
@override
int get hashCode => log.hashCode;
@override
String toString() => '$name: { $log }';
}
@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class _Keys {
const _Keys();
final mode = 'mode';
}
class ThemeConfig {
static const String name = 'theme';
// ignore: library_private_types_in_public_api
static const _Keys keys = _Keys();
final ThemeMode mode;
const ThemeConfig({this.mode = ThemeMode.system});
@override
bool operator ==(Object other) => identical(this, other) || (other is ThemeConfig && other.mode == mode);
@override
int get hashCode => mode.hashCode;
@override
String toString() => '$name: {${keys.mode}: $mode}';
}
@@ -0,0 +1,23 @@
import 'package:immich_mobile/domain/models/metadata_value.dart';
class SystemMetadata implements MetadataValue {
static const String name = 'system-metadata';
const SystemMetadata();
factory SystemMetadata.fromJson(Map<String, Object?> json) => const SystemMetadata();
@override
Map<String, Object?> toJson() => const {};
SystemMetadata copyWith() => this;
@override
bool operator ==(Object other) => other is SystemMetadata;
@override
int get hashCode => 0;
@override
String toString() => '$name: {}';
}
@@ -0,0 +1,16 @@
import 'package:immich_mobile/domain/models/config/app_config.dart';
import 'package:immich_mobile/domain/models/config/system_config.dart';
import 'package:immich_mobile/domain/models/metadata/system_metadata.dart';
import 'package:immich_mobile/domain/models/metadata_value.dart';
enum MetadataKind<T extends MetadataValue> {
appConfig<AppConfig>(AppConfig.name, AppConfig.fromJson, AppConfig()),
systemConfig<SystemConfig>(SystemConfig.name, SystemConfig.fromJson, SystemConfig()),
systemMetadata<SystemMetadata>(SystemMetadata.name, SystemMetadata.fromJson, SystemMetadata());
final String key;
final T Function(Map<String, Object?>) fromJson;
final T defaultValue;
const MetadataKind(this.key, this.fromJson, this.defaultValue);
}
@@ -0,0 +1,5 @@
abstract class MetadataValue {
const MetadataValue();
Map<String, Object?> toJson();
}
+2 -2
View File
@@ -22,7 +22,7 @@ enum StoreKey<T> {
// user settings from [AppSettingsEnum] below:
loadPreview<bool>._(100),
loadOriginal<bool>._(101),
themeMode<String>._(102),
// id 102 (themeMode) moved to user_config.theme-mode
tilesPerRow<int>._(103),
dynamicLayout<bool>._(104),
groupAssetsBy<int>._(105),
@@ -35,7 +35,7 @@ enum StoreKey<T> {
albumThumbnailCacheSize<int>._(112),
selectedAlbumSortOrder<int>._(113),
advancedTroubleshooting<bool>._(114),
logLevel<int>._(115),
// id 115 (logLevel) moved to app_metadata.log-level
preferRemoteImage<bool>._(116),
loopVideo<bool>._(117),
// map related settings
+13 -13
View File
@@ -2,20 +2,20 @@ import 'dart:async';
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/domain/models/metadata_kind.dart';
import 'package:immich_mobile/infrastructure/repositories/cached_metadata.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/log.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:logging/logging.dart';
/// Service responsible for handling application logging.
///
/// It listens to Dart's [Logger.root], buffers logs in memory (optionally),
/// writes them to a persistent [ILogRepository], and manages log levels
/// via [IStoreRepository]
/// writes them to a persistent [LogRepository], and manages log levels via
/// [CachedMetadataRepository].
class LogService {
final LogRepository _logRepository;
final DriftStoreRepository _storeRepository;
final CachedMetadataRepository _metadataRepository;
final List<LogMessage> _msgBuffer = [];
@@ -38,12 +38,12 @@ class LogService {
static Future<LogService> init({
required LogRepository logRepository,
required DriftStoreRepository storeRepository,
required CachedMetadataRepository metadataRepository,
bool shouldBuffer = true,
}) async {
_instance ??= await create(
logRepository: logRepository,
storeRepository: storeRepository,
metadataRepository: metadataRepository,
shouldBuffer: shouldBuffer,
);
return _instance!;
@@ -51,17 +51,17 @@ class LogService {
static Future<LogService> create({
required LogRepository logRepository,
required DriftStoreRepository storeRepository,
required CachedMetadataRepository metadataRepository,
bool shouldBuffer = true,
}) async {
final instance = LogService._(logRepository, storeRepository, shouldBuffer);
final instance = LogService._(logRepository, metadataRepository, shouldBuffer);
await logRepository.truncate(limit: kLogTruncateLimit);
final level = await instance._storeRepository.tryGet(StoreKey.logLevel) ?? LogLevel.info.index;
Logger.root.level = Level.LEVELS.elementAtOrNull(level) ?? Level.INFO;
final level = instance._metadataRepository.read(MetadataKind.systemConfig).log.level;
Logger.root.level = Level.LEVELS.elementAtOrNull(level.index) ?? Level.INFO;
return instance;
}
LogService._(this._logRepository, this._storeRepository, this._shouldBuffer) {
LogService._(this._logRepository, this._metadataRepository, this._shouldBuffer) {
_logSubscription = Logger.root.onRecord.listen(_handleLogRecord);
}
@@ -91,7 +91,7 @@ class LogService {
}
Future<void> setLogLevel(LogLevel level) async {
await _storeRepository.upsert(StoreKey.logLevel, level.index);
await _metadataRepository.update(MetadataKind.systemConfig, (current) => current.copyWith(logLevel: level));
Logger.root.level = level.toLevel();
}