From 43c397007ccac2497531c702599694c0d80c02a7 Mon Sep 17 00:00:00 2001 From: chen08209 Date: Thu, 6 Jun 2024 16:31:08 +0800 Subject: [PATCH] Optimize delay test Add check ip --- core/common.go | 2 +- core/hub.go | 23 +- lib/clash/core.dart | 27 ++- lib/clash/generated/clash_ffi.dart | 21 +- lib/common/ip.dart | 0 lib/common/measure.dart | 20 +- lib/common/request.dart | 13 +- lib/controller.dart | 31 --- .../dashboard/network_detection.dart | 199 +++++++++--------- lib/fragments/dashboard/start_button.dart | 11 +- lib/fragments/proxies.dart | 185 ++++++++-------- lib/l10n/arb/intl_en.arb | 4 +- lib/l10n/arb/intl_zh_CN.arb | 4 +- lib/l10n/intl/messages_en.dart | 2 + lib/l10n/intl/messages_zh_CN.dart | 2 + lib/l10n/l10n.dart | 20 ++ lib/models/app.dart | 4 +- .../generated/clash_config.freezed.dart | 2 +- lib/models/generated/clash_config.g.dart | 2 +- lib/models/generated/config.g.dart | 2 +- lib/models/generated/selector.freezed.dart | 190 ++++++++--------- lib/models/ip.dart | 5 + lib/models/selector.dart | 27 ++- lib/state.dart | 16 -- lib/widgets/clash_message_container.dart | 8 - pubspec.lock | 32 +++ pubspec.yaml | 3 +- 27 files changed, 443 insertions(+), 412 deletions(-) delete mode 100644 lib/common/ip.dart diff --git a/core/common.go b/core/common.go index 7a68c48..745e2b4 100644 --- a/core/common.go +++ b/core/common.go @@ -412,6 +412,6 @@ func applyConfig(isPatch bool) { patchConfig(cfg.General) } else { executor.ApplyConfig(cfg, true) - healthcheck() + hcCompatibleProvider(tunnel.Providers()) } } diff --git a/core/hub.go b/core/hub.go index d1bd84b..60b2024 100644 --- a/core/hub.go +++ b/core/hub.go @@ -181,7 +181,8 @@ func getTraffic() *C.char { } //export asyncTestDelay -func asyncTestDelay(s *C.char) { +func asyncTestDelay(s *C.char, port C.longlong) { + i := int64(port) go func() { paramsString := C.GoString(s) var params = &TestDelayParams{} @@ -205,26 +206,25 @@ func asyncTestDelay(s *C.char) { Name: params.ProxyName, } - message := bridge.Message{ - Type: bridge.Delay, - Data: delayData, - } - if proxy == nil { delayData.Value = -1 - bridge.SendMessage(message) + data, _ := json.Marshal(delayData) + bridge.SendToPort(i, string(data)) return } delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus) if err != nil || delay == 0 { delayData.Value = -1 - bridge.SendMessage(message) + data, _ := json.Marshal(delayData) + bridge.SendToPort(i, string(data)) return } delayData.Value = int32(delay) - bridge.SendMessage(message) + data, _ := json.Marshal(delayData) + bridge.SendToPort(i, string(data)) + return }() } @@ -374,11 +374,6 @@ func updateExternalProvider(providerName *C.char, providerType *C.char, port C.l }() } -//export healthcheck -func healthcheck() { - hcCompatibleProvider(tunnel.Providers()) -} - //export initNativeApiBridge func initNativeApiBridge(api unsafe.Pointer, port C.longlong) { bridge.InitDartApi(api) diff --git a/lib/clash/core.dart b/lib/clash/core.dart index 5d6e0e5..a4446e3 100644 --- a/lib/clash/core.dart +++ b/lib/clash/core.dart @@ -156,23 +156,36 @@ class ClashCore { return clashFFI.changeProxy(params.toNativeUtf8().cast()) == 1; } - bool delay(String proxyName) { + Future getDelay(String proxyName) { final delayParams = { "proxy-name": proxyName, "timeout": httpTimeoutDuration.inMilliseconds, }; - clashFFI.asyncTestDelay(json.encode(delayParams).toNativeUtf8().cast()); - return true; + final completer = Completer(); + final receiver = ReceivePort(); + receiver.listen((message) { + if (!completer.isCompleted) { + completer.complete(Delay.fromJson(json.decode(message))); + receiver.close(); + } + }); + clashFFI.asyncTestDelay( + json.encode(delayParams).toNativeUtf8().cast(), + receiver.sendPort.nativePort, + ); + Future.delayed(httpTimeoutDuration + moreDuration, () { + receiver.close(); + completer.complete( + Delay(name: proxyName, value: -1), + ); + }); + return completer.future; } clearEffect(String path) { clashFFI.clearEffect(path.toNativeUtf8().cast()); } - healthcheck() { - clashFFI.healthcheck(); - } - VersionInfo getVersionInfo() { final versionInfoRaw = clashFFI.getVersionInfo(); final versionInfo = json.decode(versionInfoRaw.cast().toDartString()); diff --git a/lib/clash/generated/clash_ffi.dart b/lib/clash/generated/clash_ffi.dart index 577f8b4..c7ba220 100644 --- a/lib/clash/generated/clash_ffi.dart +++ b/lib/clash/generated/clash_ffi.dart @@ -977,17 +977,20 @@ class ClashFFI { void asyncTestDelay( ffi.Pointer s, + int port, ) { return _asyncTestDelay( s, + port, ); } - late final _asyncTestDelayPtr = - _lookup)>>( - 'asyncTestDelay'); - late final _asyncTestDelay = - _asyncTestDelayPtr.asFunction)>(); + late final _asyncTestDelayPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, ffi.LongLong)>>('asyncTestDelay'); + late final _asyncTestDelay = _asyncTestDelayPtr + .asFunction, int)>(); ffi.Pointer getVersionInfo() { return _getVersionInfo(); @@ -1086,14 +1089,6 @@ class ClashFFI { late final _updateExternalProvider = _updateExternalProviderPtr.asFunction< void Function(ffi.Pointer, ffi.Pointer, int)>(); - void healthcheck() { - return _healthcheck(); - } - - late final _healthcheckPtr = - _lookup>('healthcheck'); - late final _healthcheck = _healthcheckPtr.asFunction(); - void initNativeApiBridge( ffi.Pointer api, int port, diff --git a/lib/common/ip.dart b/lib/common/ip.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/common/measure.dart b/lib/common/measure.dart index b6de767..cdd08c1 100644 --- a/lib/common/measure.dart +++ b/lib/common/measure.dart @@ -24,9 +24,9 @@ class Measure { double? _bodySmallHeight; double? _labelSmallHeight; double? _titleLargeHeight; + double? _titleMediumHeight; - - double get bodyMediumHeight{ + double get bodyMediumHeight { _bodyMediumHeight ??= computeTextSize( Text( "", @@ -36,7 +36,7 @@ class Measure { return _bodyMediumHeight!; } - double get bodySmallHeight{ + double get bodySmallHeight { _bodySmallHeight ??= computeTextSize( Text( "", @@ -46,7 +46,7 @@ class Measure { return _bodySmallHeight!; } - double get labelSmallHeight{ + double get labelSmallHeight { _labelSmallHeight ??= computeTextSize( Text( "", @@ -56,7 +56,7 @@ class Measure { return _labelSmallHeight!; } - double get titleLargeHeight{ + double get titleLargeHeight { _titleLargeHeight ??= computeTextSize( Text( "", @@ -65,4 +65,14 @@ class Measure { ).height; return _titleLargeHeight!; } + + double get titleMediumHeight { + _titleMediumHeight ??= computeTextSize( + Text( + "", + style: context.textTheme.titleMedium, + ), + ).height; + return _titleMediumHeight!; + } } diff --git a/lib/common/request.dart b/lib/common/request.dart index 00aedcf..80b7ece 100644 --- a/lib/common/request.dart +++ b/lib/common/request.dart @@ -9,6 +9,7 @@ import 'package:fl_clash/state.dart'; class Request { late final Dio _dio; int? _port; + bool _isStart = false; Request() { _dio = Dio( @@ -29,11 +30,14 @@ class Request { _syncProxy() { final port = globalState.appController.clashConfig.mixedPort; - if (_port != port) { + final isStart = globalState.appController.appState.isStart; + if (_port != port || isStart != _isStart) { _port = port; + _isStart = isStart; _dio.httpClientAdapter = IOHttpClientAdapter( createHttpClient: () { final client = HttpClient(); + if(!_isStart) return client; client.findProxy = (url) { return "PROXY localhost:$_port;DIRECT"; }; @@ -82,11 +86,12 @@ class Request { "https://ipinfo.io/json/": IpInfo.fromIpInfoIoJson, }; - Future checkIp() async { + Future checkIp(CancelToken cancelToken) async { for (final source in _ipInfoSources.entries) { try { final response = await _dio.get>( source.key, + cancelToken: cancelToken, ); if (response.statusCode == 200 && response.data != null) { return source.value(response.data!); @@ -95,8 +100,8 @@ class Request { continue; } } - throw "无法检索ip"; + return null; } } -final request = Request(); +final request = Request(); \ No newline at end of file diff --git a/lib/controller.dart b/lib/controller.dart index 9a8bf73..dabd4db 100644 --- a/lib/controller.dart +++ b/lib/controller.dart @@ -39,8 +39,6 @@ class AppController { updateRunTime, updateTraffic, ]; - clearShowProxyDelay(); - testShowProxyDelay(); } else { await globalState.stopSystemProxy(); appState.traffics = []; @@ -263,19 +261,6 @@ class AppController { autoCheckUpdate(); } - healthcheck() { - if (globalState.healthcheckLock) return; - for (final delay in appState.delayMap.entries) { - setDelay( - Delay( - name: delay.key, - value: 0, - ), - ); - } - clashCore.healthcheck(); - } - setDelay(Delay delay) { appState.setDelay(delay); } @@ -385,22 +370,6 @@ class AppController { addProfileFormURL(url); } - clearShowProxyDelay() { - final showProxyDelay = appState.getRealProxyName(appState.showProxyName); - if (showProxyDelay != null) { - appState.setDelay( - Delay(name: showProxyDelay, value: null), - ); - } - } - - testShowProxyDelay() { - final showProxyDelay = appState.getRealProxyName(appState.showProxyName); - if (showProxyDelay != null) { - globalState.updateCurrentDelay(showProxyDelay); - } - } - updateViewWidth(double width) { WidgetsBinding.instance.addPostFrameCallback((_) { appState.viewWidth = width; diff --git a/lib/fragments/dashboard/network_detection.dart b/lib/fragments/dashboard/network_detection.dart index aadacaa..5455c9d 100644 --- a/lib/fragments/dashboard/network_detection.dart +++ b/lib/fragments/dashboard/network_detection.dart @@ -1,3 +1,5 @@ +import 'package:country_flags/country_flags.dart'; +import 'package:dio/dio.dart'; import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/state.dart'; @@ -13,118 +15,119 @@ class NetworkDetection extends StatefulWidget { } class _NetworkDetectionState extends State { - Widget _buildDescription(String? currentProxyName, int? delay) { - if (currentProxyName == null) { - return TooltipText( - text: Text( - appLocalizations.noProxyDesc, - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Theme.of(context).colorScheme.secondary, - ), - overflow: TextOverflow.ellipsis, - ), - ); + final ipInfoNotifier = ValueNotifier(null); + CancelToken? cancelToken; + + _checkIp( + bool isInit, + ) async { + if (!isInit) return; + await Future.delayed(const Duration(milliseconds: 300)); + if (cancelToken != null) { + cancelToken!.cancel(); + cancelToken = null; } - if (delay == 0 || delay == null) { - return const AspectRatio( - aspectRatio: 1, - child: CircularProgressIndicator( - strokeCap: StrokeCap.round, - ), - ); - } - if (delay > 0) { - return Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - TooltipText( - text: Text( - "$delay", - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: context.textTheme.titleLarge - ?.copyWith( - color: context.colorScheme.primary, - ) - .toSoftBold(), - ), - ), - const Flexible( - child: SizedBox( - width: 4, - ), - ), - Flexible( - flex: 0, - child: Text( - 'ms', - style: Theme.of(context).textTheme.bodyMedium?.toLight(), - ), - ), - ], - ); - } - return Text( - "Timeout", - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: Colors.red, - ), + ipInfoNotifier.value = null; + cancelToken = CancelToken(); + ipInfoNotifier.value = await request.checkIp(cancelToken!); + } + + _checkIpContainer(Widget child) { + return Selector2( + selector: (_, appState, config) { + return CheckIpSelectorState( + isInit: appState.isInit, + selectedMap: appState.selectedMap, + isStart: appState.isStart, + ); + }, + builder: (_, state, __) { + _checkIp( + state.isInit, + ); + return child; + }, + child: child, ); } @override Widget build(BuildContext context) { - return CommonCard( - info: Info( - iconData: Icons.network_check, - label: appLocalizations.networkDetection, - ), - child: Selector( - selector: (_, appState) { - return NetworkDetectionSelectorState( - currentProxyName: appState.showProxyName, - delay: appState.getDelay( - appState.showProxyName, - ), - ); - }, - builder: (_, state, __) { - return Container( - padding: const EdgeInsets.all(16).copyWith(top: 0), + return _checkIpContainer( + ValueListenableBuilder( + valueListenable: ipInfoNotifier, + builder: (_, ipInfo, __) { + return CommonCard( child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( flex: 0, - child: TooltipText( - text: Text( - state.currentProxyName ?? appLocalizations.noProxy, - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: Theme.of(context) - .textTheme - .titleMedium - ?.toSoftBold(), - ), - ), - ), - const SizedBox( - height: 8, - ), - Flexible( child: Container( - height: globalState.appController.measure.titleLargeHeight, - alignment: Alignment.centerLeft, - child: FadeBox( - child: _buildDescription( - state.currentProxyName, - state.delay, - ), + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Icon( + Icons.network_check, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + child: FadeBox( + child: ipInfo != null + ? CountryFlag.fromCountryCode( + ipInfo.countryCode, + width: 24, + height: 24, + ) + : TooltipText( + text: Text( + appLocalizations.checking, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .titleMedium, + ), + ), + ), + ), + ], ), ), ), + Container( + height: + globalState.appController.measure.titleLargeHeight + 24, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.all(16).copyWith(top: 0), + child: FadeBox( + child: ipInfo != null + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + flex: 1, + child: TooltipText( + text: Text( + ipInfo.ip, + style: context.textTheme.titleLarge + ?.toSoftBold(), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ) + : const SizedBox( + child: CircularProgressIndicator(), + ), + ), + ) ], ), ); diff --git a/lib/fragments/dashboard/start_button.dart b/lib/fragments/dashboard/start_button.dart index 8353493..2fc9e3c 100644 --- a/lib/fragments/dashboard/start_button.dart +++ b/lib/fragments/dashboard/start_button.dart @@ -19,7 +19,7 @@ class _StartButtonState extends State @override void initState() { - isStart = context.read().runTime != null; + isStart = globalState.appController.appState.isStart; _controller = AnimationController( vsync: this, value: isStart ? 1 : 0, @@ -48,9 +48,12 @@ class _StartButtonState extends State } } - updateSystemProxy() async { - final appController = globalState.appController; - await appController.updateSystemProxy(isStart); + updateSystemProxy() { + WidgetsBinding.instance.addPostFrameCallback((_) async { + final appController = globalState.appController; + await appController.updateSystemProxy(isStart); + }); + ; } @override diff --git a/lib/fragments/proxies.dart b/lib/fragments/proxies.dart index 483a0ad..3774b3b 100644 --- a/lib/fragments/proxies.dart +++ b/lib/fragments/proxies.dart @@ -1,3 +1,4 @@ +import 'package:fl_clash/clash/clash.dart'; import 'package:fl_clash/state.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -57,72 +58,69 @@ class _ProxiesFragmentState extends State @override Widget build(BuildContext context) { - return DelayTestButtonContainer( - child: Selector( - selector: (_, appState) => appState.currentLabel == 'proxies', - builder: (_, isCurrent, child) { - if (isCurrent) { - _initActions(); - } - return child!; + return Selector( + selector: (_, appState) => appState.currentLabel == 'proxies', + builder: (_, isCurrent, child) { + if (isCurrent) { + _initActions(); + } + return child!; + }, + child: Selector3( + selector: (_, appState, config, clashConfig) { + final currentGroups = appState.currentGroups; + final groupNames = currentGroups.map((e) => e.name).toList(); + return ProxiesSelectorState( + groupNames: groupNames, + ); }, - child: Selector3( - selector: (_, appState, config, clashConfig) { - final currentGroups = appState.currentGroups; - final groupNames = currentGroups.map((e) => e.name).toList(); - return ProxiesSelectorState( - groupNames: groupNames, - ); - }, - shouldRebuild: (prev, next) { - if (prev.groupNames.length != next.groupNames.length) { - _tabController?.dispose(); - _tabController = null; - } - return prev != next; - }, - builder: (_, state, __) { - _tabController ??= TabController( - length: state.groupNames.length, - vsync: this, - ); - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TabBar( + shouldRebuild: (prev, next) { + if (prev.groupNames.length != next.groupNames.length) { + _tabController?.dispose(); + _tabController = null; + } + return prev != next; + }, + builder: (_, state, __) { + _tabController ??= TabController( + length: state.groupNames.length, + vsync: this, + ); + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TabBar( + controller: _tabController, + padding: const EdgeInsets.symmetric(horizontal: 16), + dividerColor: Colors.transparent, + isScrollable: true, + tabAlignment: TabAlignment.start, + overlayColor: const WidgetStatePropertyAll(Colors.transparent), + tabs: [ + for (final groupName in state.groupNames) + Tab( + text: groupName, + ), + ], + ), + Expanded( + child: TabBarView( controller: _tabController, - padding: const EdgeInsets.symmetric(horizontal: 16), - dividerColor: Colors.transparent, - isScrollable: true, - tabAlignment: TabAlignment.start, - overlayColor: - const WidgetStatePropertyAll(Colors.transparent), - tabs: [ + children: [ for (final groupName in state.groupNames) - Tab( - text: groupName, + KeepContainer( + key: ObjectKey(groupName), + child: ProxiesTabView( + groupName: groupName, + ), ), ], ), - Expanded( - child: TabBarView( - controller: _tabController, - children: [ - for (final groupName in state.groupNames) - KeepContainer( - key: ObjectKey(groupName), - child: ProxiesTabView( - groupName: groupName, - ), - ), - ], - ), - ) - ], - ); - }, - ), + ) + ], + ); + }, ), ); } @@ -196,6 +194,18 @@ class ProxiesTabView extends StatelessWidget { } } + _delayTest(List proxies) async { + for (final proxy in proxies) { + globalState.appController.setDelay( + Delay(name: proxy.name, value: 0), + ); + clashCore.getDelay(proxy.name).then((delay) { + globalState.appController.setDelay(delay); + }); + } + await Future.delayed(httpTimeoutDuration + moreDuration); + } + @override Widget build(BuildContext context) { return Selector2( @@ -213,25 +223,32 @@ class ProxiesTabView extends StatelessWidget { state.group.all, state.proxiesSortType, ); - return Align( - alignment: Alignment.topCenter, - child: GridView.builder( - padding: const EdgeInsets.all(16), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: _getColumns(state.viewMode), - mainAxisSpacing: 8, - crossAxisSpacing: 8, - mainAxisExtent: _getItemHeight(context), + return DelayTestButtonContainer( + onClick: () async { + await _delayTest( + state.group.all, + ); + }, + child: Align( + alignment: Alignment.topCenter, + child: GridView.builder( + padding: const EdgeInsets.all(16), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: _getColumns(state.viewMode), + mainAxisSpacing: 8, + crossAxisSpacing: 8, + mainAxisExtent: _getItemHeight(context), + ), + itemCount: proxies.length, + itemBuilder: (_, index) { + final proxy = proxies[index]; + return ProxyCard( + key: ValueKey('$groupName.${proxy.name}'), + proxy: proxy, + groupName: groupName, + ); + }, ), - itemCount: proxies.length, - itemBuilder: (_, index) { - final proxy = proxies[index]; - return ProxyCard( - key: ValueKey('$groupName.${proxy.name}'), - proxy: proxy, - groupName: groupName, - ); - }, ), ); }, @@ -384,10 +401,12 @@ class ProxyCard extends StatelessWidget { class DelayTestButtonContainer extends StatefulWidget { final Widget child; + final Future Function() onClick; const DelayTestButtonContainer({ super.key, required this.child, + required this.onClick, }); @override @@ -401,12 +420,9 @@ class _DelayTestButtonContainerState extends State late Animation _scale; _healthcheck() async { - if (globalState.healthcheckLock) return; _controller.forward(); - globalState.appController.healthcheck(); - Future.delayed(httpTimeoutDuration + moreDuration, () { - _controller.reverse(); - }); + await widget.onClick(); + _controller.reverse(); } @override @@ -415,7 +431,7 @@ class _DelayTestButtonContainerState extends State _controller = AnimationController( vsync: this, duration: const Duration( - milliseconds: 600, + milliseconds: 1200, ), ); _scale = Tween( @@ -441,6 +457,7 @@ class _DelayTestButtonContainerState extends State @override Widget build(BuildContext context) { + _controller.reverse(); return FloatLayout( floatingWidget: FloatWrapper( child: AnimatedBuilder( diff --git a/lib/l10n/arb/intl_en.arb b/lib/l10n/arb/intl_en.arb index b424a65..762ef13 100644 --- a/lib/l10n/arb/intl_en.arb +++ b/lib/l10n/arb/intl_en.arb @@ -156,5 +156,7 @@ "goDownload": "Go to download", "unknown": "Unknown", "geoData": "GeoData", - "externalResources": "External resources" + "externalResources": "External resources", + "checking": "Checking...", + "country": "Country" } \ No newline at end of file diff --git a/lib/l10n/arb/intl_zh_CN.arb b/lib/l10n/arb/intl_zh_CN.arb index 6ea7d7d..db4aec1 100644 --- a/lib/l10n/arb/intl_zh_CN.arb +++ b/lib/l10n/arb/intl_zh_CN.arb @@ -156,5 +156,7 @@ "goDownload": "前往下载", "unknown": "未知", "geoData": "地理数据", - "externalResources": "外部资源" + "externalResources": "外部资源", + "checking": "检测中...", + "country": "区域" } \ No newline at end of file diff --git a/lib/l10n/intl/messages_en.dart b/lib/l10n/intl/messages_en.dart index dd80cc9..95d0d53 100644 --- a/lib/l10n/intl/messages_en.dart +++ b/lib/l10n/intl/messages_en.dart @@ -80,6 +80,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Check for updates"), "checkUpdateError": MessageLookupByLibrary.simpleMessage( "The current application is already the latest version"), + "checking": MessageLookupByLibrary.simpleMessage("Checking..."), "compatible": MessageLookupByLibrary.simpleMessage("Compatibility mode"), "compatibleDesc": MessageLookupByLibrary.simpleMessage( @@ -88,6 +89,7 @@ class MessageLookup extends MessageLookupByLibrary { "connectivity": MessageLookupByLibrary.simpleMessage("Connectivity:"), "core": MessageLookupByLibrary.simpleMessage("Core"), "coreInfo": MessageLookupByLibrary.simpleMessage("Core info"), + "country": MessageLookupByLibrary.simpleMessage("Country"), "create": MessageLookupByLibrary.simpleMessage("Create"), "dark": MessageLookupByLibrary.simpleMessage("Dark"), "dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"), diff --git a/lib/l10n/intl/messages_zh_CN.dart b/lib/l10n/intl/messages_zh_CN.dart index 7db2f09..dbac9d4 100644 --- a/lib/l10n/intl/messages_zh_CN.dart +++ b/lib/l10n/intl/messages_zh_CN.dart @@ -65,6 +65,7 @@ class MessageLookup extends MessageLookupByLibrary { "cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"), "checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"), "checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"), + "checking": MessageLookupByLibrary.simpleMessage("检测中..."), "compatible": MessageLookupByLibrary.simpleMessage("兼容模式"), "compatibleDesc": MessageLookupByLibrary.simpleMessage("开启将失去部分应用能力,获得全量的Clash的支持"), @@ -72,6 +73,7 @@ class MessageLookup extends MessageLookupByLibrary { "connectivity": MessageLookupByLibrary.simpleMessage("连通性:"), "core": MessageLookupByLibrary.simpleMessage("内核"), "coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"), + "country": MessageLookupByLibrary.simpleMessage("区域"), "create": MessageLookupByLibrary.simpleMessage("创建"), "dark": MessageLookupByLibrary.simpleMessage("深色"), "dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"), diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart index e6e2ca5..401821e 100644 --- a/lib/l10n/l10n.dart +++ b/lib/l10n/l10n.dart @@ -1629,6 +1629,26 @@ class AppLocalizations { args: [], ); } + + /// `Checking...` + String get checking { + return Intl.message( + 'Checking...', + name: 'checking', + desc: '', + args: [], + ); + } + + /// `Country` + String get country { + return Intl.message( + 'Country', + name: 'country', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/models/app.dart b/lib/models/app.dart index 53297d3..5b65c93 100644 --- a/lib/models/app.dart +++ b/lib/models/app.dart @@ -89,6 +89,8 @@ class AppState with ChangeNotifier { } } + bool get isStart => _runTime != null; + int? get runTime => _runTime; set runTime(int? value) { @@ -166,7 +168,7 @@ class AppState with ChangeNotifier { addLog(Log log) { _logs.add(log); - if(_logs.length > 60){ + if (_logs.length > 60) { _logs = _logs.sublist(_logs.length - 60); } notifyListeners(); diff --git a/lib/models/generated/clash_config.freezed.dart b/lib/models/generated/clash_config.freezed.dart index 7a4ec1a..392f2f8 100644 --- a/lib/models/generated/clash_config.freezed.dart +++ b/lib/models/generated/clash_config.freezed.dart @@ -135,7 +135,7 @@ class _$TunImpl implements _Tun { const _$TunImpl( {this.enable = false, this.device = appName, - this.stack = TunStack.gvisor, + this.stack = TunStack.mixed, @JsonKey(name: "dns-hijack") final List dnsHijack = const ["any:53"]}) : _dnsHijack = dnsHijack; diff --git a/lib/models/generated/clash_config.g.dart b/lib/models/generated/clash_config.g.dart index e3c9b8d..e591a97 100644 --- a/lib/models/generated/clash_config.g.dart +++ b/lib/models/generated/clash_config.g.dart @@ -79,7 +79,7 @@ _$TunImpl _$$TunImplFromJson(Map json) => _$TunImpl( enable: json['enable'] as bool? ?? false, device: json['device'] as String? ?? appName, stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ?? - TunStack.gvisor, + TunStack.mixed, dnsHijack: (json['dns-hijack'] as List?) ?.map((e) => e as String) .toList() ?? diff --git a/lib/models/generated/config.g.dart b/lib/models/generated/config.g.dart index 7845d1e..1d509ee 100644 --- a/lib/models/generated/config.g.dart +++ b/lib/models/generated/config.g.dart @@ -31,7 +31,7 @@ Config _$ConfigFromJson(Map json) => Config() ? null : DAV.fromJson(json['dav'] as Map) ..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true - ..isCompatible = json['isCompatible'] as bool? ?? false + ..isCompatible = json['isCompatible'] as bool? ?? true ..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true; Map _$ConfigToJson(Config instance) => { diff --git a/lib/models/generated/selector.freezed.dart b/lib/models/generated/selector.freezed.dart index 71cb1aa..4276c4b 100644 --- a/lib/models/generated/selector.freezed.dart +++ b/lib/models/generated/selector.freezed.dart @@ -157,34 +157,30 @@ abstract class _StartButtonSelectorState implements StartButtonSelectorState { } /// @nodoc -mixin _$UpdateCurrentDelaySelectorState { - String? get currentProxyName => throw _privateConstructorUsedError; - bool get isCurrent => throw _privateConstructorUsedError; - int? get delay => throw _privateConstructorUsedError; +mixin _$CheckIpSelectorState { bool get isInit => throw _privateConstructorUsedError; + bool get isStart => throw _privateConstructorUsedError; + Map get selectedMap => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $UpdateCurrentDelaySelectorStateCopyWith - get copyWith => throw _privateConstructorUsedError; + $CheckIpSelectorStateCopyWith get copyWith => + throw _privateConstructorUsedError; } /// @nodoc -abstract class $UpdateCurrentDelaySelectorStateCopyWith<$Res> { - factory $UpdateCurrentDelaySelectorStateCopyWith( - UpdateCurrentDelaySelectorState value, - $Res Function(UpdateCurrentDelaySelectorState) then) = - _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res, - UpdateCurrentDelaySelectorState>; +abstract class $CheckIpSelectorStateCopyWith<$Res> { + factory $CheckIpSelectorStateCopyWith(CheckIpSelectorState value, + $Res Function(CheckIpSelectorState) then) = + _$CheckIpSelectorStateCopyWithImpl<$Res, CheckIpSelectorState>; @useResult - $Res call( - {String? currentProxyName, bool isCurrent, int? delay, bool isInit}); + $Res call({bool isInit, bool isStart, Map selectedMap}); } /// @nodoc -class _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res, - $Val extends UpdateCurrentDelaySelectorState> - implements $UpdateCurrentDelaySelectorStateCopyWith<$Res> { - _$UpdateCurrentDelaySelectorStateCopyWithImpl(this._value, this._then); +class _$CheckIpSelectorStateCopyWithImpl<$Res, + $Val extends CheckIpSelectorState> + implements $CheckIpSelectorStateCopyWith<$Res> { + _$CheckIpSelectorStateCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; @@ -194,154 +190,136 @@ class _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res, @pragma('vm:prefer-inline') @override $Res call({ - Object? currentProxyName = freezed, - Object? isCurrent = null, - Object? delay = freezed, Object? isInit = null, + Object? isStart = null, + Object? selectedMap = null, }) { return _then(_value.copyWith( - currentProxyName: freezed == currentProxyName - ? _value.currentProxyName - : currentProxyName // ignore: cast_nullable_to_non_nullable - as String?, - isCurrent: null == isCurrent - ? _value.isCurrent - : isCurrent // ignore: cast_nullable_to_non_nullable - as bool, - delay: freezed == delay - ? _value.delay - : delay // ignore: cast_nullable_to_non_nullable - as int?, isInit: null == isInit ? _value.isInit : isInit // ignore: cast_nullable_to_non_nullable as bool, + isStart: null == isStart + ? _value.isStart + : isStart // ignore: cast_nullable_to_non_nullable + as bool, + selectedMap: null == selectedMap + ? _value.selectedMap + : selectedMap // ignore: cast_nullable_to_non_nullable + as Map, ) as $Val); } } /// @nodoc -abstract class _$$UpdateCurrentDelaySelectorStateImplCopyWith<$Res> - implements $UpdateCurrentDelaySelectorStateCopyWith<$Res> { - factory _$$UpdateCurrentDelaySelectorStateImplCopyWith( - _$UpdateCurrentDelaySelectorStateImpl value, - $Res Function(_$UpdateCurrentDelaySelectorStateImpl) then) = - __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<$Res>; +abstract class _$$CheckIpSelectorStateImplCopyWith<$Res> + implements $CheckIpSelectorStateCopyWith<$Res> { + factory _$$CheckIpSelectorStateImplCopyWith(_$CheckIpSelectorStateImpl value, + $Res Function(_$CheckIpSelectorStateImpl) then) = + __$$CheckIpSelectorStateImplCopyWithImpl<$Res>; @override @useResult - $Res call( - {String? currentProxyName, bool isCurrent, int? delay, bool isInit}); + $Res call({bool isInit, bool isStart, Map selectedMap}); } /// @nodoc -class __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<$Res> - extends _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res, - _$UpdateCurrentDelaySelectorStateImpl> - implements _$$UpdateCurrentDelaySelectorStateImplCopyWith<$Res> { - __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl( - _$UpdateCurrentDelaySelectorStateImpl _value, - $Res Function(_$UpdateCurrentDelaySelectorStateImpl) _then) +class __$$CheckIpSelectorStateImplCopyWithImpl<$Res> + extends _$CheckIpSelectorStateCopyWithImpl<$Res, _$CheckIpSelectorStateImpl> + implements _$$CheckIpSelectorStateImplCopyWith<$Res> { + __$$CheckIpSelectorStateImplCopyWithImpl(_$CheckIpSelectorStateImpl _value, + $Res Function(_$CheckIpSelectorStateImpl) _then) : super(_value, _then); @pragma('vm:prefer-inline') @override $Res call({ - Object? currentProxyName = freezed, - Object? isCurrent = null, - Object? delay = freezed, Object? isInit = null, + Object? isStart = null, + Object? selectedMap = null, }) { - return _then(_$UpdateCurrentDelaySelectorStateImpl( - currentProxyName: freezed == currentProxyName - ? _value.currentProxyName - : currentProxyName // ignore: cast_nullable_to_non_nullable - as String?, - isCurrent: null == isCurrent - ? _value.isCurrent - : isCurrent // ignore: cast_nullable_to_non_nullable - as bool, - delay: freezed == delay - ? _value.delay - : delay // ignore: cast_nullable_to_non_nullable - as int?, + return _then(_$CheckIpSelectorStateImpl( isInit: null == isInit ? _value.isInit : isInit // ignore: cast_nullable_to_non_nullable as bool, + isStart: null == isStart + ? _value.isStart + : isStart // ignore: cast_nullable_to_non_nullable + as bool, + selectedMap: null == selectedMap + ? _value._selectedMap + : selectedMap // ignore: cast_nullable_to_non_nullable + as Map, )); } } /// @nodoc -class _$UpdateCurrentDelaySelectorStateImpl - implements _UpdateCurrentDelaySelectorState { - const _$UpdateCurrentDelaySelectorStateImpl( - {required this.currentProxyName, - required this.isCurrent, - required this.delay, - required this.isInit}); +class _$CheckIpSelectorStateImpl implements _CheckIpSelectorState { + const _$CheckIpSelectorStateImpl( + {required this.isInit, + required this.isStart, + required final Map selectedMap}) + : _selectedMap = selectedMap; - @override - final String? currentProxyName; - @override - final bool isCurrent; - @override - final int? delay; @override final bool isInit; + @override + final bool isStart; + final Map _selectedMap; + @override + Map get selectedMap { + if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_selectedMap); + } @override String toString() { - return 'UpdateCurrentDelaySelectorState(currentProxyName: $currentProxyName, isCurrent: $isCurrent, delay: $delay, isInit: $isInit)'; + return 'CheckIpSelectorState(isInit: $isInit, isStart: $isStart, selectedMap: $selectedMap)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$UpdateCurrentDelaySelectorStateImpl && - (identical(other.currentProxyName, currentProxyName) || - other.currentProxyName == currentProxyName) && - (identical(other.isCurrent, isCurrent) || - other.isCurrent == isCurrent) && - (identical(other.delay, delay) || other.delay == delay) && - (identical(other.isInit, isInit) || other.isInit == isInit)); + other is _$CheckIpSelectorStateImpl && + (identical(other.isInit, isInit) || other.isInit == isInit) && + (identical(other.isStart, isStart) || other.isStart == isStart) && + const DeepCollectionEquality() + .equals(other._selectedMap, _selectedMap)); } @override - int get hashCode => - Object.hash(runtimeType, currentProxyName, isCurrent, delay, isInit); + int get hashCode => Object.hash(runtimeType, isInit, isStart, + const DeepCollectionEquality().hash(_selectedMap)); @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$UpdateCurrentDelaySelectorStateImplCopyWith< - _$UpdateCurrentDelaySelectorStateImpl> - get copyWith => __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl< - _$UpdateCurrentDelaySelectorStateImpl>(this, _$identity); + _$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl> + get copyWith => + __$$CheckIpSelectorStateImplCopyWithImpl<_$CheckIpSelectorStateImpl>( + this, _$identity); } -abstract class _UpdateCurrentDelaySelectorState - implements UpdateCurrentDelaySelectorState { - const factory _UpdateCurrentDelaySelectorState( - {required final String? currentProxyName, - required final bool isCurrent, - required final int? delay, - required final bool isInit}) = _$UpdateCurrentDelaySelectorStateImpl; +abstract class _CheckIpSelectorState implements CheckIpSelectorState { + const factory _CheckIpSelectorState( + {required final bool isInit, + required final bool isStart, + required final Map selectedMap}) = + _$CheckIpSelectorStateImpl; - @override - String? get currentProxyName; - @override - bool get isCurrent; - @override - int? get delay; @override bool get isInit; @override + bool get isStart; + @override + Map get selectedMap; + @override @JsonKey(ignore: true) - _$$UpdateCurrentDelaySelectorStateImplCopyWith< - _$UpdateCurrentDelaySelectorStateImpl> + _$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/models/ip.dart b/lib/models/ip.dart index 19a7877..cc72c11 100644 --- a/lib/models/ip.dart +++ b/lib/models/ip.dart @@ -62,4 +62,9 @@ class IpInfo { _ => throw const FormatException("invalid json"), }; } + + @override + String toString() { + return 'IpInfo{ip: $ip, countryCode: $countryCode}'; + } } diff --git a/lib/models/selector.dart b/lib/models/selector.dart index fff8fac..989e547 100644 --- a/lib/models/selector.dart +++ b/lib/models/selector.dart @@ -1,4 +1,5 @@ import 'package:fl_clash/enum/enum.dart'; +import 'package:fl_clash/models/models.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'config.dart'; @@ -17,13 +18,12 @@ class StartButtonSelectorState with _$StartButtonSelectorState { } @freezed -class UpdateCurrentDelaySelectorState with _$UpdateCurrentDelaySelectorState { - const factory UpdateCurrentDelaySelectorState({ - required String? currentProxyName, - required bool isCurrent, - required int? delay, +class CheckIpSelectorState with _$CheckIpSelectorState { + const factory CheckIpSelectorState({ required bool isInit, - }) = _UpdateCurrentDelaySelectorState; + required bool isStart, + required SelectedMap selectedMap, + }) = _CheckIpSelectorState; } @freezed @@ -53,24 +53,23 @@ class ApplicationSelectorState with _$ApplicationSelectorState { } @freezed -class TrayContainerSelectorState with _$TrayContainerSelectorState{ +class TrayContainerSelectorState with _$TrayContainerSelectorState { const factory TrayContainerSelectorState({ required Mode mode, required bool autoLaunch, required bool isRun, required String? locale, - })=_TrayContainerSelectorState; + }) = _TrayContainerSelectorState; } @freezed -class UpdateNavigationsSelector with _$UpdateNavigationsSelector{ +class UpdateNavigationsSelector with _$UpdateNavigationsSelector { const factory UpdateNavigationsSelector({ required bool openLogs, required bool hasProxies, }) = _UpdateNavigationsSelector; } - @freezed class HomeSelectorState with _$HomeSelectorState { const factory HomeSelectorState({ @@ -89,21 +88,21 @@ class HomeBodySelectorState with _$HomeBodySelectorState { } @freezed -class ProxiesCardSelectorState with _$ProxiesCardSelectorState{ +class ProxiesCardSelectorState with _$ProxiesCardSelectorState { const factory ProxiesCardSelectorState({ required bool isSelected, }) = _ProxiesCardSelectorState; } @freezed -class ProxiesSelectorState with _$ProxiesSelectorState{ +class ProxiesSelectorState with _$ProxiesSelectorState { const factory ProxiesSelectorState({ required List groupNames, }) = _ProxiesSelectorState; } @freezed -class ProxiesTabViewSelectorState with _$ProxiesTabViewSelectorState{ +class ProxiesTabViewSelectorState with _$ProxiesTabViewSelectorState { const factory ProxiesTabViewSelectorState({ required ProxiesSortType proxiesSortType, required num sortNum, @@ -125,4 +124,4 @@ class PackageListSelectorState with _$PackageListSelectorState { required AccessControl accessControl, required bool isAccessControl, }) = _PackageListSelectorState; -} \ No newline at end of file +} diff --git a/lib/state.dart b/lib/state.dart index 8ed7c8e..e44fc11 100644 --- a/lib/state.dart +++ b/lib/state.dart @@ -13,7 +13,6 @@ import 'common/common.dart'; class GlobalState { Timer? timer; - Function? healthcheckLockDebounce; Timer? groupsUpdateTimer; Function? updateCurrentDelayDebounce; PageController? pageController; @@ -22,7 +21,6 @@ class GlobalState { late AppController appController; GlobalKey homeScaffoldKey = GlobalKey(); List updateFunctionLists = []; - bool healthcheckLock = false; startListenUpdate() { if (timer != null && timer!.isActive == true) return; @@ -253,20 +251,6 @@ class GlobalState { ); } - void updateCurrentDelay( - String? proxyName, - ) { - updateCurrentDelayDebounce ??= debounce((proxyName) { - if (proxyName != null) { - debugPrint("[delay]=====> $proxyName"); - clashCore.delay( - proxyName, - ); - } - }); - updateCurrentDelayDebounce!([proxyName]); - } - Future safeRun( FutureOr Function() futureFunction, { String? title, diff --git a/lib/widgets/clash_message_container.dart b/lib/widgets/clash_message_container.dart index e3766bf..c8ee8d5 100644 --- a/lib/widgets/clash_message_container.dart +++ b/lib/widgets/clash_message_container.dart @@ -38,16 +38,8 @@ class _ClashMessageContainerState extends State @override void onDelay(Delay delay) { - globalState.healthcheckLock = true; final appController = globalState.appController; appController.setDelay(delay); - globalState.healthcheckLockDebounce ??= debounce( - () async { - globalState.healthcheckLock = false; - }, - milliseconds: 5000, - ); - globalState.healthcheckLockDebounce!(); super.onDelay(delay); } diff --git a/pubspec.lock b/pubspec.lock index 54d92aa..31f2b30 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -193,6 +193,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.1.1" + country_flags: + dependency: "direct main" + description: + name: country_flags + sha256: dbc4f76e7c801619b2d841023e0327191ba00663f1f1b4770394e9bc6632c444 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" cross_file: dependency: transitive description: @@ -509,6 +517,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.4" + jovial_misc: + dependency: transitive + description: + name: jovial_misc + sha256: f6e64f789ee311025bb367be9c9afe9759f76dd8209070b7f38e735b5f529eb1 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.8.5" + jovial_svg: + dependency: transitive + description: + name: jovial_svg + sha256: "000cafe183bdeba28f76d65bf93fc9b636d6f7eaa7e2a33e80c4345a28866ea8" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.21" js: dependency: transitive description: @@ -741,6 +765,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.9.1" pool: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 84b0a14..052f6b2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: fl_clash description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free. publish_to: 'none' -version: 0.8.12 +version: 0.8.13 environment: sdk: '>=3.1.0 <4.0.0' @@ -38,6 +38,7 @@ dependencies: image: ^4.1.7 webdav_client: ^1.2.2 dio: ^5.4.3+1 + country_flags: ^2.2.0 dev_dependencies: flutter_test: sdk: flutter