Merge branch 'main' into feature/load-previews

This commit is contained in:
Luis Nachtigall
2026-05-07 14:57:57 +02:00
committed by GitHub
572 changed files with 38318 additions and 7451 deletions
@@ -8,6 +8,7 @@ enum AlbumUserRole {
// do not change this order!
editor,
viewer,
owner,
}
// Model for an album stored in the server
@@ -0,0 +1,18 @@
import 'package:immich_mobile/domain/models/config/theme_config.dart';
class AppConfig {
final ThemeConfig theme;
const AppConfig({this.theme = const ThemeConfig()});
AppConfig copyWith({ThemeConfig? theme}) => .new(theme: theme ?? this.theme);
@override
bool operator ==(Object other) => identical(this, other) || (other is AppConfig && other.theme == theme);
@override
int get hashCode => theme.hashCode;
@override
String toString() => 'AppConfig(theme: $theme)';
}
@@ -0,0 +1,18 @@
import 'package:immich_mobile/domain/models/log.model.dart';
class SystemConfig {
final LogLevel logLevel;
const SystemConfig({this.logLevel = .info});
SystemConfig copyWith({LogLevel? logLevel}) => SystemConfig(logLevel: logLevel ?? this.logLevel);
@override
bool operator ==(Object other) => identical(this, other) || (other is SystemConfig && other.logLevel == logLevel);
@override
int get hashCode => logLevel.hashCode;
@override
String toString() => 'SystemConfig(logLevel: $logLevel)';
}
@@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
class ThemeConfig {
final ThemeMode mode;
const ThemeConfig({this.mode = .system});
ThemeConfig copyWith({ThemeMode? mode}) => .new(mode: mode ?? this.mode);
@override
bool operator ==(Object other) => identical(this, other) || (other is ThemeConfig && other.mode == mode);
@override
int get hashCode => mode.hashCode;
@override
String toString() => 'ThemeConfig(mode: $mode)';
}
+101
View File
@@ -0,0 +1,101 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
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/log.model.dart';
enum MetadataDomain<T extends Object> {
appConfig<AppConfig>('config.app'),
systemConfig<SystemConfig>('config.system');
final String prefix;
const MetadataDomain(this.prefix);
}
enum MetadataKey<T extends Object> {
themeMode<ThemeMode>(.appConfig, 'theme.mode', .system, _EnumCodec(ThemeMode.values)),
logLevel<LogLevel>(.systemConfig, 'log.level', .info, _EnumCodec(LogLevel.values));
final MetadataDomain domain;
final String name;
final T defaultValue;
final _MetadataCodec<T>? _codecOverride;
const MetadataKey(this.domain, this.name, this.defaultValue, [this._codecOverride]);
String get key => '${domain.prefix}.$name';
_MetadataCodec<T> get _codec => _codecOverride ?? _MetadataCodec.forPrimitive(defaultValue);
String encode(T value) => _codec.encode(value);
T decode(String raw) => _codec.decode(raw) ?? defaultValue;
static Map<String, MetadataKey<Object>> asKeyMap() => {for (var value in MetadataKey.values) value.key: value};
}
sealed class _MetadataCodec<T extends Object> {
const _MetadataCodec();
String encode(T value);
T? decode(String raw);
static const Map<Type, _MetadataCodec<Object>> _primitives = {
int: _PrimitiveCodec.integer,
double: _PrimitiveCodec.real,
bool: _PrimitiveCodec.boolean,
String: _PrimitiveCodec.string,
DateTime: _DateTimeCodec(),
};
static _MetadataCodec<T> forPrimitive<T extends Object>(T sample) {
final codec = _primitives[sample.runtimeType];
if (codec == null) {
throw StateError(
'No primitive codec for ${sample.runtimeType}. Provide an explicit codec when defining the MetadataKey.',
);
}
return codec as _MetadataCodec<T>;
}
}
final class _EnumCodec<T extends Enum> extends _MetadataCodec<T> {
final List<T> values;
const _EnumCodec(this.values);
@override
String encode(T value) => value.name;
@override
T? decode(String raw) => values.firstWhereOrNull((v) => v.name == raw);
}
final class _DateTimeCodec extends _MetadataCodec<DateTime> {
const _DateTimeCodec();
@override
String encode(DateTime value) => value.toIso8601String();
@override
DateTime? decode(String raw) => DateTime.tryParse(raw);
}
final class _PrimitiveCodec<T extends Object> extends _MetadataCodec<T> {
final T? Function(String) _parse;
const _PrimitiveCodec._(this._parse);
@override
String encode(T value) => value.toString();
@override
T? decode(String raw) => _parse(raw);
static const integer = _PrimitiveCodec<int>._(int.tryParse);
static const real = _PrimitiveCodec<double>._(double.tryParse);
static const boolean = _PrimitiveCodec<bool>._(bool.tryParse);
static const string = _PrimitiveCodec<String>._(_identity);
static String? _identity(String s) => s;
}
+5 -3
View File
@@ -22,7 +22,6 @@ enum StoreKey<T> {
// user settings from [AppSettingsEnum] below:
loadPreview<bool>._(100),
loadOriginal<bool>._(101),
themeMode<String>._(102),
tilesPerRow<int>._(103),
dynamicLayout<bool>._(104),
groupAssetsBy<int>._(105),
@@ -35,7 +34,6 @@ enum StoreKey<T> {
albumThumbnailCacheSize<int>._(112),
selectedAlbumSortOrder<int>._(113),
advancedTroubleshooting<bool>._(114),
logLevel<int>._(115),
preferRemoteImage<bool>._(116),
loopVideo<bool>._(117),
// map related settings
@@ -94,7 +92,11 @@ enum StoreKey<T> {
cleanupCutoffDaysAgo<int>._(1011),
cleanupDefaultsInitialized<bool>._(1012),
syncMigrationStatus<String>._(1013);
syncMigrationStatus<String>._(1013),
// Legacy keys that have been migrated to the new metadata store
legacyThemeMode<String>._(102),
legacyLogLevel<int>._(115);
const StoreKey._(this.id);
final int id;