From 4f4931309fe71f9af96816cd9300a2d630aa8307 Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Sun, 31 Dec 2023 10:28:52 +0330 Subject: [PATCH] Add experimental feature notice --- assets/translations/strings_en.i18n.json | 5 +- assets/translations/strings_fa.i18n.json | 5 +- assets/translations/strings_ru.i18n.json | 5 +- assets/translations/strings_tr.i18n.json | 5 +- assets/translations/strings_zh-CN.i18n.json | 5 +- .../model/config_option_entity.dart | 12 +++ .../widget/experimental_feature_notice.dart | 85 +++++++++++++++++++ .../home/widget/connection_button.dart | 28 +++++- 8 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 lib/features/connection/widget/experimental_feature_notice.dart diff --git a/assets/translations/strings_en.i18n.json b/assets/translations/strings_en.i18n.json index cdb7dbbe..0f42d5e8 100644 --- a/assets/translations/strings_en.i18n.json +++ b/assets/translations/strings_en.i18n.json @@ -25,7 +25,10 @@ "tapToConnect": "Tap to Connect", "connecting": "Connecting", "disconnecting": "Disconnecting", - "connected": "Connected" + "connected": "Connected", + "experimentalNotice": "Experimental Features In Use", + "experimentalNoticeMsg": "You've enabled some experimental features which might affect connection quality and cause unexpected errors. You can always change or reset these options from Config options page.", + "disableExperimentalNotice": "Don't show again" }, "stats": { "traffic": "Live Traffic", diff --git a/assets/translations/strings_fa.i18n.json b/assets/translations/strings_fa.i18n.json index 5bec26e9..e4bda1cb 100644 --- a/assets/translations/strings_fa.i18n.json +++ b/assets/translations/strings_fa.i18n.json @@ -25,7 +25,10 @@ "tapToConnect": "برای اتصال ضربه بزنید", "connecting": "در حال اتصال", "disconnecting": "در حال قطع اتصال", - "connected": "متصل" + "connected": "متصل", + "experimentalNotice": "اخطار استفاده از ویژگی‌های آزمایشی", + "experimentalNoticeMsg": "برخی از ویژگی‌های آزمایشی را فعال کرده‌اید که ممکن است بر کیفیت اتصال تأثیر بگذارد و باعث خطاهای غیرمنتظره شود. همیشه می‌توانید این گزینه‌ها را از صفحه تنظیمات کانفیگ تغییر دهید یا بازنشانی کنید.", + "disableExperimentalNotice": "دیگر نشان نده" }, "stats": { "traffic": "مصرف لحظه‌ای", diff --git a/assets/translations/strings_ru.i18n.json b/assets/translations/strings_ru.i18n.json index 5496be89..43c2fd48 100644 --- a/assets/translations/strings_ru.i18n.json +++ b/assets/translations/strings_ru.i18n.json @@ -25,7 +25,10 @@ "tapToConnect": "Нажмите для подключения", "connecting": "Подключение", "disconnecting": "Отключение", - "connected": "Подключено" + "connected": "Подключено", + "experimentalNotice": "Экспериментальные функции в использовании", + "experimentalNoticeMsg": "Вы включили некоторые экспериментальные функции, которые могут повлиять на качество соединения и вызвать непредвиденные ошибки. Вы всегда можете изменить или сбросить эти параметры на странице параметров конфигурации.", + "disableExperimentalNotice": "Больше не показывать" }, "stats": { "traffic": "Текущий трафик", diff --git a/assets/translations/strings_tr.i18n.json b/assets/translations/strings_tr.i18n.json index 188b3a55..2a583dec 100644 --- a/assets/translations/strings_tr.i18n.json +++ b/assets/translations/strings_tr.i18n.json @@ -25,7 +25,10 @@ "tapToConnect": "Bağlanmak için dokunun", "connecting": "Bağlanıyor", "disconnecting": "Bağlantı kesiliyor", - "connected": "Bağlandı" + "connected": "Bağlandı", + "experimentalNotice": "Kullanımdaki Deneysel Özellikler", + "experimentalNoticeMsg": "Bağlantı kalitesini etkileyebilecek ve beklenmeyen hatalara neden olabilecek bazı deneysel özellikleri etkinleştirdiniz. Bu seçenekleri istediğiniz zaman Yapılandırma seçenekleri sayfasından değiştirebilir veya sıfırlayabilirsiniz.", + "disableExperimentalNotice": "Bir daha gösterme" }, "stats": { "traffic": "Canlı Trafik", diff --git a/assets/translations/strings_zh-CN.i18n.json b/assets/translations/strings_zh-CN.i18n.json index 108ce036..ff971bdd 100644 --- a/assets/translations/strings_zh-CN.i18n.json +++ b/assets/translations/strings_zh-CN.i18n.json @@ -25,7 +25,10 @@ "tapToConnect": "点击连接", "connecting": "正在连接", "disconnecting": "正在断开连接", - "connected": "已连接" + "connected": "已连接", + "experimentalNotice": "使用中的实验功能", + "experimentalNoticeMsg": "您启用了一些实验性功能,这些功能可能会影响连接质量并导致意外错误。您始终可以从“配置选项”页面更改或重置这些选项。", + "disableExperimentalNotice": "不再显示" }, "stats": { "traffic": "实时流量", diff --git a/lib/features/config_option/model/config_option_entity.dart b/lib/features/config_option/model/config_option_entity.dart index bd058a13..5d24d908 100644 --- a/lib/features/config_option/model/config_option_entity.dart +++ b/lib/features/config_option/model/config_option_entity.dart @@ -6,6 +6,7 @@ import 'package:hiddify/core/utils/json_converters.dart'; import 'package:hiddify/features/config_option/model/config_option_patch.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; +import 'package:hiddify/utils/platform_utils.dart'; part 'config_option_entity.freezed.dart'; part 'config_option_entity.g.dart'; @@ -56,6 +57,17 @@ class ConfigOptionEntity with _$ConfigOptionEntity { serviceMode: ServiceMode.defaultMode, ); + bool hasExperimentalOptions() { + if (PlatformUtils.isDesktop && serviceMode == ServiceMode.tun) { + return true; + } + if (enableTlsFragment || enableTlsMixedSniCase || enableTlsPadding) { + return true; + } + + return false; + } + String format() { const encoder = JsonEncoder.withIndent(' '); return encoder.convert(toJson()); diff --git a/lib/features/connection/widget/experimental_feature_notice.dart b/lib/features/connection/widget/experimental_feature_notice.dart new file mode 100644 index 00000000..8016ad60 --- /dev/null +++ b/lib/features/connection/widget/experimental_feature_notice.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hiddify/core/localization/translations.dart'; +import 'package:hiddify/core/preferences/preferences_provider.dart'; +import 'package:hiddify/core/router/routes.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'experimental_feature_notice.g.dart'; + +@riverpod +class DisableExperimentalFeatureNotice + extends _$DisableExperimentalFeatureNotice { + static const _key = "disable_experimental_feature_notice"; + + @override + bool build() { + return ref.read(sharedPreferencesProvider).requireValue.getBool(_key) ?? + false; + } + + Future change(bool pref) async { + state = pref; + await ref.read(sharedPreferencesProvider).requireValue.setBool(_key, pref); + } +} + +class ExperimentalFeatureNoticeDialog extends HookConsumerWidget { + const ExperimentalFeatureNoticeDialog({super.key}); + + Future show(BuildContext context) async { + return showDialog( + context: context, + builder: (context) => this, + ); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final t = ref.watch(translationsProvider); + final shouldDisable = + useState(ref.read(disableExperimentalFeatureNoticeProvider)); + + return PopScope( + onPopInvoked: (didPop) async { + await ref + .read(disableExperimentalFeatureNoticeProvider.notifier) + .change(shouldDisable.value); + }, + child: AlertDialog( + title: Text(t.home.connection.experimentalNotice), + content: SingleChildScrollView( + child: SizedBox( + width: 468, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(t.home.connection.experimentalNoticeMsg), + CheckboxListTile( + value: shouldDisable.value, + title: Text(t.home.connection.disableExperimentalNotice), + onChanged: (value) => shouldDisable.value = value ?? false, + ), + ], + ), + ), + ), + actions: [ + TextButton( + onPressed: () async { + await Navigator.of(context).maybePop(false); + if (context.mounted) const ConfigOptionsRoute().push(context); + }, + child: Text(t.settings.config.pageTitle), + ), + TextButton( + onPressed: () => Navigator.of(context).maybePop(true), + child: Text(MaterialLocalizations.of(context).okButtonLabel), + ), + ], + ), + ); + } +} diff --git a/lib/features/home/widget/connection_button.dart b/lib/features/home/widget/connection_button.dart index f2ff6489..3fcbad18 100644 --- a/lib/features/home/widget/connection_button.dart +++ b/lib/features/home/widget/connection_button.dart @@ -4,8 +4,10 @@ import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; import 'package:hiddify/core/theme/theme_extensions.dart'; +import 'package:hiddify/features/config_option/notifier/config_option_notifier.dart'; import 'package:hiddify/features/connection/model/connection_status.dart'; import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; +import 'package:hiddify/features/connection/widget/experimental_feature_notice.dart'; import 'package:hiddify/gen/assets.gen.dart'; import 'package:hiddify/utils/alerts.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -42,8 +44,30 @@ class ConnectionButton extends HookConsumerWidget { : buttonTheme.idleColor!; return _ConnectionButton( - onTap: () => - ref.read(connectionNotifierProvider.notifier).toggleConnection(), + onTap: () async { + var canConnect = true; + if (status case Disconnected()) { + final hasExperimental = + await ref.read(configOptionNotifierProvider.future).then( + (value) => value.hasExperimentalOptions(), + onError: (_) => false, + ); + final canShowNotice = + !ref.read(disableExperimentalFeatureNoticeProvider); + + if (hasExperimental && canShowNotice && context.mounted) { + canConnect = await const ExperimentalFeatureNoticeDialog() + .show(context) ?? + true; + } + } + + if (canConnect) { + await ref + .read(connectionNotifierProvider.notifier) + .toggleConnection(); + } + }, enabled: !status.isSwitching, label: status.present(t), buttonColor: connectionLogoColor,