Files
umbrix/lib/features/config_option/overview/config_options_page.dart

378 lines
18 KiB
Dart
Raw Normal View History

2023-12-01 12:56:24 +03:30
import 'package:dartx/dartx.dart';
import 'package:flutter/material.dart';
2024-03-07 17:04:32 +03:30
import 'package:flutter_hooks/flutter_hooks.dart';
2023-12-01 12:56:24 +03:30
import 'package:gap/gap.dart';
import 'package:hiddify/core/localization/translations.dart';
2024-02-15 19:39:35 +03:30
import 'package:hiddify/core/model/optional_range.dart';
import 'package:hiddify/core/model/region.dart';
2024-03-04 15:58:56 +03:30
import 'package:hiddify/core/notification/in_app_notification_controller.dart';
2024-02-15 15:23:02 +03:30
import 'package:hiddify/core/widget/adaptive_icon.dart';
2023-12-29 15:35:01 +03:30
import 'package:hiddify/core/widget/tip_card.dart';
2024-03-04 15:58:56 +03:30
import 'package:hiddify/features/common/confirmation_dialogs.dart';
2024-02-11 13:47:08 +03:30
import 'package:hiddify/features/common/nested_app_bar.dart';
2024-03-02 22:53:14 +03:30
import 'package:hiddify/features/config_option/data/config_option_repository.dart';
2023-12-01 12:56:24 +03:30
import 'package:hiddify/features/config_option/notifier/config_option_notifier.dart';
2024-02-03 12:36:27 +03:30
import 'package:hiddify/features/config_option/overview/warp_options_widgets.dart';
2024-03-02 22:53:14 +03:30
import 'package:hiddify/features/config_option/widget/preference_tile.dart';
2023-12-01 12:56:24 +03:30
import 'package:hiddify/features/log/model/log_level.dart';
import 'package:hiddify/features/settings/widgets/sections_widgets.dart';
import 'package:hiddify/features/settings/widgets/settings_input_dialog.dart';
import 'package:hiddify/singbox/model/singbox_config_enum.dart';
import 'package:hiddify/utils/utils.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:humanizer/humanizer.dart';
2024-03-07 17:04:32 +03:30
enum ConfigOptionSection {
warp,
fragment;
2024-03-07 17:04:32 +03:30
static final _warpKey = GlobalKey(debugLabel: "warp-section-key");
static final _fragmentKey = GlobalKey(debugLabel: "fragment-section-key");
2024-03-07 17:04:32 +03:30
GlobalKey get key => switch (this) {
ConfigOptionSection.warp => _warpKey,
ConfigOptionSection.fragment => _fragmentKey,
};
2024-03-07 17:04:32 +03:30
}
2023-12-01 12:56:24 +03:30
class ConfigOptionsPage extends HookConsumerWidget {
ConfigOptionsPage({super.key, String? section}) : section = section != null ? ConfigOptionSection.values.byName(section) : null;
2024-03-07 17:04:32 +03:30
final ConfigOptionSection? section;
2023-12-01 12:56:24 +03:30
@override
Widget build(BuildContext context, WidgetRef ref) {
final t = ref.watch(translationsProvider);
2024-03-07 17:04:32 +03:30
final scrollController = useScrollController();
useMemoized(
() {
if (section != null) {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
final box = section!.key.currentContext?.findRenderObject() as RenderBox?;
2024-03-07 17:04:32 +03:30
final offset = box?.localToGlobal(Offset.zero);
if (offset == null) return;
final height = scrollController.offset + offset.dy - MediaQueryData.fromView(View.of(context)).padding.top - kToolbarHeight;
2024-03-07 17:04:32 +03:30
scrollController.animateTo(
height,
duration: const Duration(milliseconds: 500),
curve: Curves.decelerate,
);
},
);
}
},
);
2023-12-01 12:56:24 +03:30
2023-12-29 15:35:01 +03:30
String experimental(String txt) {
return "$txt (${t.settings.experimental})";
}
2023-12-01 12:56:24 +03:30
return Scaffold(
2024-02-11 13:47:08 +03:30
body: CustomScrollView(
2024-03-07 17:04:32 +03:30
controller: scrollController,
shrinkWrap: true,
2024-02-11 13:47:08 +03:30
slivers: [
NestedAppBar(
2024-03-08 17:24:43 +03:30
title: Text(t.config.pageTitle),
2024-02-11 13:47:08 +03:30
actions: [
2024-03-02 22:53:14 +03:30
PopupMenuButton(
icon: Icon(AdaptiveIcon(context).more),
itemBuilder: (context) {
return [
PopupMenuItem(
onTap: () async => ref.read(configOptionNotifierProvider.notifier).exportJsonToClipboard().then((success) {
2024-03-04 15:58:56 +03:30
if (success) {
ref.read(inAppNotificationControllerProvider).showSuccessToast(
2024-03-04 15:58:56 +03:30
t.general.clipboardExportSuccessMsg,
);
}
}),
child: Text(t.settings.exportOptions),
),
// if (ref.watch(debugModeNotifierProvider))
PopupMenuItem(
onTap: () async => ref.read(configOptionNotifierProvider.notifier).exportJsonToClipboard(excludePrivate: false).then((success) {
if (success) {
ref.read(inAppNotificationControllerProvider).showSuccessToast(
t.general.clipboardExportSuccessMsg,
);
}
}),
child: Text(t.settings.exportAllOptions),
),
2024-03-04 15:58:56 +03:30
PopupMenuItem(
onTap: () async {
final shouldImport = await showConfirmationDialog(
context,
title: t.settings.importOptions,
message: t.settings.importOptionsMsg,
);
if (shouldImport) {
await ref.read(configOptionNotifierProvider.notifier).importFromClipboard();
2024-03-04 15:58:56 +03:30
}
},
child: Text(t.settings.importOptions),
2024-02-11 13:47:08 +03:30
),
2024-03-02 22:53:14 +03:30
PopupMenuItem(
2024-03-08 17:24:43 +03:30
child: Text(t.config.resetBtn),
2024-03-02 22:53:14 +03:30
onTap: () async {
await ref.read(configOptionNotifierProvider.notifier).resetOption();
2024-02-11 13:47:08 +03:30
},
),
2024-03-02 22:53:14 +03:30
];
},
),
],
),
2024-03-07 17:04:32 +03:30
SliverToBoxAdapter(
child: SingleChildScrollView(
child: Column(
children: [
TipCard(message: t.settings.experimentalMsg),
ChoicePreferenceWidget(
selected: ref.watch(ConfigOptions.logLevel),
preferences: ref.watch(ConfigOptions.logLevel.notifier),
choices: LogLevel.choices,
2024-03-08 17:24:43 +03:30
title: t.config.logLevel,
2024-03-07 17:04:32 +03:30
presentChoice: (value) => value.name.toUpperCase(),
),
2024-08-06 16:14:00 -04:00
2024-03-07 17:04:32 +03:30
const SettingsDivider(),
2024-03-08 17:24:43 +03:30
SettingsSection(t.config.section.route),
ChoicePreferenceWidget(
selected: ref.watch(ConfigOptions.region),
preferences: ref.watch(ConfigOptions.region.notifier),
choices: Region.values,
title: t.settings.general.region,
presentChoice: (value) => value.present(t),
2024-07-27 23:51:54 +02:00
onChanged: (val) => ref.watch(ConfigOptions.directDnsAddress.notifier).reset(),
),
SwitchListTile(
title: Text(experimental(t.config.blockAds)),
value: ref.watch(ConfigOptions.blockAds),
onChanged: ref.watch(ConfigOptions.blockAds.notifier).update,
),
2024-03-07 17:04:32 +03:30
SwitchListTile(
2024-03-08 17:24:43 +03:30
title: Text(experimental(t.config.bypassLan)),
2024-03-07 17:04:32 +03:30
value: ref.watch(ConfigOptions.bypassLan),
onChanged: ref.watch(ConfigOptions.bypassLan.notifier).update,
2024-03-07 17:04:32 +03:30
),
SwitchListTile(
2024-03-08 17:24:43 +03:30
title: Text(t.config.resolveDestination),
2024-03-07 17:04:32 +03:30
value: ref.watch(ConfigOptions.resolveDestination),
onChanged: ref.watch(ConfigOptions.resolveDestination.notifier).update,
2024-03-07 17:04:32 +03:30
),
ChoicePreferenceWidget(
selected: ref.watch(ConfigOptions.ipv6Mode),
preferences: ref.watch(ConfigOptions.ipv6Mode.notifier),
choices: IPv6Mode.values,
2024-03-08 17:24:43 +03:30
title: t.config.ipv6Mode,
2024-03-07 17:04:32 +03:30
presentChoice: (value) => value.present(t),
),
const SettingsDivider(),
2024-03-08 17:24:43 +03:30
SettingsSection(t.config.section.dns),
2024-03-07 17:04:32 +03:30
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.remoteDnsAddress),
preferences: ref.watch(ConfigOptions.remoteDnsAddress.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.remoteDnsAddress,
2024-03-07 17:04:32 +03:30
),
ChoicePreferenceWidget(
selected: ref.watch(ConfigOptions.remoteDnsDomainStrategy),
preferences: ref.watch(ConfigOptions.remoteDnsDomainStrategy.notifier),
2024-03-07 17:04:32 +03:30
choices: DomainStrategy.values,
2024-03-08 17:24:43 +03:30
title: t.config.remoteDnsDomainStrategy,
2024-03-07 17:04:32 +03:30
presentChoice: (value) => value.displayName,
),
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.directDnsAddress),
preferences: ref.watch(ConfigOptions.directDnsAddress.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.directDnsAddress,
2024-03-07 17:04:32 +03:30
),
ChoicePreferenceWidget(
selected: ref.watch(ConfigOptions.directDnsDomainStrategy),
preferences: ref.watch(ConfigOptions.directDnsDomainStrategy.notifier),
2024-03-07 17:04:32 +03:30
choices: DomainStrategy.values,
2024-03-08 17:24:43 +03:30
title: t.config.directDnsDomainStrategy,
2024-03-07 17:04:32 +03:30
presentChoice: (value) => value.displayName,
),
SwitchListTile(
2024-03-08 17:24:43 +03:30
title: Text(t.config.enableDnsRouting),
2024-03-07 17:04:32 +03:30
value: ref.watch(ConfigOptions.enableDnsRouting),
onChanged: ref.watch(ConfigOptions.enableDnsRouting.notifier).update,
2024-03-07 17:04:32 +03:30
),
2024-03-17 14:44:57 +01:00
// const SettingsDivider(),
// SettingsSection(experimental(t.config.section.mux)),
// SwitchListTile(
// title: Text(t.config.enableMux),
// value: ref.watch(ConfigOptions.enableMux),
// onChanged:
// ref.watch(ConfigOptions.enableMux.notifier).update,
// ),
// ChoicePreferenceWidget(
// selected: ref.watch(ConfigOptions.muxProtocol),
// preferences: ref.watch(ConfigOptions.muxProtocol.notifier),
// choices: MuxProtocol.values,
// title: t.config.muxProtocol,
// presentChoice: (value) => value.name,
// ),
// ValuePreferenceWidget(
// value: ref.watch(ConfigOptions.muxMaxStreams),
// preferences:
// ref.watch(ConfigOptions.muxMaxStreams.notifier),
// title: t.config.muxMaxStreams,
// inputToValue: int.tryParse,
// digitsOnly: true,
// ),
2024-03-07 17:04:32 +03:30
const SettingsDivider(),
2024-03-08 17:24:43 +03:30
SettingsSection(t.config.section.inbound),
2024-03-07 17:04:32 +03:30
ChoicePreferenceWidget(
selected: ref.watch(ConfigOptions.serviceMode),
preferences: ref.watch(ConfigOptions.serviceMode.notifier),
choices: ServiceMode.choices,
2024-03-08 17:24:43 +03:30
title: t.config.serviceMode,
2024-03-07 17:04:32 +03:30
presentChoice: (value) => value.present(t),
),
SwitchListTile(
2024-03-08 17:24:43 +03:30
title: Text(t.config.strictRoute),
2024-03-07 17:04:32 +03:30
value: ref.watch(ConfigOptions.strictRoute),
onChanged: ref.watch(ConfigOptions.strictRoute.notifier).update,
2024-03-07 17:04:32 +03:30
),
ChoicePreferenceWidget(
selected: ref.watch(ConfigOptions.tunImplementation),
preferences: ref.watch(ConfigOptions.tunImplementation.notifier),
2024-03-07 17:04:32 +03:30
choices: TunImplementation.values,
2024-03-08 17:24:43 +03:30
title: t.config.tunImplementation,
2024-03-07 17:04:32 +03:30
presentChoice: (value) => value.name,
),
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.mixedPort),
preferences: ref.watch(ConfigOptions.mixedPort.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.mixedPort,
2024-03-07 17:04:32 +03:30
inputToValue: int.tryParse,
digitsOnly: true,
validateInput: isPort,
),
2024-03-19 11:56:19 +01:00
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.tproxyPort),
preferences: ref.watch(ConfigOptions.tproxyPort.notifier),
title: t.config.tproxyPort,
inputToValue: int.tryParse,
digitsOnly: true,
validateInput: isPort,
),
2024-03-07 17:04:32 +03:30
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.localDnsPort),
preferences: ref.watch(ConfigOptions.localDnsPort.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.localDnsPort,
2024-03-07 17:04:32 +03:30
inputToValue: int.tryParse,
digitsOnly: true,
validateInput: isPort,
),
SwitchListTile(
title: Text(
2024-03-08 17:24:43 +03:30
experimental(t.config.allowConnectionFromLan),
2024-03-07 17:04:32 +03:30
),
value: ref.watch(ConfigOptions.allowConnectionFromLan),
onChanged: ref.read(ConfigOptions.allowConnectionFromLan.notifier).update,
2024-03-07 17:04:32 +03:30
),
const SettingsDivider(),
2024-03-08 17:15:17 +03:30
SettingsSection(
2024-03-08 17:24:43 +03:30
experimental(t.config.section.tlsTricks),
key: ConfigOptionSection._fragmentKey,
2024-03-08 17:15:17 +03:30
),
2024-03-07 17:04:32 +03:30
SwitchListTile(
2024-03-08 17:24:43 +03:30
title: Text(t.config.enableTlsFragment),
2024-03-07 17:04:32 +03:30
value: ref.watch(ConfigOptions.enableTlsFragment),
onChanged: ref.watch(ConfigOptions.enableTlsFragment.notifier).update,
2024-03-07 17:04:32 +03:30
),
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.tlsFragmentSize),
preferences: ref.watch(ConfigOptions.tlsFragmentSize.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.tlsFragmentSize,
2024-03-07 17:04:32 +03:30
inputToValue: OptionalRange.tryParse,
presentValue: (value) => value.present(t),
formatInputValue: (value) => value.format(),
),
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.tlsFragmentSleep),
preferences: ref.watch(ConfigOptions.tlsFragmentSleep.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.tlsFragmentSleep,
2024-03-07 17:04:32 +03:30
inputToValue: OptionalRange.tryParse,
presentValue: (value) => value.present(t),
formatInputValue: (value) => value.format(),
),
SwitchListTile(
2024-03-08 17:24:43 +03:30
title: Text(t.config.enableTlsMixedSniCase),
2024-03-07 17:04:32 +03:30
value: ref.watch(ConfigOptions.enableTlsMixedSniCase),
onChanged: ref.watch(ConfigOptions.enableTlsMixedSniCase.notifier).update,
2024-03-07 17:04:32 +03:30
),
SwitchListTile(
2024-03-08 17:24:43 +03:30
title: Text(t.config.enableTlsPadding),
2024-03-07 17:04:32 +03:30
value: ref.watch(ConfigOptions.enableTlsPadding),
onChanged: ref.watch(ConfigOptions.enableTlsPadding.notifier).update,
2024-03-07 17:04:32 +03:30
),
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.tlsPaddingSize),
preferences: ref.watch(ConfigOptions.tlsPaddingSize.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.tlsPaddingSize,
2024-03-07 17:04:32 +03:30
inputToValue: OptionalRange.tryParse,
presentValue: (value) => value.format(),
formatInputValue: (value) => value.format(),
),
const SettingsDivider(),
2024-03-08 17:24:43 +03:30
SettingsSection(experimental(t.config.section.warp)),
2024-03-07 17:04:32 +03:30
WarpOptionsTiles(key: ConfigOptionSection._warpKey),
const SettingsDivider(),
2024-03-08 17:24:43 +03:30
SettingsSection(t.config.section.misc),
2024-03-07 17:04:32 +03:30
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.connectionTestUrl),
preferences: ref.watch(ConfigOptions.connectionTestUrl.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.connectionTestUrl,
2024-03-07 17:04:32 +03:30
),
ListTile(
2024-03-08 17:24:43 +03:30
title: Text(t.config.urlTestInterval),
2024-03-07 17:04:32 +03:30
subtitle: Text(
ref.watch(ConfigOptions.urlTestInterval).toApproximateTime(isRelativeToNow: false),
2024-03-07 17:04:32 +03:30
),
onTap: () async {
final urlTestInterval = await SettingsSliderDialog(
2024-03-08 17:24:43 +03:30
title: t.config.urlTestInterval,
initialValue: ref.watch(ConfigOptions.urlTestInterval).inMinutes.coerceIn(0, 60).toDouble(),
onReset: ref.read(ConfigOptions.urlTestInterval.notifier).reset,
2024-03-07 17:04:32 +03:30
min: 1,
max: 60,
divisions: 60,
labelGen: (value) => Duration(minutes: value.toInt()).toApproximateTime(isRelativeToNow: false),
2024-03-07 17:04:32 +03:30
).show(context);
if (urlTestInterval == null) return;
await ref.read(ConfigOptions.urlTestInterval.notifier).update(Duration(minutes: urlTestInterval.toInt()));
2024-03-07 17:04:32 +03:30
},
),
ValuePreferenceWidget(
value: ref.watch(ConfigOptions.clashApiPort),
preferences: ref.watch(ConfigOptions.clashApiPort.notifier),
2024-03-08 17:24:43 +03:30
title: t.config.clashApiPort,
2024-03-07 17:04:32 +03:30
validateInput: isPort,
digitsOnly: true,
inputToValue: int.tryParse,
),
2024-08-06 16:14:00 -04:00
SwitchListTile(
title: Text(experimental(t.config.useXrayCoreWhenPossible.Label)),
subtitle: Text(t.config.useXrayCoreWhenPossible.Description),
value: ref.watch(ConfigOptions.useXrayCoreWhenPossible),
onChanged: ref.watch(ConfigOptions.useXrayCoreWhenPossible.notifier).update,
),
const Gap(24),
2024-03-07 17:04:32 +03:30
],
2024-03-02 22:53:14 +03:30
),
2024-03-07 17:04:32 +03:30
),
2024-03-02 22:53:14 +03:30
),
2024-02-11 13:47:08 +03:30
],
),
2023-12-01 12:56:24 +03:30
);
}
}