diff --git a/mobile/lib/domain/models/config/app_config.dart b/mobile/lib/domain/models/config/app_config.dart index 956d9fd100..942260158b 100644 --- a/mobile/lib/domain/models/config/app_config.dart +++ b/mobile/lib/domain/models/config/app_config.dart @@ -1,4 +1,5 @@ import 'package:immich_mobile/domain/models/config/cleanup_config.dart'; +import 'package:immich_mobile/domain/models/config/image_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'; @@ -8,19 +9,28 @@ class AppConfig { final CleanupConfig cleanup; final MapConfig map; final TimelineConfig timeline; + final ImageConfig image; const AppConfig({ this.theme = const .new(), this.cleanup = const .new(), this.map = const .new(), this.timeline = const .new(), + this.image = const .new(), }); - AppConfig copyWith({ThemeConfig? theme, CleanupConfig? cleanup, MapConfig? map, TimelineConfig? timeline}) => .new( + AppConfig copyWith({ + ThemeConfig? theme, + CleanupConfig? cleanup, + MapConfig? map, + TimelineConfig? timeline, + ImageConfig? image, + }) => .new( theme: theme ?? this.theme, cleanup: cleanup ?? this.cleanup, map: map ?? this.map, timeline: timeline ?? this.timeline, + image: image ?? this.image, ); @override @@ -30,11 +40,12 @@ class AppConfig { other.theme == theme && other.cleanup == cleanup && other.map == map && - other.timeline == timeline); + other.timeline == timeline && + other.image == image); @override - int get hashCode => Object.hash(theme, cleanup, map, timeline); + int get hashCode => Object.hash(theme, cleanup, map, timeline, image); @override - String toString() => 'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline)'; + String toString() => 'AppConfig(theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image)'; } diff --git a/mobile/lib/domain/models/config/image_config.dart b/mobile/lib/domain/models/config/image_config.dart new file mode 100644 index 0000000000..8410a9010b --- /dev/null +++ b/mobile/lib/domain/models/config/image_config.dart @@ -0,0 +1,20 @@ +class ImageConfig { + final bool preferRemote; + final bool loadOriginal; + + const ImageConfig({this.preferRemote = false, this.loadOriginal = false}); + + ImageConfig copyWith({bool? preferRemote, bool? loadOriginal}) => + ImageConfig(preferRemote: preferRemote ?? this.preferRemote, loadOriginal: loadOriginal ?? this.loadOriginal); + + @override + bool operator ==(Object other) => + identical(this, other) || + (other is ImageConfig && other.preferRemote == preferRemote && other.loadOriginal == loadOriginal); + + @override + int get hashCode => Object.hash(preferRemote, loadOriginal); + + @override + String toString() => 'ImageConfig(preferRemoteImage: $preferRemote, loadOriginal: $loadOriginal)'; +} diff --git a/mobile/lib/domain/models/metadata_key.dart b/mobile/lib/domain/models/metadata_key.dart index 0d75ec8be3..c692d77f6b 100644 --- a/mobile/lib/domain/models/metadata_key.dart +++ b/mobile/lib/domain/models/metadata_key.dart @@ -24,6 +24,10 @@ enum MetadataKey { themeDynamic(.appConfig, 'theme.dynamic', false), themeColorfulInterface(.appConfig, 'theme.colorfulInterface', true), + // Image + imagePreferRemote(.appConfig, 'image.preferRemote', false), + imageLoadOriginal(.appConfig, 'image.loadOriginal', false), + // Timeline timelineTilesPerRow(.appConfig, 'timeline.tilesPerRow', 4), timelineGroupAssetsBy( diff --git a/mobile/lib/domain/models/setting.model.dart b/mobile/lib/domain/models/setting.model.dart index de50ec22f4..f7cb340ee3 100644 --- a/mobile/lib/domain/models/setting.model.dart +++ b/mobile/lib/domain/models/setting.model.dart @@ -1,10 +1,8 @@ import 'package:immich_mobile/domain/models/store.model.dart'; enum Setting { - loadOriginal(StoreKey.loadOriginal, false), loadOriginalVideo(StoreKey.loadOriginalVideo, false), autoPlayVideo(StoreKey.autoPlayVideo, true), - preferRemoteImage(StoreKey.preferRemoteImage, false), advancedTroubleshooting(StoreKey.advancedTroubleshooting, false), enableBackup(StoreKey.enableBackup, false); diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 2720c5d3e6..9244eb3c52 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -20,12 +20,8 @@ enum StoreKey { sslClientCertData._(15), sslClientPasswd._(16), uploadErrorNotificationGracePeriod._(106), - thumbnailCacheSize._(110), - imageCacheSize._(111), - albumThumbnailCacheSize._(112), selectedAlbumSortOrder._(113), advancedTroubleshooting._(114), - preferRemoteImage._(116), selfSignedCert._(120), selectedAlbumSortReverse._(123), enableHapticFeedback._(126), @@ -57,6 +53,8 @@ enum StoreKey { syncMigrationStatus._(1013), // Legacy keys that have been migrated to the new metadata store + legacyPreferRemoteImage._(116), + legacyLoadOriginal._(101), legacyPrimaryColor._(128), legacyDynamicTheme._(129), legacyColorfulInterface._(130), diff --git a/mobile/lib/infrastructure/repositories/metadata.repository.dart b/mobile/lib/infrastructure/repositories/metadata.repository.dart index 676842c8ea..ef9ad6b8ab 100644 --- a/mobile/lib/infrastructure/repositories/metadata.repository.dart +++ b/mobile/lib/infrastructure/repositories/metadata.repository.dart @@ -132,6 +132,7 @@ extension on MetadataDomain { groupAssetsBy: repo._read(.timelineGroupAssetsBy), storageIndicator: repo._read(.timelineStorageIndicator), ), + image: .new(preferRemote: repo._read(.imagePreferRemote), loadOriginal: repo._read(.imageLoadOriginal)), ); case .systemConfig: repo._systemConfig = .new(logLevel: repo._read(.logLevel)); diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index ea416d9d71..9364fdd091 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -3,9 +3,8 @@ import 'dart:ui' as ui; import 'package:async/async.dart'; import 'package:flutter/widgets.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/domain/services/setting.service.dart'; import 'package:immich_mobile/infrastructure/loaders/image_request.dart'; +import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/timeline/constants.dart'; @@ -189,4 +188,6 @@ ImageProvider? getThumbnailImageProvider(BaseAsset asset, {Size size = kThumbnai } bool _shouldUseLocalAsset(BaseAsset asset) => - asset.hasLocal && (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage)) && !asset.isEdited; + asset.hasLocal && + (!asset.hasRemote || !MetadataRepository.instance.appConfig.image.preferRemote) && + !asset.isEdited; diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index ed8289a66a..6376e07405 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -1,9 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; -import 'package:immich_mobile/domain/models/store.model.dart'; -import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/loaders/image_request.dart'; +import 'package:immich_mobile/infrastructure/repositories/metadata.repository.dart'; import 'package:immich_mobile/presentation/widgets/images/animated_image_stream_completer.dart'; import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/one_frame_multi_image_stream_completer.dart'; @@ -105,7 +104,7 @@ class LocalFullImageProvider extends CancellableImageProvider { - loadOriginal(StoreKey.loadOriginal, "loadOriginal", false), uploadErrorNotificationGracePeriod( StoreKey.uploadErrorNotificationGracePeriod, "uploadErrorNotificationGracePeriod", 2, ), - thumbnailCacheSize(StoreKey.thumbnailCacheSize, "thumbnailCacheSize", 10000), - imageCacheSize(StoreKey.imageCacheSize, "imageCacheSize", 350), - albumThumbnailCacheSize(StoreKey.albumThumbnailCacheSize, "albumThumbnailCacheSize", 200), selectedAlbumSortOrder(StoreKey.selectedAlbumSortOrder, "selectedAlbumSortOrder", 2), advancedTroubleshooting(StoreKey.advancedTroubleshooting, null, false), manageLocalMediaAndroid(StoreKey.manageLocalMediaAndroid, null, false), - preferRemoteImage(StoreKey.preferRemoteImage, null, false), loopVideo(StoreKey.loopVideo, "loopVideo", true), loadOriginalVideo(StoreKey.loadOriginalVideo, "loadOriginalVideo", false), autoPlayVideo(StoreKey.autoPlayVideo, "autoPlayVideo", true), diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index 07df7958a1..e6d2143468 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -88,6 +88,9 @@ Future _migrateTo26(Drift drift) async { GroupAssetsBy.values, ); await migrator.migrateBool(StoreKey.legacyStorageIndicator, MetadataKey.timelineStorageIndicator); + // Image + await migrator.migrateBool(StoreKey.legacyPreferRemoteImage, MetadataKey.imagePreferRemote); + await migrator.migrateBool(StoreKey.legacyLoadOriginal, MetadataKey.imageLoadOriginal); await migrator.complete(); } diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index e2502aebae..60557aaaca 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -32,7 +32,11 @@ class AdvancedSettings extends HookConsumerWidget { final isManageMediaSupported = useState(false); final manageMediaAndroidPermission = useState(false); final levelId = useState(ref.read(systemConfigProvider).logLevel.index); - final preferRemote = useAppSettingsState(AppSettingsEnum.preferRemoteImage); + final preferRemote = useState(ref.read(appConfigProvider).image.preferRemote); + useValueChanged( + preferRemote.value, + (_, __) => ref.read(metadataProvider).write(.imagePreferRemote, preferRemote.value), + ); final readonlyModeEnabled = useAppSettingsState(AppSettingsEnum.readonlyModeEnabled); final logLevel = Level.LEVELS[levelId.value].name; diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart index 66c28be22d..7858033401 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.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_switch_list_tile.dart'; @@ -12,7 +12,10 @@ class ImageViewerQualitySetting extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isOriginal = useAppSettingsState(AppSettingsEnum.loadOriginal); + final isOriginal = useState(ref.read(appConfigProvider).image.loadOriginal); + useValueChanged(isOriginal.value, (_, __) { + ref.read(metadataProvider).write(.imageLoadOriginal, isOriginal.value); + }); return Column( crossAxisAlignment: CrossAxisAlignment.start,