From 9fb2a1a02a25fd92949666b3454ba07b2b028852 Mon Sep 17 00:00:00 2001 From: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Date: Mon, 4 May 2026 00:49:35 +0700 Subject: [PATCH] migrate timeline config --- .../lib/domain/models/config/app_config.dart | 27 +++++++++++++---- .../lib/domain/models/config/map_config.dart | 8 ++--- .../domain/models/config/timeline_config.dart | 30 +++++++++++++++++++ mobile/lib/domain/models/metadata_key.dart | 11 +++++++ mobile/lib/domain/models/setting.model.dart | 3 -- mobile/lib/domain/models/store.model.dart | 19 +++++------- .../lib/domain/services/timeline.service.dart | 15 +++++----- .../repositories/metadata.repository.dart | 5 ++++ .../widgets/images/thumbnail_tile.widget.dart | 7 ++--- .../widgets/timeline/timeline.state.dart | 7 ++--- .../widgets/timeline/timeline.widget.dart | 10 +++---- .../infrastructure/timeline.provider.dart | 4 +-- mobile/lib/services/app_settings.service.dart | 3 -- mobile/lib/utils/migration.dart | 9 ++++++ .../asset_list_group_settings.dart | 16 +++++----- .../asset_list_layout_settings.dart | 14 ++++++--- .../asset_list_settings.dart | 12 ++++---- .../domain/services/store_service_test.dart | 8 ++--- 18 files changed, 137 insertions(+), 71 deletions(-) create mode 100644 mobile/lib/domain/models/config/timeline_config.dart diff --git a/mobile/lib/domain/models/config/app_config.dart b/mobile/lib/domain/models/config/app_config.dart index ed6254e168..956d9fd100 100644 --- a/mobile/lib/domain/models/config/app_config.dart +++ b/mobile/lib/domain/models/config/app_config.dart @@ -1,25 +1,40 @@ 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'; +import 'package:immich_mobile/domain/models/config/timeline_config.dart'; class AppConfig { final ThemeConfig theme; final CleanupConfig cleanup; final MapConfig map; + final TimelineConfig timeline; - const AppConfig({this.theme = const .new(), this.cleanup = const .new(), this.map = const .new()}); + const AppConfig({ + this.theme = const .new(), + this.cleanup = const .new(), + this.map = const .new(), + this.timeline = const .new(), + }); - AppConfig copyWith({ThemeConfig? theme, CleanupConfig? cleanup, MapConfig? map}) => - .new(theme: theme ?? this.theme, cleanup: cleanup ?? this.cleanup, map: map ?? this.map); + AppConfig copyWith({ThemeConfig? theme, CleanupConfig? cleanup, MapConfig? map, TimelineConfig? timeline}) => .new( + theme: theme ?? this.theme, + cleanup: cleanup ?? this.cleanup, + map: map ?? this.map, + timeline: timeline ?? this.timeline, + ); @override bool operator ==(Object other) => identical(this, other) || - (other is AppConfig && other.theme == theme && other.cleanup == cleanup && other.map == map); + (other is AppConfig && + other.theme == theme && + other.cleanup == cleanup && + other.map == map && + other.timeline == timeline); @override - int get hashCode => Object.hash(theme, cleanup, map); + int get hashCode => Object.hash(theme, cleanup, map, timeline); @override - String toString() => 'AppConfig(theme: $theme, cleanup: $cleanup, map: $map)'; + String toString() => 'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline)'; } diff --git a/mobile/lib/domain/models/config/map_config.dart b/mobile/lib/domain/models/config/map_config.dart index aa60f8879f..e37ab0f431 100644 --- a/mobile/lib/domain/models/config/map_config.dart +++ b/mobile/lib/domain/models/config/map_config.dart @@ -16,14 +16,14 @@ class MapConfig { }); MapConfig copyWith({ - int? relativeDate, - bool? showFavoriteOnly, + int? relativeDays, + bool? favoritesOnly, bool? includeArchived, ThemeMode? themeMode, bool? withPartners, }) => MapConfig( - relativeDays: relativeDate ?? this.relativeDays, - favoritesOnly: showFavoriteOnly ?? this.favoritesOnly, + relativeDays: relativeDays ?? this.relativeDays, + favoritesOnly: favoritesOnly ?? this.favoritesOnly, includeArchived: includeArchived ?? this.includeArchived, themeMode: themeMode ?? this.themeMode, withPartners: withPartners ?? this.withPartners, diff --git a/mobile/lib/domain/models/config/timeline_config.dart b/mobile/lib/domain/models/config/timeline_config.dart new file mode 100644 index 0000000000..4b6b9d5625 --- /dev/null +++ b/mobile/lib/domain/models/config/timeline_config.dart @@ -0,0 +1,30 @@ +import 'package:immich_mobile/domain/models/timeline.model.dart'; + +class TimelineConfig { + final int tilesPerRow; + final GroupAssetsBy groupAssetsBy; + final bool storageIndicator; + + const TimelineConfig({this.tilesPerRow = 4, this.groupAssetsBy = GroupAssetsBy.day, this.storageIndicator = true}); + + TimelineConfig copyWith({int? tilesPerRow, GroupAssetsBy? groupAssetsBy, bool? storageIndicator}) => TimelineConfig( + tilesPerRow: tilesPerRow ?? this.tilesPerRow, + groupAssetsBy: groupAssetsBy ?? this.groupAssetsBy, + storageIndicator: storageIndicator ?? this.storageIndicator, + ); + + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TimelineConfig && + other.tilesPerRow == tilesPerRow && + other.groupAssetsBy == groupAssetsBy && + other.storageIndicator == storageIndicator); + + @override + int get hashCode => Object.hash(tilesPerRow, groupAssetsBy, storageIndicator); + + @override + String toString() => + 'TimelineConfig(tilesPerRow: $tilesPerRow, groupAssetsBy: $groupAssetsBy, storageIndicator: $storageIndicator)'; +} diff --git a/mobile/lib/domain/models/metadata_key.dart b/mobile/lib/domain/models/metadata_key.dart index 1def3a7644..15df8ca490 100644 --- a/mobile/lib/domain/models/metadata_key.dart +++ b/mobile/lib/domain/models/metadata_key.dart @@ -7,6 +7,7 @@ import 'package:immich_mobile/constants/enums.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'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; enum MetadataDomain { appConfig('config.app'), @@ -23,6 +24,16 @@ enum MetadataKey { themeDynamic(.appConfig, 'theme.dynamic', false), themeColorfulInterface(.appConfig, 'theme.colorfulInterface', true), + // Timeline + timelineTilesPerRow(.appConfig, 'timeline.tilesPerRow', 4), + timelineGroupAssetsBy( + .appConfig, + 'timeline.groupAssetsBy', + GroupAssetsBy.day, + _EnumCodec(GroupAssetsBy.values), + ), + timelineStorageIndicator(.appConfig, 'timeline.storageIndicator', true), + // Log logLevel(.systemConfig, 'log.level', .info, _EnumCodec(LogLevel.values)), diff --git a/mobile/lib/domain/models/setting.model.dart b/mobile/lib/domain/models/setting.model.dart index 2c46507331..de50ec22f4 100644 --- a/mobile/lib/domain/models/setting.model.dart +++ b/mobile/lib/domain/models/setting.model.dart @@ -1,9 +1,6 @@ import 'package:immich_mobile/domain/models/store.model.dart'; enum Setting { - tilesPerRow(StoreKey.tilesPerRow, 4), - groupAssetsBy(StoreKey.groupAssetsBy, 0), - showStorageIndicator(StoreKey.storageIndicator, true), loadOriginal(StoreKey.loadOriginal, false), loadOriginalVideo(StoreKey.loadOriginalVideo, false), autoPlayVideo(StoreKey.autoPlayVideo, true), diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 0cedf557dc..dd836bf037 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -19,24 +19,17 @@ enum StoreKey { backgroundBackup._(14), sslClientCertData._(15), sslClientPasswd._(16), - // user settings from [AppSettingsEnum] below: - loadOriginal._(101), - tilesPerRow._(103), - groupAssetsBy._(105), uploadErrorNotificationGracePeriod._(106), - storageIndicator._(109), thumbnailCacheSize._(110), imageCacheSize._(111), albumThumbnailCacheSize._(112), selectedAlbumSortOrder._(113), advancedTroubleshooting._(114), preferRemoteImage._(116), - loopVideo._(117), selfSignedCert._(120), selectedAlbumSortReverse._(123), enableHapticFeedback._(126), customHeaders._(127), - syncAlbums._(131), // Auto endpoint switching @@ -45,17 +38,16 @@ enum StoreKey { localEndpoint._(134), externalEndpointList._(135), - // Video settings - loadOriginalVideo._(136), manageLocalMediaAndroid._(137), - // Read-only Mode settings readonlyModeEnabled._(138), - - autoPlayVideo._(139), albumGridView._(140), + loadOriginal._(101), // Image viewer navigation settings + loopVideo._(117), + loadOriginalVideo._(136), + autoPlayVideo._(139), tapToNavigate._(141), // Experimental stuff @@ -74,6 +66,9 @@ enum StoreKey { legacyCleanupKeepAlbumIds._(1010), legacyCleanupCutoffDaysAgo._(1011), legacyCleanupDefaultsInitialized._(1012), + legacyTilesPerRow._(103), + legacyGroupAssetsBy._(105), + legacyStorageIndicator._(109), legacyMapRelativeDate._(119), legacyMapShowFavoriteOnly._(118), legacyMapIncludeArchived._(121), diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index a055f8bcae..ee2bb39850 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -5,10 +5,9 @@ import 'package:collection/collection.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/events.model.dart'; -import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; -import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; @@ -39,14 +38,16 @@ enum TimelineOrigin { class TimelineFactory { final DriftTimelineRepository _timelineRepository; - final SettingsService _settingsService; + final MetadataRepository _metadataRepository; - const TimelineFactory({required DriftTimelineRepository timelineRepository, required SettingsService settingsService}) - : _timelineRepository = timelineRepository, - _settingsService = settingsService; + const TimelineFactory({ + required DriftTimelineRepository timelineRepository, + required MetadataRepository metadataRepository, + }) : _timelineRepository = timelineRepository, + _metadataRepository = metadataRepository; GroupAssetsBy get groupBy { - final group = GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; + final group = _metadataRepository.appConfig.timeline.groupAssetsBy; // We do not support auto grouping in the new timeline yet, fallback to day grouping return group == GroupAssetsBy.auto ? GroupAssetsBy.day : group; } diff --git a/mobile/lib/infrastructure/repositories/metadata.repository.dart b/mobile/lib/infrastructure/repositories/metadata.repository.dart index 5f049929a9..06568d93c7 100644 --- a/mobile/lib/infrastructure/repositories/metadata.repository.dart +++ b/mobile/lib/infrastructure/repositories/metadata.repository.dart @@ -121,6 +121,11 @@ extension on MetadataDomain { themeMode: repo._read(.mapThemeMode), withPartners: repo._read(.mapWithPartners), ), + timeline: .new( + tilesPerRow: repo._read(.timelineTilesPerRow), + groupAssetsBy: repo._read(.timelineGroupAssetsBy), + storageIndicator: repo._read(.timelineStorageIndicator), + ), ); case .systemConfig: repo._systemConfig = .new(logLevel: repo._read(.logLevel)); diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index 406ca30820..8720cc4253 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -2,15 +2,14 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/duration_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; +import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart'; import 'package:immich_mobile/providers/backup/asset_upload_progress.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class ThumbnailTile extends ConsumerStatefulWidget { @@ -61,7 +60,7 @@ class _ThumbnailTileState extends ConsumerState { ); final bool storageIndicator = - ref.watch(settingsProvider.select((s) => s.get(Setting.showStorageIndicator))) && widget.showStorageIndicator; + ref.watch(appConfigProvider.select((s) => s.timeline.storageIndicator)) && widget.showStorageIndicator; if (!isCurrentAsset) { _hideIndicators = false; diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart index 1e1d4130f7..7b88800f22 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.state.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -1,12 +1,11 @@ import 'dart:math' as math; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; import 'package:immich_mobile/presentation/widgets/timeline/fixed/segment_builder.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; -import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; class TimelineArgs { @@ -93,7 +92,7 @@ final timelineSegmentProvider = StreamProvider.autoDispose>((ref) final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); final tileExtent = math.max(0, availableTileWidth) / columnCount; - final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; + final groupBy = args.groupBy ?? ref.watch(appConfigProvider.select((config) => config.timeline.groupAssetsBy)); final timelineService = ref.watch(timelineServiceProvider); yield* timelineService.watchBuckets().map((buckets) { @@ -102,7 +101,7 @@ final timelineSegmentProvider = StreamProvider.autoDispose>((ref) tileHeight: tileExtent, columnCount: columnCount, spacing: spacing, - groupBy: groupBy, + groupBy: groupBy!, ).generate(); }); }, dependencies: [timelineServiceProvider, timelineArgsProvider]); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 578bd37a23..a189069631 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -10,7 +10,7 @@ import 'package:flutter/rendering.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/events.model.dart'; -import 'package:immich_mobile/domain/models/setting.model.dart'; +import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; @@ -22,8 +22,8 @@ import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/providers/infrastructure/readonly_mode.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart'; @@ -74,7 +74,7 @@ class Timeline extends StatelessWidget { (ref) => TimelineArgs( maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight, - columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))), + columnCount: ref.watch(appConfigProvider.select((config) => config.timeline.tilesPerRow)), showStorageIndicator: showStorageIndicator, withStack: withStack, groupBy: groupBy, @@ -161,7 +161,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { _scrollController = ScrollController(onAttach: _restoreAssetPosition); _eventSubscription = EventStream.shared.listen(_onEvent); - final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); + final currentTilesPerRow = ref.read(appConfigProvider.select((config) => config.timeline.tilesPerRow)); _perRow = currentTilesPerRow; _scaleFactor = 7.0 - _perRow; _baseScaleFactor = _scaleFactor; @@ -453,7 +453,7 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { _restoreAssetIndex = targetAssetIndex; }); - ref.read(settingsProvider.notifier).set(Setting.tilesPerRow, _perRow); + ref.read(metadataProvider).write(MetadataKey.timelineTilesPerRow, _perRow); } }; }, diff --git a/mobile/lib/providers/infrastructure/timeline.provider.dart b/mobile/lib/providers/infrastructure/timeline.provider.dart index 06ec0242b2..9f2fdec519 100644 --- a/mobile/lib/providers/infrastructure/timeline.provider.dart +++ b/mobile/lib/providers/infrastructure/timeline.provider.dart @@ -3,7 +3,7 @@ import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; final timelineRepositoryProvider = Provider( @@ -29,7 +29,7 @@ final timelineServiceProvider = Provider( final timelineFactoryProvider = Provider( (ref) => TimelineFactory( timelineRepository: ref.watch(timelineRepositoryProvider), - settingsService: ref.watch(settingsProvider), + metadataRepository: ref.watch(metadataProvider), ), ); diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index a6c6d4418f..a98912228c 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -3,14 +3,11 @@ import 'package:immich_mobile/entities/store.entity.dart'; enum AppSettingsEnum { loadOriginal(StoreKey.loadOriginal, "loadOriginal", false), - tilesPerRow(StoreKey.tilesPerRow, "tilesPerRow", 4), - groupAssetsBy(StoreKey.groupAssetsBy, "groupBy", 0), uploadErrorNotificationGracePeriod( StoreKey.uploadErrorNotificationGracePeriod, "uploadErrorNotificationGracePeriod", 2, ), - storageIndicator(StoreKey.storageIndicator, "storageIndicator", true), thumbnailCacheSize(StoreKey.thumbnailCacheSize, "thumbnailCacheSize", 10000), imageCacheSize(StoreKey.imageCacheSize, "imageCacheSize", 350), albumThumbnailCacheSize(StoreKey.albumThumbnailCacheSize, "albumThumbnailCacheSize", 200), diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 144faf1b16..ab03ff0601 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -7,6 +7,7 @@ import 'package:immich_mobile/constants/enums.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/store.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/metadata.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; @@ -75,6 +76,14 @@ Future _migrateTo26(Drift drift) async { await migrator.migrateBool(StoreKey.legacyMapIncludeArchived, MetadataKey.mapIncludeArchived); await migrator.migrateEnumIndex(StoreKey.legacyMapThemeMode, MetadataKey.mapThemeMode, ThemeMode.values); await migrator.migrateBool(StoreKey.legacyMapwithPartners, MetadataKey.mapWithPartners); + // Timeline + await migrator.migrateInt(StoreKey.legacyTilesPerRow, MetadataKey.timelineTilesPerRow); + await migrator.migrateEnumIndex( + StoreKey.legacyGroupAssetsBy, + MetadataKey.timelineGroupAssetsBy, + GroupAssetsBy.values, + ); + await migrator.migrateBool(StoreKey.legacyStorageIndicator, MetadataKey.timelineStorageIndicator); await migrator.complete(); } diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart index 42ea3acfc0..b9f81da79e 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart @@ -1,12 +1,13 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart'; @@ -15,18 +16,17 @@ class GroupSettings extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final groupByIndex = useAppSettingsState(AppSettingsEnum.groupAssetsBy); - final groupBy = GroupAssetsBy.values[groupByIndex.value]; + final groupBy = useValueNotifier(ref.watch(appConfigProvider.select((s) => s.timeline.groupAssetsBy))); Future updateAppSettings(GroupAssetsBy groupBy) async { - await ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.groupAssetsBy, groupBy.index); + await ref.read(metadataProvider).write(MetadataKey.timelineGroupAssetsBy, groupBy); ref.invalidate(appSettingsServiceProvider); } void changeGroupValue(GroupAssetsBy? value) { if (value != null) { - groupByIndex.value = value.index; - unawaited(updateAppSettings(groupBy)); + groupBy.value = value; + unawaited(updateAppSettings(value)); } } @@ -52,7 +52,7 @@ class GroupSettings extends HookConsumerWidget { value: GroupAssetsBy.auto, ), ], - groupBy: groupBy, + groupBy: groupBy.value, onRadioChanged: changeGroupValue, ), ], diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart index 55c8195947..20025286f4 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart @@ -1,10 +1,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; @@ -13,7 +14,10 @@ class LayoutSettings extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final tilesPerRow = useAppSettingsState(AppSettingsEnum.tilesPerRow); + final tilesPerRow = useState(ref.read(appConfigProvider.select((s) => s.timeline.tilesPerRow))); + useValueChanged(tilesPerRow.value, (_, __) { + ref.read(metadataProvider).write(MetadataKey.timelineTilesPerRow, tilesPerRow.value); + }); return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -29,7 +33,9 @@ class LayoutSettings extends HookConsumerWidget { maxValue: 6, minValue: 2, noDivisons: 4, - onChangeEnd: (_) => ref.invalidate(appSettingsServiceProvider), + onChangeEnd: (value) { + ref.invalidate(appSettingsServiceProvider); + }, ), ], ); diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart index 82394bdc07..21d751c26f 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart @@ -1,10 +1,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/metadata_key.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/metadata.provider.dart'; import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; -import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_group_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_layout_settings.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; @@ -15,13 +16,14 @@ class AssetListSettings extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final showStorageIndicator = useAppSettingsState(AppSettingsEnum.storageIndicator); + final storageIndicator = useValueNotifier(ref.watch(appConfigProvider.select((s) => s.timeline.storageIndicator))); final assetListSetting = [ SettingsSwitchListTile( - valueNotifier: showStorageIndicator, + valueNotifier: storageIndicator, title: 'theme_setting_asset_list_storage_indicator_title'.tr(), - onChanged: (_) { + onChanged: (value) { + ref.read(metadataProvider).write(MetadataKey.timelineStorageIndicator, value); ref.invalidate(appSettingsServiceProvider); ref.invalidate(settingsProvider); }, diff --git a/mobile/test/domain/services/store_service_test.dart b/mobile/test/domain/services/store_service_test.dart index 8ceb1e3c9c..9f6a30eefe 100644 --- a/mobile/test/domain/services/store_service_test.dart +++ b/mobile/test/domain/services/store_service_test.dart @@ -10,7 +10,7 @@ import '../../infrastructure/repository.mock.dart'; const _kAccessToken = '#ThisIsAToken'; const _kBackgroundBackup = false; -const _kGroupAssetsBy = 2; +const _kVersion = 2; final _kBackupFailedSince = DateTime.utc(2023); void main() { @@ -31,7 +31,7 @@ void main() { (_) async => [ const StoreDto(StoreKey.accessToken, _kAccessToken), const StoreDto(StoreKey.backgroundBackup, _kBackgroundBackup), - const StoreDto(StoreKey.groupAssetsBy, _kGroupAssetsBy), + const StoreDto(StoreKey.version, _kVersion), StoreDto(StoreKey.backupFailedSince, _kBackupFailedSince), ], ); @@ -50,7 +50,7 @@ void main() { verify(() => mockDriftStoreRepo.getAll()).called(1); expect(sut.tryGet(StoreKey.accessToken), _kAccessToken); expect(sut.tryGet(StoreKey.backgroundBackup), _kBackgroundBackup); - expect(sut.tryGet(StoreKey.groupAssetsBy), _kGroupAssetsBy); + expect(sut.tryGet(StoreKey.version), _kVersion); expect(sut.tryGet(StoreKey.backupFailedSince), _kBackupFailedSince); // Other keys should be null expect(sut.tryGet(StoreKey.currentUser), isNull); @@ -152,7 +152,7 @@ void main() { verify(() => mockDriftStoreRepo.deleteAll()).called(1); expect(sut.tryGet(StoreKey.accessToken), isNull); expect(sut.tryGet(StoreKey.backgroundBackup), isNull); - expect(sut.tryGet(StoreKey.groupAssetsBy), isNull); + expect(sut.tryGet(StoreKey.version), isNull); expect(sut.tryGet(StoreKey.backupFailedSince), isNull); }); });