From 702c59c3bc34755c171c36b2b739087288ed0419 Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Thu, 15 Feb 2024 19:39:35 +0330 Subject: [PATCH] Change mapping and bug fixes --- .../http_client/http_client_provider.dart | 8 +- lib/core/model/optional_range.dart | 54 +++ lib/core/model/range.dart | 57 --- lib/core/utils/json_converters.dart | 10 +- .../data/config_option_repository.dart | 55 +-- .../model/config_option_entity.dart | 351 ++++++++++++------ .../model/config_option_patch.dart | 60 --- .../notifier/config_option_notifier.dart | 1 - .../overview/config_options_page.dart | 13 +- .../overview/warp_options_widgets.dart | 8 +- .../data/connection_platform_source.dart | 2 - lib/features/log/model/log_level.dart | 4 + .../proxy/active/active_proxy_footer.dart | 1 - .../overview/proxies_overview_notifier.dart | 13 +- .../notifier/system_tray_notifier.dart | 2 +- lib/singbox/model/singbox_config_enum.dart | 52 ++- lib/singbox/model/singbox_config_option.dart | 175 +++++---- lib/singbox/model/singbox_rule.dart | 50 +-- lib/singbox/service/ffi_singbox_service.dart | 2 +- .../service/platform_singbox_service.dart | 2 +- lib/utils/link_parsers.dart | 3 +- 21 files changed, 501 insertions(+), 422 deletions(-) create mode 100644 lib/core/model/optional_range.dart delete mode 100644 lib/core/model/range.dart delete mode 100644 lib/features/config_option/model/config_option_patch.dart diff --git a/lib/core/http_client/http_client_provider.dart b/lib/core/http_client/http_client_provider.dart index 8335ec9b..8b73ca7c 100644 --- a/lib/core/http_client/http_client_provider.dart +++ b/lib/core/http_client/http_client_provider.dart @@ -15,11 +15,9 @@ DioHttpClient httpClient(HttpClientRef ref) { ); ref.listen( - configOptionNotifierProvider, - (_, next) { - if (next case AsyncData(value: final options)) { - client.setProxyPort(options.mixedPort); - } + configOptionNotifierProvider.selectAsync((data) => data.mixedPort), + (_, next) async { + client.setProxyPort(await next); }, fireImmediately: true, ); diff --git a/lib/core/model/optional_range.dart b/lib/core/model/optional_range.dart new file mode 100644 index 00000000..ae9fa649 --- /dev/null +++ b/lib/core/model/optional_range.dart @@ -0,0 +1,54 @@ +import 'package:dart_mappable/dart_mappable.dart'; +import 'package:dartx/dartx.dart'; +import 'package:hiddify/core/localization/translations.dart'; + +part 'optional_range.mapper.dart'; + +@MappableClass() +class OptionalRange with OptionalRangeMappable { + const OptionalRange({this.min, this.max}); + + final int? min; + final int? max; + + String format() => [min, max].whereNotNull().join("-"); + String present(TranslationsEn t) => + format().isEmpty ? t.general.notSet : format(); + + factory OptionalRange._fromString( + String input, { + bool allowEmpty = true, + }) => + switch (input.split("-")) { + [final String val] when val.isEmpty && allowEmpty => + const OptionalRange(), + [final String min] => OptionalRange(min: int.parse(min)), + [final String min, final String max] => OptionalRange( + min: int.parse(min), + max: int.parse(max), + ), + _ => throw Exception("Invalid range: $input"), + }; + + static OptionalRange? tryParse( + String input, { + bool allowEmpty = false, + }) { + try { + return OptionalRange._fromString(input); + } catch (_) { + return null; + } + } +} + +class OptionalRangeJsonMapper extends SimpleMapper { + const OptionalRangeJsonMapper(); + + @override + OptionalRange decode(dynamic value) => + OptionalRange._fromString(value as String); + + @override + dynamic encode(OptionalRange self) => self.format(); +} diff --git a/lib/core/model/range.dart b/lib/core/model/range.dart deleted file mode 100644 index d644da6f..00000000 --- a/lib/core/model/range.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:dartx/dartx.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:hiddify/core/localization/translations.dart'; - -part 'range.freezed.dart'; - -@freezed -class RangeWithOptionalCeil with _$RangeWithOptionalCeil { - const RangeWithOptionalCeil._(); - - const factory RangeWithOptionalCeil({ - int? min, - int? max, - }) = _RangeWithOptionalCeil; - - String format() => [min, max].whereNotNull().join("-"); - String present(TranslationsEn t) => - format().isEmpty ? t.general.notSet : format(); - - factory RangeWithOptionalCeil._fromString( - String input, { - bool allowEmpty = true, - }) => - switch (input.split("-")) { - [final String val] when val.isEmpty && allowEmpty => - const RangeWithOptionalCeil(), - [final String min] => RangeWithOptionalCeil(min: int.parse(min)), - [final String min, final String max] => RangeWithOptionalCeil( - min: int.parse(min), - max: int.parse(max), - ), - _ => throw Exception("Invalid range: $input"), - }; - - static RangeWithOptionalCeil? tryParse( - String input, { - bool allowEmpty = false, - }) { - try { - return RangeWithOptionalCeil._fromString(input); - } catch (_) { - return null; - } - } -} - -class RangeWithOptionalCeilJsonConverter - implements JsonConverter { - const RangeWithOptionalCeilJsonConverter(); - - @override - RangeWithOptionalCeil fromJson(String json) => - RangeWithOptionalCeil._fromString(json); - - @override - String toJson(RangeWithOptionalCeil object) => object.format(); -} diff --git a/lib/core/utils/json_converters.dart b/lib/core/utils/json_converters.dart index 290f6da8..14585a76 100644 --- a/lib/core/utils/json_converters.dart +++ b/lib/core/utils/json_converters.dart @@ -1,11 +1,11 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:dart_mappable/dart_mappable.dart'; -class IntervalInSecondsConverter implements JsonConverter { - const IntervalInSecondsConverter(); +class IntervalInSecondsMapper extends SimpleMapper { + const IntervalInSecondsMapper(); @override - Duration fromJson(int json) => Duration(seconds: json); + Duration decode(dynamic value) => Duration(seconds: value as int); @override - int toJson(Duration object) => object.inSeconds; + dynamic encode(Duration self) => self.inSeconds; } diff --git a/lib/features/config_option/data/config_option_repository.dart b/lib/features/config_option/data/config_option_repository.dart index 79519308..9370b598 100644 --- a/lib/features/config_option/data/config_option_repository.dart +++ b/lib/features/config_option/data/config_option_repository.dart @@ -3,10 +3,8 @@ import 'package:hiddify/core/model/region.dart'; import 'package:hiddify/core/utils/exception_handler.dart'; import 'package:hiddify/features/config_option/model/config_option_entity.dart'; import 'package:hiddify/features/config_option/model/config_option_failure.dart'; -import 'package:hiddify/features/config_option/model/config_option_patch.dart'; import 'package:hiddify/features/geo_asset/data/geo_asset_path_resolver.dart'; import 'package:hiddify/features/geo_asset/data/geo_asset_repository.dart'; -import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hiddify/singbox/model/singbox_config_option.dart'; import 'package:hiddify/singbox/model/singbox_rule.dart'; import 'package:hiddify/utils/utils.dart'; @@ -36,7 +34,7 @@ class ConfigOptionRepositoryImpl @override Either getConfigOption() { try { - final map = ConfigOptionEntity.initial.toJson(); + final map = ConfigOptionEntity.initial().toMap(); for (final key in map.keys) { final persisted = preferences.get(key); if (persisted != null) { @@ -51,7 +49,7 @@ class ConfigOptionRepositoryImpl map[key] = persisted; } } - final options = ConfigOptionEntity.fromJson(map); + final options = ConfigOptionEntityMapper.fromMap(map); return right(options); } catch (error, stackTrace) { return left(ConfigOptionUnexpectedFailure(error, stackTrace)); @@ -64,7 +62,7 @@ class ConfigOptionRepositoryImpl ) { return exceptionHandler( () async { - final map = patch.toJson(); + final map = patch.toMap(); await updateByJson(map); return right(unit); }, @@ -76,7 +74,7 @@ class ConfigOptionRepositoryImpl TaskEither resetConfigOption() { return exceptionHandler( () async { - final map = ConfigOptionEntity.initial.toJson(); + final map = ConfigOptionEntity.initial().toMap(); await updateByJson(map); return right(unit); }, @@ -88,7 +86,7 @@ class ConfigOptionRepositoryImpl Future updateByJson( Map options, ) async { - final map = ConfigOptionEntity.initial.toJson(); + final map = ConfigOptionEntity.initial().toMap(); for (final key in map.keys) { final value = options[key]; if (value != null) { @@ -172,48 +170,7 @@ class SingBoxConfigOptionRepositoryImpl final persisted = optionsRepository.getConfigOption().getOrElse((l) => throw l); - final singboxConfigOption = SingboxConfigOption( - executeConfigAsIs: false, - logLevel: persisted.logLevel, - resolveDestination: persisted.resolveDestination, - ipv6Mode: persisted.ipv6Mode, - remoteDnsAddress: persisted.remoteDnsAddress, - remoteDnsDomainStrategy: persisted.remoteDnsDomainStrategy, - directDnsAddress: persisted.directDnsAddress, - directDnsDomainStrategy: persisted.directDnsDomainStrategy, - mixedPort: persisted.mixedPort, - localDnsPort: persisted.localDnsPort, - tunImplementation: persisted.tunImplementation, - mtu: persisted.mtu, - strictRoute: persisted.strictRoute, - connectionTestUrl: persisted.connectionTestUrl, - urlTestInterval: persisted.urlTestInterval, - enableClashApi: persisted.enableClashApi, - clashApiPort: persisted.clashApiPort, - enableTun: persisted.serviceMode == ServiceMode.tun, - enableTunService: persisted.serviceMode == ServiceMode.tunService, - setSystemProxy: persisted.serviceMode == ServiceMode.systemProxy, - bypassLan: persisted.bypassLan, - allowConnectionFromLan: persisted.allowConnectionFromLan, - enableFakeDns: persisted.enableFakeDns, - enableDnsRouting: persisted.enableDnsRouting, - independentDnsCache: persisted.independentDnsCache, - enableTlsFragment: persisted.enableTlsFragment, - tlsFragmentSize: persisted.tlsFragmentSize, - tlsFragmentSleep: persisted.tlsFragmentSleep, - enableTlsMixedSniCase: persisted.enableTlsMixedSniCase, - enableTlsPadding: persisted.enableTlsPadding, - tlsPaddingSize: persisted.tlsPaddingSize, - enableMux: persisted.enableMux, - muxPadding: persisted.muxPadding, - muxMaxStreams: persisted.muxMaxStreams, - muxProtocol: persisted.muxProtocol, - enableWarp: persisted.enableWarp, - warpDetourMode: persisted.warpDetourMode, - warpLicenseKey: persisted.warpLicenseKey, - warpCleanIp: persisted.warpCleanIp, - warpPort: persisted.warpPort, - warpNoise: persisted.warpNoise, + final singboxConfigOption = persisted.toSingbox( geoipPath: geoAssetPathResolver.relativePath( geoAssets.geoip.providerName, geoAssets.geoip.fileName, diff --git a/lib/features/config_option/model/config_option_entity.dart b/lib/features/config_option/model/config_option_entity.dart index 676f575a..1c99e0f2 100644 --- a/lib/features/config_option/model/config_option_entity.dart +++ b/lib/features/config_option/model/config_option_entity.dart @@ -1,75 +1,107 @@ import 'dart:convert'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:hiddify/core/model/range.dart'; +import 'package:dart_mappable/dart_mappable.dart'; +import 'package:hiddify/core/model/optional_range.dart'; 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/singbox/model/singbox_config_option.dart'; +import 'package:hiddify/singbox/model/singbox_rule.dart'; import 'package:hiddify/utils/platform_utils.dart'; -part 'config_option_entity.freezed.dart'; -part 'config_option_entity.g.dart'; +part 'config_option_entity.mapper.dart'; -@freezed -class ConfigOptionEntity with _$ConfigOptionEntity { - const ConfigOptionEntity._(); +@MappableClass( + caseStyle: CaseStyle.paramCase, + includeCustomMappers: [ + OptionalRangeJsonMapper(), + IntervalInSecondsMapper(), + ], +) +class ConfigOptionEntity with ConfigOptionEntityMappable { + const ConfigOptionEntity({ + required this.serviceMode, + this.logLevel = LogLevel.warn, + this.resolveDestination = false, + this.ipv6Mode = IPv6Mode.disable, + this.remoteDnsAddress = "http://1.1.1.1", + this.remoteDnsDomainStrategy = DomainStrategy.auto, + this.directDnsAddress = "1.1.1.1", + this.directDnsDomainStrategy = DomainStrategy.auto, + this.mixedPort = 2334, + this.localDnsPort = 6450, + this.tunImplementation = TunImplementation.mixed, + this.mtu = 9000, + this.strictRoute = true, + this.connectionTestUrl = "http://cp.cloudflare.com/", + this.urlTestInterval = const Duration(minutes: 10), + this.enableClashApi = true, + this.clashApiPort = 6756, + this.bypassLan = false, + this.allowConnectionFromLan = false, + this.enableFakeDns = false, + this.enableDnsRouting = true, + this.independentDnsCache = true, + this.enableTlsFragment = false, + this.tlsFragmentSize = const OptionalRange(min: 10, max: 100), + this.tlsFragmentSleep = const OptionalRange(min: 50, max: 200), + this.enableTlsMixedSniCase = false, + this.enableTlsPadding = false, + this.tlsPaddingSize = const OptionalRange(min: 100, max: 200), + this.enableMux = false, + this.muxPadding = false, + this.muxMaxStreams = 8, + this.muxProtocol = MuxProtocol.h2mux, + this.enableWarp = false, + this.warpDetourMode = WarpDetourMode.outbound, + this.warpLicenseKey = "", + this.warpCleanIp = "auto", + this.warpPort = 0, + this.warpNoise = const OptionalRange(), + }); - @JsonSerializable(fieldRename: FieldRename.kebab) - const factory ConfigOptionEntity({ - required ServiceMode serviceMode, - @Default(LogLevel.warn) LogLevel logLevel, - @Default(false) bool resolveDestination, - @Default(IPv6Mode.disable) IPv6Mode ipv6Mode, - @Default("udp://1.1.1.1") String remoteDnsAddress, - @Default(DomainStrategy.auto) DomainStrategy remoteDnsDomainStrategy, - @Default("1.1.1.1") String directDnsAddress, - @Default(DomainStrategy.auto) DomainStrategy directDnsDomainStrategy, - @Default(2334) int mixedPort, - @Default(6450) int localDnsPort, - @Default(TunImplementation.mixed) TunImplementation tunImplementation, - @Default(9000) int mtu, - @Default(true) bool strictRoute, - @Default("http://cp.cloudflare.com/") String connectionTestUrl, - @IntervalInSecondsConverter() - @Default(Duration(minutes: 10)) - Duration urlTestInterval, - @Default(true) bool enableClashApi, - @Default(6756) int clashApiPort, - @Default(false) bool bypassLan, - @Default(false) bool allowConnectionFromLan, - @Default(false) bool enableFakeDns, - @Default(true) bool enableDnsRouting, - @Default(true) bool independentDnsCache, - @Default(false) bool enableTlsFragment, - @RangeWithOptionalCeilJsonConverter() - @Default(RangeWithOptionalCeil(min: 10, max: 100)) - RangeWithOptionalCeil tlsFragmentSize, - @RangeWithOptionalCeilJsonConverter() - @Default(RangeWithOptionalCeil(min: 50, max: 200)) - RangeWithOptionalCeil tlsFragmentSleep, - @Default(false) bool enableTlsMixedSniCase, - @Default(false) bool enableTlsPadding, - @RangeWithOptionalCeilJsonConverter() - @Default(RangeWithOptionalCeil(min: 100, max: 200)) - RangeWithOptionalCeil tlsPaddingSize, - @Default(false) bool enableMux, - @Default(false) bool muxPadding, - @Default(8) int muxMaxStreams, - @Default(MuxProtocol.h2mux) MuxProtocol muxProtocol, - @Default(false) bool enableWarp, - @Default(WarpDetourMode.outbound) WarpDetourMode warpDetourMode, - @Default("") String warpLicenseKey, - @Default("auto") String warpCleanIp, - @Default(0) int warpPort, - @RangeWithOptionalCeilJsonConverter() - @Default(RangeWithOptionalCeil()) - RangeWithOptionalCeil warpNoise, - }) = _ConfigOptionEntity; + final ServiceMode serviceMode; + final LogLevel logLevel; + final bool resolveDestination; + @MappableField(key: "ipv6-mode") + final IPv6Mode ipv6Mode; + final String remoteDnsAddress; + final DomainStrategy remoteDnsDomainStrategy; + final String directDnsAddress; + final DomainStrategy directDnsDomainStrategy; + final int mixedPort; + final int localDnsPort; + final TunImplementation tunImplementation; + final int mtu; + final bool strictRoute; + final String connectionTestUrl; + final Duration urlTestInterval; + final bool enableClashApi; + final int clashApiPort; + final bool bypassLan; + final bool allowConnectionFromLan; + final bool enableFakeDns; + final bool enableDnsRouting; + final bool independentDnsCache; + final bool enableTlsFragment; + final OptionalRange tlsFragmentSize; + final OptionalRange tlsFragmentSleep; + final bool enableTlsMixedSniCase; + final bool enableTlsPadding; + final OptionalRange tlsPaddingSize; + final bool enableMux; + final bool muxPadding; + final int muxMaxStreams; + final MuxProtocol muxProtocol; + final bool enableWarp; + final WarpDetourMode warpDetourMode; + final String warpLicenseKey; + final String warpCleanIp; + final int warpPort; + final OptionalRange warpNoise; - static ConfigOptionEntity initial = ConfigOptionEntity( - serviceMode: ServiceMode.defaultMode, - ); + factory ConfigOptionEntity.initial() => + ConfigOptionEntity(serviceMode: ServiceMode.defaultMode); bool hasExperimentalOptions() { if (PlatformUtils.isDesktop && serviceMode == ServiceMode.tun) { @@ -88,56 +120,157 @@ class ConfigOptionEntity with _$ConfigOptionEntity { String format() { const encoder = JsonEncoder.withIndent(' '); - return encoder.convert(toJson()); + return encoder.convert(toMap()); } ConfigOptionEntity patch(ConfigOptionPatch patch) { - return copyWith( - serviceMode: patch.serviceMode ?? serviceMode, - logLevel: patch.logLevel ?? logLevel, - resolveDestination: patch.resolveDestination ?? resolveDestination, - ipv6Mode: patch.ipv6Mode ?? ipv6Mode, - remoteDnsAddress: patch.remoteDnsAddress ?? remoteDnsAddress, - remoteDnsDomainStrategy: - patch.remoteDnsDomainStrategy ?? remoteDnsDomainStrategy, - directDnsAddress: patch.directDnsAddress ?? directDnsAddress, - directDnsDomainStrategy: - patch.directDnsDomainStrategy ?? directDnsDomainStrategy, - mixedPort: patch.mixedPort ?? mixedPort, - localDnsPort: patch.localDnsPort ?? localDnsPort, - tunImplementation: patch.tunImplementation ?? tunImplementation, - mtu: patch.mtu ?? mtu, - strictRoute: patch.strictRoute ?? strictRoute, - connectionTestUrl: patch.connectionTestUrl ?? connectionTestUrl, - urlTestInterval: patch.urlTestInterval ?? urlTestInterval, - enableClashApi: patch.enableClashApi ?? enableClashApi, - clashApiPort: patch.clashApiPort ?? clashApiPort, - bypassLan: patch.bypassLan ?? bypassLan, - allowConnectionFromLan: - patch.allowConnectionFromLan ?? allowConnectionFromLan, - enableFakeDns: patch.enableFakeDns ?? enableFakeDns, - enableDnsRouting: patch.enableDnsRouting ?? enableDnsRouting, - independentDnsCache: patch.independentDnsCache ?? independentDnsCache, - enableTlsFragment: patch.enableTlsFragment ?? enableTlsFragment, - tlsFragmentSize: patch.tlsFragmentSize ?? tlsFragmentSize, - tlsFragmentSleep: patch.tlsFragmentSleep ?? tlsFragmentSleep, - enableTlsMixedSniCase: - patch.enableTlsMixedSniCase ?? enableTlsMixedSniCase, - enableTlsPadding: patch.enableTlsPadding ?? enableTlsPadding, - tlsPaddingSize: patch.tlsPaddingSize ?? tlsPaddingSize, - enableMux: patch.enableMux ?? enableMux, - muxPadding: patch.muxPadding ?? muxPadding, - muxMaxStreams: patch.muxMaxStreams ?? muxMaxStreams, - muxProtocol: patch.muxProtocol ?? muxProtocol, - enableWarp: patch.enableWarp ?? enableWarp, - warpDetourMode: patch.warpDetourMode ?? warpDetourMode, - warpLicenseKey: patch.warpLicenseKey ?? warpLicenseKey, - warpCleanIp: patch.warpCleanIp ?? warpCleanIp, - warpPort: patch.warpPort ?? warpPort, - warpNoise: patch.warpNoise ?? warpNoise, - ); + return copyWith.$delta(patch.delta()); } - factory ConfigOptionEntity.fromJson(Map json) => - _$ConfigOptionEntityFromJson(json); + SingboxConfigOption toSingbox({ + required String geoipPath, + required String geositePath, + required List rules, + }) { + return SingboxConfigOption( + executeConfigAsIs: false, + logLevel: logLevel, + resolveDestination: resolveDestination, + ipv6Mode: ipv6Mode, + remoteDnsAddress: remoteDnsAddress, + remoteDnsDomainStrategy: remoteDnsDomainStrategy, + directDnsAddress: directDnsAddress, + directDnsDomainStrategy: directDnsDomainStrategy, + mixedPort: mixedPort, + localDnsPort: localDnsPort, + tunImplementation: tunImplementation, + mtu: mtu, + strictRoute: strictRoute, + connectionTestUrl: connectionTestUrl, + urlTestInterval: urlTestInterval, + enableClashApi: enableClashApi, + clashApiPort: clashApiPort, + enableTun: serviceMode == ServiceMode.tun, + enableTunService: serviceMode == ServiceMode.tunService, + setSystemProxy: serviceMode == ServiceMode.systemProxy, + bypassLan: bypassLan, + allowConnectionFromLan: allowConnectionFromLan, + enableFakeDns: enableFakeDns, + enableDnsRouting: enableDnsRouting, + independentDnsCache: independentDnsCache, + enableTlsFragment: enableTlsFragment, + tlsFragmentSize: tlsFragmentSize, + tlsFragmentSleep: tlsFragmentSleep, + enableTlsMixedSniCase: enableTlsMixedSniCase, + enableTlsPadding: enableTlsPadding, + tlsPaddingSize: tlsPaddingSize, + enableMux: enableMux, + muxPadding: muxPadding, + muxMaxStreams: muxMaxStreams, + muxProtocol: muxProtocol, + enableWarp: enableWarp, + warpDetourMode: warpDetourMode, + warpLicenseKey: warpLicenseKey, + warpCleanIp: warpCleanIp, + warpPort: warpPort, + warpNoise: warpNoise, + geoipPath: geoipPath, + geositePath: geositePath, + rules: rules, + ); + } +} + +@MappableClass( + caseStyle: CaseStyle.paramCase, + ignoreNull: true, + includeCustomMappers: [ + OptionalRangeJsonMapper(), + IntervalInSecondsMapper(), + ], +) +class ConfigOptionPatch with ConfigOptionPatchMappable { + const ConfigOptionPatch({ + this.serviceMode, + this.logLevel, + this.resolveDestination, + this.ipv6Mode, + this.remoteDnsAddress, + this.remoteDnsDomainStrategy, + this.directDnsAddress, + this.directDnsDomainStrategy, + this.mixedPort, + this.localDnsPort, + this.tunImplementation, + this.mtu, + this.strictRoute, + this.connectionTestUrl, + this.urlTestInterval, + this.enableClashApi, + this.clashApiPort, + this.bypassLan, + this.allowConnectionFromLan, + this.enableFakeDns, + this.enableDnsRouting, + this.independentDnsCache, + this.enableTlsFragment, + this.tlsFragmentSize, + this.tlsFragmentSleep, + this.enableTlsMixedSniCase, + this.enableTlsPadding, + this.tlsPaddingSize, + this.enableMux, + this.muxPadding, + this.muxMaxStreams, + this.muxProtocol, + this.enableWarp, + this.warpDetourMode, + this.warpLicenseKey, + this.warpCleanIp, + this.warpPort, + this.warpNoise, + }); + + final ServiceMode? serviceMode; + final LogLevel? logLevel; + final bool? resolveDestination; + @MappableField(key: "ipv6-mode") + final IPv6Mode? ipv6Mode; + final String? remoteDnsAddress; + final DomainStrategy? remoteDnsDomainStrategy; + final String? directDnsAddress; + final DomainStrategy? directDnsDomainStrategy; + final int? mixedPort; + final int? localDnsPort; + final TunImplementation? tunImplementation; + final int? mtu; + final bool? strictRoute; + final String? connectionTestUrl; + final Duration? urlTestInterval; + final bool? enableClashApi; + final int? clashApiPort; + final bool? bypassLan; + final bool? allowConnectionFromLan; + final bool? enableFakeDns; + final bool? enableDnsRouting; + final bool? independentDnsCache; + final bool? enableTlsFragment; + final OptionalRange? tlsFragmentSize; + final OptionalRange? tlsFragmentSleep; + final bool? enableTlsMixedSniCase; + final bool? enableTlsPadding; + final OptionalRange? tlsPaddingSize; + final bool? enableMux; + final bool? muxPadding; + final int? muxMaxStreams; + final MuxProtocol? muxProtocol; + final bool? enableWarp; + final WarpDetourMode? warpDetourMode; + final String? warpLicenseKey; + final String? warpCleanIp; + final int? warpPort; + final OptionalRange? warpNoise; + + Map delta() => + toMap()..removeWhere((key, value) => value == null); } diff --git a/lib/features/config_option/model/config_option_patch.dart b/lib/features/config_option/model/config_option_patch.dart deleted file mode 100644 index b9558a3f..00000000 --- a/lib/features/config_option/model/config_option_patch.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:hiddify/core/model/range.dart'; -import 'package:hiddify/core/utils/json_converters.dart'; -import 'package:hiddify/features/log/model/log_level.dart'; -import 'package:hiddify/singbox/model/singbox_config_enum.dart'; - -part 'config_option_patch.freezed.dart'; -part 'config_option_patch.g.dart'; - -@freezed -class ConfigOptionPatch with _$ConfigOptionPatch { - const ConfigOptionPatch._(); - - @JsonSerializable(fieldRename: FieldRename.kebab) - const factory ConfigOptionPatch({ - ServiceMode? serviceMode, - LogLevel? logLevel, - bool? resolveDestination, - IPv6Mode? ipv6Mode, - String? remoteDnsAddress, - DomainStrategy? remoteDnsDomainStrategy, - String? directDnsAddress, - DomainStrategy? directDnsDomainStrategy, - int? mixedPort, - int? localDnsPort, - TunImplementation? tunImplementation, - int? mtu, - bool? strictRoute, - String? connectionTestUrl, - @IntervalInSecondsConverter() Duration? urlTestInterval, - bool? enableClashApi, - int? clashApiPort, - bool? bypassLan, - bool? allowConnectionFromLan, - bool? enableFakeDns, - bool? enableDnsRouting, - bool? independentDnsCache, - bool? enableTlsFragment, - @RangeWithOptionalCeilJsonConverter() - RangeWithOptionalCeil? tlsFragmentSize, - @RangeWithOptionalCeilJsonConverter() - RangeWithOptionalCeil? tlsFragmentSleep, - bool? enableTlsMixedSniCase, - bool? enableTlsPadding, - @RangeWithOptionalCeilJsonConverter() RangeWithOptionalCeil? tlsPaddingSize, - bool? enableMux, - bool? muxPadding, - int? muxMaxStreams, - MuxProtocol? muxProtocol, - bool? enableWarp, - WarpDetourMode? warpDetourMode, - String? warpLicenseKey, - String? warpCleanIp, - int? warpPort, - @RangeWithOptionalCeilJsonConverter() RangeWithOptionalCeil? warpNoise, - }) = _ConfigOptionPatch; - - factory ConfigOptionPatch.fromJson(Map json) => - _$ConfigOptionPatchFromJson(json); -} diff --git a/lib/features/config_option/notifier/config_option_notifier.dart b/lib/features/config_option/notifier/config_option_notifier.dart index 77a67763..d826b5f0 100644 --- a/lib/features/config_option/notifier/config_option_notifier.dart +++ b/lib/features/config_option/notifier/config_option_notifier.dart @@ -1,6 +1,5 @@ import 'package:hiddify/features/config_option/data/config_option_data_providers.dart'; import 'package:hiddify/features/config_option/model/config_option_entity.dart'; -import 'package:hiddify/features/config_option/model/config_option_patch.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; diff --git a/lib/features/config_option/overview/config_options_page.dart b/lib/features/config_option/overview/config_options_page.dart index 46df415c..29fee700 100644 --- a/lib/features/config_option/overview/config_options_page.dart +++ b/lib/features/config_option/overview/config_options_page.dart @@ -5,12 +5,11 @@ import 'package:flutter/services.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/failures.dart'; -import 'package:hiddify/core/model/range.dart'; +import 'package:hiddify/core/model/optional_range.dart'; import 'package:hiddify/core/widget/adaptive_icon.dart'; import 'package:hiddify/core/widget/tip_card.dart'; import 'package:hiddify/features/common/nested_app_bar.dart'; import 'package:hiddify/features/config_option/model/config_option_entity.dart'; -import 'package:hiddify/features/config_option/model/config_option_patch.dart'; import 'package:hiddify/features/config_option/notifier/config_option_notifier.dart'; import 'package:hiddify/features/config_option/overview/warp_options_widgets.dart'; import 'package:hiddify/features/log/model/log_level.dart'; @@ -28,7 +27,7 @@ class ConfigOptionsPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider); - final defaultOptions = ConfigOptionEntity.initial; + final defaultOptions = ConfigOptionEntity.initial(); final asyncOptions = ref.watch(configOptionNotifierProvider); Future changeOption(ConfigOptionPatch patch) async { @@ -349,8 +348,7 @@ class ConfigOptionsPage extends HookConsumerWidget { if (range == null) return; await changeOption( ConfigOptionPatch( - tlsFragmentSize: - RangeWithOptionalCeil.tryParse(range), + tlsFragmentSize: OptionalRange.tryParse(range), ), ); }, @@ -367,8 +365,7 @@ class ConfigOptionsPage extends HookConsumerWidget { if (range == null) return; await changeOption( ConfigOptionPatch( - tlsFragmentSleep: - RangeWithOptionalCeil.tryParse(range), + tlsFragmentSleep: OptionalRange.tryParse(range), ), ); }, @@ -402,7 +399,7 @@ class ConfigOptionsPage extends HookConsumerWidget { if (range == null) return; await changeOption( ConfigOptionPatch( - tlsPaddingSize: RangeWithOptionalCeil.tryParse(range), + tlsPaddingSize: OptionalRange.tryParse(range), ), ); }, diff --git a/lib/features/config_option/overview/warp_options_widgets.dart b/lib/features/config_option/overview/warp_options_widgets.dart index 951bae41..f88888a2 100644 --- a/lib/features/config_option/overview/warp_options_widgets.dart +++ b/lib/features/config_option/overview/warp_options_widgets.dart @@ -3,9 +3,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; -import 'package:hiddify/core/model/range.dart'; +import 'package:hiddify/core/model/optional_range.dart'; import 'package:hiddify/features/config_option/model/config_option_entity.dart'; -import 'package:hiddify/features/config_option/model/config_option_patch.dart'; import 'package:hiddify/features/config_option/notifier/warp_option_notifier.dart'; import 'package:hiddify/features/settings/widgets/settings_input_dialog.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; @@ -134,10 +133,7 @@ class WarpOptionsTiles extends HookConsumerWidget { if (warpNoise == null) return; await onChange( ConfigOptionPatch( - warpNoise: RangeWithOptionalCeil.tryParse( - warpNoise, - allowEmpty: true, - ), + warpNoise: OptionalRange.tryParse(warpNoise, allowEmpty: true), ), ); }, diff --git a/lib/features/connection/data/connection_platform_source.dart b/lib/features/connection/data/connection_platform_source.dart index e0e88a7c..a2c6093e 100644 --- a/lib/features/connection/data/connection_platform_source.dart +++ b/lib/features/connection/data/connection_platform_source.dart @@ -1,12 +1,10 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'package:hiddify/core/utils/ffi_utils.dart'; import 'package:hiddify/utils/custom_loggers.dart'; import 'package:hiddify/utils/utils.dart'; -import 'package:path/path.dart' as p; import 'package:posix/posix.dart'; import 'package:win32/win32.dart'; diff --git a/lib/features/log/model/log_level.dart b/lib/features/log/model/log_level.dart index a81cf47c..714e7d38 100644 --- a/lib/features/log/model/log_level.dart +++ b/lib/features/log/model/log_level.dart @@ -1,6 +1,10 @@ +import 'package:dart_mappable/dart_mappable.dart'; import 'package:dartx/dartx.dart'; import 'package:flutter/material.dart'; +part 'log_level.mapper.dart'; + +@MappableEnum() enum LogLevel { trace, debug, diff --git a/lib/features/proxy/active/active_proxy_footer.dart b/lib/features/proxy/active/active_proxy_footer.dart index a33250f9..2cd90270 100644 --- a/lib/features/proxy/active/active_proxy_footer.dart +++ b/lib/features/proxy/active/active_proxy_footer.dart @@ -1,7 +1,6 @@ import 'package:dartx/dartx.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/semantics.dart'; import 'package:gap/gap.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/widget/animated_visibility.dart'; diff --git a/lib/features/proxy/overview/proxies_overview_notifier.dart b/lib/features/proxy/overview/proxies_overview_notifier.dart index 292094a4..029631c4 100644 --- a/lib/features/proxy/overview/proxies_overview_notifier.dart +++ b/lib/features/proxy/overview/proxies_overview_notifier.dart @@ -8,7 +8,6 @@ import 'package:hiddify/features/connection/notifier/connection_notifier.dart'; import 'package:hiddify/features/proxy/data/proxy_data_providers.dart'; import 'package:hiddify/features/proxy/model/proxy_entity.dart'; import 'package:hiddify/features/proxy/model/proxy_failure.dart'; -import 'package:hiddify/singbox/model/singbox_proxy_type.dart'; import 'package:hiddify/utils/pref_notifier.dart'; import 'package:hiddify/utils/riverpod_utils.dart'; import 'package:hiddify/utils/utils.dart'; @@ -94,14 +93,14 @@ class ProxiesOverviewNotifier extends _$ProxiesOverviewNotifier with AppLogger { for (final group in proxies) { final sortedItems = switch (sortBy) { ProxiesSort.name => group.items.sortedWith((a, b) { - if(a.type.isGroup && !b.type.isGroup) return -1; - if(!a.type.isGroup && b.type.isGroup) return 1; + if (a.type.isGroup && !b.type.isGroup) return -1; + if (!a.type.isGroup && b.type.isGroup) return 1; return a.tag.compareTo(b.tag); - }), + }), ProxiesSort.delay => group.items.sortedWith((a, b) { - if(a.type.isGroup && !b.type.isGroup) return -1; - if(!a.type.isGroup && b.type.isGroup) return 1; - + if (a.type.isGroup && !b.type.isGroup) return -1; + if (!a.type.isGroup && b.type.isGroup) return 1; + final ai = a.urlTestDelay; final bi = b.urlTestDelay; if (ai == 0 && bi == 0) return -1; diff --git a/lib/features/system_tray/notifier/system_tray_notifier.dart b/lib/features/system_tray/notifier/system_tray_notifier.dart index 37a9cbdb..8ed0f646 100644 --- a/lib/features/system_tray/notifier/system_tray_notifier.dart +++ b/lib/features/system_tray/notifier/system_tray_notifier.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/core/model/constants.dart'; import 'package:hiddify/core/router/router.dart'; -import 'package:hiddify/features/config_option/model/config_option_patch.dart'; +import 'package:hiddify/features/config_option/model/config_option_entity.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'; diff --git a/lib/singbox/model/singbox_config_enum.dart b/lib/singbox/model/singbox_config_enum.dart index 9bcdde20..10d616a3 100644 --- a/lib/singbox/model/singbox_config_enum.dart +++ b/lib/singbox/model/singbox_config_enum.dart @@ -1,19 +1,24 @@ import 'dart:io'; +import 'package:dart_mappable/dart_mappable.dart'; import 'package:hiddify/core/localization/translations.dart'; import 'package:hiddify/utils/platform_utils.dart'; -import 'package:json_annotation/json_annotation.dart'; -@JsonEnum(valueField: 'key') +part 'singbox_config_enum.mapper.dart'; + +@MappableEnum() enum ServiceMode { - proxy("proxy"), - systemProxy("system-proxy"), - tun("vpn"), - tunService("vpn-service"); + @MappableValue("proxy") + proxy, - const ServiceMode(this.key); + @MappableValue("system-proxy") + systemProxy, - final String key; + @MappableValue("vpn") + tun, + + @MappableValue("vpn-service") + tunService; static ServiceMode get defaultMode => PlatformUtils.isDesktop ? systemProxy : tun; @@ -39,16 +44,19 @@ enum ServiceMode { }; } -@JsonEnum(valueField: 'key') +@MappableEnum() enum IPv6Mode { - disable("ipv4_only"), - enable("prefer_ipv4"), - prefer("prefer_ipv6"), - only("ipv6_only"); + @MappableValue("ipv4_only") + disable, - const IPv6Mode(this.key); + @MappableValue("prefer_ipv4") + enable, - final String key; + @MappableValue("prefer_ipv6") + prefer, + + @MappableValue("ipv6_only") + only; String present(TranslationsEn t) => switch (this) { disable => t.settings.config.ipv6Modes.disable, @@ -58,12 +66,21 @@ enum IPv6Mode { }; } -@JsonEnum(valueField: 'key') +@MappableEnum() enum DomainStrategy { + @MappableValue("") auto(""), + + @MappableValue("prefer_ipv6") preferIpv6("prefer_ipv6"), + + @MappableValue("prefer_ipv4") preferIpv4("prefer_ipv4"), + + @MappableValue("ipv4_only") ipv4Only("ipv4_only"), + + @MappableValue("ipv6_only") ipv6Only("ipv6_only"); const DomainStrategy(this.key); @@ -76,18 +93,21 @@ enum DomainStrategy { }; } +@MappableEnum() enum TunImplementation { mixed, system, gVisor; } +@MappableEnum() enum MuxProtocol { h2mux, smux, yamux; } +@MappableEnum() enum WarpDetourMode { outbound, inbound; diff --git a/lib/singbox/model/singbox_config_option.dart b/lib/singbox/model/singbox_config_option.dart index 1dd9dbd8..511838fa 100644 --- a/lib/singbox/model/singbox_config_option.dart +++ b/lib/singbox/model/singbox_config_option.dart @@ -1,86 +1,127 @@ import 'dart:convert'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:hiddify/core/model/range.dart'; +import 'package:dart_mappable/dart_mappable.dart'; +import 'package:hiddify/core/model/optional_range.dart'; import 'package:hiddify/features/log/model/log_level.dart'; import 'package:hiddify/singbox/model/singbox_config_enum.dart'; import 'package:hiddify/singbox/model/singbox_rule.dart'; -part 'singbox_config_option.freezed.dart'; -part 'singbox_config_option.g.dart'; +part 'singbox_config_option.mapper.dart'; -@freezed -class SingboxConfigOption with _$SingboxConfigOption { - const SingboxConfigOption._(); +@MappableClass( + caseStyle: CaseStyle.paramCase, + includeCustomMappers: [ + OptionalRangeJsonMapper(), + IntervalMapper(), + ], +) +class SingboxConfigOption with SingboxConfigOptionMappable { + const SingboxConfigOption({ + required this.executeConfigAsIs, + required this.logLevel, + required this.resolveDestination, + required this.ipv6Mode, + required this.remoteDnsAddress, + required this.remoteDnsDomainStrategy, + required this.directDnsAddress, + required this.directDnsDomainStrategy, + required this.mixedPort, + required this.localDnsPort, + required this.tunImplementation, + required this.mtu, + required this.strictRoute, + required this.connectionTestUrl, + required this.urlTestInterval, + required this.enableClashApi, + required this.clashApiPort, + required this.enableTun, + required this.enableTunService, + required this.setSystemProxy, + required this.bypassLan, + required this.allowConnectionFromLan, + required this.enableFakeDns, + required this.enableDnsRouting, + required this.independentDnsCache, + required this.enableTlsFragment, + required this.tlsFragmentSize, + required this.tlsFragmentSleep, + required this.enableTlsMixedSniCase, + required this.enableTlsPadding, + required this.tlsPaddingSize, + required this.enableMux, + required this.muxPadding, + required this.muxMaxStreams, + required this.muxProtocol, + required this.enableWarp, + required this.warpDetourMode, + required this.warpLicenseKey, + required this.warpCleanIp, + required this.warpPort, + required this.warpNoise, + required this.geoipPath, + required this.geositePath, + required this.rules, + }); - @JsonSerializable(fieldRename: FieldRename.kebab) - const factory SingboxConfigOption({ - required bool executeConfigAsIs, - required LogLevel logLevel, - required bool resolveDestination, - required IPv6Mode ipv6Mode, - required String remoteDnsAddress, - required DomainStrategy remoteDnsDomainStrategy, - required String directDnsAddress, - required DomainStrategy directDnsDomainStrategy, - required int mixedPort, - required int localDnsPort, - required TunImplementation tunImplementation, - required int mtu, - required bool strictRoute, - required String connectionTestUrl, - @IntervalConverter() required Duration urlTestInterval, - required bool enableClashApi, - required int clashApiPort, - required bool enableTun, - required bool enableTunService, - required bool setSystemProxy, - required bool bypassLan, - required bool allowConnectionFromLan, - required bool enableFakeDns, - required bool enableDnsRouting, - required bool independentDnsCache, - required bool enableTlsFragment, - @RangeWithOptionalCeilJsonConverter() - required RangeWithOptionalCeil tlsFragmentSize, - @RangeWithOptionalCeilJsonConverter() - required RangeWithOptionalCeil tlsFragmentSleep, - required bool enableTlsMixedSniCase, - required bool enableTlsPadding, - @RangeWithOptionalCeilJsonConverter() - required RangeWithOptionalCeil tlsPaddingSize, - required bool enableMux, - required bool muxPadding, - required int muxMaxStreams, - required MuxProtocol muxProtocol, - required bool enableWarp, - required WarpDetourMode warpDetourMode, - required String warpLicenseKey, - required String warpCleanIp, - required int warpPort, - @RangeWithOptionalCeilJsonConverter() - required RangeWithOptionalCeil warpNoise, - required String geoipPath, - required String geositePath, - required List rules, - }) = _SingboxConfigOption; + final bool executeConfigAsIs; + final LogLevel logLevel; + final bool resolveDestination; + @MappableField(key: "ipv6-mode") + final IPv6Mode ipv6Mode; + final String remoteDnsAddress; + final DomainStrategy remoteDnsDomainStrategy; + final String directDnsAddress; + final DomainStrategy directDnsDomainStrategy; + final int mixedPort; + final int localDnsPort; + final TunImplementation tunImplementation; + final int mtu; + final bool strictRoute; + final String connectionTestUrl; + final Duration urlTestInterval; + final bool enableClashApi; + final int clashApiPort; + final bool enableTun; + final bool enableTunService; + final bool setSystemProxy; + final bool bypassLan; + final bool allowConnectionFromLan; + final bool enableFakeDns; + final bool enableDnsRouting; + final bool independentDnsCache; + final bool enableTlsFragment; + final OptionalRange tlsFragmentSize; + final OptionalRange tlsFragmentSleep; + final bool enableTlsMixedSniCase; + final bool enableTlsPadding; + final OptionalRange tlsPaddingSize; + final bool enableMux; + final bool muxPadding; + final int muxMaxStreams; + final MuxProtocol muxProtocol; + final bool enableWarp; + final WarpDetourMode warpDetourMode; + final String warpLicenseKey; + final String warpCleanIp; + final int warpPort; + final OptionalRange warpNoise; + final String geoipPath; + final String geositePath; + final List rules; String format() { const encoder = JsonEncoder.withIndent(' '); - return encoder.convert(toJson()); + return encoder.convert(toMap()); } - - factory SingboxConfigOption.fromJson(Map json) => - _$SingboxConfigOptionFromJson(json); } -class IntervalConverter implements JsonConverter { - const IntervalConverter(); +class IntervalMapper extends SimpleMapper { + const IntervalMapper(); @override - Duration fromJson(String json) => - Duration(minutes: int.parse(json.replaceAll("m", ""))); + Duration decode(dynamic value) => + Duration(minutes: int.parse((value as String).replaceAll("m", ""))); @override - String toJson(Duration object) => "${object.inMinutes}m"; + String encode(Duration self) => "${self.inMinutes}m"; } diff --git a/lib/singbox/model/singbox_rule.dart b/lib/singbox/model/singbox_rule.dart index b927ffee..d93b2562 100644 --- a/lib/singbox/model/singbox_rule.dart +++ b/lib/singbox/model/singbox_rule.dart @@ -1,35 +1,37 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:dart_mappable/dart_mappable.dart'; -part 'singbox_rule.freezed.dart'; -part 'singbox_rule.g.dart'; +part 'singbox_rule.mapper.dart'; -@freezed -class SingboxRule with _$SingboxRule { - const SingboxRule._(); +@MappableClass() +class SingboxRule with SingboxRuleMappable { + const SingboxRule({ + this.domains, + this.ip, + this.port, + this.protocol, + this.network = RuleNetwork.tcpAndUdp, + this.outbound = RuleOutbound.proxy, + }); - @JsonSerializable(fieldRename: FieldRename.kebab) - const factory SingboxRule({ - String? domains, - String? ip, - String? port, - String? protocol, - @Default(RuleNetwork.tcpAndUdp) RuleNetwork network, - @Default(RuleOutbound.proxy) RuleOutbound outbound, - }) = _SingboxRule; - - factory SingboxRule.fromJson(Map json) => - _$SingboxRuleFromJson(json); + final String? domains; + final String? ip; + final String? port; + final String? protocol; + final RuleNetwork network; + final RuleOutbound outbound; } +@MappableEnum() enum RuleOutbound { proxy, bypass, block } -@JsonEnum(valueField: 'key') +@MappableEnum() enum RuleNetwork { - tcpAndUdp(""), - tcp("tcp"), - udp("udp"); + @MappableValue("") + tcpAndUdp, - const RuleNetwork(this.key); + @MappableValue("tcp") + tcp, - final String? key; + @MappableValue("udp") + udp; } diff --git a/lib/singbox/service/ffi_singbox_service.dart b/lib/singbox/service/ffi_singbox_service.dart index 3626c5b9..ee60a00c 100644 --- a/lib/singbox/service/ffi_singbox_service.dart +++ b/lib/singbox/service/ffi_singbox_service.dart @@ -121,7 +121,7 @@ class FFISingboxService with InfraLogger implements SingboxService { return TaskEither( () => CombineWorker().execute( () { - final json = jsonEncode(options.toJson()); + final json = options.toJson(); final err = _box .changeConfigOptions(json.toNativeUtf8().cast()) .cast() diff --git a/lib/singbox/service/platform_singbox_service.dart b/lib/singbox/service/platform_singbox_service.dart index 2aadacf5..bdf96804 100644 --- a/lib/singbox/service/platform_singbox_service.dart +++ b/lib/singbox/service/platform_singbox_service.dart @@ -80,7 +80,7 @@ class PlatformSingboxService with InfraLogger implements SingboxService { loggy.debug("changing options"); await methodChannel.invokeMethod( "change_config_options", - jsonEncode(options.toJson()), + options.toJson(), ); return right(unit); }, diff --git a/lib/utils/link_parsers.dart b/lib/utils/link_parsers.dart index 23dee9c1..74b1463f 100644 --- a/lib/utils/link_parsers.dart +++ b/lib/utils/link_parsers.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:dartx/dartx.dart'; -import 'package:fpdart/fpdart.dart'; import 'package:hiddify/features/profile/data/profile_parser.dart'; import 'package:hiddify/features/profile/data/profile_repository.dart'; import 'package:hiddify/singbox/model/singbox_proxy_type.dart'; @@ -71,7 +70,7 @@ abstract class LinkParser { if (subinfo.name.isNotNullOrEmpty && subinfo.name != "Remote Profile") { name = subinfo.name; } - + return (content: normalContent, name: name ?? ProxyType.unknown.label); }