From 4d6a50c2cbd1ec76963e07e09dc0dec068c1db25 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 12 May 2026 10:43:52 +0700 Subject: [PATCH] refactor: move map config to metadata table (#28226) * 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 * migrate mapconfig --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../lib/domain/models/config/app_config.dart | 15 +++--- .../lib/domain/models/config/map_config.dart | 48 +++++++++++++++++++ mobile/lib/domain/models/metadata_key.dart | 7 +++ mobile/lib/domain/models/store.model.dart | 11 ++--- .../repositories/metadata.repository.dart | 7 +++ .../presentation/widgets/map/map.state.dart | 24 +++++----- .../lib/providers/map/map_state.provider.dart | 26 +++++----- mobile/lib/services/app_settings.service.dart | 5 -- mobile/lib/utils/migration.dart | 10 +++- 9 files changed, 110 insertions(+), 43 deletions(-) create mode 100644 mobile/lib/domain/models/config/map_config.dart diff --git a/mobile/lib/domain/models/config/app_config.dart b/mobile/lib/domain/models/config/app_config.dart index 7179137af4..ed6254e168 100644 --- a/mobile/lib/domain/models/config/app_config.dart +++ b/mobile/lib/domain/models/config/app_config.dart @@ -1,22 +1,25 @@ import 'package:immich_mobile/domain/models/config/cleanup_config.dart'; +import 'package:immich_mobile/domain/models/config/map_config.dart'; import 'package:immich_mobile/domain/models/config/theme_config.dart'; class AppConfig { final ThemeConfig theme; final CleanupConfig cleanup; + final MapConfig map; - const AppConfig({this.theme = const .new(), this.cleanup = const .new()}); + const AppConfig({this.theme = const .new(), this.cleanup = const .new(), this.map = const .new()}); - AppConfig copyWith({ThemeConfig? theme, CleanupConfig? cleanup}) => - .new(theme: theme ?? this.theme, cleanup: cleanup ?? this.cleanup); + AppConfig copyWith({ThemeConfig? theme, CleanupConfig? cleanup, MapConfig? map}) => + .new(theme: theme ?? this.theme, cleanup: cleanup ?? this.cleanup, map: map ?? this.map); @override bool operator ==(Object other) => - identical(this, other) || (other is AppConfig && other.theme == theme && other.cleanup == cleanup); + identical(this, other) || + (other is AppConfig && other.theme == theme && other.cleanup == cleanup && other.map == map); @override - int get hashCode => Object.hash(theme, cleanup); + int get hashCode => Object.hash(theme, cleanup, map); @override - String toString() => 'AppConfig(theme: $theme, cleanup: $cleanup)'; + String toString() => 'AppConfig(theme: $theme, cleanup: $cleanup, map: $map)'; } diff --git a/mobile/lib/domain/models/config/map_config.dart b/mobile/lib/domain/models/config/map_config.dart new file mode 100644 index 0000000000..e37ab0f431 --- /dev/null +++ b/mobile/lib/domain/models/config/map_config.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +class MapConfig { + final int relativeDays; + final bool favoritesOnly; + final bool includeArchived; + final ThemeMode themeMode; + final bool withPartners; + + const MapConfig({ + this.relativeDays = 0, + this.favoritesOnly = false, + this.includeArchived = false, + this.themeMode = ThemeMode.system, + this.withPartners = false, + }); + + MapConfig copyWith({ + int? relativeDays, + bool? favoritesOnly, + bool? includeArchived, + ThemeMode? themeMode, + bool? withPartners, + }) => MapConfig( + relativeDays: relativeDays ?? this.relativeDays, + favoritesOnly: favoritesOnly ?? this.favoritesOnly, + includeArchived: includeArchived ?? this.includeArchived, + themeMode: themeMode ?? this.themeMode, + withPartners: withPartners ?? this.withPartners, + ); + + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MapConfig && + other.relativeDays == relativeDays && + other.favoritesOnly == favoritesOnly && + other.includeArchived == includeArchived && + other.themeMode == themeMode && + other.withPartners == withPartners); + + @override + int get hashCode => Object.hash(relativeDays, favoritesOnly, includeArchived, themeMode, withPartners); + + @override + String toString() => + 'MapConfig(relativeDays: $relativeDays, favoritesOnly: $favoritesOnly, includeArchived: $includeArchived, themeMode: $themeMode, withPartners: $withPartners)'; +} diff --git a/mobile/lib/domain/models/metadata_key.dart b/mobile/lib/domain/models/metadata_key.dart index 356882670a..00b5a5f675 100644 --- a/mobile/lib/domain/models/metadata_key.dart +++ b/mobile/lib/domain/models/metadata_key.dart @@ -26,6 +26,13 @@ enum MetadataKey { // Log logLevel(.systemConfig, 'log.level', .info, _EnumCodec(LogLevel.values)), + // Map + mapShowFavoriteOnly(.appConfig, 'map.showFavoriteOnly', false), + mapRelativeDate(.appConfig, 'map.relativeDate', 0), + mapIncludeArchived(.appConfig, 'map.includeArchived', false), + mapThemeMode(.appConfig, 'map.themeMode', .system, _EnumCodec(ThemeMode.values)), + mapWithPartners(.appConfig, 'map.withPartners', false), + // Cleanup cleanupKeepFavorites(.appConfig, 'cleanup.keepFavorites', true), cleanupKeepMediaType( diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index d0f2b52080..125866a4ee 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -36,15 +36,9 @@ enum StoreKey { advancedTroubleshooting._(114), preferRemoteImage._(116), loopVideo._(117), - // map related settings - mapShowFavoriteOnly._(118), - mapRelativeDate._(119), selfSignedCert._(120), - mapIncludeArchived._(121), ignoreIcloudAssets._(122), selectedAlbumSortReverse._(123), - mapThemeMode._(124), - mapwithPartners._(125), enableHapticFeedback._(126), customHeaders._(127), @@ -93,6 +87,11 @@ enum StoreKey { legacyCleanupKeepAlbumIds._(1010), legacyCleanupCutoffDaysAgo._(1011), legacyCleanupDefaultsInitialized._(1012), + legacyMapRelativeDate._(119), + legacyMapShowFavoriteOnly._(118), + legacyMapIncludeArchived._(121), + legacyMapThemeMode._(124), + legacyMapwithPartners._(125), legacyLogLevel._(115); const StoreKey._(this.id); diff --git a/mobile/lib/infrastructure/repositories/metadata.repository.dart b/mobile/lib/infrastructure/repositories/metadata.repository.dart index 4b9ff04e79..2f76ee22ae 100644 --- a/mobile/lib/infrastructure/repositories/metadata.repository.dart +++ b/mobile/lib/infrastructure/repositories/metadata.repository.dart @@ -120,6 +120,13 @@ extension on MetadataDomain { cutoffDaysAgo: repo._read(.cleanupCutoffDaysAgo), defaultsInitialized: repo._read(.cleanupDefaultsInitialized), ), + map: .new( + relativeDays: repo._read(.mapRelativeDate), + favoritesOnly: repo._read(.mapShowFavoriteOnly), + includeArchived: repo._read(.mapIncludeArchived), + themeMode: repo._read(.mapThemeMode), + withPartners: repo._read(.mapWithPartners), + ), ); case .systemConfig: repo._systemConfig = .new(logLevel: repo._read(.logLevel)); diff --git a/mobile/lib/presentation/widgets/map/map.state.dart b/mobile/lib/presentation/widgets/map/map.state.dart index bfd3011050..b90ce922aa 100644 --- a/mobile/lib/presentation/widgets/map/map.state.dart +++ b/mobile/lib/presentation/widgets/map/map.state.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/events.model.dart'; +import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/infrastructure/map.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/providers/map/map_state.provider.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; class MapState { @@ -81,38 +81,38 @@ class MapStateNotifier extends Notifier { } void switchFavoriteOnly(bool isFavoriteOnly) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapShowFavoriteOnly, isFavoriteOnly); + ref.read(metadataProvider).write(MetadataKey.mapShowFavoriteOnly, isFavoriteOnly); state = state.copyWith(onlyFavorites: isFavoriteOnly); EventStream.shared.emit(const MapMarkerReloadEvent()); } void switchIncludeArchived(bool isIncludeArchived) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapIncludeArchived, isIncludeArchived); + ref.read(metadataProvider).write(MetadataKey.mapIncludeArchived, isIncludeArchived); state = state.copyWith(includeArchived: isIncludeArchived); EventStream.shared.emit(const MapMarkerReloadEvent()); } void switchWithPartners(bool isWithPartners) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapwithPartners, isWithPartners); + ref.read(metadataProvider).write(MetadataKey.mapWithPartners, isWithPartners); state = state.copyWith(withPartners: isWithPartners); EventStream.shared.emit(const MapMarkerReloadEvent()); } void setRelativeTime(int relativeDays) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapRelativeDate, relativeDays); + ref.read(metadataProvider).write(MetadataKey.mapRelativeDate, relativeDays); state = state.copyWith(relativeDays: relativeDays); EventStream.shared.emit(const MapMarkerReloadEvent()); } @override MapState build() { - final appSettingsService = ref.read(appSettingsServiceProvider); + final mapConfig = ref.read(appConfigProvider.select((config) => config.map)); return MapState( - themeMode: ThemeMode.values[appSettingsService.getSetting(AppSettingsEnum.mapThemeMode)], - onlyFavorites: appSettingsService.getSetting(AppSettingsEnum.mapShowFavoriteOnly), - includeArchived: appSettingsService.getSetting(AppSettingsEnum.mapIncludeArchived), - withPartners: appSettingsService.getSetting(AppSettingsEnum.mapwithPartners), - relativeDays: appSettingsService.getSetting(AppSettingsEnum.mapRelativeDate), + themeMode: mapConfig.themeMode, + onlyFavorites: mapConfig.favoritesOnly, + includeArchived: mapConfig.includeArchived, + withPartners: mapConfig.withPartners, + relativeDays: mapConfig.relativeDays, bounds: LatLngBounds(northeast: const LatLng(0, 0), southwest: const LatLng(0, 0)), ); } diff --git a/mobile/lib/providers/map/map_state.provider.dart b/mobile/lib/providers/map/map_state.provider.dart index 63b277ac83..b0a59f6a1e 100644 --- a/mobile/lib/providers/map/map_state.provider.dart +++ b/mobile/lib/providers/map/map_state.provider.dart @@ -1,38 +1,38 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/models/map/map_state.model.dart'; -import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; final mapStateNotifierProvider = NotifierProvider(MapStateNotifier.new); class MapStateNotifier extends Notifier { @override MapState build() { - final appSettingsProvider = ref.read(appSettingsServiceProvider); + final mapConfig = ref.read(appConfigProvider.select((config) => config.map)); final lightStyleUrl = ref.read(serverInfoProvider).serverConfig.mapLightStyleUrl; final darkStyleUrl = ref.read(serverInfoProvider).serverConfig.mapDarkStyleUrl; return MapState( - themeMode: ThemeMode.values[appSettingsProvider.getSetting(AppSettingsEnum.mapThemeMode)], - showFavoriteOnly: appSettingsProvider.getSetting(AppSettingsEnum.mapShowFavoriteOnly), - includeArchived: appSettingsProvider.getSetting(AppSettingsEnum.mapIncludeArchived), - withPartners: appSettingsProvider.getSetting(AppSettingsEnum.mapwithPartners), - relativeTime: appSettingsProvider.getSetting(AppSettingsEnum.mapRelativeDate), + themeMode: mapConfig.themeMode, + showFavoriteOnly: mapConfig.favoritesOnly, + includeArchived: mapConfig.includeArchived, + withPartners: mapConfig.withPartners, + relativeTime: mapConfig.relativeDays, lightStyleFetched: AsyncData(lightStyleUrl), darkStyleFetched: AsyncData(darkStyleUrl), ); } void switchTheme(ThemeMode mode) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapThemeMode, mode.index); + ref.read(metadataProvider).write(MetadataKey.mapThemeMode, mode); state = state.copyWith(themeMode: mode); } void switchFavoriteOnly(bool isFavoriteOnly) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapShowFavoriteOnly, isFavoriteOnly); + ref.read(metadataProvider).write(MetadataKey.mapShowFavoriteOnly, isFavoriteOnly); state = state.copyWith(showFavoriteOnly: isFavoriteOnly, shouldRefetchMarkers: true); } @@ -41,17 +41,17 @@ class MapStateNotifier extends Notifier { } void switchIncludeArchived(bool isIncludeArchived) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapIncludeArchived, isIncludeArchived); + ref.read(metadataProvider).write(MetadataKey.mapIncludeArchived, isIncludeArchived); state = state.copyWith(includeArchived: isIncludeArchived, shouldRefetchMarkers: true); } void switchWithPartners(bool isWithPartners) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapwithPartners, isWithPartners); + ref.read(metadataProvider).write(MetadataKey.mapWithPartners, isWithPartners); state = state.copyWith(withPartners: isWithPartners, shouldRefetchMarkers: true); } void setRelativeTime(int relativeTime) { - ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapRelativeDate, relativeTime); + ref.read(metadataProvider).write(MetadataKey.mapRelativeDate, relativeTime); state = state.copyWith(relativeTime: relativeTime, shouldRefetchMarkers: true); } } diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index 72fd389871..cbb0745275 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -30,11 +30,6 @@ enum AppSettingsEnum { loadOriginalVideo(StoreKey.loadOriginalVideo, "loadOriginalVideo", false), autoPlayVideo(StoreKey.autoPlayVideo, "autoPlayVideo", true), tapToNavigate(StoreKey.tapToNavigate, "tapToNavigate", false), - mapThemeMode(StoreKey.mapThemeMode, null, 0), - mapShowFavoriteOnly(StoreKey.mapShowFavoriteOnly, null, false), - mapIncludeArchived(StoreKey.mapIncludeArchived, null, false), - mapwithPartners(StoreKey.mapwithPartners, null, false), - mapRelativeDate(StoreKey.mapRelativeDate, null, 0), allowSelfSignedSSLCert(StoreKey.selfSignedCert, null, false), ignoreIcloudAssets(StoreKey.ignoreIcloudAssets, null, false), selectedAlbumSortReverse(StoreKey.selectedAlbumSortReverse, null, true), diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 8fcc5e7a6b..a0397e5ca4 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -46,11 +46,13 @@ Future _migrateTo25() async { Future _migrateTo26(Drift drift) async { final migrator = _StoreMigrator(drift); - await migrator.migrateEnumName(StoreKey.legacyThemeMode, MetadataKey.themeMode, ThemeMode.values); await migrator.migrateEnumIndex(StoreKey.legacyLogLevel, MetadataKey.logLevel, LogLevel.values); + // Theme + await migrator.migrateEnumName(StoreKey.legacyThemeMode, MetadataKey.themeMode, ThemeMode.values); await migrator.migrateEnumName(StoreKey.legacyPrimaryColor, MetadataKey.themePrimaryColor, ImmichColorPreset.values); await migrator.migrateBool(StoreKey.legacyDynamicTheme, MetadataKey.themeDynamic); await migrator.migrateBool(StoreKey.legacyColorfulInterface, MetadataKey.themeColorfulInterface); + // Cleanup final cleanupKeepAlbumIds = await migrator.readLegacyStoreString(StoreKey.legacyCleanupKeepAlbumIds.id); if (cleanupKeepAlbumIds != null) { final ids = cleanupKeepAlbumIds.split(',').where((id) => id.isNotEmpty).toList(); @@ -71,6 +73,12 @@ Future _migrateTo26(Drift drift) async { ); await migrator.migrateInt(StoreKey.legacyCleanupCutoffDaysAgo, MetadataKey.cleanupCutoffDaysAgo); await migrator.migrateBool(StoreKey.legacyCleanupDefaultsInitialized, MetadataKey.cleanupDefaultsInitialized); + // Map + await migrator.migrateBool(StoreKey.legacyMapShowFavoriteOnly, MetadataKey.mapShowFavoriteOnly); + await migrator.migrateInt(StoreKey.legacyMapRelativeDate, MetadataKey.mapRelativeDate); + await migrator.migrateBool(StoreKey.legacyMapIncludeArchived, MetadataKey.mapIncludeArchived); + await migrator.migrateEnumIndex(StoreKey.legacyMapThemeMode, MetadataKey.mapThemeMode, ThemeMode.values); + await migrator.migrateBool(StoreKey.legacyMapwithPartners, MetadataKey.mapWithPartners); await migrator.complete(); }