Compare commits

...

4 Commits

Author SHA1 Message Date
chen08209
472cea9037 Add compatibility mode and adapt clash scheme. 2024-05-11 14:10:06 +08:00
chen08209
08d07498b9 update Version 2024-05-07 18:32:21 +08:00
chen08209
d5aa09949a Reconstruction application proxy logic 2024-05-07 18:31:14 +08:00
chen08209
fd1dfe5c60 Fix Tab destroy error 2024-05-06 19:05:27 +08:00
33 changed files with 1008 additions and 592 deletions

View File

@@ -3,6 +3,7 @@ package main
import "C" import "C"
import ( import (
"github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/adapter/inbound"
ap "github.com/metacubex/mihomo/adapter/provider"
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/process" "github.com/metacubex/mihomo/component/process"
"github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resolver"
@@ -16,15 +17,53 @@ import (
"math" "math"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"runtime" "runtime"
"strings"
"sync" "sync"
"syscall" "syscall"
) )
type healthCheckSchema struct {
Enable bool `provider:"enable"`
URL string `provider:"url"`
Interval int `provider:"interval"`
TestTimeout int `provider:"timeout,omitempty"`
Lazy bool `provider:"lazy,omitempty"`
ExpectedStatus string `provider:"expected-status,omitempty"`
}
type proxyProviderSchema struct {
Type string `provider:"type"`
Path string `provider:"path,omitempty"`
URL string `provider:"url,omitempty"`
Proxy string `provider:"proxy,omitempty"`
Interval int `provider:"interval,omitempty"`
Filter string `provider:"filter,omitempty"`
ExcludeFilter string `provider:"exclude-filter,omitempty"`
ExcludeType string `provider:"exclude-type,omitempty"`
DialerProxy string `provider:"dialer-proxy,omitempty"`
HealthCheck healthCheckSchema `provider:"health-check,omitempty"`
Override ap.OverrideSchema `provider:"override,omitempty"`
Header map[string][]string `provider:"header,omitempty"`
}
type ruleProviderSchema struct {
Type string `provider:"type"`
Behavior string `provider:"behavior"`
Path string `provider:"path,omitempty"`
URL string `provider:"url,omitempty"`
Proxy string `provider:"proxy,omitempty"`
Format string `provider:"format,omitempty"`
Interval int `provider:"interval,omitempty"`
}
type GenerateConfigParams struct { type GenerateConfigParams struct {
ProfilePath *string `json:"profile-path"` ProfilePath *string `json:"profile-path"`
Config *config.RawConfig `json:"config" ` Config *config.RawConfig `json:"config" `
IsPatch *bool `json:"is-patch"` IsPatch *bool `json:"is-patch"`
IsCompatible *bool `json:"is-compatible"`
} }
type ChangeProxyParams struct { type ChangeProxyParams struct {
@@ -90,6 +129,19 @@ func readFile(path string) ([]byte, error) {
return data, err return data, err
} }
func removeFile(path string) error {
absPath, err := filepath.Abs(path)
if err != nil {
return err
}
err = os.Remove(absPath)
if err != nil {
return err
}
return nil
}
func getRawConfigWithPath(path *string) *config.RawConfig { func getRawConfigWithPath(path *string) *config.RawConfig {
if path == nil { if path == nil {
return config.DefaultRawConfig() return config.DefaultRawConfig()
@@ -108,18 +160,164 @@ func getRawConfigWithPath(path *string) *config.RawConfig {
} }
} }
func decorationConfig(profilePath *string, cfg config.RawConfig) *config.RawConfig { func decorationConfig(profilePath *string, cfg config.RawConfig, compatible bool) *config.RawConfig {
prof := getRawConfigWithPath(profilePath) prof := getRawConfigWithPath(profilePath)
overwriteConfig(prof, cfg) overwriteConfig(prof, cfg, compatible)
return prof return prof
} }
func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig) { func Reduce[T any, U any](s []T, initVal U, f func(U, T) U) U {
for _, v := range s {
initVal = f(initVal, v)
}
return initVal
}
func Map[T, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
}
return result
}
func replaceFromMap(s string, m map[string]string) string {
for k, v := range m {
s = strings.ReplaceAll(s, k, v)
}
return s
}
func removeDuplicateFromSlice[T any](slice []T) []T {
result := make([]T, 0)
seen := make(map[any]struct{})
for _, value := range slice {
if _, ok := seen[value]; !ok {
result = append(result, value)
seen[value] = struct{}{}
}
}
return result
}
func generateProxyGroupAndRule(proxyGroup *[]map[string]any, rule *[]string) {
var replacements = map[string]string{}
var selectArr []map[string]any
var urlTestArr []map[string]any
var fallbackArr []map[string]any
for _, group := range *proxyGroup {
switch group["type"] {
case "select":
selectArr = append(selectArr, group)
replacements[group["name"].(string)] = "Proxy"
break
case "url-test":
urlTestArr = append(urlTestArr, group)
replacements[group["name"].(string)] = "Auto"
break
case "fallback":
fallbackArr = append(fallbackArr, group)
replacements[group["name"].(string)] = "Fallback"
break
default:
break
}
}
ProxyProxies := Reduce(selectArr, []string{}, func(res []string, cur map[string]any) []string {
if cur["proxies"] == nil {
return res
}
for _, proxyName := range cur["proxies"].([]interface{}) {
if str, ok := proxyName.(string); ok {
str = replaceFromMap(str, replacements)
if str != "Proxy" {
res = append(res, str)
}
}
}
return res
})
ProxyProxies = removeDuplicateFromSlice(ProxyProxies)
AutoProxies := Reduce(urlTestArr, []string{}, func(res []string, cur map[string]any) []string {
if cur["proxies"] == nil {
return res
}
for _, proxyName := range cur["proxies"].([]interface{}) {
if str, ok := proxyName.(string); ok {
str = replaceFromMap(str, replacements)
if str != "Auto" {
res = append(res, str)
}
}
}
return res
})
AutoProxies = removeDuplicateFromSlice(AutoProxies)
FallbackProxies := Reduce(fallbackArr, []string{}, func(res []string, cur map[string]any) []string {
if cur["proxies"] == nil {
return res
}
for _, proxyName := range cur["proxies"].([]interface{}) {
if str, ok := proxyName.(string); ok {
str = replaceFromMap(str, replacements)
if str != "Fallback" {
res = append(res, str)
}
}
}
return res
})
FallbackProxies = removeDuplicateFromSlice(FallbackProxies)
var computedProxyGroup []map[string]any
if len(ProxyProxies) > 0 {
computedProxyGroup = append(computedProxyGroup,
map[string]any{
"name": "Proxy",
"type": "select",
"proxies": ProxyProxies,
})
}
if len(AutoProxies) > 0 {
computedProxyGroup = append(computedProxyGroup,
map[string]any{
"name": "Auto",
"type": "url-test",
"proxies": AutoProxies,
})
}
if len(FallbackProxies) > 0 {
computedProxyGroup = append(computedProxyGroup,
map[string]any{
"name": "Fallback",
"type": "fallback",
"proxies": FallbackProxies,
})
}
computedRule := Map(*rule, func(value string) string {
return replaceFromMap(value, replacements)
})
*proxyGroup = computedProxyGroup
*rule = computedRule
}
func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig, compatible bool) {
targetConfig.ExternalController = "" targetConfig.ExternalController = ""
targetConfig.ExternalUI = "" targetConfig.ExternalUI = ""
targetConfig.Interface = "" targetConfig.Interface = ""
targetConfig.ExternalUIURL = "" targetConfig.ExternalUIURL = ""
targetConfig.IPv6 = patchConfig.IPv6 //targetConfig.IPv6 = patchConfig.IPv6
targetConfig.LogLevel = patchConfig.LogLevel targetConfig.LogLevel = patchConfig.LogLevel
targetConfig.FindProcessMode = process.FindProcessAlways targetConfig.FindProcessMode = process.FindProcessAlways
targetConfig.AllowLan = patchConfig.AllowLan targetConfig.AllowLan = patchConfig.AllowLan
@@ -129,23 +327,22 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.Tun.Device = patchConfig.Tun.Device targetConfig.Tun.Device = patchConfig.Tun.Device
targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack
targetConfig.Tun.Stack = patchConfig.Tun.Stack targetConfig.Tun.Stack = patchConfig.Tun.Stack
targetConfig.GeodataLoader = "standard"
targetConfig.Profile.StoreSelected = false targetConfig.Profile.StoreSelected = false
if targetConfig.DNS.Enable == false { if targetConfig.DNS.Enable == false {
targetConfig.DNS = patchConfig.DNS targetConfig.DNS = patchConfig.DNS
} else {
targetConfig.DNS.UseHosts = patchConfig.DNS.UseHosts
targetConfig.DNS.EnhancedMode = patchConfig.DNS.EnhancedMode
targetConfig.DNS.IPv6 = patchConfig.DNS.IPv6
targetConfig.DNS.DefaultNameserver = append(patchConfig.DNS.DefaultNameserver, targetConfig.DNS.DefaultNameserver...)
targetConfig.DNS.NameServer = append(patchConfig.DNS.NameServer, targetConfig.DNS.NameServer...)
targetConfig.DNS.FakeIPFilter = append(patchConfig.DNS.FakeIPFilter, targetConfig.DNS.FakeIPFilter...)
targetConfig.DNS.Fallback = append(patchConfig.DNS.Fallback, targetConfig.DNS.Fallback...)
if runtime.GOOS == "android" {
targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, "dhcp://"+dns.SystemDNSPlaceholder)
} else if runtime.GOOS == "windows" {
targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, dns.SystemDNSPlaceholder)
}
} }
if runtime.GOOS == "android" {
targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, "dhcp://"+dns.SystemDNSPlaceholder)
} else if runtime.GOOS == "windows" {
targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, dns.SystemDNSPlaceholder)
}
if compatible == false {
targetConfig.ProxyProvider = make(map[string]map[string]any)
targetConfig.RuleProvider = make(map[string]map[string]any)
generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule)
}
} }
func patchConfig(general *config.General) { func patchConfig(general *config.General) {
@@ -175,10 +372,8 @@ func patchConfig(general *config.General) {
const concurrentCount = math.MaxInt const concurrentCount = math.MaxInt
var wg = sync.WaitGroup{}
var exit = make(chan struct{})
func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) { func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) {
wg := sync.WaitGroup{}
ch := make(chan struct{}, concurrentCount) ch := make(chan struct{}, concurrentCount)
for _, proxyProvider := range proxyProviders { for _, proxyProvider := range proxyProviders {
proxyProvider := proxyProvider proxyProvider := proxyProvider
@@ -186,11 +381,6 @@ func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) {
log.Infoln("Start initial Compatible provider %s", proxyProvider.Name()) log.Infoln("Start initial Compatible provider %s", proxyProvider.Name())
wg.Add(1) wg.Add(1)
ch <- struct{}{} ch <- struct{}{}
select {
case <-exit:
return // 收到退出信号,退出协程
default:
}
go func() { go func() {
defer func() { <-ch; wg.Done() }() defer func() { <-ch; wg.Done() }()
if err := proxyProvider.Initial(); err != nil { if err := proxyProvider.Initial(); err != nil {
@@ -202,13 +392,6 @@ func hcCompatibleProvider(proxyProviders map[string]provider.ProxyProvider) {
} }
} }
func cancelHc() {
close(exit)
wg.Wait()
exit = make(chan struct{})
wg = sync.WaitGroup{}
}
func applyConfig(isPatch bool) { func applyConfig(isPatch bool) {
cfg, err := config.ParseRawConfig(currentConfig) cfg, err := config.ParseRawConfig(currentConfig)
if err != nil { if err != nil {

View File

@@ -19,11 +19,11 @@ type Message struct {
Data interface{} `json:"data"` Data interface{} `json:"data"`
} }
func (message *Message) toJson() string { func (message *Message) Json() string {
data, _ := json.Marshal(message) data, _ := json.Marshal(message)
return string(data) return string(data)
} }
func SendMessage(message Message) { func SendMessage(message Message) {
SendToPort(*Port, message.toJson()) SendToPort(*Port, message.Json())
} }

View File

@@ -8,6 +8,7 @@ import (
"github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/adapter/outboundgroup"
"github.com/metacubex/mihomo/adapter/provider" "github.com/metacubex/mihomo/adapter/provider"
"github.com/metacubex/mihomo/common/structure"
"github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/common/utils"
"github.com/metacubex/mihomo/config" "github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant"
@@ -74,7 +75,7 @@ func updateConfig(s *C.char, port C.longlong) {
bridge.SendToPort(i, err.Error()) bridge.SendToPort(i, err.Error())
return return
} }
prof := decorationConfig(params.ProfilePath, *params.Config) prof := decorationConfig(params.ProfilePath, *params.Config, *params.IsCompatible)
currentConfig = prof currentConfig = prof
if *params.IsPatch { if *params.IsPatch {
applyConfig(true) applyConfig(true)
@@ -85,6 +86,39 @@ func updateConfig(s *C.char, port C.longlong) {
}() }()
} }
//export clearEffect
func clearEffect(s *C.char) {
path := C.GoString(s)
go func() {
rawCfg := getRawConfigWithPath(&path)
for _, mapping := range rawCfg.RuleProvider {
schema := &ruleProviderSchema{}
decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true})
if err := decoder.Decode(mapping, schema); err != nil {
return
}
if schema.Type == "http" {
_ = removeFile(constant.Path.Resolve(schema.Path))
}
}
for _, mapping := range rawCfg.ProxyProvider {
schema := &proxyProviderSchema{
HealthCheck: healthCheckSchema{
Lazy: true,
},
}
decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true})
if err := decoder.Decode(mapping, schema); err != nil {
return
}
if schema.Type == "http" {
_ = removeFile(constant.Path.Resolve(schema.Path))
}
}
_ = removeFile(path)
}()
}
//export getProxies //export getProxies
func getProxies() *C.char { func getProxies() *C.char {
data, err := json.Marshal(tunnel.ProxiesWithProviders()) data, err := json.Marshal(tunnel.ProxiesWithProviders())
@@ -96,34 +130,31 @@ func getProxies() *C.char {
//export changeProxy //export changeProxy
func changeProxy(s *C.char) bool { func changeProxy(s *C.char) bool {
paramsString := C.GoString(s) go func() {
var params = &ChangeProxyParams{} paramsString := C.GoString(s)
err := json.Unmarshal([]byte(paramsString), params) var params = &ChangeProxyParams{}
if err != nil { err := json.Unmarshal([]byte(paramsString), params)
log.Infoln("Unmarshal ChangeProxyParams %v", err) if err != nil {
return false log.Infoln("Unmarshal ChangeProxyParams %v", err)
} }
proxies := tunnel.ProxiesWithProviders() proxies := tunnel.ProxiesWithProviders()
proxy := proxies[*params.GroupName] proxy := proxies[*params.GroupName]
if proxy == nil { if proxy == nil {
return false return
} }
log.Infoln("change proxy %s", proxy.Name()) log.Infoln("change proxy %s", proxy.Name())
adapterProxy := proxy.(*adapter.Proxy) adapterProxy := proxy.(*adapter.Proxy)
selector, ok := adapterProxy.ProxyAdapter.(*outboundgroup.Selector) selector, ok := adapterProxy.ProxyAdapter.(*outboundgroup.Selector)
if !ok { if !ok {
return false return
} }
if err := selector.Set(*params.ProxyName); err != nil { if err := selector.Set(*params.ProxyName); err != nil {
return false return
} }
}()
return true return true
} }
// clearEffect
func clearConfigEffect() {
}
//export getTraffic //export getTraffic
func getTraffic() *C.char { func getTraffic() *C.char {
up, down := statistic.DefaultManager.Now() up, down := statistic.DefaultManager.Now()

View File

@@ -27,8 +27,14 @@ runAppWithPreferences(
ChangeNotifierProvider<Config>( ChangeNotifierProvider<Config>(
create: (_) => config, create: (_) => config,
), ),
ChangeNotifierProvider<AppState>( ChangeNotifierProxyProvider2<Config, ClashConfig, AppState>(
create: (_) => appState, create: (_) => appState,
update: (_, config, clashConfig, appState) {
appState?.mode = clashConfig.mode;
appState?.isCompatible = config.isCompatible;
appState?.selectedMap = config.currentSelectedMap;
return appState!;
},
) )
], ],
child: child, child: child,
@@ -70,6 +76,7 @@ class ApplicationState extends State<Application> {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
appController.afterInit(); appController.afterInit();
appController.initLink(); appController.initLink();
_updateGroups();
}); });
} }
@@ -102,6 +109,18 @@ class ApplicationState extends State<Application> {
}); });
} }
_updateGroups() {
if (globalState.groupsUpdateTimer != null) {
globalState.groupsUpdateTimer?.cancel();
globalState.groupsUpdateTimer = null;
}
globalState.groupsUpdateTimer ??=
Timer.periodic(appConstant.httpTimeoutDuration, (timer) async {
await appController.updateGroups();
appController.appState.sortNum++;
});
}
@override @override
Widget build(context) { Widget build(context) {
return AppStateContainer( return AppStateContainer(

View File

@@ -100,9 +100,6 @@ class ClashCore {
.map( .map(
(name) => proxies[name], (name) => proxies[name],
) )
.where(
(proxy) => proxy["type"] != GroupType.Selector.value,
)
.toList(); .toList();
return group; return group;
}).toList(); }).toList();
@@ -110,6 +107,31 @@ class ClashCore {
}); });
} }
Future<DelayMap> getDelayMap() {
final proxiesRaw = clashFFI.getProxies();
final proxiesRawString = proxiesRaw.cast<Utf8>().toDartString();
return Isolate.run<DelayMap>(() {
final proxies = json.decode(proxiesRawString) as Map<String, dynamic>;
return proxies.map<String, int?>(
(k, v) {
final history = v["history"] as List<dynamic>;
if (history.isEmpty) {
return MapEntry(
k,
null,
);
} else {
final delay = history.last["delay"];
return MapEntry(
k,
delay != 0 ? delay : -1,
);
}
},
);
});
}
bool changeProxy(ChangeProxyParams changeProxyParams) { bool changeProxy(ChangeProxyParams changeProxyParams) {
final params = json.encode(changeProxyParams); final params = json.encode(changeProxyParams);
return clashFFI.changeProxy(params.toNativeUtf8().cast()) == 1; return clashFFI.changeProxy(params.toNativeUtf8().cast()) == 1;
@@ -124,6 +146,10 @@ class ClashCore {
return true; return true;
} }
clearEffect(String path) {
clashFFI.clearEffect(path.toNativeUtf8().cast());
}
healthcheck() { healthcheck() {
clashFFI.healthcheck(); clashFFI.healthcheck();
} }

View File

@@ -924,6 +924,20 @@ class ClashFFI {
late final _updateConfig = late final _updateConfig =
_updateConfigPtr.asFunction<void Function(ffi.Pointer<ffi.Char>, int)>(); _updateConfigPtr.asFunction<void Function(ffi.Pointer<ffi.Char>, int)>();
void clearEffect(
ffi.Pointer<ffi.Char> s,
) {
return _clearEffect(
s,
);
}
late final _clearEffectPtr =
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(
'clearEffect');
late final _clearEffect =
_clearEffectPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
ffi.Pointer<ffi.Char> getProxies() { ffi.Pointer<ffi.Char> getProxies() {
return _getProxies(); return _getProxies();
} }

View File

@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/models/models.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'core.dart'; import 'core.dart';
class ClashService { class ClashService {

View File

@@ -1,6 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -87,13 +85,7 @@ class AppController {
config.deleteProfileById(id); config.deleteProfileById(id);
final profilePath = await appPath.getProfilePath(id); final profilePath = await appPath.getProfilePath(id);
if (profilePath == null) return; if (profilePath == null) return;
final file = File(profilePath); clashCore.clearEffect(profilePath);
Isolate.run(() async {
final isExists = await file.exists();
if (isExists) {
file.delete();
}
});
if (config.currentProfileId == id) { if (config.currentProfileId == id) {
if (config.profiles.isNotEmpty) { if (config.profiles.isNotEmpty) {
final updateId = config.profiles.first.id; final updateId = config.profiles.first.id;
@@ -172,13 +164,6 @@ class AppController {
appState.systemColorSchemes = systemColorSchemes; appState.systemColorSchemes = systemColorSchemes;
} }
clearCurrentDelay() {
final currentProxyName =
appState.getCurrentProxyName(config.currentProxyName, clashConfig.mode);
if (currentProxyName == null) return;
appState.setDelay(Delay(name: currentProxyName, value: null));
}
savePreferences() async { savePreferences() async {
await saveConfigPreferences(); await saveConfigPreferences();
await saveClashConfigPreferences(); await saveClashConfigPreferences();
@@ -251,6 +236,10 @@ class AppController {
appState.setDelay(delay); appState.setDelay(delay);
} }
updateDelayMap() async {
appState.delayMap = await clashCore.getDelayMap();
}
toPage(int index, {bool hasAnimate = false}) { toPage(int index, {bool hasAnimate = false}) {
final nextLabel = globalState.currentNavigationItems[index].label; final nextLabel = globalState.currentNavigationItems[index].label;
appState.currentLabel = nextLabel; appState.currentLabel = nextLabel;
@@ -361,4 +350,13 @@ class AppController {
}, },
); );
} }
clearShowProxyDelay() {
final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
if (showProxyDelay != null) {
appState.setDelay(
Delay(name: showProxyDelay, value: null),
);
}
}
} }

View File

@@ -2,6 +2,8 @@
enum GroupType { Selector, URLTest, Fallback } enum GroupType { Selector, URLTest, Fallback }
enum GroupName { GLOBAL, Proxy, Auto, Fallback }
extension GroupTypeExtension on GroupType { extension GroupTypeExtension on GroupType {
static List<String> get valueList => GroupType.values static List<String> get valueList => GroupType.values
.map( .map(

View File

@@ -35,6 +35,26 @@ class ApplicationSettingFragment extends StatelessWidget {
); );
}, },
), ),
Selector<Config, bool>(
selector: (_, config) => config.isCompatible,
builder: (_, isCompatible, __) {
return ListItem.switchItem(
leading: const Icon(Icons.device_hub),
title: const Text("兼容模式"),
subtitle: const Text("开启将失去部分应用能力获得全量的Clash的支持"),
delegate: SwitchDelegate(
value: isCompatible,
onChanged: (bool value) async {
final appController = context.appController;
appController.config.isCompatible = value;
await appController.updateClashConfig(isPatch: false);
await appController.updateGroups();
appController.changeProxy();
},
),
);
},
),
if (system.isDesktop) if (system.isDesktop)
Selector<Config, bool>( Selector<Config, bool>(
selector: (_, config) => config.autoLaunch, selector: (_, config) => config.autoLaunch,

View File

@@ -120,7 +120,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
return ListView.separated( return ListView.separated(
itemBuilder: (_, index) { itemBuilder: (_, index) {
return Container( return Container(
height: 84, padding: kMaterialListPadding,
alignment: Alignment.center, alignment: Alignment.center,
child: items[index], child: items[index],
); );

View File

@@ -1,6 +1,6 @@
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart'; import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -25,11 +25,6 @@ class _NetworkDetectionState extends State<NetworkDetection> {
), ),
); );
} }
if (currentProxyName == UsedProxy.DIRECT.name) {
return const Icon(
Icons.offline_bolt_outlined,
);
}
if (delay == 0 || delay == null) { if (delay == 0 || delay == null) {
return const AspectRatio( return const AspectRatio(
aspectRatio: 1, aspectRatio: 1,
@@ -78,6 +73,52 @@ class _NetworkDetectionState extends State<NetworkDetection> {
); );
} }
_updateCurrentDelay(
String? currentProxyName,
int? delay,
bool isCurrent,
bool isInit,
) {
if (!isCurrent || currentProxyName == null || !isInit) return;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (delay == null) {
context.appController.setDelay(
Delay(
name: currentProxyName,
value: 0,
),
);
globalState.updateCurrentDelay(
currentProxyName,
);
}
});
}
_updateCurrentDelayContainer(Widget child) {
return Selector2<AppState, Config, UpdateCurrentDelaySelectorState>(
selector: (_, appState, config) {
return UpdateCurrentDelaySelectorState(
isInit: appState.isInit,
currentProxyName: appState.getRealProxyName(appState.showProxyName),
delay: appState
.delayMap[appState.getRealProxyName(appState.showProxyName)],
isCurrent: appState.currentLabel == 'dashboard',
);
},
builder: (_, state, __) {
_updateCurrentDelay(
state.currentProxyName,
state.delay,
state.isCurrent,
state.isInit,
);
return child;
},
child: child,
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CommonCard( return CommonCard(
@@ -85,59 +126,57 @@ class _NetworkDetectionState extends State<NetworkDetection> {
iconData: Icons.network_check, iconData: Icons.network_check,
label: appLocalizations.networkDetection, label: appLocalizations.networkDetection,
), ),
child: Selector3<AppState, Config, ClashConfig, child: _updateCurrentDelayContainer(
NetworkDetectionSelectorState>( Selector<AppState, NetworkDetectionSelectorState>(
selector: (_, appState, config, clashConfig) { selector: (_, appState) {
final proxyName = appState.getCurrentProxyName( return NetworkDetectionSelectorState(
config.currentProxyName, currentProxyName: appState.showProxyName,
clashConfig.mode, delay: appState.getDelay(
); appState.showProxyName,
return NetworkDetectionSelectorState( ),
isInit: appState.isInit, );
currentProxyName: proxyName, },
delay: appState.getDelay( builder: (_, state, __) {
proxyName, return Container(
), padding: const EdgeInsets.all(16).copyWith(top: 0),
); child: Column(
}, mainAxisSize: MainAxisSize.min,
builder: (_, state, __) { crossAxisAlignment: CrossAxisAlignment.start,
return Container( children: [
padding: const EdgeInsets.all(16).copyWith(top: 0), Flexible(
child: Column( flex: 0,
mainAxisSize: MainAxisSize.min, child: TooltipText(
crossAxisAlignment: CrossAxisAlignment.start, text: Text(
children: [ state.currentProxyName ?? appLocalizations.noProxy,
Flexible( overflow: TextOverflow.ellipsis,
flex: 0, maxLines: 1,
child: TooltipText( style: Theme.of(context)
text: Text( .textTheme
state.currentProxyName ?? appLocalizations.noProxy, .titleMedium
overflow: TextOverflow.ellipsis, ?.toSoftBold(),
maxLines: 1,
style:
Theme.of(context).textTheme.titleMedium?.toSoftBold(),
),
),
),
const SizedBox(
height: 8,
),
Flexible(
child: Container(
height: context.appController.measure.titleLargeHeight,
alignment: Alignment.centerLeft,
child: FadeBox(
child: _buildDescription(
state.currentProxyName,
state.delay,
), ),
), ),
), ),
), const SizedBox(
], height: 8,
), ),
); Flexible(
}, child: Container(
height: context.appController.measure.titleLargeHeight,
alignment: Alignment.centerLeft,
child: FadeBox(
child: _buildDescription(
state.currentProxyName,
state.delay,
),
),
),
),
],
),
);
},
),
), ),
); );
} }

View File

@@ -11,10 +11,18 @@ class OutboundMode extends StatelessWidget {
_changeMode(BuildContext context, Mode? value) async { _changeMode(BuildContext context, Mode? value) async {
final appController = context.appController; final appController = context.appController;
final clashConfig = context.read<ClashConfig>(); final clashConfig = appController.clashConfig;
final config = appController.config;
if (value == null || clashConfig.mode == value) return; if (value == null || clashConfig.mode == value) return;
clashConfig.mode = value; clashConfig.mode = value;
await appController.updateClashConfig(); await appController.updateClashConfig();
if (!config.isCompatible) {
final proxySelected = config.currentSelectedMap[GroupName.Proxy.name];
final globalSelected = config.currentSelectedMap[GroupName.GLOBAL.name];
if (proxySelected != null && globalSelected == null) {
config.updateCurrentSelectedMap(GroupName.GLOBAL.name, proxySelected);
}
}
appController.changeProxy(); appController.changeProxy();
} }
@@ -54,7 +62,8 @@ class OutboundMode extends StatelessWidget {
), ),
title: Text( title: Text(
Intl.message(item.name), Intl.message(item.name),
style: Theme.of(context) style: Theme
.of(context)
.textTheme .textTheme
.titleMedium .titleMedium
?.toSoftBold(), ?.toSoftBold(),

View File

@@ -51,7 +51,7 @@ class _StartButtonState extends State<StartButton>
final appController = context.appController; final appController = context.appController;
await appController.updateSystemProxy(isStart); await appController.updateSystemProxy(isStart);
if (isStart && mounted) { if (isStart && mounted) {
appController.healthcheck(); appController.clearShowProxyDelay();
} }
} }
@@ -66,14 +66,17 @@ class _StartButtonState extends State<StartButton>
if (!state.isInit || !state.hasProfile) { if (!state.isInit || !state.hasProfile) {
return Container(); return Container();
} }
final textWidth = context.appController.measure.computeTextSize( final textWidth = context.appController.measure
Text( .computeTextSize(
Other.getTimeDifference( Text(
DateTime.now(), Other.getTimeDifference(
), DateTime.now(),
style: Theme.of(context).textTheme.titleMedium?.toSoftBold(), ),
), style:
).width + Theme.of(context).textTheme.titleMedium?.toSoftBold(),
),
)
.width +
16; 16;
return AnimatedBuilder( return AnimatedBuilder(
animation: _controller.view, animation: _controller.view,

View File

@@ -1,4 +1,4 @@
import 'package:fl_clash/clash/clash.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart'; import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -69,29 +69,23 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
}, },
child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>( child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>(
selector: (_, appState, config, clashConfig) { selector: (_, appState, config, clashConfig) {
final currentGroups = appState.getCurrentGroups(clashConfig.mode); final currentGroups = appState.currentGroups;
final groupNames = currentGroups.map((e) => e.name).toList(); final groupNames = currentGroups.map((e) => e.name).toList();
final currentProxyName = appState.getCurrentGroupNameWithGroups(
currentGroups,
config.currentGroupName,
clashConfig.mode,
);
final currentIndex = currentGroups
.indexWhere((element) => element.name == currentProxyName);
return ProxiesSelectorState( return ProxiesSelectorState(
currentIndex: currentIndex,
groupNames: groupNames, groupNames: groupNames,
); );
}, },
builder: (_, state, __) { shouldRebuild: (prev, next) {
if (_tabController != null) { if (prev.groupNames.length != next.groupNames.length) {
_tabController!.dispose(); _tabController?.dispose();
_tabController = null; _tabController = null;
} }
_tabController = TabController( return prev != next;
},
builder: (_, state, __) {
_tabController ??= TabController(
length: state.groupNames.length, length: state.groupNames.length,
vsync: this, vsync: this,
initialIndex: state.currentIndex,
); );
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
@@ -152,12 +146,11 @@ class ProxiesTabView extends StatelessWidget {
List<Proxy> _sortOfDelay(BuildContext context, List<Proxy> proxies) { List<Proxy> _sortOfDelay(BuildContext context, List<Proxy> proxies) {
final appState = context.read<AppState>(); final appState = context.read<AppState>();
final delayMap = appState.delayMap;
return proxies = List.of(proxies) return proxies = List.of(proxies)
..sort( ..sort(
(a, b) { (a, b) {
final aDelay = delayMap[a.name]; final aDelay = appState.delayMap[a.name];
final bDelay = delayMap[b.name]; final bDelay = appState.delayMap[b.name];
if (aDelay == null && bDelay == null) { if (aDelay == null && bDelay == null) {
return 0; return 0;
} }
@@ -236,12 +229,22 @@ class ProxiesTabView extends StatelessWidget {
), ),
SizedBox( SizedBox(
height: measure.bodySmallHeight, height: measure.bodySmallHeight,
child: Text( child: Selector<AppState, String>(
proxy.type, selector: (context, appState) => appState.getDesc(
style: context.textTheme.bodySmall?.copyWith( proxy.type,
overflow: TextOverflow.ellipsis, proxy.name,
color: context.textTheme.bodySmall?.color?.toLight(),
), ),
builder: (_, desc, __) {
return TooltipText(
text: Text(
desc,
style: context.textTheme.bodySmall?.copyWith(
overflow: TextOverflow.ellipsis,
color: context.textTheme.bodySmall?.color?.toLight(),
),
),
);
},
), ),
), ),
const SizedBox( const SizedBox(
@@ -250,7 +253,9 @@ class ProxiesTabView extends StatelessWidget {
SizedBox( SizedBox(
height: measure.labelSmallHeight, height: measure.labelSmallHeight,
child: Selector<AppState, int?>( child: Selector<AppState, int?>(
selector: (context, appState) => appState.getDelay(proxy.name), selector: (context, appState) => appState.getDelay(
proxy.name,
),
builder: (_, delay, __) { builder: (_, delay, __) {
return FadeBox( return FadeBox(
child: Builder( child: Builder(
@@ -307,23 +312,12 @@ class ProxiesTabView extends StatelessWidget {
return Selector3<AppState, Config, ClashConfig, return Selector3<AppState, Config, ClashConfig,
ProxiesCardSelectorState>( ProxiesCardSelectorState>(
selector: (_, appState, config, clashConfig) { selector: (_, appState, config, clashConfig) {
final currentGroupName = appState.getCurrentGroupName( final group = appState.getGroupWithName(groupName)!;
config.currentGroupName, bool isSelected = config.currentSelectedMap[group.name] == proxy.name ||
clashConfig.mode, (config.currentSelectedMap[group.name] == null &&
); group.now == proxy.name);
final currentProxyName = appState.getCurrentProxyName(
config.currentProxyName,
clashConfig.mode,
);
final group = appState.getGroupWithName(groupName);
final isSelected = group.type == GroupType.Selector
? group.name == currentGroupName &&
proxy.name == currentProxyName
: group.now == proxy.name;
return ProxiesCardSelectorState( return ProxiesCardSelectorState(
isSelected: isSelected, isSelected: isSelected,
currentGroupName: currentGroupName,
currentProxyName: currentProxyName,
); );
}, },
builder: (_, state, __) { builder: (_, state, __) {
@@ -331,15 +325,21 @@ class ProxiesTabView extends StatelessWidget {
context, context,
isSelected: state.isSelected, isSelected: state.isSelected,
onPressed: () { onPressed: () {
final appController = context.appController;
final group = final group =
context.appController.appState.getGroupWithName(groupName); appController.appState.getGroupWithName(groupName)!;
if (group.type == GroupType.Selector) { if (group.type != GroupType.Selector) {
final config = context.read<Config>(); globalState.showSnackBar(
config.currentProfile?.groupName = group.name; context,
config.currentProfile?.proxyName = proxy.name; message: "当前代理组无法选择",
config.update(); );
context.appController.changeProxy(); return;
} }
context.appController.config.updateCurrentSelectedMap(
groupName,
proxy.name,
);
context.appController.changeProxy();
}, },
proxy: proxy, proxy: proxy,
); );
@@ -356,7 +356,7 @@ class ProxiesTabView extends StatelessWidget {
return ProxiesTabViewSelectorState( return ProxiesTabViewSelectorState(
proxiesSortType: config.proxiesSortType, proxiesSortType: config.proxiesSortType,
sortNum: appState.sortNum, sortNum: appState.sortNum,
group: appState.getGroupWithName(groupName), group: appState.getGroupWithName(groupName)!,
); );
}, },
builder: (_, state, __) { builder: (_, state, __) {

View File

@@ -17,7 +17,11 @@ Future<void> main() async {
await window?.init(); await window?.init();
final config = await preferences.getConfig() ?? Config(); final config = await preferences.getConfig() ?? Config();
final clashConfig = await preferences.getClashConfig() ?? ClashConfig(); final clashConfig = await preferences.getClashConfig() ?? ClashConfig();
final appState = AppState(); final appState = AppState(
mode: clashConfig.mode,
isCompatible: config.isCompatible,
selectedMap: config.currentSelectedMap,
);
await globalState.init( await globalState.init(
appState: appState, appState: appState,
config: config, config: config,
@@ -41,7 +45,11 @@ Future<void> vpnService() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
final config = await preferences.getConfig() ?? Config(); final config = await preferences.getConfig() ?? Config();
final clashConfig = await preferences.getClashConfig() ?? ClashConfig(); final clashConfig = await preferences.getClashConfig() ?? ClashConfig();
final appState = AppState(); final appState = AppState(
mode: clashConfig.mode,
isCompatible: config.isCompatible,
selectedMap: config.currentSelectedMap,
);
clashMessage.addListener(ClashMessageListenerWithVpn(onTun: (String fd) { clashMessage.addListener(ClashMessageListenerWithVpn(onTun: (String fd) {
proxyManager.setProtect( proxyManager.setProtect(
int.parse(fd), int.parse(fd),

View File

@@ -5,35 +5,47 @@ import 'ffi.dart';
import 'log.dart'; import 'log.dart';
import 'navigation.dart'; import 'navigation.dart';
import 'package.dart'; import 'package.dart';
import 'profile.dart';
import 'proxy.dart'; import 'proxy.dart';
import 'system_color_scheme.dart'; import 'system_color_scheme.dart';
import 'traffic.dart'; import 'traffic.dart';
import 'version.dart'; import 'version.dart';
typedef DelayMap = Map<String, int?>;
class AppState with ChangeNotifier { class AppState with ChangeNotifier {
List<NavigationItem> _navigationItems; List<NavigationItem> _navigationItems;
int? _runTime; int? _runTime;
bool _isInit; bool _isInit;
DelayMap _delayMap;
VersionInfo? _versionInfo; VersionInfo? _versionInfo;
List<Traffic> _traffics; List<Traffic> _traffics;
List<Log> _logs; List<Log> _logs;
List<Package> _packages; List<Package> _packages;
String _currentLabel; String _currentLabel;
SystemColorSchemes _systemColorSchemes; SystemColorSchemes _systemColorSchemes;
List<Group> _groups;
num _sortNum; num _sortNum;
Mode _mode;
DelayMap _delayMap;
SelectedMap _selectedMap;
bool _isCompatible;
List<Group> _groups;
AppState() AppState({
: _navigationItems = [], required Mode mode,
_delayMap = {}, required bool isCompatible,
required SelectedMap selectedMap,
}) : _navigationItems = [],
_isInit = false, _isInit = false,
_currentLabel = "dashboard", _currentLabel = "dashboard",
_traffics = [], _traffics = [],
_logs = [], _logs = [],
_groups = [], _selectedMap = selectedMap,
_packages = [], _packages = [],
_sortNum = 0, _sortNum = 0,
_mode = mode,
_delayMap = {},
_groups = [],
_isCompatible = isCompatible,
_systemColorSchemes = SystemColorSchemes(); _systemColorSchemes = SystemColorSchemes();
String get currentLabel => _currentLabel; String get currentLabel => _currentLabel;
@@ -72,29 +84,38 @@ class AppState with ChangeNotifier {
} }
} }
DelayMap get delayMap => _delayMap; String getDesc(String type, String? proxyName) {
final groupTypeNamesList = GroupType.values.map((e) => e.name).toList();
set delayMap(DelayMap value) { if (!groupTypeNamesList.contains(type)) {
if (_delayMap != value) { return type;
_delayMap = value; } else {
notifyListeners(); final index = groups.indexWhere((element) => element.name == proxyName);
if (index == -1) return type;
return "$type(${groups[index].now})";
} }
} }
String? getRealProxyName(String? proxyName) {
if (proxyName == null) return null;
final index = groups.indexWhere((element) => element.name == proxyName);
if (index == -1) return proxyName;
final group = groups[index];
return getRealProxyName(selectedMap.containsKey(proxyName)
? selectedMap[proxyName]
: group.now);
}
String? get showProxyName {
if (currentGroups.isEmpty) {
return UsedProxy.DIRECT.name;
}
final firstGroup = currentGroups.first;
final firstGroupName = firstGroup.name;
return selectedMap[firstGroupName] ?? firstGroup.now;
}
int? getDelay(String? proxyName) { int? getDelay(String? proxyName) {
if (proxyName == null) return null; return _delayMap[getRealProxyName(proxyName)];
final index = groups.indexWhere((element) => element.name == proxyName);
if (index == -1) return _delayMap[proxyName];
final group = groups[index];
if (group.now == null) return null;
return _delayMap[group.now];
}
setDelay(Delay delay) {
if (_delayMap[delay.name] != delay.value) {
_delayMap = Map.from(_delayMap)..[delay.name] = delay.value;
notifyListeners();
}
} }
VersionInfo? get versionInfo => _versionInfo; VersionInfo? get versionInfo => _versionInfo;
@@ -170,53 +191,86 @@ class AppState with ChangeNotifier {
} }
} }
List<Group> getCurrentGroups(Mode mode) { Mode get mode => _mode;
set mode(Mode value) {
if (_mode != value) {
_mode = value;
notifyListeners();
}
}
// String? get currentProxyName {
// if (mode == Mode.direct) return UsedProxy.DIRECT.name;
// if (_currentProxyName != null) return _currentProxyName!;
// return currentGroup?.now;
// }
//
// set currentProxyName(String? value) {
// if (_currentProxyName != value) {
// _currentProxyName = value;
// notifyListeners();
// }
// }
bool get isCompatible {
return _isCompatible;
}
set isCompatible(bool value) {
if (_isCompatible != value) {
_isCompatible = value;
notifyListeners();
}
}
SelectedMap get selectedMap {
return _selectedMap;
}
set selectedMap(SelectedMap value) {
if (!const MapEquality<String, String>().equals(_selectedMap, value)) {
_selectedMap = value;
notifyListeners();
}
}
List<Group> get currentGroups {
switch (mode) { switch (mode) {
case Mode.direct: case Mode.direct:
return []; return [];
case Mode.global: case Mode.global:
return groups return groups
.where((element) => element.name == UsedProxy.GLOBAL.name) .where((element) => element.name == GroupName.GLOBAL.name)
.toList(); .toList();
case Mode.rule: case Mode.rule:
return groups return groups
.where((element) => element.name != UsedProxy.GLOBAL.name) .where((element) => element.name != GroupName.GLOBAL.name)
.toList(); .toList();
} }
} }
String? getCurrentGroupNameWithGroups( DelayMap get delayMap {
List<Group> groups, return _delayMap;
String? groupName, }
Mode mode,
) { set delayMap(DelayMap value) {
switch (mode) { if (!const MapEquality<String, int?>().equals(_delayMap, value)) {
case Mode.direct: _delayMap = value;
return null; notifyListeners();
case Mode.global:
return UsedProxy.GLOBAL.name;
case Mode.rule:
return groupName ?? (groups.isNotEmpty ? groups.first.name : null);
} }
} }
String? getCurrentGroupName(String? groupName, Mode mode) { setDelay(Delay delay) {
final currentGroups = getCurrentGroups(mode); if (_delayMap[delay.name] != delay.value) {
return getCurrentGroupNameWithGroups(currentGroups, groupName, mode); _delayMap = Map.from(_delayMap)..[delay.name] = delay.value;
} notifyListeners();
Group getGroupWithName(String groupName) {
return groups.firstWhere((e) => e.name == groupName);
}
String? getCurrentProxyName(String? proxyName, Mode mode) {
final currentGroups = getCurrentGroups(mode);
switch (mode) {
case Mode.direct:
return UsedProxy.DIRECT.name;
case Mode.global || Mode.rule:
return proxyName ??
(currentGroups.isNotEmpty ? currentGroups.first.now : null);
} }
} }
Group? getGroupWithName(String groupName) {
final index =
currentGroups.indexWhere((element) => element.name == groupName);
return index != -1 ? currentGroups[index] : null;
}
} }

View File

@@ -61,6 +61,7 @@ class AccessControl {
@JsonSerializable() @JsonSerializable()
class Config extends ChangeNotifier { class Config extends ChangeNotifier {
List<Profile> _profiles; List<Profile> _profiles;
bool _isCompatible;
String? _currentProfileId; String? _currentProfileId;
bool _autoLaunch; bool _autoLaunch;
bool _silentLaunch; bool _silentLaunch;
@@ -82,6 +83,7 @@ class Config extends ChangeNotifier {
_autoRun = false, _autoRun = false,
_themeMode = ThemeMode.system, _themeMode = ThemeMode.system,
_openLog = false, _openLog = false,
_isCompatible = false,
_primaryColor = appConstant.defaultPrimaryColor.value, _primaryColor = appConstant.defaultPrimaryColor.value,
_proxiesSortType = ProxiesSortType.none, _proxiesSortType = ProxiesSortType.none,
_isMinimizeOnExit = true, _isMinimizeOnExit = true,
@@ -159,9 +161,18 @@ class Config extends ChangeNotifier {
} }
} }
String? get currentProxyName => currentProfile?.proxyName;
String? get currentGroupName => currentProfile?.groupName; SelectedMap get currentSelectedMap {
return currentProfile?.selectedMap ?? {};
}
updateCurrentSelectedMap(String groupName, String proxyName) {
if (currentProfile?.selectedMap[groupName] != proxyName) {
currentProfile?.selectedMap = Map.from(currentProfile?.selectedMap ?? {})
..[groupName] = proxyName;
notifyListeners();
}
}
@JsonKey(defaultValue: false) @JsonKey(defaultValue: false)
bool get autoLaunch { bool get autoLaunch {
@@ -289,6 +300,18 @@ class Config extends ChangeNotifier {
} }
} }
@JsonKey(defaultValue: false)
bool get isCompatible {
return _isCompatible;
}
set isCompatible(bool value) {
if (_isCompatible != value) {
_isCompatible = value;
notifyListeners();
}
}
update() { update() {
notifyListeners(); notifyListeners();
} }

View File

@@ -13,7 +13,8 @@ class UpdateConfigParams with _$UpdateConfigParams {
const factory UpdateConfigParams({ const factory UpdateConfigParams({
@JsonKey(name: "profile-path") String? profilePath, @JsonKey(name: "profile-path") String? profilePath,
required ClashConfig config, required ClashConfig config,
@JsonKey(name: "is-patch") bool? isPatch, @JsonKey(name: "is-patch") required bool isPatch,
@JsonKey(name: "is-compatible") required bool isCompatible,
}) = _UpdateConfigParams; }) = _UpdateConfigParams;
factory UpdateConfigParams.fromJson(Map<String, Object?> json) => factory UpdateConfigParams.fromJson(Map<String, Object?> json) =>

View File

@@ -55,7 +55,8 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
..isAccessControl = json['isAccessControl'] as bool? ?? false ..isAccessControl = json['isAccessControl'] as bool? ?? false
..accessControl = ..accessControl =
AccessControl.fromJson(json['accessControl'] as Map<String, dynamic>) AccessControl.fromJson(json['accessControl'] as Map<String, dynamic>)
..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true; ..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true
..isCompatible = json['isCompatible'] as bool? ?? false;
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
'profiles': instance.profiles, 'profiles': instance.profiles,
@@ -72,6 +73,7 @@ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{
'isAccessControl': instance.isAccessControl, 'isAccessControl': instance.isAccessControl,
'accessControl': instance.accessControl, 'accessControl': instance.accessControl,
'isAnimateToPage': instance.isAnimateToPage, 'isAnimateToPage': instance.isAnimateToPage,
'isCompatible': instance.isCompatible,
}; };
const _$ThemeModeEnumMap = { const _$ThemeModeEnumMap = {

View File

@@ -24,7 +24,9 @@ mixin _$UpdateConfigParams {
String? get profilePath => throw _privateConstructorUsedError; String? get profilePath => throw _privateConstructorUsedError;
ClashConfig get config => throw _privateConstructorUsedError; ClashConfig get config => throw _privateConstructorUsedError;
@JsonKey(name: "is-patch") @JsonKey(name: "is-patch")
bool? get isPatch => throw _privateConstructorUsedError; bool get isPatch => throw _privateConstructorUsedError;
@JsonKey(name: "is-compatible")
bool get isCompatible => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
@@ -41,7 +43,8 @@ abstract class $UpdateConfigParamsCopyWith<$Res> {
$Res call( $Res call(
{@JsonKey(name: "profile-path") String? profilePath, {@JsonKey(name: "profile-path") String? profilePath,
ClashConfig config, ClashConfig config,
@JsonKey(name: "is-patch") bool? isPatch}); @JsonKey(name: "is-patch") bool isPatch,
@JsonKey(name: "is-compatible") bool isCompatible});
} }
/// @nodoc /// @nodoc
@@ -59,7 +62,8 @@ class _$UpdateConfigParamsCopyWithImpl<$Res, $Val extends UpdateConfigParams>
$Res call({ $Res call({
Object? profilePath = freezed, Object? profilePath = freezed,
Object? config = null, Object? config = null,
Object? isPatch = freezed, Object? isPatch = null,
Object? isCompatible = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
profilePath: freezed == profilePath profilePath: freezed == profilePath
@@ -70,10 +74,14 @@ class _$UpdateConfigParamsCopyWithImpl<$Res, $Val extends UpdateConfigParams>
? _value.config ? _value.config
: config // ignore: cast_nullable_to_non_nullable : config // ignore: cast_nullable_to_non_nullable
as ClashConfig, as ClashConfig,
isPatch: freezed == isPatch isPatch: null == isPatch
? _value.isPatch ? _value.isPatch
: isPatch // ignore: cast_nullable_to_non_nullable : isPatch // ignore: cast_nullable_to_non_nullable
as bool?, as bool,
isCompatible: null == isCompatible
? _value.isCompatible
: isCompatible // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val); ) as $Val);
} }
} }
@@ -89,7 +97,8 @@ abstract class _$$UpdateConfigParamsImplCopyWith<$Res>
$Res call( $Res call(
{@JsonKey(name: "profile-path") String? profilePath, {@JsonKey(name: "profile-path") String? profilePath,
ClashConfig config, ClashConfig config,
@JsonKey(name: "is-patch") bool? isPatch}); @JsonKey(name: "is-patch") bool isPatch,
@JsonKey(name: "is-compatible") bool isCompatible});
} }
/// @nodoc /// @nodoc
@@ -105,7 +114,8 @@ class __$$UpdateConfigParamsImplCopyWithImpl<$Res>
$Res call({ $Res call({
Object? profilePath = freezed, Object? profilePath = freezed,
Object? config = null, Object? config = null,
Object? isPatch = freezed, Object? isPatch = null,
Object? isCompatible = null,
}) { }) {
return _then(_$UpdateConfigParamsImpl( return _then(_$UpdateConfigParamsImpl(
profilePath: freezed == profilePath profilePath: freezed == profilePath
@@ -116,10 +126,14 @@ class __$$UpdateConfigParamsImplCopyWithImpl<$Res>
? _value.config ? _value.config
: config // ignore: cast_nullable_to_non_nullable : config // ignore: cast_nullable_to_non_nullable
as ClashConfig, as ClashConfig,
isPatch: freezed == isPatch isPatch: null == isPatch
? _value.isPatch ? _value.isPatch
: isPatch // ignore: cast_nullable_to_non_nullable : isPatch // ignore: cast_nullable_to_non_nullable
as bool?, as bool,
isCompatible: null == isCompatible
? _value.isCompatible
: isCompatible // ignore: cast_nullable_to_non_nullable
as bool,
)); ));
} }
} }
@@ -130,7 +144,8 @@ class _$UpdateConfigParamsImpl implements _UpdateConfigParams {
const _$UpdateConfigParamsImpl( const _$UpdateConfigParamsImpl(
{@JsonKey(name: "profile-path") this.profilePath, {@JsonKey(name: "profile-path") this.profilePath,
required this.config, required this.config,
@JsonKey(name: "is-patch") this.isPatch}); @JsonKey(name: "is-patch") required this.isPatch,
@JsonKey(name: "is-compatible") required this.isCompatible});
factory _$UpdateConfigParamsImpl.fromJson(Map<String, dynamic> json) => factory _$UpdateConfigParamsImpl.fromJson(Map<String, dynamic> json) =>
_$$UpdateConfigParamsImplFromJson(json); _$$UpdateConfigParamsImplFromJson(json);
@@ -142,11 +157,14 @@ class _$UpdateConfigParamsImpl implements _UpdateConfigParams {
final ClashConfig config; final ClashConfig config;
@override @override
@JsonKey(name: "is-patch") @JsonKey(name: "is-patch")
final bool? isPatch; final bool isPatch;
@override
@JsonKey(name: "is-compatible")
final bool isCompatible;
@override @override
String toString() { String toString() {
return 'UpdateConfigParams(profilePath: $profilePath, config: $config, isPatch: $isPatch)'; return 'UpdateConfigParams(profilePath: $profilePath, config: $config, isPatch: $isPatch, isCompatible: $isCompatible)';
} }
@override @override
@@ -157,12 +175,15 @@ class _$UpdateConfigParamsImpl implements _UpdateConfigParams {
(identical(other.profilePath, profilePath) || (identical(other.profilePath, profilePath) ||
other.profilePath == profilePath) && other.profilePath == profilePath) &&
(identical(other.config, config) || other.config == config) && (identical(other.config, config) || other.config == config) &&
(identical(other.isPatch, isPatch) || other.isPatch == isPatch)); (identical(other.isPatch, isPatch) || other.isPatch == isPatch) &&
(identical(other.isCompatible, isCompatible) ||
other.isCompatible == isCompatible));
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
int get hashCode => Object.hash(runtimeType, profilePath, config, isPatch); int get hashCode =>
Object.hash(runtimeType, profilePath, config, isPatch, isCompatible);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@@ -183,7 +204,8 @@ abstract class _UpdateConfigParams implements UpdateConfigParams {
const factory _UpdateConfigParams( const factory _UpdateConfigParams(
{@JsonKey(name: "profile-path") final String? profilePath, {@JsonKey(name: "profile-path") final String? profilePath,
required final ClashConfig config, required final ClashConfig config,
@JsonKey(name: "is-patch") final bool? isPatch}) = @JsonKey(name: "is-patch") required final bool isPatch,
@JsonKey(name: "is-compatible") required final bool isCompatible}) =
_$UpdateConfigParamsImpl; _$UpdateConfigParamsImpl;
factory _UpdateConfigParams.fromJson(Map<String, dynamic> json) = factory _UpdateConfigParams.fromJson(Map<String, dynamic> json) =
@@ -196,7 +218,10 @@ abstract class _UpdateConfigParams implements UpdateConfigParams {
ClashConfig get config; ClashConfig get config;
@override @override
@JsonKey(name: "is-patch") @JsonKey(name: "is-patch")
bool? get isPatch; bool get isPatch;
@override
@JsonKey(name: "is-compatible")
bool get isCompatible;
@override @override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$UpdateConfigParamsImplCopyWith<_$UpdateConfigParamsImpl> get copyWith => _$$UpdateConfigParamsImplCopyWith<_$UpdateConfigParamsImpl> get copyWith =>

View File

@@ -11,7 +11,8 @@ _$UpdateConfigParamsImpl _$$UpdateConfigParamsImplFromJson(
_$UpdateConfigParamsImpl( _$UpdateConfigParamsImpl(
profilePath: json['profile-path'] as String?, profilePath: json['profile-path'] as String?,
config: ClashConfig.fromJson(json['config'] as Map<String, dynamic>), config: ClashConfig.fromJson(json['config'] as Map<String, dynamic>),
isPatch: json['is-patch'] as bool?, isPatch: json['is-patch'] as bool,
isCompatible: json['is-compatible'] as bool,
); );
Map<String, dynamic> _$$UpdateConfigParamsImplToJson( Map<String, dynamic> _$$UpdateConfigParamsImplToJson(
@@ -20,6 +21,7 @@ Map<String, dynamic> _$$UpdateConfigParamsImplToJson(
'profile-path': instance.profilePath, 'profile-path': instance.profilePath,
'config': instance.config, 'config': instance.config,
'is-patch': instance.isPatch, 'is-patch': instance.isPatch,
'is-compatible': instance.isCompatible,
}; };
_$ChangeProxyParamsImpl _$$ChangeProxyParamsImplFromJson( _$ChangeProxyParamsImpl _$$ChangeProxyParamsImplFromJson(

View File

@@ -27,11 +27,13 @@ Profile _$ProfileFromJson(Map<String, dynamic> json) => Profile(
userInfo: json['userInfo'] == null userInfo: json['userInfo'] == null
? null ? null
: UserInfo.fromJson(json['userInfo'] as Map<String, dynamic>), : UserInfo.fromJson(json['userInfo'] as Map<String, dynamic>),
groupName: json['groupName'] as String?,
proxyName: json['proxyName'] as String?, proxyName: json['proxyName'] as String?,
lastUpdateDate: json['lastUpdateDate'] == null lastUpdateDate: json['lastUpdateDate'] == null
? null ? null
: DateTime.parse(json['lastUpdateDate'] as String), : DateTime.parse(json['lastUpdateDate'] as String),
selectedMap: (json['selectedMap'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
),
autoUpdateDuration: json['autoUpdateDuration'] == null autoUpdateDuration: json['autoUpdateDuration'] == null
? null ? null
: Duration(microseconds: (json['autoUpdateDuration'] as num).toInt()), : Duration(microseconds: (json['autoUpdateDuration'] as num).toInt()),
@@ -41,11 +43,11 @@ Profile _$ProfileFromJson(Map<String, dynamic> json) => Profile(
Map<String, dynamic> _$ProfileToJson(Profile instance) => <String, dynamic>{ Map<String, dynamic> _$ProfileToJson(Profile instance) => <String, dynamic>{
'id': instance.id, 'id': instance.id,
'label': instance.label, 'label': instance.label,
'groupName': instance.groupName,
'proxyName': instance.proxyName, 'proxyName': instance.proxyName,
'url': instance.url, 'url': instance.url,
'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(), 'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(),
'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds, 'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds,
'userInfo': instance.userInfo, 'userInfo': instance.userInfo,
'autoUpdate': instance.autoUpdate, 'autoUpdate': instance.autoUpdate,
'selectedMap': instance.selectedMap,
}; };

View File

@@ -219,6 +219,7 @@ Proxy _$ProxyFromJson(Map<String, dynamic> json) {
mixin _$Proxy { mixin _$Proxy {
String get name => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError;
String get type => throw _privateConstructorUsedError; String get type => throw _privateConstructorUsedError;
String? get now => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
@@ -230,7 +231,7 @@ abstract class $ProxyCopyWith<$Res> {
factory $ProxyCopyWith(Proxy value, $Res Function(Proxy) then) = factory $ProxyCopyWith(Proxy value, $Res Function(Proxy) then) =
_$ProxyCopyWithImpl<$Res, Proxy>; _$ProxyCopyWithImpl<$Res, Proxy>;
@useResult @useResult
$Res call({String name, String type}); $Res call({String name, String type, String? now});
} }
/// @nodoc /// @nodoc
@@ -248,6 +249,7 @@ class _$ProxyCopyWithImpl<$Res, $Val extends Proxy>
$Res call({ $Res call({
Object? name = null, Object? name = null,
Object? type = null, Object? type = null,
Object? now = freezed,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
name: null == name name: null == name
@@ -258,6 +260,10 @@ class _$ProxyCopyWithImpl<$Res, $Val extends Proxy>
? _value.type ? _value.type
: type // ignore: cast_nullable_to_non_nullable : type // ignore: cast_nullable_to_non_nullable
as String, as String,
now: freezed == now
? _value.now
: now // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val); ) as $Val);
} }
} }
@@ -269,7 +275,7 @@ abstract class _$$ProxyImplCopyWith<$Res> implements $ProxyCopyWith<$Res> {
__$$ProxyImplCopyWithImpl<$Res>; __$$ProxyImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({String name, String type}); $Res call({String name, String type, String? now});
} }
/// @nodoc /// @nodoc
@@ -285,6 +291,7 @@ class __$$ProxyImplCopyWithImpl<$Res>
$Res call({ $Res call({
Object? name = null, Object? name = null,
Object? type = null, Object? type = null,
Object? now = freezed,
}) { }) {
return _then(_$ProxyImpl( return _then(_$ProxyImpl(
name: null == name name: null == name
@@ -295,6 +302,10 @@ class __$$ProxyImplCopyWithImpl<$Res>
? _value.type ? _value.type
: type // ignore: cast_nullable_to_non_nullable : type // ignore: cast_nullable_to_non_nullable
as String, as String,
now: freezed == now
? _value.now
: now // ignore: cast_nullable_to_non_nullable
as String?,
)); ));
} }
} }
@@ -302,21 +313,21 @@ class __$$ProxyImplCopyWithImpl<$Res>
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$ProxyImpl implements _Proxy { class _$ProxyImpl implements _Proxy {
const _$ProxyImpl({this.name = "", this.type = ""}); const _$ProxyImpl({required this.name, required this.type, this.now});
factory _$ProxyImpl.fromJson(Map<String, dynamic> json) => factory _$ProxyImpl.fromJson(Map<String, dynamic> json) =>
_$$ProxyImplFromJson(json); _$$ProxyImplFromJson(json);
@override @override
@JsonKey()
final String name; final String name;
@override @override
@JsonKey()
final String type; final String type;
@override
final String? now;
@override @override
String toString() { String toString() {
return 'Proxy(name: $name, type: $type)'; return 'Proxy(name: $name, type: $type, now: $now)';
} }
@override @override
@@ -325,12 +336,13 @@ class _$ProxyImpl implements _Proxy {
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$ProxyImpl && other is _$ProxyImpl &&
(identical(other.name, name) || other.name == name) && (identical(other.name, name) || other.name == name) &&
(identical(other.type, type) || other.type == type)); (identical(other.type, type) || other.type == type) &&
(identical(other.now, now) || other.now == now));
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
int get hashCode => Object.hash(runtimeType, name, type); int get hashCode => Object.hash(runtimeType, name, type, now);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@@ -347,7 +359,10 @@ class _$ProxyImpl implements _Proxy {
} }
abstract class _Proxy implements Proxy { abstract class _Proxy implements Proxy {
const factory _Proxy({final String name, final String type}) = _$ProxyImpl; const factory _Proxy(
{required final String name,
required final String type,
final String? now}) = _$ProxyImpl;
factory _Proxy.fromJson(Map<String, dynamic> json) = _$ProxyImpl.fromJson; factory _Proxy.fromJson(Map<String, dynamic> json) = _$ProxyImpl.fromJson;
@@ -356,6 +371,8 @@ abstract class _Proxy implements Proxy {
@override @override
String get type; String get type;
@override @override
String? get now;
@override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$ProxyImplCopyWith<_$ProxyImpl> get copyWith => _$$ProxyImplCopyWith<_$ProxyImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;

View File

@@ -31,12 +31,14 @@ const _$GroupTypeEnumMap = {
}; };
_$ProxyImpl _$$ProxyImplFromJson(Map<String, dynamic> json) => _$ProxyImpl( _$ProxyImpl _$$ProxyImplFromJson(Map<String, dynamic> json) => _$ProxyImpl(
name: json['name'] as String? ?? "", name: json['name'] as String,
type: json['type'] as String? ?? "", type: json['type'] as String,
now: json['now'] as String?,
); );
Map<String, dynamic> _$$ProxyImplToJson(_$ProxyImpl instance) => Map<String, dynamic> _$$ProxyImplToJson(_$ProxyImpl instance) =>
<String, dynamic>{ <String, dynamic>{
'name': instance.name, 'name': instance.name,
'type': instance.type, 'type': instance.type,
'now': instance.now,
}; };

View File

@@ -349,7 +349,6 @@ abstract class _UpdateCurrentDelaySelectorState
mixin _$NetworkDetectionSelectorState { mixin _$NetworkDetectionSelectorState {
String? get currentProxyName => throw _privateConstructorUsedError; String? get currentProxyName => throw _privateConstructorUsedError;
int? get delay => throw _privateConstructorUsedError; int? get delay => throw _privateConstructorUsedError;
bool get isInit => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
$NetworkDetectionSelectorStateCopyWith<NetworkDetectionSelectorState> $NetworkDetectionSelectorStateCopyWith<NetworkDetectionSelectorState>
@@ -364,7 +363,7 @@ abstract class $NetworkDetectionSelectorStateCopyWith<$Res> {
_$NetworkDetectionSelectorStateCopyWithImpl<$Res, _$NetworkDetectionSelectorStateCopyWithImpl<$Res,
NetworkDetectionSelectorState>; NetworkDetectionSelectorState>;
@useResult @useResult
$Res call({String? currentProxyName, int? delay, bool isInit}); $Res call({String? currentProxyName, int? delay});
} }
/// @nodoc /// @nodoc
@@ -383,7 +382,6 @@ class _$NetworkDetectionSelectorStateCopyWithImpl<$Res,
$Res call({ $Res call({
Object? currentProxyName = freezed, Object? currentProxyName = freezed,
Object? delay = freezed, Object? delay = freezed,
Object? isInit = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
currentProxyName: freezed == currentProxyName currentProxyName: freezed == currentProxyName
@@ -394,10 +392,6 @@ class _$NetworkDetectionSelectorStateCopyWithImpl<$Res,
? _value.delay ? _value.delay
: delay // ignore: cast_nullable_to_non_nullable : delay // ignore: cast_nullable_to_non_nullable
as int?, as int?,
isInit: null == isInit
? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val); ) as $Val);
} }
} }
@@ -411,7 +405,7 @@ abstract class _$$NetworkDetectionSelectorStateImplCopyWith<$Res>
__$$NetworkDetectionSelectorStateImplCopyWithImpl<$Res>; __$$NetworkDetectionSelectorStateImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({String? currentProxyName, int? delay, bool isInit}); $Res call({String? currentProxyName, int? delay});
} }
/// @nodoc /// @nodoc
@@ -429,7 +423,6 @@ class __$$NetworkDetectionSelectorStateImplCopyWithImpl<$Res>
$Res call({ $Res call({
Object? currentProxyName = freezed, Object? currentProxyName = freezed,
Object? delay = freezed, Object? delay = freezed,
Object? isInit = null,
}) { }) {
return _then(_$NetworkDetectionSelectorStateImpl( return _then(_$NetworkDetectionSelectorStateImpl(
currentProxyName: freezed == currentProxyName currentProxyName: freezed == currentProxyName
@@ -440,10 +433,6 @@ class __$$NetworkDetectionSelectorStateImplCopyWithImpl<$Res>
? _value.delay ? _value.delay
: delay // ignore: cast_nullable_to_non_nullable : delay // ignore: cast_nullable_to_non_nullable
as int?, as int?,
isInit: null == isInit
? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable
as bool,
)); ));
} }
} }
@@ -453,20 +442,16 @@ class __$$NetworkDetectionSelectorStateImplCopyWithImpl<$Res>
class _$NetworkDetectionSelectorStateImpl class _$NetworkDetectionSelectorStateImpl
implements _NetworkDetectionSelectorState { implements _NetworkDetectionSelectorState {
const _$NetworkDetectionSelectorStateImpl( const _$NetworkDetectionSelectorStateImpl(
{required this.currentProxyName, {required this.currentProxyName, required this.delay});
required this.delay,
required this.isInit});
@override @override
final String? currentProxyName; final String? currentProxyName;
@override @override
final int? delay; final int? delay;
@override
final bool isInit;
@override @override
String toString() { String toString() {
return 'NetworkDetectionSelectorState(currentProxyName: $currentProxyName, delay: $delay, isInit: $isInit)'; return 'NetworkDetectionSelectorState(currentProxyName: $currentProxyName, delay: $delay)';
} }
@override @override
@@ -476,12 +461,11 @@ class _$NetworkDetectionSelectorStateImpl
other is _$NetworkDetectionSelectorStateImpl && other is _$NetworkDetectionSelectorStateImpl &&
(identical(other.currentProxyName, currentProxyName) || (identical(other.currentProxyName, currentProxyName) ||
other.currentProxyName == currentProxyName) && other.currentProxyName == currentProxyName) &&
(identical(other.delay, delay) || other.delay == delay) && (identical(other.delay, delay) || other.delay == delay));
(identical(other.isInit, isInit) || other.isInit == isInit));
} }
@override @override
int get hashCode => Object.hash(runtimeType, currentProxyName, delay, isInit); int get hashCode => Object.hash(runtimeType, currentProxyName, delay);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@@ -496,16 +480,13 @@ abstract class _NetworkDetectionSelectorState
implements NetworkDetectionSelectorState { implements NetworkDetectionSelectorState {
const factory _NetworkDetectionSelectorState( const factory _NetworkDetectionSelectorState(
{required final String? currentProxyName, {required final String? currentProxyName,
required final int? delay, required final int? delay}) = _$NetworkDetectionSelectorStateImpl;
required final bool isInit}) = _$NetworkDetectionSelectorStateImpl;
@override @override
String? get currentProxyName; String? get currentProxyName;
@override @override
int? get delay; int? get delay;
@override @override
bool get isInit;
@override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$NetworkDetectionSelectorStateImplCopyWith< _$$NetworkDetectionSelectorStateImplCopyWith<
_$NetworkDetectionSelectorStateImpl> _$NetworkDetectionSelectorStateImpl>
@@ -1741,158 +1722,8 @@ abstract class _HomeNavigationSelectorState
get copyWith => throw _privateConstructorUsedError; get copyWith => throw _privateConstructorUsedError;
} }
/// @nodoc
mixin _$ProxiesSelectorState {
int get currentIndex => throw _privateConstructorUsedError;
List<String> get groupNames => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ProxiesSelectorStateCopyWith<ProxiesSelectorState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ProxiesSelectorStateCopyWith<$Res> {
factory $ProxiesSelectorStateCopyWith(ProxiesSelectorState value,
$Res Function(ProxiesSelectorState) then) =
_$ProxiesSelectorStateCopyWithImpl<$Res, ProxiesSelectorState>;
@useResult
$Res call({int currentIndex, List<String> groupNames});
}
/// @nodoc
class _$ProxiesSelectorStateCopyWithImpl<$Res,
$Val extends ProxiesSelectorState>
implements $ProxiesSelectorStateCopyWith<$Res> {
_$ProxiesSelectorStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? currentIndex = null,
Object? groupNames = null,
}) {
return _then(_value.copyWith(
currentIndex: null == currentIndex
? _value.currentIndex
: currentIndex // ignore: cast_nullable_to_non_nullable
as int,
groupNames: null == groupNames
? _value.groupNames
: groupNames // ignore: cast_nullable_to_non_nullable
as List<String>,
) as $Val);
}
}
/// @nodoc
abstract class _$$ProxiesSelectorStateImplCopyWith<$Res>
implements $ProxiesSelectorStateCopyWith<$Res> {
factory _$$ProxiesSelectorStateImplCopyWith(_$ProxiesSelectorStateImpl value,
$Res Function(_$ProxiesSelectorStateImpl) then) =
__$$ProxiesSelectorStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({int currentIndex, List<String> groupNames});
}
/// @nodoc
class __$$ProxiesSelectorStateImplCopyWithImpl<$Res>
extends _$ProxiesSelectorStateCopyWithImpl<$Res, _$ProxiesSelectorStateImpl>
implements _$$ProxiesSelectorStateImplCopyWith<$Res> {
__$$ProxiesSelectorStateImplCopyWithImpl(_$ProxiesSelectorStateImpl _value,
$Res Function(_$ProxiesSelectorStateImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? currentIndex = null,
Object? groupNames = null,
}) {
return _then(_$ProxiesSelectorStateImpl(
currentIndex: null == currentIndex
? _value.currentIndex
: currentIndex // ignore: cast_nullable_to_non_nullable
as int,
groupNames: null == groupNames
? _value._groupNames
: groupNames // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
}
/// @nodoc
class _$ProxiesSelectorStateImpl implements _ProxiesSelectorState {
const _$ProxiesSelectorStateImpl(
{required this.currentIndex, required final List<String> groupNames})
: _groupNames = groupNames;
@override
final int currentIndex;
final List<String> _groupNames;
@override
List<String> get groupNames {
if (_groupNames is EqualUnmodifiableListView) return _groupNames;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_groupNames);
}
@override
String toString() {
return 'ProxiesSelectorState(currentIndex: $currentIndex, groupNames: $groupNames)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ProxiesSelectorStateImpl &&
(identical(other.currentIndex, currentIndex) ||
other.currentIndex == currentIndex) &&
const DeepCollectionEquality()
.equals(other._groupNames, _groupNames));
}
@override
int get hashCode => Object.hash(runtimeType, currentIndex,
const DeepCollectionEquality().hash(_groupNames));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$ProxiesSelectorStateImplCopyWith<_$ProxiesSelectorStateImpl>
get copyWith =>
__$$ProxiesSelectorStateImplCopyWithImpl<_$ProxiesSelectorStateImpl>(
this, _$identity);
}
abstract class _ProxiesSelectorState implements ProxiesSelectorState {
const factory _ProxiesSelectorState(
{required final int currentIndex,
required final List<String> groupNames}) = _$ProxiesSelectorStateImpl;
@override
int get currentIndex;
@override
List<String> get groupNames;
@override
@JsonKey(ignore: true)
_$$ProxiesSelectorStateImplCopyWith<_$ProxiesSelectorStateImpl>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc /// @nodoc
mixin _$ProxiesCardSelectorState { mixin _$ProxiesCardSelectorState {
String? get currentGroupName => throw _privateConstructorUsedError;
String? get currentProxyName => throw _privateConstructorUsedError;
bool get isSelected => throw _privateConstructorUsedError; bool get isSelected => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
@@ -1906,8 +1737,7 @@ abstract class $ProxiesCardSelectorStateCopyWith<$Res> {
$Res Function(ProxiesCardSelectorState) then) = $Res Function(ProxiesCardSelectorState) then) =
_$ProxiesCardSelectorStateCopyWithImpl<$Res, ProxiesCardSelectorState>; _$ProxiesCardSelectorStateCopyWithImpl<$Res, ProxiesCardSelectorState>;
@useResult @useResult
$Res call( $Res call({bool isSelected});
{String? currentGroupName, String? currentProxyName, bool isSelected});
} }
/// @nodoc /// @nodoc
@@ -1924,19 +1754,9 @@ class _$ProxiesCardSelectorStateCopyWithImpl<$Res,
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? currentGroupName = freezed,
Object? currentProxyName = freezed,
Object? isSelected = null, Object? isSelected = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
currentGroupName: freezed == currentGroupName
? _value.currentGroupName
: currentGroupName // ignore: cast_nullable_to_non_nullable
as String?,
currentProxyName: freezed == currentProxyName
? _value.currentProxyName
: currentProxyName // ignore: cast_nullable_to_non_nullable
as String?,
isSelected: null == isSelected isSelected: null == isSelected
? _value.isSelected ? _value.isSelected
: isSelected // ignore: cast_nullable_to_non_nullable : isSelected // ignore: cast_nullable_to_non_nullable
@@ -1954,8 +1774,7 @@ abstract class _$$ProxiesCardSelectorStateImplCopyWith<$Res>
__$$ProxiesCardSelectorStateImplCopyWithImpl<$Res>; __$$ProxiesCardSelectorStateImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call( $Res call({bool isSelected});
{String? currentGroupName, String? currentProxyName, bool isSelected});
} }
/// @nodoc /// @nodoc
@@ -1971,19 +1790,9 @@ class __$$ProxiesCardSelectorStateImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? currentGroupName = freezed,
Object? currentProxyName = freezed,
Object? isSelected = null, Object? isSelected = null,
}) { }) {
return _then(_$ProxiesCardSelectorStateImpl( return _then(_$ProxiesCardSelectorStateImpl(
currentGroupName: freezed == currentGroupName
? _value.currentGroupName
: currentGroupName // ignore: cast_nullable_to_non_nullable
as String?,
currentProxyName: freezed == currentProxyName
? _value.currentProxyName
: currentProxyName // ignore: cast_nullable_to_non_nullable
as String?,
isSelected: null == isSelected isSelected: null == isSelected
? _value.isSelected ? _value.isSelected
: isSelected // ignore: cast_nullable_to_non_nullable : isSelected // ignore: cast_nullable_to_non_nullable
@@ -1995,21 +1804,14 @@ class __$$ProxiesCardSelectorStateImplCopyWithImpl<$Res>
/// @nodoc /// @nodoc
class _$ProxiesCardSelectorStateImpl implements _ProxiesCardSelectorState { class _$ProxiesCardSelectorStateImpl implements _ProxiesCardSelectorState {
const _$ProxiesCardSelectorStateImpl( const _$ProxiesCardSelectorStateImpl({required this.isSelected});
{required this.currentGroupName,
required this.currentProxyName,
required this.isSelected});
@override
final String? currentGroupName;
@override
final String? currentProxyName;
@override @override
final bool isSelected; final bool isSelected;
@override @override
String toString() { String toString() {
return 'ProxiesCardSelectorState(currentGroupName: $currentGroupName, currentProxyName: $currentProxyName, isSelected: $isSelected)'; return 'ProxiesCardSelectorState(isSelected: $isSelected)';
} }
@override @override
@@ -2017,17 +1819,12 @@ class _$ProxiesCardSelectorStateImpl implements _ProxiesCardSelectorState {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$ProxiesCardSelectorStateImpl && other is _$ProxiesCardSelectorStateImpl &&
(identical(other.currentGroupName, currentGroupName) ||
other.currentGroupName == currentGroupName) &&
(identical(other.currentProxyName, currentProxyName) ||
other.currentProxyName == currentProxyName) &&
(identical(other.isSelected, isSelected) || (identical(other.isSelected, isSelected) ||
other.isSelected == isSelected)); other.isSelected == isSelected));
} }
@override @override
int get hashCode => int get hashCode => Object.hash(runtimeType, isSelected);
Object.hash(runtimeType, currentGroupName, currentProxyName, isSelected);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@@ -2038,15 +1835,9 @@ class _$ProxiesCardSelectorStateImpl implements _ProxiesCardSelectorState {
} }
abstract class _ProxiesCardSelectorState implements ProxiesCardSelectorState { abstract class _ProxiesCardSelectorState implements ProxiesCardSelectorState {
const factory _ProxiesCardSelectorState( const factory _ProxiesCardSelectorState({required final bool isSelected}) =
{required final String? currentGroupName, _$ProxiesCardSelectorStateImpl;
required final String? currentProxyName,
required final bool isSelected}) = _$ProxiesCardSelectorStateImpl;
@override
String? get currentGroupName;
@override
String? get currentProxyName;
@override @override
bool get isSelected; bool get isSelected;
@override @override
@@ -2055,6 +1846,135 @@ abstract class _ProxiesCardSelectorState implements ProxiesCardSelectorState {
get copyWith => throw _privateConstructorUsedError; get copyWith => throw _privateConstructorUsedError;
} }
/// @nodoc
mixin _$ProxiesSelectorState {
List<String> get groupNames => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ProxiesSelectorStateCopyWith<ProxiesSelectorState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ProxiesSelectorStateCopyWith<$Res> {
factory $ProxiesSelectorStateCopyWith(ProxiesSelectorState value,
$Res Function(ProxiesSelectorState) then) =
_$ProxiesSelectorStateCopyWithImpl<$Res, ProxiesSelectorState>;
@useResult
$Res call({List<String> groupNames});
}
/// @nodoc
class _$ProxiesSelectorStateCopyWithImpl<$Res,
$Val extends ProxiesSelectorState>
implements $ProxiesSelectorStateCopyWith<$Res> {
_$ProxiesSelectorStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? groupNames = null,
}) {
return _then(_value.copyWith(
groupNames: null == groupNames
? _value.groupNames
: groupNames // ignore: cast_nullable_to_non_nullable
as List<String>,
) as $Val);
}
}
/// @nodoc
abstract class _$$ProxiesSelectorStateImplCopyWith<$Res>
implements $ProxiesSelectorStateCopyWith<$Res> {
factory _$$ProxiesSelectorStateImplCopyWith(_$ProxiesSelectorStateImpl value,
$Res Function(_$ProxiesSelectorStateImpl) then) =
__$$ProxiesSelectorStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<String> groupNames});
}
/// @nodoc
class __$$ProxiesSelectorStateImplCopyWithImpl<$Res>
extends _$ProxiesSelectorStateCopyWithImpl<$Res, _$ProxiesSelectorStateImpl>
implements _$$ProxiesSelectorStateImplCopyWith<$Res> {
__$$ProxiesSelectorStateImplCopyWithImpl(_$ProxiesSelectorStateImpl _value,
$Res Function(_$ProxiesSelectorStateImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? groupNames = null,
}) {
return _then(_$ProxiesSelectorStateImpl(
groupNames: null == groupNames
? _value._groupNames
: groupNames // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
}
/// @nodoc
class _$ProxiesSelectorStateImpl implements _ProxiesSelectorState {
const _$ProxiesSelectorStateImpl({required final List<String> groupNames})
: _groupNames = groupNames;
final List<String> _groupNames;
@override
List<String> get groupNames {
if (_groupNames is EqualUnmodifiableListView) return _groupNames;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_groupNames);
}
@override
String toString() {
return 'ProxiesSelectorState(groupNames: $groupNames)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ProxiesSelectorStateImpl &&
const DeepCollectionEquality()
.equals(other._groupNames, _groupNames));
}
@override
int get hashCode => Object.hash(
runtimeType, const DeepCollectionEquality().hash(_groupNames));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$ProxiesSelectorStateImplCopyWith<_$ProxiesSelectorStateImpl>
get copyWith =>
__$$ProxiesSelectorStateImplCopyWithImpl<_$ProxiesSelectorStateImpl>(
this, _$identity);
}
abstract class _ProxiesSelectorState implements ProxiesSelectorState {
const factory _ProxiesSelectorState(
{required final List<String> groupNames}) = _$ProxiesSelectorStateImpl;
@override
List<String> get groupNames;
@override
@JsonKey(ignore: true)
_$$ProxiesSelectorStateImplCopyWith<_$ProxiesSelectorStateImpl>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc /// @nodoc
mixin _$ProxiesTabViewSelectorState { mixin _$ProxiesTabViewSelectorState {
ProxiesSortType get proxiesSortType => throw _privateConstructorUsedError; ProxiesSortType get proxiesSortType => throw _privateConstructorUsedError;

View File

@@ -12,6 +12,8 @@ import 'common.dart';
part 'generated/profile.g.dart'; part 'generated/profile.g.dart';
typedef SelectedMap = Map<String, String>;
@JsonSerializable() @JsonSerializable()
class UserInfo { class UserInfo {
int upload; int upload;
@@ -62,27 +64,28 @@ class UserInfo {
class Profile { class Profile {
String id; String id;
String? label; String? label;
String? groupName;
String? proxyName; String? proxyName;
String? url; String? url;
DateTime? lastUpdateDate; DateTime? lastUpdateDate;
Duration autoUpdateDuration; Duration autoUpdateDuration;
UserInfo? userInfo; UserInfo? userInfo;
bool autoUpdate; bool autoUpdate;
SelectedMap selectedMap;
Profile({ Profile({
String? id, String? id,
this.label, this.label,
this.url, this.url,
this.userInfo, this.userInfo,
this.groupName,
this.proxyName, this.proxyName,
this.lastUpdateDate, this.lastUpdateDate,
SelectedMap? selectedMap,
Duration? autoUpdateDuration, Duration? autoUpdateDuration,
this.autoUpdate = true, this.autoUpdate = true,
}) : id = id ?? DateTime.now().millisecondsSinceEpoch.toString(), }) : id = id ?? DateTime.now().millisecondsSinceEpoch.toString(),
autoUpdateDuration = autoUpdateDuration =
autoUpdateDuration ?? appConstant.defaultUpdateDuration; autoUpdateDuration ?? appConstant.defaultUpdateDuration,
selectedMap = selectedMap ?? {};
ProfileType get type => url == null ? ProfileType.file : ProfileType.url; ProfileType get type => url == null ? ProfileType.file : ProfileType.url;
@@ -158,7 +161,6 @@ class Profile {
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
id == other.id && id == other.id &&
label == other.label && label == other.label &&
groupName == other.groupName &&
proxyName == other.proxyName && proxyName == other.proxyName &&
url == other.url && url == other.url &&
lastUpdateDate == other.lastUpdateDate && lastUpdateDate == other.lastUpdateDate &&
@@ -170,7 +172,6 @@ class Profile {
int get hashCode => int get hashCode =>
id.hashCode ^ id.hashCode ^
label.hashCode ^ label.hashCode ^
groupName.hashCode ^
proxyName.hashCode ^ proxyName.hashCode ^
url.hashCode ^ url.hashCode ^
lastUpdateDate.hashCode ^ lastUpdateDate.hashCode ^
@@ -180,7 +181,7 @@ class Profile {
@override @override
String toString() { String toString() {
return 'Profile{id: $id, label: $label, groupName: $groupName, proxyName: $proxyName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, userInfo: $userInfo, autoUpdate: $autoUpdate}'; return 'Profile{id: $id, label: $label, proxyName: $proxyName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, userInfo: $userInfo, autoUpdate: $autoUpdate}';
} }
Profile copyWith({ Profile copyWith({
@@ -192,14 +193,15 @@ class Profile {
DateTime? lastUpdateDate, DateTime? lastUpdateDate,
Duration? autoUpdateDuration, Duration? autoUpdateDuration,
bool? autoUpdate, bool? autoUpdate,
SelectedMap? selectedMap,
}) { }) {
return Profile( return Profile(
id: id, id: id,
label: label ?? this.label, label: label ?? this.label,
url: url ?? this.url, url: url ?? this.url,
groupName: groupName ?? this.groupName,
proxyName: proxyName ?? this.proxyName, proxyName: proxyName ?? this.proxyName,
userInfo: userInfo ?? this.userInfo, userInfo: userInfo ?? this.userInfo,
selectedMap: selectedMap ?? this.selectedMap,
lastUpdateDate: lastUpdateDate ?? this.lastUpdateDate, lastUpdateDate: lastUpdateDate ?? this.lastUpdateDate,
autoUpdateDuration: autoUpdateDuration ?? this.autoUpdateDuration, autoUpdateDuration: autoUpdateDuration ?? this.autoUpdateDuration,
autoUpdate: autoUpdate ?? this.autoUpdate, autoUpdate: autoUpdate ?? this.autoUpdate,

View File

@@ -6,7 +6,7 @@ part 'generated/proxy.g.dart';
part 'generated/proxy.freezed.dart'; part 'generated/proxy.freezed.dart';
typedef DelayMap = Map<String, int?>; typedef ProxyMap = Map<String, Proxy>;
@freezed @freezed
class Group with _$Group { class Group with _$Group {
@@ -23,8 +23,9 @@ class Group with _$Group {
@freezed @freezed
class Proxy with _$Proxy { class Proxy with _$Proxy {
const factory Proxy({ const factory Proxy({
@Default("") String name, required String name,
@Default("") String type, required String type,
String? now,
}) = _Proxy; }) = _Proxy;
factory Proxy.fromJson(Map<String, Object?> json) => _$ProxyFromJson(json); factory Proxy.fromJson(Map<String, Object?> json) => _$ProxyFromJson(json);

View File

@@ -32,7 +32,6 @@ class NetworkDetectionSelectorState with _$NetworkDetectionSelectorState {
const factory NetworkDetectionSelectorState({ const factory NetworkDetectionSelectorState({
required String? currentProxyName, required String? currentProxyName,
required int? delay, required int? delay,
required bool isInit,
}) = _NetworkDetectionSelectorState; }) = _NetworkDetectionSelectorState;
} }
@@ -104,20 +103,17 @@ class HomeNavigationSelectorState with _$HomeNavigationSelectorState{
} }
@freezed @freezed
class ProxiesSelectorState with _$ProxiesSelectorState{ class ProxiesCardSelectorState with _$ProxiesCardSelectorState{
const factory ProxiesSelectorState({ const factory ProxiesCardSelectorState({
required int currentIndex, required bool isSelected,
required List<String> groupNames, }) = _ProxiesCardSelectorState;
}) = _ProxiesSelectorState;
} }
@freezed @freezed
class ProxiesCardSelectorState with _$ProxiesCardSelectorState{ class ProxiesSelectorState with _$ProxiesSelectorState{
const factory ProxiesCardSelectorState({ const factory ProxiesSelectorState({
required String? currentGroupName, required List<String> groupNames,
required String? currentProxyName, }) = _ProxiesSelectorState;
required bool isSelected,
}) = _ProxiesCardSelectorState;
} }
@freezed @freezed

View File

@@ -14,8 +14,9 @@ import 'common/common.dart';
class GlobalState { class GlobalState {
Timer? timer; Timer? timer;
Function? updateCurrentDelayDebounce;
Function? updateSortNumDebounce; Function? updateSortNumDebounce;
Timer? groupsUpdateTimer;
Function? updateCurrentDelayDebounce;
PageController? pageController; PageController? pageController;
final navigatorKey = GlobalKey<NavigatorState>(); final navigatorKey = GlobalKey<NavigatorState>();
final Map<int, String?> packageNameMap = {}; final Map<int, String?> packageNameMap = {};
@@ -49,6 +50,7 @@ class GlobalState {
profilePath: profilePath, profilePath: profilePath,
config: clashConfig, config: clashConfig,
isPatch: isPatch, isPatch: isPatch,
isCompatible: config.isCompatible,
)); ));
} }
@@ -74,19 +76,6 @@ class GlobalState {
stopListenUpdate(); stopListenUpdate();
} }
void updateCurrentDelay(
String? proxyName,
) {
updateCurrentDelayDebounce ??= debounce<Function(String?)>((proxyName) {
if (proxyName != null) {
clashCore.delay(
proxyName,
);
}
});
updateCurrentDelayDebounce!([proxyName]);
}
applyProfile({ applyProfile({
required AppState appState, required AppState appState,
required Config config, required Config config,
@@ -132,29 +121,20 @@ class GlobalState {
required Config config, required Config config,
required ClashConfig clashConfig, required ClashConfig clashConfig,
}) { }) {
final currentGroupName = WidgetsBinding.instance.addPostFrameCallback((_) {
appState.getCurrentGroupName(config.currentGroupName, clashConfig.mode); if (config.profiles.isEmpty) {
final currentProxyName = stopSystemProxy();
appState.getCurrentProxyName(config.currentProxyName, clashConfig.mode); return;
if (config.profiles.isEmpty || currentProxyName == null) { }
stopSystemProxy(); config.currentSelectedMap.forEach((key, value) {
return; clashCore.changeProxy(
} ChangeProxyParams(
if (currentGroupName == null) return; groupName: key,
final groupIndex = appState.groups.indexWhere( proxyName: value,
(element) => element.name == currentGroupName, ),
); );
if (groupIndex == -1) return; });
final proxyIndex = appState.groups[groupIndex].all.indexWhere( });
(element) => element.name == currentProxyName,
);
if (proxyIndex == -1) return;
clashCore.changeProxy(
ChangeProxyParams(
groupName: currentGroupName,
proxyName: currentProxyName,
),
);
} }
updatePackages(AppState appState) async { updatePackages(AppState appState) async {
@@ -170,11 +150,11 @@ class GlobalState {
required Config config, required Config config,
required ClashConfig clashConfig, required ClashConfig clashConfig,
}) { }) {
final hasGroups = appState.getCurrentGroups(clashConfig.mode).isNotEmpty; final group = appState.currentGroups;
final hasProfile = config.profiles.isNotEmpty; final hasProfile = config.profiles.isNotEmpty;
appState.navigationItems = navigation.getItems( appState.navigationItems = navigation.getItems(
openLogs: config.openLogs, openLogs: config.openLogs,
hasProxies: hasGroups && hasProfile, hasProxies: group.isNotEmpty && hasProfile,
); );
} }
@@ -258,6 +238,52 @@ class GlobalState {
); );
} }
} }
showSnackBar(
BuildContext context, {
required String message,
SnackBarAction? action,
}) {
final width = context.width;
EdgeInsets margin;
if (width < 600) {
margin = const EdgeInsets.only(
bottom: 96,
right: 16,
left: 16,
);
} else {
margin = EdgeInsets.only(
bottom: 16,
left: 16,
right: width - 316,
);
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
action: action,
content: Text(message),
behavior: SnackBarBehavior.floating,
duration: const Duration(milliseconds: 1500),
margin: margin,
),
);
}
void updateCurrentDelay(
String? proxyName,
) {
updateCurrentDelayDebounce ??= debounce<Function(String?)>((proxyName) {
if (proxyName != null) {
debugPrint("[delay]=====> $proxyName");
clashCore.delay(
proxyName,
);
}
});
updateCurrentDelayDebounce!([proxyName]);
}
} }
final globalState = GlobalState(); final globalState = GlobalState();

View File

@@ -23,14 +23,13 @@ class AppStateContainer extends StatelessWidget {
} }
_updateNavigationsContainer(Widget child) { _updateNavigationsContainer(Widget child) {
return Selector3<AppState, Config, ClashConfig, UpdateNavigationsSelector>( return Selector2<AppState, Config, UpdateNavigationsSelector>(
selector: (_, appState, config, clashConfig) { selector: (_, appState, config) {
final hasGroups = final group = appState.currentGroups;
appState.getCurrentGroups(clashConfig.mode).isNotEmpty;
final hasProfile = config.profiles.isNotEmpty; final hasProfile = config.profiles.isNotEmpty;
return UpdateNavigationsSelector( return UpdateNavigationsSelector(
openLogs: config.openLogs, openLogs: config.openLogs,
hasProxies: hasGroups && hasProfile, hasProxies: group.isNotEmpty && hasProfile,
); );
}, },
builder: (context, state, child) { builder: (context, state, child) {

View File

@@ -38,17 +38,8 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
@override @override
void onDelay(Delay delay) { void onDelay(Delay delay) {
context.appController.setDelay(delay); final appController = context.appController;
WidgetsBinding.instance.addPostFrameCallback((_) { appController.setDelay(delay);
globalState.updateSortNumDebounce ??= debounce<Function()>(
() {
context.appController.updateGroups();
context.appController.appState.sortNum++;
},
milliseconds: 5000,
);
globalState.updateSortNumDebounce!();
});
super.onDelay(delay); super.onDelay(delay);
} }

View File

@@ -1,7 +1,7 @@
name: fl_clash name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free. description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none' publish_to: 'none'
version: 0.7.12 version: 0.8.0
environment: environment:
sdk: '>=3.1.0 <4.0.0' sdk: '>=3.1.0 <4.0.0'