mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
refactor: move cleanup config to metadata table (#28225)
* refactor: app metadata * refactor to per row store * cleanup * more test * review changes * more refactor * refactor * migrate primary color * migrate dynamic theme * migrate colorfulInterface * cleanup providers * migrate cleanup --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
@@ -1,18 +1,22 @@
|
|||||||
|
import 'package:immich_mobile/domain/models/config/cleanup_config.dart';
|
||||||
import 'package:immich_mobile/domain/models/config/theme_config.dart';
|
import 'package:immich_mobile/domain/models/config/theme_config.dart';
|
||||||
|
|
||||||
class AppConfig {
|
class AppConfig {
|
||||||
final ThemeConfig theme;
|
final ThemeConfig theme;
|
||||||
|
final CleanupConfig cleanup;
|
||||||
|
|
||||||
const AppConfig({this.theme = const ThemeConfig()});
|
const AppConfig({this.theme = const .new(), this.cleanup = const .new()});
|
||||||
|
|
||||||
AppConfig copyWith({ThemeConfig? theme}) => .new(theme: theme ?? this.theme);
|
AppConfig copyWith({ThemeConfig? theme, CleanupConfig? cleanup}) =>
|
||||||
|
.new(theme: theme ?? this.theme, cleanup: cleanup ?? this.cleanup);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || (other is AppConfig && other.theme == theme);
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) || (other is AppConfig && other.theme == theme && other.cleanup == cleanup);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => theme.hashCode;
|
int get hashCode => Object.hash(theme, cleanup);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'AppConfig(theme: $theme)';
|
String toString() => 'AppConfig(theme: $theme, cleanup: $cleanup)';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
|
|
||||||
|
class CleanupConfig {
|
||||||
|
final bool keepFavorites;
|
||||||
|
final AssetKeepType keepMediaType;
|
||||||
|
final List<String> keepAlbumIds;
|
||||||
|
final int cutoffDaysAgo;
|
||||||
|
final bool defaultsInitialized;
|
||||||
|
|
||||||
|
const CleanupConfig({
|
||||||
|
this.keepFavorites = true,
|
||||||
|
this.keepMediaType = AssetKeepType.none,
|
||||||
|
this.keepAlbumIds = const [],
|
||||||
|
this.cutoffDaysAgo = -1,
|
||||||
|
this.defaultsInitialized = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
CleanupConfig copyWith({
|
||||||
|
bool? keepFavorites,
|
||||||
|
AssetKeepType? keepMediaType,
|
||||||
|
List<String>? keepAlbumIds,
|
||||||
|
int? cutoffDaysAgo,
|
||||||
|
bool? defaultsInitialized,
|
||||||
|
}) => .new(
|
||||||
|
keepFavorites: keepFavorites ?? this.keepFavorites,
|
||||||
|
keepMediaType: keepMediaType ?? this.keepMediaType,
|
||||||
|
keepAlbumIds: keepAlbumIds ?? this.keepAlbumIds,
|
||||||
|
cutoffDaysAgo: cutoffDaysAgo ?? this.cutoffDaysAgo,
|
||||||
|
defaultsInitialized: defaultsInitialized ?? this.defaultsInitialized,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is CleanupConfig &&
|
||||||
|
other.keepFavorites == keepFavorites &&
|
||||||
|
other.keepMediaType == keepMediaType &&
|
||||||
|
other.keepAlbumIds == keepAlbumIds &&
|
||||||
|
other.cutoffDaysAgo == cutoffDaysAgo &&
|
||||||
|
other.defaultsInitialized == defaultsInitialized);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(keepFavorites, keepMediaType, keepAlbumIds, cutoffDaysAgo, defaultsInitialized);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'CleanupConfig(keepFavorites: $keepFavorites, keepMediaType: $keepMediaType, keepAlbumIds: $keepAlbumIds, cutoffDaysAgo: $cutoffDaysAgo, defaultsInitialized: $defaultsInitialized)';
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/constants/colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/config/app_config.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/config/system_config.dart';
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||||
@@ -15,13 +18,25 @@ enum MetadataDomain<T extends Object> {
|
|||||||
|
|
||||||
enum MetadataKey<T extends Object> {
|
enum MetadataKey<T extends Object> {
|
||||||
// Theme
|
// Theme
|
||||||
primaryColor<ImmichColorPreset>(.appConfig, 'theme.primaryColor', .indigo, _EnumCodec(ImmichColorPreset.values)),
|
themePrimaryColor<ImmichColorPreset>(.appConfig, 'theme.primaryColor', .indigo, _EnumCodec(ImmichColorPreset.values)),
|
||||||
themeMode<ThemeMode>(.appConfig, 'theme.mode', .system, _EnumCodec(ThemeMode.values)),
|
themeMode<ThemeMode>(.appConfig, 'theme.mode', .system, _EnumCodec(ThemeMode.values)),
|
||||||
dynamicTheme<bool>(.appConfig, 'theme.dynamicTheme', false),
|
themeDynamic<bool>(.appConfig, 'theme.dynamic', false),
|
||||||
colorfulInterface<bool>(.appConfig, 'theme.colorfulInterface', true),
|
themeColorfulInterface<bool>(.appConfig, 'theme.colorfulInterface', true),
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
logLevel<LogLevel>(.systemConfig, 'log.level', .info, _EnumCodec(LogLevel.values));
|
logLevel<LogLevel>(.systemConfig, 'log.level', .info, _EnumCodec(LogLevel.values)),
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
cleanupKeepFavorites<bool>(.appConfig, 'cleanup.keepFavorites', true),
|
||||||
|
cleanupKeepMediaType<AssetKeepType>(
|
||||||
|
.appConfig,
|
||||||
|
'cleanup.keepMediaType',
|
||||||
|
AssetKeepType.none,
|
||||||
|
_EnumCodec(AssetKeepType.values),
|
||||||
|
),
|
||||||
|
cleanupKeepAlbumIds<List<String>>(.appConfig, 'cleanup.keepAlbumIds', [], _ListCodec(_PrimitiveCodec.string)),
|
||||||
|
cleanupCutoffDaysAgo<int>(.appConfig, 'cleanup.cutoffDaysAgo', -1),
|
||||||
|
cleanupDefaultsInitialized<bool>(.appConfig, 'cleanup.defaultsInitialized', false);
|
||||||
|
|
||||||
final MetadataDomain domain;
|
final MetadataDomain domain;
|
||||||
final String name;
|
final String name;
|
||||||
@@ -88,6 +103,33 @@ final class _DateTimeCodec extends _MetadataCodec<DateTime> {
|
|||||||
DateTime? decode(String raw) => DateTime.tryParse(raw);
|
DateTime? decode(String raw) => DateTime.tryParse(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class _ListCodec<T extends Object> extends _MetadataCodec<List<T>> {
|
||||||
|
final _MetadataCodec<T> _elementCodec;
|
||||||
|
|
||||||
|
const _ListCodec(this._elementCodec);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String encode(List<T> value) => jsonEncode(value.map(_elementCodec.encode).toList());
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<T>? decode(String raw) {
|
||||||
|
try {
|
||||||
|
final decoded = jsonDecode(raw);
|
||||||
|
if (decoded is! List) return null;
|
||||||
|
final result = <T>[];
|
||||||
|
for (final item in decoded) {
|
||||||
|
if (item is! String) return null;
|
||||||
|
final element = _elementCodec.decode(item);
|
||||||
|
if (element == null) return null;
|
||||||
|
result.add(element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} on FormatException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class _PrimitiveCodec<T extends Object> extends _MetadataCodec<T> {
|
final class _PrimitiveCodec<T extends Object> extends _MetadataCodec<T> {
|
||||||
final T? Function(String) _parse;
|
final T? Function(String) _parse;
|
||||||
|
|
||||||
|
|||||||
@@ -81,12 +81,6 @@ enum StoreKey<T> {
|
|||||||
shouldResetSync<bool>._(1007),
|
shouldResetSync<bool>._(1007),
|
||||||
|
|
||||||
// Free up space
|
// Free up space
|
||||||
cleanupKeepFavorites<bool>._(1008),
|
|
||||||
cleanupKeepMediaType<int>._(1009),
|
|
||||||
cleanupKeepAlbumIds<String>._(1010),
|
|
||||||
cleanupCutoffDaysAgo<int>._(1011),
|
|
||||||
cleanupDefaultsInitialized<bool>._(1012),
|
|
||||||
|
|
||||||
syncMigrationStatus<String>._(1013),
|
syncMigrationStatus<String>._(1013),
|
||||||
|
|
||||||
// Legacy keys that have been migrated to the new metadata store
|
// Legacy keys that have been migrated to the new metadata store
|
||||||
@@ -94,6 +88,11 @@ enum StoreKey<T> {
|
|||||||
legacyDynamicTheme<bool>._(129),
|
legacyDynamicTheme<bool>._(129),
|
||||||
legacyColorfulInterface<bool>._(130),
|
legacyColorfulInterface<bool>._(130),
|
||||||
legacyThemeMode<String>._(102),
|
legacyThemeMode<String>._(102),
|
||||||
|
legacyCleanupKeepFavorites<bool>._(1008),
|
||||||
|
legacyCleanupKeepMediaType<int>._(1009),
|
||||||
|
legacyCleanupKeepAlbumIds<String>._(1010),
|
||||||
|
legacyCleanupCutoffDaysAgo<int>._(1011),
|
||||||
|
legacyCleanupDefaultsInitialized<bool>._(1012),
|
||||||
legacyLogLevel<int>._(115);
|
legacyLogLevel<int>._(115);
|
||||||
|
|
||||||
const StoreKey._(this.id);
|
const StoreKey._(this.id);
|
||||||
|
|||||||
@@ -103,9 +103,16 @@ extension<T extends Object> on MetadataDomain<T> {
|
|||||||
repo._appConfig = .new(
|
repo._appConfig = .new(
|
||||||
theme: .new(
|
theme: .new(
|
||||||
mode: repo._read(.themeMode),
|
mode: repo._read(.themeMode),
|
||||||
primaryColor: repo._read(.primaryColor),
|
primaryColor: repo._read(.themePrimaryColor),
|
||||||
dynamicTheme: repo._read(.dynamicTheme),
|
dynamicTheme: repo._read(.themeDynamic),
|
||||||
colorfulInterface: repo._read(.colorfulInterface),
|
colorfulInterface: repo._read(.themeColorfulInterface),
|
||||||
|
),
|
||||||
|
cleanup: .new(
|
||||||
|
keepFavorites: repo._read(.cleanupKeepFavorites),
|
||||||
|
keepMediaType: repo._read(.cleanupKeepMediaType),
|
||||||
|
keepAlbumIds: repo._read(.cleanupKeepAlbumIds),
|
||||||
|
cutoffDaysAgo: repo._read(.cleanupCutoffDaysAgo),
|
||||||
|
defaultsInitialized: repo._read(.cleanupDefaultsInitialized),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
case .systemConfig:
|
case .systemConfig:
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class BootstrapErrorWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext _) {
|
Widget build(BuildContext _) {
|
||||||
final immichTheme = MetadataKey.primaryColor.defaultValue.themeOfPreset;
|
final immichTheme = MetadataKey.themePrimaryColor.defaultValue.themeOfPreset;
|
||||||
|
|
||||||
return EasyLocalization(
|
return EasyLocalization(
|
||||||
supportedLocales: locales.values.toList(),
|
supportedLocales: locales.values.toList(),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/enums.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/services/cleanup.service.dart';
|
import 'package:immich_mobile/services/cleanup.service.dart';
|
||||||
|
|
||||||
class CleanupState {
|
class CleanupState {
|
||||||
@@ -54,27 +54,25 @@ final cleanupProvider = StateNotifierProvider<CleanupNotifier, CleanupState>((re
|
|||||||
return CleanupNotifier(
|
return CleanupNotifier(
|
||||||
ref.watch(cleanupServiceProvider),
|
ref.watch(cleanupServiceProvider),
|
||||||
ref.watch(currentUserProvider)?.id,
|
ref.watch(currentUserProvider)?.id,
|
||||||
ref.watch(appSettingsServiceProvider),
|
ref.watch(metadataProvider),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
class CleanupNotifier extends StateNotifier<CleanupState> {
|
class CleanupNotifier extends StateNotifier<CleanupState> {
|
||||||
final CleanupService _cleanupService;
|
final CleanupService _cleanupService;
|
||||||
final String? _userId;
|
final String? _userId;
|
||||||
final AppSettingsService _appSettingsService;
|
final MetadataRepository _metadataRepository;
|
||||||
|
|
||||||
CleanupNotifier(this._cleanupService, this._userId, this._appSettingsService) : super(const CleanupState()) {
|
CleanupNotifier(this._cleanupService, this._userId, this._metadataRepository) : super(const CleanupState()) {
|
||||||
_loadPersistedSettings();
|
_loadPersistedSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _loadPersistedSettings() {
|
void _loadPersistedSettings() {
|
||||||
final keepFavorites = _appSettingsService.getSetting(AppSettingsEnum.cleanupKeepFavorites);
|
final cleanup = _metadataRepository.appConfig.cleanup;
|
||||||
final keepMediaTypeIndex = _appSettingsService.getSetting(AppSettingsEnum.cleanupKeepMediaType);
|
final keepFavorites = cleanup.keepFavorites;
|
||||||
final keepAlbumIdsString = _appSettingsService.getSetting(AppSettingsEnum.cleanupKeepAlbumIds);
|
final keepMediaType = cleanup.keepMediaType;
|
||||||
final cutoffDaysAgo = _appSettingsService.getSetting(AppSettingsEnum.cleanupCutoffDaysAgo);
|
final keepAlbumIds = cleanup.keepAlbumIds.toSet();
|
||||||
|
final cutoffDaysAgo = cleanup.cutoffDaysAgo;
|
||||||
final keepMediaType = AssetKeepType.values[keepMediaTypeIndex.clamp(0, AssetKeepType.values.length - 1)];
|
|
||||||
final keepAlbumIds = keepAlbumIdsString.isEmpty ? <String>{} : keepAlbumIdsString.split(',').toSet();
|
|
||||||
final selectedDate = cutoffDaysAgo >= 0 ? DateTime.now().subtract(Duration(days: cutoffDaysAgo)) : null;
|
final selectedDate = cutoffDaysAgo >= 0 ? DateTime.now().subtract(Duration(days: cutoffDaysAgo)) : null;
|
||||||
|
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
@@ -89,18 +87,18 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
|
|||||||
state = state.copyWith(selectedDate: date, assetsToDelete: []);
|
state = state.copyWith(selectedDate: date, assetsToDelete: []);
|
||||||
if (date != null) {
|
if (date != null) {
|
||||||
final daysAgo = DateTime.now().difference(date).inDays;
|
final daysAgo = DateTime.now().difference(date).inDays;
|
||||||
_appSettingsService.setSetting(AppSettingsEnum.cleanupCutoffDaysAgo, daysAgo);
|
_metadataRepository.write(.cleanupCutoffDaysAgo, daysAgo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setKeepMediaType(AssetKeepType keepMediaType) {
|
void setKeepMediaType(AssetKeepType keepMediaType) {
|
||||||
state = state.copyWith(keepMediaType: keepMediaType, assetsToDelete: []);
|
state = state.copyWith(keepMediaType: keepMediaType, assetsToDelete: []);
|
||||||
_appSettingsService.setSetting(AppSettingsEnum.cleanupKeepMediaType, keepMediaType.index);
|
_metadataRepository.write(.cleanupKeepMediaType, keepMediaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setKeepFavorites(bool keepFavorites) {
|
void setKeepFavorites(bool keepFavorites) {
|
||||||
state = state.copyWith(keepFavorites: keepFavorites, assetsToDelete: []);
|
state = state.copyWith(keepFavorites: keepFavorites, assetsToDelete: []);
|
||||||
_appSettingsService.setSetting(AppSettingsEnum.cleanupKeepFavorites, keepFavorites);
|
_metadataRepository.write(.cleanupKeepFavorites, keepFavorites);
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggleKeepAlbum(String albumId) {
|
void toggleKeepAlbum(String albumId) {
|
||||||
@@ -120,7 +118,7 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _persistExcludedAlbumIds(Set<String> albumIds) {
|
void _persistExcludedAlbumIds(Set<String> albumIds) {
|
||||||
_appSettingsService.setSetting(AppSettingsEnum.cleanupKeepAlbumIds, albumIds.join(','));
|
_metadataRepository.write(.cleanupKeepAlbumIds, albumIds.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanupStaleAlbumIds(Set<String> existingAlbumIds) {
|
void cleanupStaleAlbumIds(Set<String> existingAlbumIds) {
|
||||||
@@ -133,7 +131,7 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void applyDefaultAlbumSelections(List<(String id, String name)> albums) {
|
void applyDefaultAlbumSelections(List<(String id, String name)> albums) {
|
||||||
final isInitialized = _appSettingsService.getSetting(AppSettingsEnum.cleanupDefaultsInitialized);
|
final isInitialized = _metadataRepository.appConfig.cleanup.defaultsInitialized;
|
||||||
if (isInitialized) return;
|
if (isInitialized) return;
|
||||||
|
|
||||||
final toKeep = _cleanupService.getDefaultKeepAlbumIds(albums);
|
final toKeep = _cleanupService.getDefaultKeepAlbumIds(albums);
|
||||||
@@ -144,7 +142,7 @@ class CleanupNotifier extends StateNotifier<CleanupState> {
|
|||||||
_persistExcludedAlbumIds(keepAlbumIds);
|
_persistExcludedAlbumIds(keepAlbumIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
_appSettingsService.setSetting(AppSettingsEnum.cleanupDefaultsInitialized, true);
|
_metadataRepository.write(.cleanupDefaultsInitialized, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> scanAssets() async {
|
Future<void> scanAssets() async {
|
||||||
|
|||||||
@@ -49,12 +49,7 @@ enum AppSettingsEnum<T> {
|
|||||||
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false),
|
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false),
|
||||||
albumGridView<bool>(StoreKey.albumGridView, "albumGridView", false),
|
albumGridView<bool>(StoreKey.albumGridView, "albumGridView", false),
|
||||||
backupRequireCharging<bool>(StoreKey.backupRequireCharging, null, false),
|
backupRequireCharging<bool>(StoreKey.backupRequireCharging, null, false),
|
||||||
backupTriggerDelay<int>(StoreKey.backupTriggerDelay, null, 30),
|
backupTriggerDelay<int>(StoreKey.backupTriggerDelay, null, 30);
|
||||||
cleanupKeepFavorites<bool>(StoreKey.cleanupKeepFavorites, null, true),
|
|
||||||
cleanupKeepMediaType<int>(StoreKey.cleanupKeepMediaType, null, 0),
|
|
||||||
cleanupKeepAlbumIds<String>(StoreKey.cleanupKeepAlbumIds, null, ""),
|
|
||||||
cleanupCutoffDaysAgo<int>(StoreKey.cleanupCutoffDaysAgo, null, -1),
|
|
||||||
cleanupDefaultsInitialized<bool>(StoreKey.cleanupDefaultsInitialized, null, false);
|
|
||||||
|
|
||||||
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:immich_mobile/constants/colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
@@ -43,9 +44,29 @@ Future<void> _migrateTo26(Drift drift) async {
|
|||||||
final migrator = _StoreMigrator(drift);
|
final migrator = _StoreMigrator(drift);
|
||||||
await migrator.migrateEnumName(StoreKey.legacyThemeMode, MetadataKey.themeMode, ThemeMode.values);
|
await migrator.migrateEnumName(StoreKey.legacyThemeMode, MetadataKey.themeMode, ThemeMode.values);
|
||||||
await migrator.migrateEnumIndex(StoreKey.legacyLogLevel, MetadataKey.logLevel, LogLevel.values);
|
await migrator.migrateEnumIndex(StoreKey.legacyLogLevel, MetadataKey.logLevel, LogLevel.values);
|
||||||
await migrator.migrateEnumName(StoreKey.legacyPrimaryColor, MetadataKey.primaryColor, ImmichColorPreset.values);
|
await migrator.migrateEnumName(StoreKey.legacyPrimaryColor, MetadataKey.themePrimaryColor, ImmichColorPreset.values);
|
||||||
await migrator.migrateBool(StoreKey.legacyDynamicTheme, MetadataKey.dynamicTheme);
|
await migrator.migrateBool(StoreKey.legacyDynamicTheme, MetadataKey.themeDynamic);
|
||||||
await migrator.migrateBool(StoreKey.legacyColorfulInterface, MetadataKey.colorfulInterface);
|
await migrator.migrateBool(StoreKey.legacyColorfulInterface, MetadataKey.themeColorfulInterface);
|
||||||
|
final cleanupKeepAlbumIds = await migrator.readLegacyStoreString(StoreKey.legacyCleanupKeepAlbumIds.id);
|
||||||
|
if (cleanupKeepAlbumIds != null) {
|
||||||
|
final ids = cleanupKeepAlbumIds.split(',').where((id) => id.isNotEmpty).toList();
|
||||||
|
await drift.metadataEntity.insertOnConflictUpdate(
|
||||||
|
MetadataEntityCompanion.insert(
|
||||||
|
key: MetadataKey.cleanupKeepAlbumIds.key,
|
||||||
|
value: MetadataKey.cleanupKeepAlbumIds.encode(ids),
|
||||||
|
updatedAt: Value(DateTime.now()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await migrator.deleteLegacyStoreRows([StoreKey.legacyCleanupKeepAlbumIds.id]);
|
||||||
|
}
|
||||||
|
await migrator.migrateBool(StoreKey.legacyCleanupKeepFavorites, MetadataKey.cleanupKeepFavorites);
|
||||||
|
await migrator.migrateEnumIndex(
|
||||||
|
StoreKey.legacyCleanupKeepMediaType,
|
||||||
|
MetadataKey.cleanupKeepMediaType,
|
||||||
|
AssetKeepType.values,
|
||||||
|
);
|
||||||
|
await migrator.migrateInt(StoreKey.legacyCleanupCutoffDaysAgo, MetadataKey.cleanupCutoffDaysAgo);
|
||||||
|
await migrator.migrateBool(StoreKey.legacyCleanupDefaultsInitialized, MetadataKey.cleanupDefaultsInitialized);
|
||||||
await migrator.complete();
|
await migrator.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +78,7 @@ class _StoreMigrator {
|
|||||||
_StoreMigrator(this._db);
|
_StoreMigrator(this._db);
|
||||||
|
|
||||||
Future<void> migrateEnumIndex<T extends Enum>(StoreKey<int> legacyKey, MetadataKey<T> newKey, List<T> values) async {
|
Future<void> migrateEnumIndex<T extends Enum>(StoreKey<int> legacyKey, MetadataKey<T> newKey, List<T> values) async {
|
||||||
final index = await _readLegacyStoreInt(legacyKey.id);
|
final index = await readLegacyStoreInt(legacyKey.id);
|
||||||
if (index == null) return;
|
if (index == null) return;
|
||||||
|
|
||||||
final enumValue = values.elementAtOrNull(index) ?? newKey.defaultValue;
|
final enumValue = values.elementAtOrNull(index) ?? newKey.defaultValue;
|
||||||
@@ -70,7 +91,7 @@ class _StoreMigrator {
|
|||||||
MetadataKey<T> newKey,
|
MetadataKey<T> newKey,
|
||||||
List<T> values,
|
List<T> values,
|
||||||
) async {
|
) async {
|
||||||
final name = await _readLegacyStoreString(legacyKey.id);
|
final name = await readLegacyStoreString(legacyKey.id);
|
||||||
if (name == null) return;
|
if (name == null) return;
|
||||||
|
|
||||||
final enumValue = values.firstWhere((e) => e.name == name, orElse: () => newKey.defaultValue);
|
final enumValue = values.firstWhere((e) => e.name == name, orElse: () => newKey.defaultValue);
|
||||||
@@ -79,7 +100,7 @@ class _StoreMigrator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> migrateBool(StoreKey<bool> legacyKey, MetadataKey<bool> newKey) async {
|
Future<void> migrateBool(StoreKey<bool> legacyKey, MetadataKey<bool> newKey) async {
|
||||||
final intValue = await _readLegacyStoreInt(legacyKey.id);
|
final intValue = await readLegacyStoreInt(legacyKey.id);
|
||||||
if (intValue == null) return;
|
if (intValue == null) return;
|
||||||
|
|
||||||
final boolValue = intValue != 0;
|
final boolValue = intValue != 0;
|
||||||
@@ -87,6 +108,14 @@ class _StoreMigrator {
|
|||||||
_migratedStoreIds.add(legacyKey.id);
|
_migratedStoreIds.add(legacyKey.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> migrateInt(StoreKey<int> legacyKey, MetadataKey<int> newKey) async {
|
||||||
|
final intValue = await readLegacyStoreInt(legacyKey.id);
|
||||||
|
if (intValue == null) return;
|
||||||
|
|
||||||
|
_cache[newKey] = intValue;
|
||||||
|
_migratedStoreIds.add(legacyKey.id);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> complete() async {
|
Future<void> complete() async {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
for (final entry in _cache.entries) {
|
for (final entry in _cache.entries) {
|
||||||
@@ -97,20 +126,20 @@ class _StoreMigrator {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await _deleteLegacyStoreRows(_migratedStoreIds);
|
await deleteLegacyStoreRows(_migratedStoreIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> _readLegacyStoreString(int id) async {
|
Future<String?> readLegacyStoreString(int id) async {
|
||||||
final row = await (_db.storeEntity.select()..where((t) => t.id.equals(id))).getSingleOrNull();
|
final row = await (_db.storeEntity.select()..where((t) => t.id.equals(id))).getSingleOrNull();
|
||||||
return row?.stringValue;
|
return row?.stringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int?> _readLegacyStoreInt(int id) async {
|
Future<int?> readLegacyStoreInt(int id) async {
|
||||||
final row = await (_db.storeEntity.select()..where((t) => t.id.equals(id))).getSingleOrNull();
|
final row = await (_db.storeEntity.select()..where((t) => t.id.equals(id))).getSingleOrNull();
|
||||||
return row?.intValue;
|
return row?.intValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _deleteLegacyStoreRows(List<int> ids) async {
|
Future<void> deleteLegacyStoreRows(List<int> ids) async {
|
||||||
if (ids.isEmpty) return;
|
if (ids.isEmpty) return;
|
||||||
await (_db.storeEntity.delete()..where((t) => t.id.isIn(ids))).go();
|
await (_db.storeEntity.delete()..where((t) => t.id.isIn(ids))).go();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/colors.dart';
|
import 'package:immich_mobile/constants/colors.dart';
|
||||||
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
@@ -27,16 +26,16 @@ class PrimaryColorSetting extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onUseSystemColorChange(bool newValue) {
|
onUseSystemColorChange(bool newValue) {
|
||||||
ref.read(metadataProvider).write(MetadataKey.dynamicTheme, newValue);
|
ref.read(metadataProvider).write(.themeDynamic, newValue);
|
||||||
popBottomSheet();
|
popBottomSheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
onPrimaryColorChange(ImmichColorPreset colorPreset) {
|
onPrimaryColorChange(ImmichColorPreset colorPreset) {
|
||||||
ref.read(metadataProvider).write(MetadataKey.primaryColor, colorPreset);
|
ref.read(metadataProvider).write(.themePrimaryColor, colorPreset);
|
||||||
|
|
||||||
//turn off system color setting
|
//turn off system color setting
|
||||||
if (themeConfig.dynamicTheme) {
|
if (themeConfig.dynamicTheme) {
|
||||||
ref.read(metadataProvider).write(MetadataKey.dynamicTheme, false);
|
ref.read(metadataProvider).write(.themeDynamic, false);
|
||||||
}
|
}
|
||||||
popBottomSheet();
|
popBottomSheet();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/metadata_key.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart';
|
||||||
@@ -23,7 +22,7 @@ class ThemeSetting extends HookConsumerWidget {
|
|||||||
|
|
||||||
void onThemeChange(bool isDark) {
|
void onThemeChange(bool isDark) {
|
||||||
currentTheme.value = isDark ? ThemeMode.dark : ThemeMode.light;
|
currentTheme.value = isDark ? ThemeMode.dark : ThemeMode.light;
|
||||||
ref.read(metadataProvider).write(MetadataKey.themeMode, currentTheme.value);
|
ref.read(metadataProvider).write(.themeMode, currentTheme.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSystemThemeChange(bool isSystem) {
|
void onSystemThemeChange(bool isSystem) {
|
||||||
@@ -40,11 +39,11 @@ class ThemeSetting extends HookConsumerWidget {
|
|||||||
currentTheme.value = ThemeMode.dark;
|
currentTheme.value = ThemeMode.dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ref.read(metadataProvider).write(MetadataKey.themeMode, currentTheme.value);
|
ref.read(metadataProvider).write(.themeMode, currentTheme.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSurfaceColorSettingChange(bool useColorfulInterface) {
|
void onSurfaceColorSettingChange(bool useColorfulInterface) {
|
||||||
ref.read(metadataProvider).write(MetadataKey.colorfulInterface, useColorfulInterface);
|
ref.read(metadataProvider).write(.themeColorfulInterface, useColorfulInterface);
|
||||||
colorfulInterface.value = useColorfulInterface;
|
colorfulInterface.value = useColorfulInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user