Compare commits

..

1 Commits

Author SHA1 Message Date
shenlong-tanwen febc162821 refactor: migrate advance config from store 2026-06-16 23:47:28 +05:30
17 changed files with 129 additions and 103 deletions
@@ -0,0 +1,33 @@
class AdvancedConfig {
final bool troubleshooting;
final bool enableHapticFeedback;
final bool readonlyModeEnabled;
const AdvancedConfig({
this.troubleshooting = false,
this.enableHapticFeedback = true,
this.readonlyModeEnabled = false,
});
AdvancedConfig copyWith({bool? troubleshooting, bool? enableHapticFeedback, bool? readonlyModeEnabled}) =>
AdvancedConfig(
troubleshooting: troubleshooting ?? this.troubleshooting,
enableHapticFeedback: enableHapticFeedback ?? this.enableHapticFeedback,
readonlyModeEnabled: readonlyModeEnabled ?? this.readonlyModeEnabled,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is AdvancedConfig &&
other.troubleshooting == troubleshooting &&
other.enableHapticFeedback == enableHapticFeedback &&
other.readonlyModeEnabled == readonlyModeEnabled);
@override
int get hashCode => Object.hash(troubleshooting, enableHapticFeedback, readonlyModeEnabled);
@override
String toString() =>
'AdvancedConfig(troubleshooting: $troubleshooting, enableHapticFeedback: $enableHapticFeedback, readonlyModeEnabled: $readonlyModeEnabled)';
}
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/constants/colors.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/config/advanced_config.dart';
import 'package:immich_mobile/domain/models/config/album_config.dart';
import 'package:immich_mobile/domain/models/config/backup_config.dart';
import 'package:immich_mobile/domain/models/config/cleanup_config.dart';
@@ -32,6 +33,7 @@ class AppConfig {
final BackupConfig backup;
final NetworkConfig network;
final ShareConfig share;
final AdvancedConfig advanced;
const AppConfig({
this.logLevel = .info,
@@ -46,6 +48,7 @@ class AppConfig {
this.backup = const .new(),
this.network = const .new(),
this.share = const .new(),
this.advanced = const .new(),
});
AppConfig copyWith({
@@ -61,6 +64,7 @@ class AppConfig {
BackupConfig? backup,
NetworkConfig? network,
ShareConfig? share,
AdvancedConfig? advanced,
}) => .new(
logLevel: logLevel ?? this.logLevel,
theme: theme ?? this.theme,
@@ -74,6 +78,7 @@ class AppConfig {
backup: backup ?? this.backup,
network: network ?? this.network,
share: share ?? this.share,
advanced: advanced ?? this.advanced,
);
@override
@@ -91,15 +96,29 @@ class AppConfig {
other.album == album &&
other.backup == backup &&
other.network == network &&
other.share == share);
other.share == share &&
other.advanced == advanced);
@override
int get hashCode =>
Object.hash(logLevel, theme, cleanup, map, timeline, image, viewer, slideshow, album, backup, network, share);
int get hashCode => Object.hash(
logLevel,
theme,
cleanup,
map,
timeline,
image,
viewer,
slideshow,
album,
backup,
network,
share,
advanced,
);
@override
String toString() =>
'AppConfig(logLevel: $logLevel, theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer, slideshow: $slideshow, album: $album, backup: $backup, network: $network, share: $share)';
'AppConfig(logLevel: $logLevel, theme: $theme, cleanup: $cleanup, map: $map, timeline: $timeline, image: $image, viewer: $viewer, slideshow: $slideshow, album: $album, backup: $backup, network: $network, share: $share, advanced: $advanced)';
T read<T>(SettingsKey<T> key) =>
(switch (key) {
@@ -147,6 +166,9 @@ class AppConfig {
.slideshowDuration => slideshow.duration,
.slideshowLook => slideshow.look,
.slideshowDirection => slideshow.direction,
.advancedTroubleshooting => advanced.troubleshooting,
.advancedEnableHapticFeedback => advanced.enableHapticFeedback,
.advancedReadonlyModeEnabled => advanced.readonlyModeEnabled,
})
as T;
@@ -201,6 +223,9 @@ class AppConfig {
.slideshowDuration => copyWith(slideshow: slideshow.copyWith(duration: value as int)),
.slideshowLook => copyWith(slideshow: slideshow.copyWith(look: value as SlideshowLook)),
.slideshowDirection => copyWith(slideshow: slideshow.copyWith(direction: value as SlideshowDirection)),
.advancedTroubleshooting => copyWith(advanced: advanced.copyWith(troubleshooting: value as bool)),
.advancedEnableHapticFeedback => copyWith(advanced: advanced.copyWith(enableHapticFeedback: value as bool)),
.advancedReadonlyModeEnabled => copyWith(advanced: advanced.copyWith(readonlyModeEnabled: value as bool)),
};
}
}
@@ -1,10 +0,0 @@
import 'package:immich_mobile/domain/models/store.model.dart';
enum Setting<T> {
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, false);
const Setting(this.storeKey, this.defaultValue);
final StoreKey<T> storeKey;
final T defaultValue;
}
+6 -1
View File
@@ -73,7 +73,12 @@ enum SettingsKey<T> {
slideshowRepeat<bool>(),
slideshowDuration<int>(),
slideshowLook<SlideshowLook>(codec: EnumCodec(SlideshowLook.values)),
slideshowDirection<SlideshowDirection>(codec: EnumCodec(SlideshowDirection.values));
slideshowDirection<SlideshowDirection>(codec: EnumCodec(SlideshowDirection.values)),
// Advanced
advancedTroubleshooting<bool>(),
advancedEnableHapticFeedback<bool>(),
advancedReadonlyModeEnabled<bool>();
final ValueCodec<T>? _codecOverride;
+3 -4
View File
@@ -3,16 +3,15 @@
enum StoreKey<T> {
version<int>._(0),
deviceId<String>._(4),
advancedTroubleshooting<bool>._(114),
enableHapticFeedback<bool>._(126),
manageLocalMediaAndroid<bool>._(137),
// Read-only Mode settings
readonlyModeEnabled<bool>._(138),
syncMigrationStatus<String>._(1013),
// Legacy keys that have been migrated to the new metadata store
legacyAdvancedTroubleshooting<bool>._(114),
legacyEnableHapticFeedback<bool>._(126),
legacyReadonlyModeEnabled<bool>._(138),
legacyServerUrl<String>._(10),
legacyAccessToken<String>._(11),
legacyServerEndpoint<String>._(12),
@@ -1,19 +0,0 @@
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/domain/services/store.service.dart';
// Singleton instance of SettingsService, to use in places
// where reactivity is not required
// ignore: non_constant_identifier_names
final AppSetting = SettingsService(storeService: StoreService.I);
class SettingsService {
final StoreService _storeService;
const SettingsService({required this._storeService});
T get<T>(Setting<T> setting) => _storeService.get(setting.storeKey, setting.defaultValue);
Future<void> set<T>(Setting<T> setting, T value) => _storeService.put(setting.storeKey, value);
Stream<T> watch<T>(Setting<T> setting) => _storeService.watch(setting.storeKey).map((v) => v ?? setting.defaultValue);
}
@@ -6,7 +6,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/providers/asset_viewer/asset_viewer.provider.dart';
import 'package:immich_mobile/providers/cast.provider.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/routes.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
@@ -33,7 +33,7 @@ class ViewerKebabMenu extends ConsumerWidget {
final isInLockedView = ref.watch(inLockedViewProvider);
final currentAlbum = ref.watch(currentRemoteAlbumProvider);
final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive;
final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(.advancedTroubleshooting);
final advancedTroubleshooting = ref.watch(appConfigProvider.select((c) => c.advanced.troubleshooting));
final actionContext = ActionButtonContext(
asset: asset,
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/enums.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
import 'package:immich_mobile/presentation/widgets/action_buttons/bulk_tag_assets_action_button.widget.dart';
@@ -24,7 +23,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_
import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
import 'package:immich_mobile/providers/infrastructure/setting.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/infrastructure/user_metadata.provider.dart';
import 'package:immich_mobile/providers/server_info.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
@@ -56,7 +55,7 @@ class _GeneralBottomSheetState extends ConsumerState<GeneralBottomSheet> {
Widget build(BuildContext context) {
final multiselect = ref.watch(multiSelectProvider);
final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash));
final advancedTroubleshooting = ref.watch(settingsProvider.notifier).get(Setting.advancedTroubleshooting);
final advancedTroubleshooting = ref.watch(appConfigProvider.select((c) => c.advanced.troubleshooting));
final tagsEnabled = ref.watch(
userMetadataPreferencesProvider.select((value) => value.valueOrNull?.tagsEnabled ?? false),
);
@@ -1,7 +1,6 @@
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
final hapticFeedbackProvider = StateNotifierProvider<HapticNotifier, void>((ref) {
return HapticNotifier(ref);
@@ -14,31 +13,31 @@ class HapticNotifier extends StateNotifier<void> {
HapticNotifier(this._ref) : super(null);
selectionClick() {
if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) {
if (_ref.read(appConfigProvider).advanced.enableHapticFeedback) {
HapticFeedback.selectionClick();
}
}
lightImpact() {
if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) {
if (_ref.read(appConfigProvider).advanced.enableHapticFeedback) {
HapticFeedback.lightImpact();
}
}
mediumImpact() {
if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) {
if (_ref.read(appConfigProvider).advanced.enableHapticFeedback) {
HapticFeedback.mediumImpact();
}
}
heavyImpact() {
if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) {
if (_ref.read(appConfigProvider).advanced.enableHapticFeedback) {
HapticFeedback.heavyImpact();
}
}
vibrate() {
if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) {
if (_ref.read(appConfigProvider).advanced.enableHapticFeedback) {
HapticFeedback.vibrate();
}
}
@@ -1,22 +1,19 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/providers/app_settings.provider.dart';
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/services/app_settings.service.dart';
class ReadOnlyModeNotifier extends Notifier<bool> {
late AppSettingsService _appSettingService;
@override
bool build() {
_appSettingService = ref.read(appSettingsServiceProvider);
final readonlyMode = _appSettingService.getSetting(AppSettingsEnum.readonlyModeEnabled);
return readonlyMode;
return ref.read(appConfigProvider).advanced.readonlyModeEnabled;
}
void setMode(bool value) {
final isLoggedIn = ref.read(authProvider).isAuthenticated;
_appSettingService.setSetting(AppSettingsEnum.readonlyModeEnabled, value);
unawaited(ref.read(settingsProvider).write(.advancedReadonlyModeEnabled, value));
state = value;
if (value && isLoggedIn) {
@@ -1,20 +0,0 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/setting.model.dart';
import 'package:immich_mobile/domain/services/setting.service.dart';
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
class SettingsNotifier extends Notifier<SettingsService> {
@override
SettingsService build() => SettingsService(storeService: ref.read(storeServiceProvider));
T get<T>(Setting<T> setting) => state.get(setting);
Future<void> set<T>(Setting<T> setting, T value) async {
await state.set(setting, value);
ref.invalidateSelf();
}
Stream<T> watch<T>(Setting<T> setting) => state.watch(setting);
}
final settingsProvider = NotifierProvider<SettingsNotifier, SettingsService>(SettingsNotifier.new);
@@ -2,10 +2,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/entities/store.entity.dart';
enum AppSettingsEnum<T> {
advancedTroubleshooting<bool>(StoreKey.advancedTroubleshooting, null, false),
manageLocalMediaAndroid<bool>(StoreKey.manageLocalMediaAndroid, null, false),
enableHapticFeedback<bool>(StoreKey.enableHapticFeedback, null, true),
readonlyModeEnabled<bool>(StoreKey.readonlyModeEnabled, "readonlyModeEnabled", false);
manageLocalMediaAndroid<bool>(StoreKey.manageLocalMediaAndroid, null, false);
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
+16 -1
View File
@@ -18,10 +18,11 @@ import 'package:immich_mobile/infrastructure/entities/settings.entity.drift.dart
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/network.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/session.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/settings.repository.dart';
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
const int targetVersion = 27;
const int targetVersion = 28;
Future<void> migrateDatabaseIfNeeded(Drift drift) async {
final int version = Store.get(StoreKey.version, targetVersion);
@@ -38,6 +39,10 @@ Future<void> migrateDatabaseIfNeeded(Drift drift) async {
await _migrateTo27(drift);
}
if (version < 28) {
await _migrateTo28(drift);
}
await Store.put(StoreKey.version, targetVersion);
return;
}
@@ -155,6 +160,16 @@ Future<void> _migrateTo27(Drift drift) async {
await SessionRepository.instance.refresh();
}
Future<void> _migrateTo28(Drift drift) async {
final migrator = _StoreMigrator.settings(drift);
await migrator.migrateBool(StoreKey.legacyAdvancedTroubleshooting, SettingsKey.advancedTroubleshooting);
await migrator.migrateBool(StoreKey.legacyEnableHapticFeedback, SettingsKey.advancedEnableHapticFeedback);
await migrator.migrateBool(StoreKey.legacyReadonlyModeEnabled, SettingsKey.advancedReadonlyModeEnabled);
await migrator.complete();
await SettingsRepository.instance.refresh();
}
Future<void> _migrateAlbumSortMode(_StoreMigrator<SettingsKey> migrator) async {
final raw = await migrator.readLegacyStoreInt(StoreKey.legacySelectedAlbumSortOrder.id);
final mode = AlbumSortMode.values.firstWhereOrNull((e) => raw != null && e.storeIndex == raw);
@@ -27,7 +27,11 @@ class AdvancedSettings extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final advancedTroubleshooting = useAppSettingsState(AppSettingsEnum.advancedTroubleshooting);
final advancedTroubleshooting = useState(ref.read(appConfigProvider).advanced.troubleshooting);
useValueChanged(
advancedTroubleshooting.value,
(_, __) => ref.read(settingsProvider).write(.advancedTroubleshooting, advancedTroubleshooting.value),
);
final manageLocalMediaAndroid = useAppSettingsState(AppSettingsEnum.manageLocalMediaAndroid);
final isManageMediaSupported = useState(false);
final manageMediaAndroidPermission = useState(false);
@@ -37,7 +41,7 @@ class AdvancedSettings extends HookConsumerWidget {
preferRemote.value,
(_, __) => ref.read(settingsProvider).write(.imagePreferRemote, preferRemote.value),
);
final readonlyModeEnabled = useAppSettingsState(AppSettingsEnum.readonlyModeEnabled);
final readonlyModeEnabled = useState(ref.read(appConfigProvider).advanced.readonlyModeEnabled);
final logLevel = Level.LEVELS[levelId.value].name;
@@ -2,21 +2,23 @@ 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/services/app_settings.service.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/widgets/settings/setting_group_title.dart';
import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart';
import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart';
class HapticSetting extends HookConsumerWidget {
const HapticSetting({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final hapticFeedbackSetting = useAppSettingsState(AppSettingsEnum.enableHapticFeedback);
final isHapticFeedbackEnabled = useValueNotifier(hapticFeedbackSetting.value);
final isHapticFeedbackEnabled = useState(ref.read(appConfigProvider).advanced.enableHapticFeedback);
useValueChanged(
isHapticFeedbackEnabled.value,
(_, __) => ref.read(settingsProvider).write(.advancedEnableHapticFeedback, isHapticFeedbackEnabled.value),
);
onHapticFeedbackChange(bool isEnabled) {
hapticFeedbackSetting.value = isEnabled;
isHapticFeedbackEnabled.value = isEnabled;
}
return Column(
@@ -23,12 +23,12 @@ void main() {
// For generics, we need to provide fallback to each concrete type to avoid runtime errors
registerFallbackValue(StoreKey.legacyAccessToken);
registerFallbackValue(StoreKey.version);
registerFallbackValue(StoreKey.advancedTroubleshooting);
registerFallbackValue(StoreKey.legacyAdvancedTroubleshooting);
when(() => mockDriftStoreRepo.getAll()).thenAnswer(
(_) async => [
const StoreDto(StoreKey.legacyAccessToken, _kAccessToken),
const StoreDto(StoreKey.advancedTroubleshooting, _kAdvancedTroubleshooting),
const StoreDto(StoreKey.legacyAdvancedTroubleshooting, _kAdvancedTroubleshooting),
const StoreDto(StoreKey.version, _kVersion),
],
);
@@ -46,7 +46,7 @@ void main() {
test('Populates the internal cache on init', () {
verify(() => mockDriftStoreRepo.getAll()).called(1);
expect(sut.tryGet(StoreKey.legacyAccessToken), _kAccessToken);
expect(sut.tryGet(StoreKey.advancedTroubleshooting), _kAdvancedTroubleshooting);
expect(sut.tryGet(StoreKey.legacyAdvancedTroubleshooting), _kAdvancedTroubleshooting);
expect(sut.tryGet(StoreKey.version), _kVersion);
// Other keys should be null
expect(sut.tryGet(StoreKey.deviceId), isNull);
@@ -147,7 +147,7 @@ void main() {
await sut.clear();
verify(() => mockDriftStoreRepo.deleteAll()).called(1);
expect(sut.tryGet(StoreKey.legacyAccessToken), isNull);
expect(sut.tryGet(StoreKey.advancedTroubleshooting), isNull);
expect(sut.tryGet(StoreKey.legacyAdvancedTroubleshooting), isNull);
expect(sut.tryGet(StoreKey.version), isNull);
});
});
@@ -17,7 +17,7 @@ Future<void> _populateStore(Drift db) async {
batch.insert(
db.storeEntity,
StoreEntityCompanion(
id: Value(StoreKey.advancedTroubleshooting.id),
id: Value(StoreKey.legacyAdvancedTroubleshooting.id),
intValue: const Value(_kTestAdvancedTroubleshooting ? 1 : 0),
stringValue: const Value(null),
),
@@ -72,10 +72,10 @@ void main() {
});
test('converts bool', () async {
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.legacyAdvancedTroubleshooting);
expect(advancedTroubleshooting, isNull);
await sut.upsert(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting);
advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
await sut.upsert(StoreKey.legacyAdvancedTroubleshooting, _kTestAdvancedTroubleshooting);
advancedTroubleshooting = await sut.tryGet(StoreKey.legacyAdvancedTroubleshooting);
expect(advancedTroubleshooting, _kTestAdvancedTroubleshooting);
});
});
@@ -86,10 +86,10 @@ void main() {
});
test('delete()', () async {
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
bool? advancedTroubleshooting = await sut.tryGet(StoreKey.legacyAdvancedTroubleshooting);
expect(advancedTroubleshooting, isFalse);
await sut.delete(StoreKey.advancedTroubleshooting);
advancedTroubleshooting = await sut.tryGet(StoreKey.advancedTroubleshooting);
await sut.delete(StoreKey.legacyAdvancedTroubleshooting);
advancedTroubleshooting = await sut.tryGet(StoreKey.legacyAdvancedTroubleshooting);
expect(advancedTroubleshooting, isNull);
});
@@ -136,12 +136,12 @@ void main() {
[
const StoreDto<Object>(StoreKey.version, _kTestVersion),
const StoreDto<Object>(StoreKey.legacyAccessToken, _kTestAccessToken),
const StoreDto<Object>(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting),
const StoreDto<Object>(StoreKey.legacyAdvancedTroubleshooting, _kTestAdvancedTroubleshooting),
],
[
const StoreDto<Object>(StoreKey.version, _kTestVersion + 10),
const StoreDto<Object>(StoreKey.legacyAccessToken, _kTestAccessToken),
const StoreDto<Object>(StoreKey.advancedTroubleshooting, _kTestAdvancedTroubleshooting),
const StoreDto<Object>(StoreKey.legacyAdvancedTroubleshooting, _kTestAdvancedTroubleshooting),
],
]),
),