Compare commits

..

43 Commits

Author SHA1 Message Date
chen08209
0512d692f2 Fix notification duplicate creation issue
Fix AccessControl click issue
2024-06-03 11:43:41 +08:00
chen08209
844ce8ffb0 Fix Linux unable to open 2024-05-31 22:12:06 +08:00
chen08209
d2588596df Update README.md 3 2024-05-31 15:40:47 +08:00
chen08209
0e999e3deb Create LICENSE
(cherry picked from commit 3ef3190785)
2024-05-31 15:11:45 +08:00
chen08209
8109bbf46c Update README.md 2
(cherry picked from commit b1c763fcfa)
2024-05-31 15:11:45 +08:00
chen08209
32ea45e5ea Update README.md
(cherry picked from commit 9452ffa514)
2024-05-31 15:11:44 +08:00
chen08209
ecce17ad75 optimize checkUpdate 2024-05-31 09:59:18 +08:00
chen08209
96738090cf Fix submit error
(cherry picked from commit fd3040283c)
2024-05-31 09:09:05 +08:00
chen08209
4a8ea37142 add WebDAV
add Auto check updates

Optimize more details
2024-05-30 17:06:53 +08:00
chen08209
a5fdb90da5 optimize delayTest 2024-05-17 19:54:26 +08:00
chen08209
f9722cc761 upgrade flutter version
(cherry picked from commit 9a07c785f2)
2024-05-17 09:35:22 +08:00
chen08209
f01fb2ed1d Update kernel
Add import profile via QR code image
2024-05-15 20:19:50 +08:00
chen08209
74f4481071 Add compatibility mode and adapt clash scheme. 2024-05-11 14:08:13 +08:00
chen08209
9018f512ae Reconstruction application proxy logic 2024-05-07 17:59:11 +08:00
chen08209
265fc4a701 Fix Tab destroy error 2024-05-06 19:03:49 +08:00
chen08209
755974fc9e Optimize repeat healthcheck 2024-05-06 17:15:42 +08:00
chen08209
ba8eab4fc9 Optimize Direct mode ui 2024-05-06 15:26:38 +08:00
chen08209
f5cb46710f Optimize Healthcheck 2024-05-06 14:31:20 +08:00
chen08209
6483e80416 Remove proxies position animation, improve performance
Add Telegram Link
2024-05-06 14:31:19 +08:00
chen08209
535e6dc3a5 Update healthcheck policy 2024-05-06 14:31:19 +08:00
chen08209
ad86c20cfb New Check URLTest 2024-05-05 21:40:12 +08:00
chen08209
665330e17a Fix the problem of invalid auto-selection 2024-05-05 16:13:52 +08:00
chen08209
0eb001e717 New Async UpdateConfig 2024-05-05 03:12:45 +08:00
chen08209
a563991d74 add changeProfileDebounce 2024-05-04 21:51:40 +08:00
chen08209
3e2a30008c Update Workflow 2024-05-04 16:50:37 +08:00
chen08209
ff68d573d6 Fix ChangeProfile block 2024-05-04 16:39:21 +08:00
chen08209
3223fca7ba Fix Release Message Error
(cherry picked from commit aef50fe0e3)
2024-05-04 16:38:03 +08:00
chen08209
006f9127fc Update Selector 2
(cherry picked from commit fc0767ed25)
2024-05-04 16:37:59 +08:00
chen08209
a2709e155c Update Version
(cherry picked from commit dbf1724cca)
2024-05-04 16:37:57 +08:00
chen08209
684fa7b58e Fix Proxies Select Error
(cherry picked from commit 909aa4038e)
2024-05-04 16:37:55 +08:00
chen08209
03b4da54b5 Fix the problem that the proxy group is empty in global mode.
(cherry picked from commit 2d0a7d8d46)
2024-05-04 16:37:54 +08:00
chen08209
a904b55d11 Fix the problem that the proxy group is empty in global mode.
(cherry picked from commit ca96cd1d82)
2024-05-04 16:37:54 +08:00
chen08209
d711935e2e Add ProxyProvider2
(cherry picked from commit 91ab1e5dac)
2024-05-04 16:37:53 +08:00
chen08209
98b1496eff Add ProxyProvider
(cherry picked from commit b3a5f74df8)
2024-05-03 21:28:41 +08:00
chen08209
442c32b6eb Update Version 2024-05-03 15:32:12 +08:00
chen08209
949a2aaac3 Update ProxyGroup Sort 2024-05-03 14:31:10 +08:00
chen08209
c77463f337 Fix Android quickStart VpnService some problems 2024-05-02 00:46:42 +08:00
chen08209
00377d6070 Update version 2024-05-01 23:39:21 +08:00
chen08209
f393b4b3e9 Set Android notification low importance 2024-05-01 23:29:32 +08:00
chen08209
75e6cfde15 Add Telegram in README_zh_CN.md
(cherry picked from commit 8a188a37c9)
2024-05-01 21:52:22 +08:00
chen08209
7bfe5617d9 Add Telegram 2024-05-01 21:49:18 +08:00
chen08209
97cc96c243 Fix the issue that VpnService can't be closed correctly in special cases 2024-05-01 21:29:54 +08:00
chen08209
1821ee2f61 Fix the problem that TileService is not destroyed correctly in some cases
Adjust tab animation defaults
2024-05-01 15:13:09 +08:00
85 changed files with 1200 additions and 33810 deletions

View File

@@ -3,7 +3,7 @@ name: build
on: on:
push: push:
tags: tags:
- '*' - 'v*'
jobs: jobs:
build: build:
@@ -17,8 +17,8 @@ jobs:
os: windows-latest os: windows-latest
- platform: linux - platform: linux
os: ubuntu-latest os: ubuntu-latest
# - platform: macos - platform: macos
# os: macos-13 os: macos-13
steps: steps:
- name: Checkout - name: Checkout
@@ -82,7 +82,6 @@ jobs:
upload-release: upload-release:
if: ${{ !endsWith(github.ref, '-debug') }}
permissions: write-all permissions: write-all
needs: [ build ] needs: [ build ]
runs-on: ubuntu-latest runs-on: ubuntu-latest

2
.gitignore vendored
View File

@@ -31,7 +31,6 @@ migrate_working_dir/
.pub-cache/ .pub-cache/
.pub/ .pub/
/build/ /build/
/dist/
# Symbolication related # Symbolication related
app.*.symbols app.*.symbols
@@ -45,7 +44,6 @@ app.*.map.json
/android/app/release /android/app/release
#libclash #libclash
/libclash/ /libclash/

View File

@@ -34,8 +34,6 @@ on Mobile:
💡 Based on Material You Design, [Surfboard](https://github.com/getsurfboard/surfboard)-like UI 💡 Based on Material You Design, [Surfboard](https://github.com/getsurfboard/surfboard)-like UI
☁️ Supports data sync via WebDAV
✨ Support subscription link, Dark mode ✨ Support subscription link, Dark mode
## Contact ## Contact

View File

@@ -34,8 +34,6 @@ on Mobile:
💡 基本 Material You 设计, 类[Surfboard](https://github.com/getsurfboard/surfboard)用户界面 💡 基本 Material You 设计, 类[Surfboard](https://github.com/getsurfboard/surfboard)用户界面
☁️ 支持通过WebDAV同步数据
✨ 支持一键导入订阅, 深色模式 ✨ 支持一键导入订阅, 深色模式
## Contact ## Contact

View File

@@ -62,7 +62,7 @@ android {
defaultConfig { defaultConfig {
applicationId "com.follow.clash" applicationId "com.follow.clash"
minSdkVersion 24 minSdkVersion 21
targetSdkVersion 34 targetSdkVersion 34
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName

View File

@@ -1,6 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-feature <uses-feature
android:name="android.hardware.touchscreen" android:name="android.hardware.touchscreen"
android:required="false" /> android:required="false" />
@@ -14,10 +17,8 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
tools:ignore="SystemPermissionTypo" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<application <application
android:name="${applicationName}" android:name="${applicationName}"
@@ -72,8 +73,7 @@
<service <service
android:name=".services.FlClashTileService" android:name=".services.FlClashTileService"
android:exported="true" android:exported="true"
android:icon="@drawable/icon" android:icon="@drawable/tile_icon"
android:foregroundServiceType="specialUse"
android:label="FlClash" android:label="FlClash"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter> <intent-filter>

View File

@@ -19,6 +19,7 @@ import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor import io.flutter.embedding.engine.dart.DartExecutor
@RequiresApi(Build.VERSION_CODES.N)
class FlClashTileService : TileService() { class FlClashTileService : TileService() {
private val observer = Observer<RunState> { runState -> private val observer = Observer<RunState> { runState ->
@@ -42,27 +43,19 @@ class FlClashTileService : TileService() {
GlobalState.runState.observeForever(observer) GlobalState.runState.observeForever(observer)
} }
@SuppressLint("StartActivityAndCollapseDeprecated")
private fun activityTransfer() { private fun activityTransfer() {
val intent = Intent(this, TempActivity::class.java) val intent = Intent(this, TempActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val pendingIntent = if (Build.VERSION.SDK_INT >= 31) { if (Build.VERSION.SDK_INT >= 34) {
PendingIntent.getActivity( val pendingIntent = PendingIntent.getActivity(
this, this,
0, 0,
intent, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT PendingIntent.FLAG_IMMUTABLE
) )
} else {
PendingIntent.getActivity(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startActivityAndCollapse(pendingIntent) startActivityAndCollapse(pendingIntent)
}else{ } else {
startActivityAndCollapse(intent) startActivityAndCollapse(intent)
} }
} }

View File

@@ -1,9 +1,10 @@
package com.follow.clash.services package com.follow.clash.services
import android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE import android.annotation.SuppressLint
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.app.Service
import android.content.Intent import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
import android.net.ProxyInfo import android.net.ProxyInfo
@@ -12,13 +13,15 @@ import android.os.Binder
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
import androidx.core.graphics.drawable.IconCompat
import com.follow.clash.GlobalState import com.follow.clash.GlobalState
import com.follow.clash.MainActivity import com.follow.clash.MainActivity
import com.follow.clash.R
import com.follow.clash.models.AccessControl import com.follow.clash.models.AccessControl
import com.follow.clash.models.AccessControlMode import com.follow.clash.models.AccessControlMode
@SuppressLint("WrongConstant")
class FlClashVpnService : VpnService() { class FlClashVpnService : VpnService() {
@@ -97,10 +100,10 @@ class FlClashVpnService : VpnService() {
} }
private val notificationBuilder: NotificationCompat.Builder by lazy { private val notificationBuilder by lazy {
val intent = Intent(this, MainActivity::class.java) val intent = Intent(this, MainActivity::class.java)
val pendingIntent = if (Build.VERSION.SDK_INT >= 31) { val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getActivity( PendingIntent.getActivity(
this, this,
0, 0,
@@ -116,43 +119,43 @@ class FlClashVpnService : VpnService() {
) )
} }
val icon = IconCompat.createWithResource(this, this.applicationInfo.icon)
with(NotificationCompat.Builder(this, CHANNEL)) { with(NotificationCompat.Builder(this, CHANNEL)) {
setSmallIcon(R.drawable.ic_stat_name) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setSmallIcon(icon)
}
setContentTitle("FlClash") setContentTitle("FlClash")
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
setContentIntent(pendingIntent) setContentIntent(pendingIntent)
setCategory(NotificationCompat.CATEGORY_SERVICE) setCategory(NotificationCompat.CATEGORY_SERVICE)
priority = NotificationCompat.PRIORITY_MIN priority = NotificationCompat.PRIORITY_LOW
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
}
setOngoing(true) setOngoing(true)
setShowWhen(false) setShowWhen(false)
setOnlyAlertOnce(true) setOnlyAlertOnce(true)
setAutoCancel(true);
} }
} }
@SuppressLint("ForegroundServiceType", "WrongConstant")
fun startForeground(title: String, content: String) { fun startForeground(title: String, content: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = val channel =
NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW) NotificationChannel(CHANNEL, "FlClash", NotificationManager.IMPORTANCE_LOW)
val manager = getSystemService(NotificationManager::class.java) val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(channel) manager.createNotificationChannel(channel)
channel.setShowBadge(false)
} }
val notification = val notification =
notificationBuilder.setContentTitle(title).setContentText(content).build() notificationBuilder.setContentTitle(title).setContentText(content).build()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE) startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
}else{ } else {
startForeground(notificationId, notification) startForeground(notificationId, notification)
} }
} }
private fun stopForeground() { private fun stopForeground() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
stopForeground(STOP_FOREGROUND_REMOVE) stopForeground(Service.STOP_FOREGROUND_REMOVE)
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -22,7 +22,6 @@ import (
"strings" "strings"
"sync" "sync"
"syscall" "syscall"
"time"
) )
type healthCheckSchema struct { type healthCheckSchema struct {
@@ -94,13 +93,6 @@ type Now struct {
Value string `json:"value"` Value string `json:"value"`
} }
type ExternalProvider struct {
Name string `json:"name"`
Type string `json:"type"`
VehicleType string `json:"vehicle-type"`
UpdateAt time.Time `json:"update-at"`
}
func restartExecutable(execPath string) { func restartExecutable(execPath string) {
var err error var err error
executor.Shutdown() executor.Shutdown()
@@ -325,14 +317,11 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.ExternalUI = "" targetConfig.ExternalUI = ""
targetConfig.Interface = "" targetConfig.Interface = ""
targetConfig.ExternalUIURL = "" targetConfig.ExternalUIURL = ""
targetConfig.GeodataMode = false
//targetConfig.IPv6 = patchConfig.IPv6 //targetConfig.IPv6 = patchConfig.IPv6
targetConfig.LogLevel = patchConfig.LogLevel targetConfig.LogLevel = patchConfig.LogLevel
targetConfig.Port = 0
targetConfig.SocksPort = 0
targetConfig.MixedPort = patchConfig.MixedPort
targetConfig.FindProcessMode = process.FindProcessAlways targetConfig.FindProcessMode = process.FindProcessAlways
targetConfig.AllowLan = patchConfig.AllowLan targetConfig.AllowLan = patchConfig.AllowLan
targetConfig.MixedPort = patchConfig.MixedPort
targetConfig.Mode = patchConfig.Mode targetConfig.Mode = patchConfig.Mode
targetConfig.Tun.Enable = patchConfig.Tun.Enable targetConfig.Tun.Enable = patchConfig.Tun.Enable
targetConfig.Tun.Device = patchConfig.Tun.Device targetConfig.Tun.Device = patchConfig.Tun.Device
@@ -353,6 +342,7 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
targetConfig.RuleProvider = make(map[string]map[string]any) targetConfig.RuleProvider = make(map[string]map[string]any)
generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule) generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule)
} }
} }
func patchConfig(general *config.General) { func patchConfig(general *config.General) {
@@ -411,6 +401,6 @@ func applyConfig(isPatch bool) {
patchConfig(cfg.General) patchConfig(cfg.General)
} else { } else {
executor.ApplyConfig(cfg, true) executor.ApplyConfig(cfg, true)
hcCompatibleProvider(tunnel.Providers()) healthcheck()
} }
} }

View File

@@ -1,6 +1,6 @@
module core module core
go 1.20 go 1.21.0
replace github.com/metacubex/mihomo => ./Clash.Meta replace github.com/metacubex/mihomo => ./Clash.Meta
@@ -8,7 +8,7 @@ require (
github.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34 github.com/Kr328/tun2socket v0.0.0-20220414050025-d07c78d06d34
github.com/metacubex/mihomo v1.17.1 github.com/metacubex/mihomo v1.17.1
github.com/miekg/dns v1.1.59 github.com/miekg/dns v1.1.59
golang.org/x/net v0.25.0 golang.org/x/net v0.24.0
golang.org/x/sync v0.7.0 golang.org/x/sync v0.7.0
) )
@@ -21,7 +21,7 @@ require (
github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect github.com/buger/jsonparser v1.1.1 // indirect
github.com/cilium/ebpf v0.12.3 // indirect github.com/cilium/ebpf v0.12.3 // indirect
github.com/cloudflare/circl v1.3.7 // indirect github.com/cloudflare/circl v1.3.6 // indirect
github.com/coreos/go-iptables v0.7.0 // indirect github.com/coreos/go-iptables v0.7.0 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
@@ -34,8 +34,8 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect github.com/gobwas/ws v1.3.2 // indirect
github.com/gofrs/uuid/v5 v5.2.0 // indirect github.com/gofrs/uuid/v5 v5.1.0 // indirect
github.com/google/btree v1.1.2 // indirect github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
@@ -51,15 +51,14 @@ require (
github.com/mdlayher/socket v0.4.1 // indirect github.com/mdlayher/socket v0.4.1 // indirect
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e // indirect github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98 // indirect
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 // indirect github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d // indirect
github.com/metacubex/sing-shadowsocks v0.2.6 // indirect github.com/metacubex/sing-shadowsocks v0.2.6 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.0 // indirect github.com/metacubex/sing-shadowsocks2 v0.2.0 // indirect
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec // indirect github.com/metacubex/sing-tun v0.2.6 // indirect
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f // indirect github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f // indirect
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 // indirect github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 // indirect
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 // indirect github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 // indirect
github.com/metacubex/utls v1.6.6 // indirect
github.com/mroth/weightedrand/v2 v2.1.0 // indirect github.com/mroth/weightedrand/v2 v2.1.0 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect
@@ -76,9 +75,10 @@ require (
github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 // indirect github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 // indirect
github.com/sagernet/sing-shadowtls v0.1.4 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
github.com/sagernet/utls v1.5.4 // indirect
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e // indirect github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e // indirect
github.com/samber/lo v1.39.0 // indirect github.com/samber/lo v1.39.0 // indirect
github.com/shirou/gopsutil/v3 v3.24.4 // indirect github.com/shirou/gopsutil/v3 v3.24.3 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
@@ -94,14 +94,14 @@ require (
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go.uber.org/mock v0.4.0 // indirect go.uber.org/mock v0.4.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/sys v0.20.0 // indirect golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.0 // indirect golang.org/x/tools v0.20.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect lukechampine.com/blake3 v1.2.2 // indirect
) )

View File

@@ -21,8 +21,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=
github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -35,16 +35,19 @@ github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIF
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg= github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c/go.mod h1:ETASDWf/FmEb6Ysrtd1QhjNedUU/ZQxBCRLh60bQ/UI=
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY= github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4= github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA= github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok= github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@@ -54,12 +57,13 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM= github.com/gofrs/uuid/v5 v5.1.0 h1:S5rqVKIigghZTCBKPCw0Y+bXkn26K3TB5mvQq2Ix8dk=
github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gofrs/uuid/v5 v5.1.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -69,6 +73,7 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -83,7 +88,9 @@ github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
@@ -98,24 +105,22 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc=
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw=
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e h1:Nzwe08FNIJpExWpy9iXkG336dN/8nJqn69yijB7vJ8g= github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98 h1:oMLlJV4a9AylNo8ZLBNUiqZ02Vme6GLLHjuWJz8amSk=
github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= github.com/metacubex/quic-go v0.42.1-0.20240418003344-f006b5735d98/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ=
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d h1:RAe0ND8J5SOPGI623oEXfaHKaaUrrzHx+U1DN9Awcco=
github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= github.com/metacubex/sing-quic v0.0.0-20240418004036-814c531c378d/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8=
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8=
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec h1:K4Wq3GOdLZ/xcqwyzAt4kmYQrjokyKQ3u/Xh5Yft14U= github.com/metacubex/sing-tun v0.2.6 h1:frc58BqnIClqcC9KcYBfVAn5bgO6WW1ANKvZW3/HYAQ=
github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= github.com/metacubex/sing-tun v0.2.6/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo=
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ=
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY= github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY=
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI=
github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo=
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c=
github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts=
github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8=
github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
@@ -125,6 +130,7 @@ github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:U
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0= github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=
github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo= github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0= github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=
@@ -146,6 +152,7 @@ github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
@@ -159,12 +166,14 @@ github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnV
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo=
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE=
github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -214,18 +223,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
@@ -245,26 +254,28 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= lukechampine.com/blake3 v1.2.2 h1:wEAbSg0IVU4ih44CVlpMqMZMpzr5hf/6aqodLlevd/w=
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.2.2/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

View File

@@ -10,13 +10,10 @@ import (
"github.com/metacubex/mihomo/adapter/provider" "github.com/metacubex/mihomo/adapter/provider"
"github.com/metacubex/mihomo/common/structure" "github.com/metacubex/mihomo/common/structure"
"github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/common/utils"
"github.com/metacubex/mihomo/component/mmdb"
"github.com/metacubex/mihomo/config" "github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant"
cp "github.com/metacubex/mihomo/constant/provider"
"github.com/metacubex/mihomo/hub/executor" "github.com/metacubex/mihomo/hub/executor"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
rp "github.com/metacubex/mihomo/rules/provider"
"github.com/metacubex/mihomo/tunnel" "github.com/metacubex/mihomo/tunnel"
"github.com/metacubex/mihomo/tunnel/statistic" "github.com/metacubex/mihomo/tunnel/statistic"
"golang.org/x/net/context" "golang.org/x/net/context"
@@ -61,17 +58,10 @@ func shutdownClash() bool {
} }
//export validateConfig //export validateConfig
func validateConfig(s *C.char, port C.longlong) { func validateConfig(s *C.char) bool {
i := int64(port) bytes := []byte(C.GoString(s))
go func() { _, err := config.UnmarshalRawConfig(bytes)
bytes := []byte(C.GoString(s)) return err == nil
_, err := config.UnmarshalRawConfig(bytes)
if err != nil {
bridge.SendToPort(i, err.Error())
return
}
bridge.SendToPort(i, "")
}()
} }
//export updateConfig //export updateConfig
@@ -181,8 +171,7 @@ func getTraffic() *C.char {
} }
//export asyncTestDelay //export asyncTestDelay
func asyncTestDelay(s *C.char, port C.longlong) { func asyncTestDelay(s *C.char) {
i := int64(port)
go func() { go func() {
paramsString := C.GoString(s) paramsString := C.GoString(s)
var params = &TestDelayParams{} var params = &TestDelayParams{}
@@ -206,25 +195,26 @@ func asyncTestDelay(s *C.char, port C.longlong) {
Name: params.ProxyName, Name: params.ProxyName,
} }
message := bridge.Message{
Type: bridge.Delay,
Data: delayData,
}
if proxy == nil { if proxy == nil {
delayData.Value = -1 delayData.Value = -1
data, _ := json.Marshal(delayData) bridge.SendMessage(message)
bridge.SendToPort(i, string(data))
return return
} }
delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus) delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus)
if err != nil || delay == 0 { if err != nil || delay == 0 {
delayData.Value = -1 delayData.Value = -1
data, _ := json.Marshal(delayData) bridge.SendMessage(message)
bridge.SendToPort(i, string(data))
return return
} }
delayData.Value = int32(delay) delayData.Value = int32(delay)
data, _ := json.Marshal(delayData) bridge.SendMessage(message)
bridge.SendToPort(i, string(data))
return
}() }()
} }
@@ -232,7 +222,7 @@ func asyncTestDelay(s *C.char, port C.longlong) {
func getVersionInfo() *C.char { func getVersionInfo() *C.char {
versionInfo := map[string]string{ versionInfo := map[string]string{
"clashName": constant.Name, "clashName": constant.Name,
"version": "1.18.5", "version": constant.Version,
} }
data, err := json.Marshal(versionInfo) data, err := json.Marshal(versionInfo)
if err != nil { if err != nil {
@@ -296,82 +286,9 @@ func getProvider(name *C.char) *C.char {
return C.CString(string(data)) return C.CString(string(data))
} }
//export getExternalProviders //export healthcheck
func getExternalProviders() *C.char { func healthcheck() {
externalProviders := make([]ExternalProvider, 0) hcCompatibleProvider(tunnel.Providers())
providers := tunnel.Providers()
for n, p := range providers {
if p.VehicleType() != cp.Compatible {
p := p.(*provider.ProxySetProvider)
externalProviders = append(externalProviders, ExternalProvider{
Name: n,
Type: p.Type().String(),
VehicleType: p.VehicleType().String(),
UpdateAt: p.UpdatedAt,
})
}
}
for n, p := range tunnel.RuleProviders() {
if p.VehicleType() != cp.Compatible {
p := p.(*rp.RuleSetProvider)
externalProviders = append(externalProviders, ExternalProvider{
Name: n,
Type: p.Type().String(),
VehicleType: p.VehicleType().String(),
UpdateAt: p.UpdatedAt,
})
}
}
data, err := json.Marshal(externalProviders)
if err != nil {
return C.CString("")
}
return C.CString(string(data))
}
//export updateExternalProvider
func updateExternalProvider(providerName *C.char, providerType *C.char, port C.longlong) {
i := int64(port)
go func() {
providerNameString := C.GoString(providerName)
providerTypeString := C.GoString(providerType)
switch providerTypeString {
case "Proxy":
providers := tunnel.Providers()
err := providers[providerNameString].Update()
if err != nil {
bridge.SendToPort(i, err.Error())
return
}
case "Rule":
providers := tunnel.RuleProviders()
err := providers[providerNameString].Update()
if err != nil {
bridge.SendToPort(i, err.Error())
return
}
case "GeoIp":
err := mmdb.DownloadMMDB(constant.Path.Resolve(providerNameString))
if err != nil {
bridge.SendToPort(i, err.Error())
return
}
case "GeoSite":
err := mmdb.DownloadGeoSite(constant.Path.Resolve(providerNameString))
if err != nil {
bridge.SendToPort(i, err.Error())
return
}
case "ASN":
err := mmdb.DownloadASN(constant.Path.Resolve(providerNameString))
if err != nil {
bridge.SendToPort(i, err.Error())
return
}
}
bridge.SendToPort(i, "")
}()
} }
//export initNativeApiBridge //export initNativeApiBridge

View File

@@ -17,35 +17,36 @@ var tunLock sync.Mutex
var tun *t.Tun var tun *t.Tun
//export startTUN //export startTUN
func startTUN(fd C.int) { func startTUN(fd C.int) bool {
go func() { tunLock.Lock()
tunLock.Lock() defer tunLock.Unlock()
defer tunLock.Unlock()
if tun != nil { if tun != nil {
tun.Close() tun.Close()
tun = nil tun = nil
} }
f := int(fd) f := int(fd)
gateway := "172.16.0.1/30" gateway := "172.16.0.1/30"
portal := "172.16.0.2" portal := "172.16.0.2"
dns := "0.0.0.0" dns := "0.0.0.0"
tempTun := &t.Tun{Closed: false, Limit: semaphore.NewWeighted(4)} tempTun := &t.Tun{Closed: false, Limit: semaphore.NewWeighted(4)}
closer, err := t.Start(f, gateway, portal, dns) closer, err := t.Start(f, gateway, portal, dns)
applyConfig(true) applyConfig(true)
if err != nil { if err != nil {
log.Errorln("startTUN error: %v", err) log.Errorln("startTUN error: %v", err)
tempTun.Close() tempTun.Close()
} return false
}
tempTun.Closer = closer tempTun.Closer = closer
tun = tempTun tun = tempTun
}()
return true
} }
//export updateMarkSocketPort //export updateMarkSocketPort
@@ -60,16 +61,14 @@ func updateMarkSocketPort(markSocketPort C.longlong) bool {
//export stopTun //export stopTun
func stopTun() { func stopTun() {
go func() { tunLock.Lock()
tunLock.Lock() defer tunLock.Unlock()
defer tunLock.Unlock()
if tun != nil { if tun != nil {
tun.Close() tun.Close()
applyConfig(true) applyConfig(true)
tun = nil tun = nil
} }
}()
} }
func init() { func init() {

View File

@@ -82,6 +82,7 @@ class ApplicationState extends State<Application> {
super.initState(); super.initState();
globalState.appController = AppController(context); globalState.appController = AppController(context);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
globalState.appController.updateViewWidth();
globalState.appController.afterInit(); globalState.appController.afterInit();
globalState.appController.initLink(); globalState.appController.initLink();
_updateGroups(); _updateGroups();
@@ -160,9 +161,8 @@ class ApplicationState extends State<Application> {
AppLocalizations.delegate.supportedLocales, AppLocalizations.delegate.supportedLocales,
themeMode: state.themeMode, themeMode: state.themeMode,
theme: ThemeData( theme: ThemeData(
useMaterial3: true,
fontFamily: '',
pageTransitionsTheme: _pageTransitionsTheme, pageTransitionsTheme: _pageTransitionsTheme,
useMaterial3: true,
colorScheme: _getAppColorScheme( colorScheme: _getAppColorScheme(
brightness: Brightness.light, brightness: Brightness.light,
systemColorSchemes: systemColorSchemes, systemColorSchemes: systemColorSchemes,
@@ -171,7 +171,6 @@ class ApplicationState extends State<Application> {
), ),
darkTheme: ThemeData( darkTheme: ThemeData(
useMaterial3: true, useMaterial3: true,
fontFamily: '',
pageTransitionsTheme: _pageTransitionsTheme, pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme( colorScheme: _getAppColorScheme(
brightness: Brightness.dark, brightness: Brightness.dark,

View File

@@ -5,9 +5,9 @@ import 'dart:io';
import 'dart:isolate'; import 'dart:isolate';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:fl_clash/common/common.dart'; import '../enum/enum.dart';
import 'package:fl_clash/enum/enum.dart'; import '../models/models.dart';
import 'package:fl_clash/models/models.dart'; import '../common/common.dart';
import 'generated/clash_ffi.dart'; import 'generated/clash_ffi.dart';
class ClashCore { class ClashCore {
@@ -58,20 +58,11 @@ class ClashCore {
bool get isInit => clashFFI.getIsInit() == 1; bool get isInit => clashFFI.getIsInit() == 1;
Future<String> validateConfig(String data) { bool validateConfig(String data) {
final completer = Completer<String>(); return clashFFI.validateConfig(
final receiver = ReceivePort(); data.toNativeUtf8().cast(),
receiver.listen((message) { ) ==
if (!completer.isCompleted) { 1;
completer.complete(message);
receiver.close();
}
});
clashFFI.validateConfig(
data.toNativeUtf8().cast(),
receiver.sendPort.nativePort,
);
return completer.future;
} }
Future<String> updateConfig(UpdateConfigParams updateConfigParams) { Future<String> updateConfig(UpdateConfigParams updateConfigParams) {
@@ -116,76 +107,53 @@ class ClashCore {
}); });
} }
Future<List<ExternalProvider>> getExternalProviders() { Future<DelayMap> getDelayMap() {
final externalProvidersRaw = clashFFI.getExternalProviders(); final proxiesRaw = clashFFI.getProxies();
final externalProvidersRawString = final proxiesRawString = proxiesRaw.cast<Utf8>().toDartString();
externalProvidersRaw.cast<Utf8>().toDartString(); return Isolate.run<DelayMap>(() {
return Isolate.run<List<ExternalProvider>>(() { final proxies = json.decode(proxiesRawString) as Map<String, dynamic>;
final externalProviders = return proxies.map<String, int?>(
(json.decode(externalProvidersRawString) as List<dynamic>) (k, v) {
.map( final history = v["history"] as List<dynamic>;
(item) => ExternalProvider.fromJson(item), if (history.isEmpty) {
) return MapEntry(
.toList(); k,
return externalProviders; null,
);
} else {
final delay = history.last["delay"];
return MapEntry(
k,
delay != 0 ? delay : -1,
);
}
},
);
}); });
} }
Future<String> updateExternalProvider({
required String providerName,
required String providerType,
}) {
final completer = Completer<String>();
final receiver = ReceivePort();
receiver.listen((message) {
if (!completer.isCompleted) {
completer.complete(message);
receiver.close();
}
});
clashFFI.updateExternalProvider(
providerName.toNativeUtf8().cast(),
providerType.toNativeUtf8().cast(),
receiver.sendPort.nativePort,
);
return completer.future;
}
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;
} }
Future<Delay> getDelay(String proxyName) { bool delay(String proxyName) {
final delayParams = { final delayParams = {
"proxy-name": proxyName, "proxy-name": proxyName,
"timeout": httpTimeoutDuration.inMilliseconds, "timeout": httpTimeoutDuration.inMilliseconds,
}; };
final completer = Completer<Delay>(); clashFFI.asyncTestDelay(json.encode(delayParams).toNativeUtf8().cast());
final receiver = ReceivePort(); return true;
receiver.listen((message) {
if (!completer.isCompleted) {
completer.complete(Delay.fromJson(json.decode(message)));
receiver.close();
}
});
clashFFI.asyncTestDelay(
json.encode(delayParams).toNativeUtf8().cast(),
receiver.sendPort.nativePort,
);
Future.delayed(httpTimeoutDuration + moreDuration, () {
receiver.close();
completer.complete(
Delay(name: proxyName, value: -1),
);
});
return completer.future;
} }
clearEffect(String path) { clearEffect(String path) {
clashFFI.clearEffect(path.toNativeUtf8().cast()); clashFFI.clearEffect(path.toNativeUtf8().cast());
} }
healthcheck() {
clashFFI.healthcheck();
}
VersionInfo getVersionInfo() { VersionInfo getVersionInfo() {
final versionInfoRaw = clashFFI.getVersionInfo(); final versionInfoRaw = clashFFI.getVersionInfo();
final versionInfo = json.decode(versionInfoRaw.cast<Utf8>().toDartString()); final versionInfo = json.decode(versionInfoRaw.cast<Utf8>().toDartString());

View File

@@ -893,22 +893,19 @@ class ClashFFI {
_lookup<ffi.NativeFunction<GoUint8 Function()>>('shutdownClash'); _lookup<ffi.NativeFunction<GoUint8 Function()>>('shutdownClash');
late final _shutdownClash = _shutdownClashPtr.asFunction<int Function()>(); late final _shutdownClash = _shutdownClashPtr.asFunction<int Function()>();
void validateConfig( int validateConfig(
ffi.Pointer<ffi.Char> s, ffi.Pointer<ffi.Char> s,
int port,
) { ) {
return _validateConfig( return _validateConfig(
s, s,
port,
); );
} }
late final _validateConfigPtr = _lookup< late final _validateConfigPtr =
ffi.NativeFunction< _lookup<ffi.NativeFunction<GoUint8 Function(ffi.Pointer<ffi.Char>)>>(
ffi.Void Function( 'validateConfig');
ffi.Pointer<ffi.Char>, ffi.LongLong)>>('validateConfig'); late final _validateConfig =
late final _validateConfig = _validateConfigPtr _validateConfigPtr.asFunction<int Function(ffi.Pointer<ffi.Char>)>();
.asFunction<void Function(ffi.Pointer<ffi.Char>, int)>();
void updateConfig( void updateConfig(
ffi.Pointer<ffi.Char> s, ffi.Pointer<ffi.Char> s,
@@ -977,20 +974,17 @@ class ClashFFI {
void asyncTestDelay( void asyncTestDelay(
ffi.Pointer<ffi.Char> s, ffi.Pointer<ffi.Char> s,
int port,
) { ) {
return _asyncTestDelay( return _asyncTestDelay(
s, s,
port,
); );
} }
late final _asyncTestDelayPtr = _lookup< late final _asyncTestDelayPtr =
ffi.NativeFunction< _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(
ffi.Void Function( 'asyncTestDelay');
ffi.Pointer<ffi.Char>, ffi.LongLong)>>('asyncTestDelay'); late final _asyncTestDelay =
late final _asyncTestDelay = _asyncTestDelayPtr _asyncTestDelayPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
.asFunction<void Function(ffi.Pointer<ffi.Char>, int)>();
ffi.Pointer<ffi.Char> getVersionInfo() { ffi.Pointer<ffi.Char> getVersionInfo() {
return _getVersionInfo(); return _getVersionInfo();
@@ -1060,34 +1054,13 @@ class ClashFFI {
late final _getProvider = _getProviderPtr late final _getProvider = _getProviderPtr
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>(); .asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
ffi.Pointer<ffi.Char> getExternalProviders() { void healthcheck() {
return _getExternalProviders(); return _healthcheck();
} }
late final _getExternalProvidersPtr = late final _healthcheckPtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>( _lookup<ffi.NativeFunction<ffi.Void Function()>>('healthcheck');
'getExternalProviders'); late final _healthcheck = _healthcheckPtr.asFunction<void Function()>();
late final _getExternalProviders =
_getExternalProvidersPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
void updateExternalProvider(
ffi.Pointer<ffi.Char> providerName,
ffi.Pointer<ffi.Char> providerType,
int port,
) {
return _updateExternalProvider(
providerName,
providerType,
port,
);
}
late final _updateExternalProviderPtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
ffi.LongLong)>>('updateExternalProvider');
late final _updateExternalProvider = _updateExternalProviderPtr.asFunction<
void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, int)>();
void initNativeApiBridge( void initNativeApiBridge(
ffi.Pointer<ffi.Void> api, ffi.Pointer<ffi.Void> api,

View File

@@ -1,41 +1,24 @@
import 'dart:io'; 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/cupertino.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'core.dart'; import 'core.dart';
class ClashService { class ClashService {
Future<void> initGeo() async { Future<bool> initMmdb() async {
final homePath = await appPath.getHomeDirPath(); final mmdbPath = await appPath.getMMDBPath();
final homeDir = Directory(homePath); var mmdbFile = File(mmdbPath);
final isExists = await homeDir.exists(); final isExists = await mmdbFile.exists();
if (!isExists) { if (isExists) return true;
await homeDir.create(recursive: true);
}
const geoFileNameList = [
mmdbFileName,
geoSiteFileName,
asnFileName,
];
try { try {
for (final geoFileName in geoFileNameList) { mmdbFile = await mmdbFile.create(recursive: true);
final geoFile = File( ByteData data = await rootBundle.load('assets/data/geoip.metadb');
join(homePath, geoFileName), List<int> bytes = data.buffer.asUint8List();
); await mmdbFile.writeAsBytes(bytes, flush: true);
final isExists = await geoFile.exists(); return true;
if (isExists) { } catch (_) {
continue; return false;
}
final data = await rootBundle.load('assets/data/$geoFileName');
List<int> bytes = data.buffer.asUint8List();
await geoFile.writeAsBytes(bytes, flush: true);
}
} catch (e) {
debugPrint("$e");
exit(0);
} }
} }
@@ -43,7 +26,8 @@ class ClashService {
required ClashConfig clashConfig, required ClashConfig clashConfig,
required Config config, required Config config,
}) async { }) async {
await initGeo(); final isInitMmdb = await initMmdb();
if (!isInitMmdb) return false;
final homeDirPath = await appPath.getHomeDirPath(); final homeDirPath = await appPath.getHomeDirPath();
final isInit = clashCore.init(homeDirPath); final isInit = clashCore.init(homeDirPath);
return isInit; return isInit;

View File

@@ -9,8 +9,6 @@ const httpTimeoutDuration = Duration(milliseconds: 5000);
const moreDuration = Duration(milliseconds: 100); const moreDuration = Duration(milliseconds: 100);
const defaultUpdateDuration = Duration(days: 1); const defaultUpdateDuration = Duration(days: 1);
const mmdbFileName = "geoip.metadb"; const mmdbFileName = "geoip.metadb";
const geoSiteFileName = "GeoSite.dat";
const asnFileName = "ASN.mmdb";
const profilesDirectoryName = "profiles"; const profilesDirectoryName = "profiles";
const localhost = "127.0.0.1"; const localhost = "127.0.0.1";
const clashConfigKey = "clash_config"; const clashConfigKey = "clash_config";

View File

@@ -1,7 +1,5 @@
import 'package:fl_clash/common/app_localizations.dart';
extension DateTimeExtension on DateTime { extension DateTimeExtension on DateTime {
bool get isBeforeNow { bool isBeforeNow() {
return isBefore(DateTime.now()); return isBefore(DateTime.now());
} }
@@ -11,32 +9,4 @@ extension DateTimeExtension on DateTime {
} }
return true; return true;
} }
}
String get lastUpdateTimeDesc {
final currentDateTime = DateTime.now();
final difference = currentDateTime.difference(this);
final days = difference.inDays;
if (days >= 365) {
return "${(days / 365).floor()} ${appLocalizations.years}${appLocalizations.ago}";
}
if (days >= 30) {
return "${(days / 30).floor()} ${appLocalizations.months}${appLocalizations.ago}";
}
if (days >= 1) {
return "$days ${appLocalizations.days}${appLocalizations.ago}";
}
final hours = difference.inHours;
if (hours >= 1) {
return "$hours ${appLocalizations.hours}${appLocalizations.ago}";
}
final minutes = difference.inMinutes;
if (minutes >= 1) {
return "$minutes ${appLocalizations.minutes}${appLocalizations.ago}";
}
return appLocalizations.just;
}
String get show {
return toIso8601String().substring(0, 10);
}
}

View File

@@ -23,11 +23,10 @@ class Measure {
double? _bodyMediumHeight; double? _bodyMediumHeight;
double? _bodySmallHeight; double? _bodySmallHeight;
double? _labelSmallHeight; double? _labelSmallHeight;
double? _labelMediumHeight;
double? _titleLargeHeight; double? _titleLargeHeight;
double? _titleMediumHeight;
double get bodyMediumHeight {
double get bodyMediumHeight{
_bodyMediumHeight ??= computeTextSize( _bodyMediumHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -37,7 +36,7 @@ class Measure {
return _bodyMediumHeight!; return _bodyMediumHeight!;
} }
double get bodySmallHeight { double get bodySmallHeight{
_bodySmallHeight ??= computeTextSize( _bodySmallHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -47,7 +46,7 @@ class Measure {
return _bodySmallHeight!; return _bodySmallHeight!;
} }
double get labelSmallHeight { double get labelSmallHeight{
_labelSmallHeight ??= computeTextSize( _labelSmallHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -57,17 +56,7 @@ class Measure {
return _labelSmallHeight!; return _labelSmallHeight!;
} }
double get labelMediumHeight { double get titleLargeHeight{
_labelMediumHeight ??= computeTextSize(
Text(
"",
style: context.textTheme.labelMedium,
),
).height;
return _labelMediumHeight!;
}
double get titleLargeHeight {
_titleLargeHeight ??= computeTextSize( _titleLargeHeight ??= computeTextSize(
Text( Text(
"", "",
@@ -76,14 +65,4 @@ class Measure {
).height; ).height;
return _titleLargeHeight!; return _titleLargeHeight!;
} }
double get titleMediumHeight {
_titleMediumHeight ??= computeTextSize(
Text(
"",
style: context.textTheme.titleMedium,
),
).height;
return _titleMediumHeight!;
}
} }

View File

@@ -29,14 +29,6 @@ class Navigation {
label: "profiles", label: "profiles",
fragment: ProfilesFragment(), fragment: ProfilesFragment(),
), ),
const NavigationItem(
icon: Icon(Icons.swap_vert_circle),
label: "resources",
description: "resourcesDesc",
keep: false,
fragment: Resources(),
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
),
NavigationItem( NavigationItem(
icon: const Icon(Icons.adb), icon: const Icon(Icons.adb),
label: "logs", label: "logs",

View File

@@ -2,8 +2,6 @@ import 'dart:io';
import 'dart:isolate'; import 'dart:isolate';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:fl_clash/common/constant.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:zxing2/qrcode.dart'; import 'package:zxing2/qrcode.dart';
import 'package:image/image.dart' as img; import 'package:image/image.dart' as img;
@@ -180,12 +178,6 @@ class Other {
.where((item) => item.isNotEmpty) .where((item) => item.isNotEmpty)
.toList(); .toList();
} }
ViewMode getViewMode(double viewWidth){
if (viewWidth <= maxMobileWidth) return ViewMode.mobile;
if (viewWidth <= maxLaptopWidth) return ViewMode.laptop;
return ViewMode.desktop;
}
} }
final other = Other(); final other = Other();

View File

@@ -36,6 +36,11 @@ class AppPath {
final directory = await getProfilesPath(); final directory = await getProfilesPath();
return join(directory, "$id.yaml"); return join(directory, "$id.yaml");
} }
Future<String> getMMDBPath() async {
var directory = await applicationSupportDirectoryCompleter.future;
return join(directory.path, mmdbFileName);
}
} }
final appPath = AppPath(); final appPath = AppPath();

View File

@@ -3,9 +3,10 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:fl_clash/models/models.dart';
class Picker { class Picker {
Future<PlatformFile?> pickerConfigFile() async { Future<Result<PlatformFile>> pickerConfigFile() async {
FilePickerResult? filePickerResult; FilePickerResult? filePickerResult;
if (Platform.isAndroid) { if (Platform.isAndroid) {
filePickerResult = await FilePicker.platform.pickFiles( filePickerResult = await FilePicker.platform.pickFiles(
@@ -22,20 +23,20 @@ class Picker {
} }
final file = filePickerResult?.files.first; final file = filePickerResult?.files.first;
if (file == null) { if (file == null) {
return null; return Result.error(appLocalizations.pleaseUploadFile);
} }
return file; return Result.success(file);
} }
Future<String?> pickerConfigQRCode() async { Future<Result<String>> pickerConfigQRCode() async {
final xFile = await ImagePicker().pickImage(source: ImageSource.gallery); final xFile = await ImagePicker().pickImage(source: ImageSource.gallery);
final bytes = await xFile?.readAsBytes(); final bytes = await xFile?.readAsBytes();
if (bytes == null) return null; if (bytes == null) return Result.error();
final result = await other.parseQRCode(bytes); final result = await other.parseQRCode(bytes);
if (result == null || !result.isUrl) { if (result == null || !result.isUrl) {
throw appLocalizations.pleaseUploadValidQrcode; return Result.error(appLocalizations.pleaseUploadValidQrcode);
} }
return result; return Result.success(result);
} }
} }

View File

@@ -11,7 +11,7 @@ class ProxyManager {
_proxy = proxy ?? proxy_plugin.Proxy(); _proxy = proxy ?? proxy_plugin.Proxy();
} }
bool get isStart => startTime != null && startTime!.isBeforeNow; bool get isStart => startTime != null && startTime!.isBeforeNow();
DateTime? get startTime => _proxy.startTime; DateTime? get startTime => _proxy.startTime;

View File

@@ -1,105 +1,36 @@
import 'dart:io'; import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/ip.dart'; import 'package:http/http.dart';
import 'package:fl_clash/state.dart'; import '../models/models.dart';
class Request { class Request {
late final Dio _dio; static Future<Result<Response>> getFileResponseForUrl(String url) async {
int? _port; final headers = {'User-Agent': coreName};
bool _isStart = false; try {
final response = await get(Uri.parse(url), headers: headers).timeout(
Request() { httpTimeoutDuration,
_dio = Dio(
BaseOptions(
headers: {"User-Agent": coreName},
),
);
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
_syncProxy();
return handler.next(options); // 继续请求
},
));
}
_syncProxy() {
final port = globalState.appController.clashConfig.mixedPort;
final isStart = globalState.appController.appState.isStart;
if (_port != port || isStart != _isStart) {
_port = port;
_isStart = isStart;
_dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
if (!_isStart) return client;
client.findProxy = (url) {
return "PROXY localhost:$_port;DIRECT";
};
return client;
},
); );
return Result.success(response);
} catch (err) {
return Result.error(err.toString());
} }
} }
Future<Response> getFileResponseForUrl(String url) async { static Future<Result<Map<String,dynamic>>> checkForUpdate() async {
final response = await _dio final response = await get(
.get( Uri.parse(
url, "https://api.github.com/repos/$repository/releases/latest",
options: Options(
responseType: ResponseType.bytes,
),
)
.timeout(
httpTimeoutDuration * 2,
);
return response;
}
Future<Map<String, dynamic>?> checkForUpdate() async {
final response = await _dio.get(
"https://api.github.com/repos/$repository/releases/latest",
options: Options(
responseType: ResponseType.json,
), ),
); );
if (response.statusCode != 200) return null; if (response.statusCode != 200) return Result.error();
final data = response.data as Map<String, dynamic>; final body = json.decode(response.body) as Map<String,dynamic>;
final remoteVersion = data['tag_name']; final remoteVersion = body['tag_name'];
final packageInfo = await appPackage.packageInfoCompleter.future; final packageInfo = await appPackage.packageInfoCompleter.future;
final version = packageInfo.version; final version = packageInfo.version;
final hasUpdate = final hasUpdate =
other.compareVersions(remoteVersion.replaceAll('v', ''), version) > 0; other.compareVersions(remoteVersion.replaceAll('v', ''), version) > 0;
if (!hasUpdate) return null; if (!hasUpdate) return Result.error();
return data; return Result.success(body);
}
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
"https://ipwho.is/": IpInfo.fromIpwhoIsJson,
"https://api.ip.sb/geoip/": IpInfo.fromIpSbJson,
"https://ipapi.co/json/": IpInfo.fromIpApiCoJson,
"https://ipinfo.io/json/": IpInfo.fromIpInfoIoJson,
};
Future<IpInfo?> checkIp(CancelToken? cancelToken) async {
for (final source in _ipInfoSources.entries) {
try {
final response = await _dio
.get<Map<String, dynamic>>(source.key, cancelToken: cancelToken)
.timeout(
httpTimeoutDuration,
);
if (response.statusCode == 200 && response.data != null) {
return source.value(response.data!);
}
} catch (e) {
continue;
}
}
return null;
} }
} }
final request = Request();

View File

@@ -6,11 +6,6 @@ extension TextStyleExtension on TextStyle {
return copyWith(color: color?.toLight()); return copyWith(color: color?.toLight());
} }
toLighter() {
return copyWith(color: color?.toLighter());
}
toSoftBold() { toSoftBold() {
return copyWith(fontWeight: FontWeight.w500); return copyWith(fontWeight: FontWeight.w500);
} }

View File

@@ -1,12 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'clash/core.dart'; import 'clash/core.dart';
import 'enum/enum.dart';
import 'models/models.dart'; import 'models/models.dart';
import 'common/common.dart'; import 'common/common.dart';
@@ -40,6 +40,8 @@ class AppController {
updateRunTime, updateRunTime,
updateTraffic, updateTraffic,
]; ];
clearShowProxyDelay();
testShowProxyDelay();
} else { } else {
await globalState.stopSystemProxy(); await globalState.stopSystemProxy();
appState.traffics = []; appState.traffics = [];
@@ -76,10 +78,10 @@ class AppController {
); );
} }
addProfile(Profile profile) async { addProfile(Profile profile) {
config.setProfile(profile); config.setProfile(profile);
if (config.currentProfileId != null) return; if (config.currentProfileId != null) return;
await changeProfile(profile.id); changeProfile(profile.id);
} }
deleteProfile(String id) async { deleteProfile(String id) async {
@@ -97,17 +99,18 @@ class AppController {
} }
} }
Future<void> updateProfile(String id) async { updateProfile(String id) async {
final profile = config.getCurrentProfileForId(id); final profile = config.getCurrentProfileForId(id);
if (profile != null) { if (profile != null) {
final tempProfile = profile.copyWith(); final res = await profile.update();
await tempProfile.update(); if (res.type == ResultType.success) {
config.setProfile(tempProfile); config.setProfile(profile);
}
} }
} }
Future<void> updateClashConfig({bool isPatch = true}) async { Future<String> updateClashConfig({bool isPatch = true}) async {
await globalState.updateClashConfig( return await globalState.updateClashConfig(
clashConfig: clashConfig, clashConfig: clashConfig,
config: config, config: config,
isPatch: isPatch, isPatch: isPatch,
@@ -115,15 +118,24 @@ class AppController {
} }
Future applyProfile() async { Future applyProfile() async {
final commonScaffoldState = globalState.homeScaffoldKey.currentState; await globalState.applyProfile(
if (commonScaffoldState?.mounted != true) return; appState: appState,
commonScaffoldState?.loadingRun(() async { config: config,
await globalState.applyProfile( clashConfig: clashConfig,
appState: appState, );
config: config, }
clashConfig: clashConfig,
); Function? _changeProfileDebounce;
changeProfileDebounce(String? profileId) {
if (profileId == config.currentProfileId) return;
config.currentProfileId = profileId;
_changeProfileDebounce ??= debounce<Function(String?)>((profileId) async {
await applyProfile();
appState.delayMap = {};
saveConfigPreferences();
}); });
_changeProfileDebounce!([profileId]);
} }
changeProfile(String? value) async { changeProfile(String? value) async {
@@ -136,25 +148,14 @@ class AppController {
autoUpdateProfiles() async { autoUpdateProfiles() async {
for (final profile in config.profiles) { for (final profile in config.profiles) {
if (!profile.autoUpdate) continue; if (!profile.autoUpdate) return;
final isNotNeedUpdate = profile.lastUpdateDate final isNotNeedUpdate = profile.lastUpdateDate
?.add( ?.add(
profile.autoUpdateDuration, profile.autoUpdateDuration,
) )
.isBeforeNow; .isBeforeNow();
if (isNotNeedUpdate == false || profile.type == ProfileType.file) { if (isNotNeedUpdate == false) continue;
continue; await profile.update();
}
await updateProfile(profile.id);
}
}
updateProfiles() async {
for (final profile in config.profiles) {
if (profile.type == ProfileType.file) {
continue;
}
await updateProfile(profile.id);
} }
} }
@@ -210,17 +211,18 @@ class AppController {
autoCheckUpdate() async { autoCheckUpdate() async {
if (!config.autoCheckUpdate) return; if (!config.autoCheckUpdate) return;
final res = await request.checkForUpdate(); final res = await Request.checkForUpdate();
checkUpdateResultHandle(data: res); checkUpdateResultHandle(result: res);
} }
checkUpdateResultHandle({ checkUpdateResultHandle({
Map<String, dynamic>? data, Result<Map<String, dynamic>>? result,
bool handleError = false, bool handleError = false
}) async { }) async {
if (data != null) { if (result == null) return;
final tagName = data['tag_name']; if (result.type == ResultType.success) {
final body = data['body']; final tagName = result.data?['tag_name'];
final body = result.data?['body'];
final submits = other.parseReleaseBody(body); final submits = other.parseReleaseBody(body);
globalState.showMessage( globalState.showMessage(
title: appLocalizations.discoverNewVersion, title: appLocalizations.discoverNewVersion,
@@ -246,7 +248,7 @@ class AppController {
}, },
confirmText: appLocalizations.goDownload, confirmText: appLocalizations.goDownload,
); );
} else if (handleError) { } else if(handleError){
globalState.showMessage( globalState.showMessage(
title: appLocalizations.checkUpdate, title: appLocalizations.checkUpdate,
message: TextSpan( message: TextSpan(
@@ -271,14 +273,28 @@ class AppController {
autoCheckUpdate(); autoCheckUpdate();
} }
healthcheck() {
if (globalState.healthcheckLock) return;
for (final delay in appState.delayMap.entries) {
setDelay(
Delay(
name: delay.key,
value: 0,
),
);
}
clashCore.healthcheck();
}
setDelay(Delay delay) { setDelay(Delay delay) {
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}) {
if (index > appState.currentNavigationItems.length - 1) {
return;
}
appState.currentLabel = appState.currentNavigationItems[index].label; appState.currentLabel = appState.currentNavigationItems[index].label;
if ((config.isAnimateToPage || hasAnimate)) { if ((config.isAnimateToPage || hasAnimate)) {
globalState.pageController?.animateToPage( globalState.pageController?.animateToPage(
@@ -334,55 +350,94 @@ class AppController {
toProfiles(); toProfiles();
final commonScaffoldState = globalState.homeScaffoldKey.currentState; final commonScaffoldState = globalState.homeScaffoldKey.currentState;
if (commonScaffoldState?.mounted != true) return; if (commonScaffoldState?.mounted != true) return;
final profile = await commonScaffoldState?.loadingRun<Profile>( commonScaffoldState?.loadingRun(
() async { () async {
await Future.delayed(const Duration(milliseconds: 300));
final profile = Profile( final profile = Profile(
url: url, url: url,
); );
await profile.update(); final res = await profile.update();
return profile; if (res.type == ResultType.success) {
addProfile(profile);
} else {
debugPrint(res.message);
globalState.showMessage(
title: "${appLocalizations.add}${appLocalizations.profile}",
message: TextSpan(text: res.message!),
);
}
}, },
title: "${appLocalizations.add}${appLocalizations.profile}",
); );
if (profile != null) {
await addProfile(profile);
}
} }
addProfileFormFile() async { addProfileFormFile() async {
final platformFile = await globalState.safeRun(picker.pickerConfigFile); final result = await picker.pickerConfigFile();
if (result.type == ResultType.error) return;
if (!context.mounted) return; if (!context.mounted) return;
globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst); globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);
toProfiles(); toProfiles();
final commonScaffoldState = globalState.homeScaffoldKey.currentState; final commonScaffoldState = globalState.homeScaffoldKey.currentState;
if (commonScaffoldState?.mounted != true) return; if (commonScaffoldState?.mounted != true) return;
final profile = await commonScaffoldState?.loadingRun<Profile?>( commonScaffoldState?.loadingRun(
() async { () async {
await Future.delayed(const Duration(milliseconds: 300)); await Future.delayed(const Duration(milliseconds: 300));
final bytes = platformFile?.bytes; final bytes = result.data?.bytes;
if (bytes == null) { if (bytes == null) {
return null; return;
} }
final profile = Profile(label: platformFile?.name); final profile = Profile(label: result.data?.name);
await profile.saveFile(bytes); final sRes = await profile.saveFile(bytes);
return profile; if (sRes.type == ResultType.error) {
debugPrint(sRes.message);
globalState.showMessage(
title: "${appLocalizations.add}${appLocalizations.profile}",
message: TextSpan(text: sRes.message!),
);
return;
}
addProfile(profile);
}, },
title: "${appLocalizations.add}${appLocalizations.profile}",
); );
if (profile != null) {
await addProfile(profile);
}
} }
addProfileFormQrCode() async { addProfileFormQrCode() async {
final url = await globalState.safeRun(picker.pickerConfigQRCode); final result = await picker.pickerConfigQRCode();
if (url == null) return; if (result.type == ResultType.error) {
addProfileFormURL(url); if (result.message != null) {
globalState.showMessage(
title: appLocalizations.tip,
message: TextSpan(
text: result.message,
),
);
}
return;
}
addProfileFormURL(result.data!);
} }
updateViewWidth(double width) { clearShowProxyDelay() {
WidgetsBinding.instance.addPostFrameCallback((_) { final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
appState.viewWidth = width; if (showProxyDelay != null) {
}); appState.setDelay(
Delay(name: showProxyDelay, value: null),
);
}
}
testShowProxyDelay() {
final showProxyDelay = appState.getRealProxyName(appState.showProxyName);
if (showProxyDelay != null) {
globalState.updateCurrentDelay(showProxyDelay);
}
}
updateViewWidth() {
appState.viewWidth = context.width;
if (appState.viewWidth == 0) {
Future.delayed(moreDuration, () {
updateViewWidth();
});
}
} }
} }

View File

@@ -61,4 +61,4 @@ enum MessageType { log, tun, delay, process, now }
enum RecoveryOption { enum RecoveryOption {
all, all,
onlyProfiles, onlyProfiles,
} }

View File

@@ -1,4 +1,5 @@
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
@@ -10,13 +11,13 @@ class AboutFragment extends StatelessWidget {
_checkUpdate(BuildContext context) async { _checkUpdate(BuildContext context) async {
final commonScaffoldState = context.commonScaffoldState; final commonScaffoldState = context.commonScaffoldState;
if (commonScaffoldState?.mounted != true) return; if (commonScaffoldState?.mounted != true) return;
final data = final res =
await commonScaffoldState?.loadingRun<Map<String, dynamic>?>( await commonScaffoldState?.loadingRun<Result<Map<String, dynamic>>>(
request.checkForUpdate, Request.checkForUpdate,
title: appLocalizations.checkUpdate, title: appLocalizations.checkUpdate,
); );
globalState.appController.checkUpdateResultHandle( globalState.appController.checkUpdateResultHandle(
data: data, result: res,
handleError: true, handleError: true,
); );
} }

View File

@@ -219,25 +219,6 @@ class _AccessFragmentState extends State<AccessFragment> {
return ValueListenableBuilder( return ValueListenableBuilder(
valueListenable: packagesListenable, valueListenable: packagesListenable,
builder: (_, packages, ___) { builder: (_, packages, ___) {
final accessControl = globalState.appController.config.accessControl;
final acceptList = accessControl.acceptList;
final rejectList = accessControl.rejectList;
final acceptPackages = packages.sorted((a, b) {
final isSelectA = acceptList.contains(a.packageName);
final isSelectB = acceptList.contains(b.packageName);
if (isSelectA && isSelectB) return 0;
if (isSelectA) return -1;
if (isSelectB) return 1;
return 0;
});
final rejectPackages = packages.sorted((a, b) {
final isSelectA = rejectList.contains(a.packageName);
final isSelectB = rejectList.contains(b.packageName);
if (isSelectA && isSelectB) return 0;
if (isSelectA) return -1;
if (isSelectB) return 1;
return 0;
});
return Selector<Config, PackageListSelectorState>( return Selector<Config, PackageListSelectorState>(
selector: (_, config) => PackageListSelectorState( selector: (_, config) => PackageListSelectorState(
accessControl: config.accessControl, accessControl: config.accessControl,
@@ -247,22 +228,18 @@ class _AccessFragmentState extends State<AccessFragment> {
final accessControl = state.accessControl; final accessControl = state.accessControl;
final isAccessControl = state.isAccessControl; final isAccessControl = state.isAccessControl;
final isFilterSystemApp = accessControl.isFilterSystemApp; final isFilterSystemApp = accessControl.isFilterSystemApp;
final currentPackages = isFilterSystemApp
? packages
.where((element) => element.isSystem == false)
.toList()
: packages;
final packageNameList =
currentPackages.map((e) => e.packageName).toList();
final accessControlMode = accessControl.mode; final accessControlMode = accessControl.mode;
final packages =
accessControlMode == AccessControlMode.acceptSelected
? acceptPackages
: rejectPackages;
final currentList = final currentList =
accessControlMode == AccessControlMode.acceptSelected accessControlMode == AccessControlMode.acceptSelected
? accessControl.acceptList ? accessControl.acceptList
: accessControl.rejectList; : accessControl.rejectList;
final currentPackages = isFilterSystemApp
? packages
.where((element) => element.isSystem == false)
.toList()
: packages;
final packageNameList =
currentPackages.map((e) => e.packageName).toList();
final valueList = currentList.intersection(packageNameList); final valueList = currentList.intersection(packageNameList);
final describe = final describe =
accessControlMode == AccessControlMode.acceptSelected accessControlMode == AccessControlMode.acceptSelected

View File

@@ -86,25 +86,25 @@ class _ConfigFragmentState extends State<ConfigFragment> {
); );
}, },
), ),
// if (system.isDesktop) if (system.isDesktop)
// Selector<ClashConfig, bool>( Selector<ClashConfig, bool>(
// selector: (_, clashConfig) => clashConfig.tun.enable, selector: (_, clashConfig) => clashConfig.tun.enable,
// builder: (_, tunEnable, __) { builder: (_, tunEnable, __) {
// return ListItem.switchItem( return ListItem.switchItem(
// leading: const Icon(Icons.support), leading: const Icon(Icons.support),
// title: Text(appLocalizations.tun), title: Text(appLocalizations.tun),
// subtitle: Text(appLocalizations.tunDesc), subtitle: Text(appLocalizations.tunDesc),
// delegate: SwitchDelegate( delegate: SwitchDelegate(
// value: tunEnable, value: tunEnable,
// onChanged: (bool value) async { onChanged: (bool value) async {
// final clashConfig = context.read<ClashConfig>(); final clashConfig = context.read<ClashConfig>();
// clashConfig.tun = Tun(enable: value); clashConfig.tun = Tun(enable: value);
// globalState.appController.updateClashConfigDebounce(); globalState.appController.updateClashConfigDebounce();
// }, },
// ), ),
// ); );
// }, },
// ), ),
Selector<Config, bool>( Selector<Config, bool>(
selector: (_, config) => config.isCompatible, selector: (_, config) => config.isCompatible,
builder: (_, isCompatible, __) { builder: (_, isCompatible, __) {

View File

@@ -1,5 +1,3 @@
import 'package:country_flags/country_flags.dart';
import 'package:dio/dio.dart';
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:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
@@ -15,160 +13,118 @@ class NetworkDetection extends StatefulWidget {
} }
class _NetworkDetectionState extends State<NetworkDetection> { class _NetworkDetectionState extends State<NetworkDetection> {
final ipInfoNotifier = ValueNotifier<IpInfo?>(null); Widget _buildDescription(String? currentProxyName, int? delay) {
final timeoutNotifier = ValueNotifier<bool>(false); if (currentProxyName == null) {
bool? _preIsStart; return TooltipText(
CancelToken? cancelToken; text: Text(
appLocalizations.noProxyDesc,
_checkIp( style: Theme.of(context).textTheme.titleMedium?.copyWith(
bool isInit, color: Theme.of(context).colorScheme.secondary,
bool isStart, ),
) async { overflow: TextOverflow.ellipsis,
if (!isInit) return; ),
if (_preIsStart == false && _preIsStart == isStart) return; );
if (cancelToken != null) {
cancelToken!.cancel();
cancelToken = null;
} }
ipInfoNotifier.value = null; if (delay == 0 || delay == null) {
final ipInfo = await request.checkIp(cancelToken); return const AspectRatio(
if (ipInfo == null) { aspectRatio: 1,
timeoutNotifier.value = true; child: CircularProgressIndicator(
return; strokeCap: StrokeCap.round,
} else { ),
timeoutNotifier.value = false; );
} }
_preIsStart = isStart; if (delay > 0) {
ipInfoNotifier.value = ipInfo; return Row(
} mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
_checkIpContainer(Widget child) { children: [
return Selector2<AppState, Config, CheckIpSelectorState>( TooltipText(
selector: (_, appState, config) { text: Text(
return CheckIpSelectorState( "$delay",
isInit: appState.isInit, overflow: TextOverflow.ellipsis,
selectedMap: appState.selectedMap, maxLines: 1,
isStart: appState.isStart, style: context.textTheme.titleLarge
); ?.copyWith(
}, color: context.colorScheme.primary,
builder: (_, state, __) { )
_checkIp(state.isInit, state.isStart); .toSoftBold(),
return child; ),
}, ),
child: child, const Flexible(
child: SizedBox(
width: 4,
),
),
Flexible(
flex: 0,
child: Text(
'ms',
style: Theme.of(context).textTheme.bodyMedium?.toLight(),
),
),
],
);
}
return Text(
"Timeout",
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Colors.red,
),
); );
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _checkIpContainer( return CommonCard(
ValueListenableBuilder<IpInfo?>( info: Info(
valueListenable: ipInfoNotifier, iconData: Icons.network_check,
builder: (_, ipInfo, __) { label: appLocalizations.networkDetection,
return CommonCard( ),
child: Selector<AppState, NetworkDetectionSelectorState>(
selector: (_, appState) {
return NetworkDetectionSelectorState(
currentProxyName: appState.showProxyName,
delay: appState.getDelay(
appState.showProxyName,
),
);
},
builder: (_, state, __) {
return Container(
padding: const EdgeInsets.all(16).copyWith(top: 0),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Flexible( Flexible(
flex: 0, flex: 0,
child: Container( child: TooltipText(
padding: const EdgeInsets.all(16), text: Text(
child: Row( state.currentProxyName ?? appLocalizations.noProxy,
children: [ overflow: TextOverflow.ellipsis,
Icon( maxLines: 1,
Icons.network_check, style: Theme.of(context)
color: Theme.of(context).colorScheme.primary, .textTheme
), .titleMedium
const SizedBox( ?.toSoftBold(),
width: 8,
),
Flexible(
flex: 1,
child: FadeBox(
child: ipInfo != null
? CountryFlag.fromCountryCode(
ipInfo.countryCode,
width: 24,
height: 24,
)
: ValueListenableBuilder(
valueListenable: timeoutNotifier,
builder: (_, timeout, __) {
if (timeout) {
return Text(
appLocalizations.checkError,
style: Theme.of(context)
.textTheme
.titleMedium,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
return TooltipText(
text: Text(
appLocalizations.checking,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.titleMedium,
),
);
},
),
),
),
],
), ),
), ),
), ),
Container( const SizedBox(
height: height: 8,
globalState.appController.measure.titleLargeHeight + 24, ),
alignment: Alignment.centerLeft, Flexible(
padding: const EdgeInsets.all(16).copyWith(top: 0), child: Container(
child: FadeBox( height: globalState.appController.measure.titleLargeHeight,
child: ipInfo != null alignment: Alignment.centerLeft,
? Column( child: FadeBox(
crossAxisAlignment: CrossAxisAlignment.start, child: _buildDescription(
mainAxisSize: MainAxisSize.min, state.currentProxyName,
children: [ state.delay,
Flexible( ),
flex: 1, ),
child: TooltipText(
text: Text(
ipInfo.ip,
style: context.textTheme.titleLarge
?.toSoftBold(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
],
)
: ValueListenableBuilder(
valueListenable: timeoutNotifier,
builder: (_, timeout, __) {
if (timeout) {
return Text(
appLocalizations.ipCheckTimeout,
style: context.textTheme.titleLarge
?.toSoftBold(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}
return Container(
padding: const EdgeInsets.all(2),
child: const AspectRatio(
aspectRatio: 1,
child: CircularProgressIndicator(),
),
);
},
),
), ),
) ),
], ],
), ),
); );

View File

@@ -19,7 +19,7 @@ class _StartButtonState extends State<StartButton>
@override @override
void initState() { void initState() {
isStart = globalState.appController.appState.isStart; isStart = context.read<AppState>().runTime != null;
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
value: isStart ? 1 : 0, value: isStart ? 1 : 0,
@@ -48,11 +48,9 @@ class _StartButtonState extends State<StartButton>
} }
} }
updateSystemProxy() { updateSystemProxy() async {
WidgetsBinding.instance.addPostFrameCallback((_) async { final appController = globalState.appController;
final appController = globalState.appController; await appController.updateSystemProxy(isStart);
await appController.updateSystemProxy(isStart);
});
} }
@override @override

View File

@@ -8,5 +8,4 @@ export 'access.dart';
export 'config.dart'; export 'config.dart';
export 'application_setting.dart'; export 'application_setting.dart';
export 'about.dart'; export 'about.dart';
export 'backup_and_recovery.dart'; export 'backup_and_recovery.dart';
export 'resources.dart';

View File

@@ -1,5 +1,4 @@
import 'dart:async'; import 'package:collection/collection.dart';
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/enum/enum.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
@@ -8,40 +7,10 @@ import 'package:provider/provider.dart';
import '../models/models.dart'; import '../models/models.dart';
import '../widgets/widgets.dart'; import '../widgets/widgets.dart';
class LogsFragment extends StatefulWidget { class LogsFragment extends StatelessWidget {
const LogsFragment({super.key}); const LogsFragment({super.key});
@override _initActions(BuildContext context) {
State<LogsFragment> createState() => _LogsFragmentState();
}
class _LogsFragmentState extends State<LogsFragment> {
final logsNotifier = ValueNotifier<List<Log>>([]);
Timer? timer;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
logsNotifier.value = context.read<AppState>().logs;
if (timer != null) {
timer?.cancel();
timer = null;
}
timer = Timer.periodic(const Duration(milliseconds: 200), (timer) {
logsNotifier.value = globalState.appController.appState.logs;
});
});
}
@override
void dispose() {
super.dispose();
timer?.cancel();
timer = null;
}
_initActions() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
final commonScaffoldState = final commonScaffoldState =
context.findAncestorStateOfType<CommonScaffoldState>(); context.findAncestorStateOfType<CommonScaffoldState>();
@@ -51,22 +20,21 @@ class _LogsFragmentState extends State<LogsFragment> {
showSearch( showSearch(
context: context, context: context,
delegate: LogsSearchDelegate( delegate: LogsSearchDelegate(
logs: logsNotifier.value.reversed.toList(), logs: globalState.appController.appState.logs.reversed.toList(),
), ),
); );
}, },
icon: const Icon(Icons.search), icon: const Icon(Icons.search),
),
const SizedBox(
width: 8,
) )
]; ];
}); });
} }
_buildList() { _buildList() {
return ValueListenableBuilder<List<Log>>( return Selector<AppState, List<Log>>(
valueListenable: logsNotifier, selector: (_, appState) => appState.logs,
shouldRebuild: (prev, next) =>
!const ListEquality<Log>().equals(prev, next),
builder: (_, List<Log> logs, __) { builder: (_, List<Log> logs, __) {
if (logs.isEmpty) { if (logs.isEmpty) {
return NullStatus( return NullStatus(
@@ -80,6 +48,7 @@ class _LogsFragmentState extends State<LogsFragment> {
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final log = logs[index]; final log = logs[index];
return LogItem( return LogItem(
key: ValueKey(log.dateTime),
log: log, log: log,
); );
}, },
@@ -96,13 +65,14 @@ class _LogsFragmentState extends State<LogsFragment> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<AppState, bool?>( return Selector<AppState, bool?>(
selector: (_, appState) => selector: (_, appState) {
appState.currentLabel == 'logs' || return appState.currentLabel == 'logs' ||
appState.viewMode == ViewMode.mobile && appState.viewMode == ViewMode.mobile &&
appState.currentLabel == "tools", appState.currentLabel == "tools";
},
builder: (_, isCurrent, child) { builder: (_, isCurrent, child) {
if (isCurrent == null || isCurrent) { if (isCurrent == null || isCurrent) {
_initActions(); _initActions(context);
} }
return child!; return child!;
}, },
@@ -142,9 +112,6 @@ class LogsSearchDelegate extends SearchDelegate {
}, },
icon: const Icon(Icons.clear), icon: const Icon(Icons.clear),
), ),
const SizedBox(
width: 8,
)
]; ];
} }

View File

@@ -1,5 +1,4 @@
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/state.dart';
import 'package:fl_clash/widgets/widgets.dart'; import 'package:fl_clash/widgets/widgets.dart';
@@ -41,7 +40,7 @@ class _EditProfileState extends State<EditProfile> {
_handleConfirm() { _handleConfirm() {
if (!_formKey.currentState!.validate()) return; if (!_formKey.currentState!.validate()) return;
final config = widget.context.read<Config>(); final config = widget.context.read<Config>();
final hasUpdate = urlController.text.isNotEmpty && widget.profile.url != urlController.text; final hasUpdate = widget.profile.url != urlController.text;
widget.profile.url = urlController.text; widget.profile.url = urlController.text;
widget.profile.label = labelController.text; widget.profile.label = labelController.text;
widget.profile.autoUpdate = autoUpdate; widget.profile.autoUpdate = autoUpdate;
@@ -83,7 +82,7 @@ class _EditProfileState extends State<EditProfile> {
}, },
), ),
), ),
if (widget.profile.type == ProfileType.url)...[ if (widget.profile.url != null)...[
ListItem( ListItem(
title: TextFormField( title: TextFormField(
controller: urlController, controller: urlController,

View File

@@ -17,9 +17,25 @@ enum ProfileActions {
delete, delete,
} }
class ProfilesFragment extends StatelessWidget { class ProfilesFragment extends StatefulWidget {
const ProfilesFragment({super.key}); const ProfilesFragment({super.key});
@override
State<ProfilesFragment> createState() => _ProfilesFragmentState();
}
class _ProfilesFragmentState extends State<ProfilesFragment> {
_handleDeleteProfile(String id) async {
globalState.appController.deleteProfile(id);
}
_handleUpdateProfile(String id) async {
context.findAncestorStateOfType<CommonScaffoldState>()?.loadingRun(
() => globalState.appController.updateProfile(id),
);
}
_handleShowAddExtendPage() { _handleShowAddExtendPage() {
showExtendPage( showExtendPage(
globalState.navigatorKey.currentState!.context, globalState.navigatorKey.currentState!.context,
@@ -30,6 +46,76 @@ class ProfilesFragment extends StatelessWidget {
); );
} }
_handleShowEditExtendPage(Profile profile) {
showExtendPage(
context,
body: EditProfile(
profile: profile.copyWith(),
context: context,
),
title: "${appLocalizations.edit}${appLocalizations.profile}",
);
}
_buildGrid({
required ProfilesSelectorState state,
int crossAxisCount = 1,
}) {
return SingleChildScrollView(
padding: crossAxisCount > 1
? const EdgeInsets.symmetric(horizontal: 16)
: EdgeInsets.zero,
child: Grid.baseGap(
crossAxisCount: crossAxisCount,
children: [
for (final profile in state.profiles)
GridItem(
child: ProfileItem(
profile: profile,
commonPopupMenu: CommonPopupMenu<ProfileActions>(
items: [
CommonPopupMenuItem(
action: ProfileActions.edit,
label: appLocalizations.edit,
iconData: Icons.edit,
),
if (profile.url != null)
CommonPopupMenuItem(
action: ProfileActions.update,
label: appLocalizations.update,
iconData: Icons.sync,
),
CommonPopupMenuItem(
action: ProfileActions.delete,
label: appLocalizations.delete,
iconData: Icons.delete,
),
],
onSelected: (ProfileActions? action) async {
switch (action) {
case ProfileActions.edit:
_handleShowEditExtendPage(profile);
break;
case ProfileActions.delete:
_handleDeleteProfile(profile.id);
break;
case ProfileActions.update:
_handleUpdateProfile(profile.id);
break;
case null:
break;
}
},
),
groupValue: state.currentProfileId,
onChanged: globalState.appController.changeProfile,
),
),
],
),
);
}
_getColumns(ViewMode viewMode) { _getColumns(ViewMode viewMode) {
switch (viewMode) { switch (viewMode) {
case ViewMode.mobile: case ViewMode.mobile:
@@ -41,85 +127,33 @@ class ProfilesFragment extends StatelessWidget {
} }
} }
_initScaffoldState(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback(
(_) {
final commonScaffoldState =
context.findAncestorStateOfType<CommonScaffoldState>();
commonScaffoldState?.actions = [
IconButton(
onPressed: () {
commonScaffoldState.loadingRun<void>(
() async {
await globalState.appController.updateProfiles();
},
);
},
icon: const Icon(Icons.download),
),
const SizedBox(
width: 8,
)
];
commonScaffoldState?.floatingActionButton = FloatingActionButton(
heroTag: null,
onPressed: _handleShowAddExtendPage,
child: const Icon(
Icons.add,
),
);
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<AppState, bool>( return FloatLayout(
selector: (_, appState) => appState.currentLabel == 'profiles', floatingWidget: Container(
builder: (_, isCurrent, child) { margin: const EdgeInsets.all(kFloatingActionButtonMargin),
if (isCurrent) { child: FloatingActionButton(
_initScaffoldState(context); heroTag: null,
} onPressed: _handleShowAddExtendPage,
return child!; child: const Icon(Icons.add),
}, ),
),
child: Selector2<AppState, Config, ProfilesSelectorState>( child: Selector2<AppState, Config, ProfilesSelectorState>(
selector: (_, appState, config) => ProfilesSelectorState( selector: (_, appState, config) => ProfilesSelectorState(
profiles: config.profiles, profiles: config.profiles,
currentProfileId: config.currentProfileId, currentProfileId: config.currentProfileId,
viewMode: appState.viewMode, viewMode: appState.viewMode),
),
builder: (context, state, child) { builder: (context, state, child) {
if (state.profiles.isEmpty) { if (state.profiles.isEmpty) {
return NullStatus( return NullStatus(
label: appLocalizations.nullProfileDesc, label: appLocalizations.nullProfileDesc,
); );
} }
final columns = _getColumns(state.viewMode);
final isMobile = state.viewMode == ViewMode.mobile;
return Align( return Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
child: SingleChildScrollView( child: _buildGrid(
padding: !isMobile state: state,
? const EdgeInsets.symmetric( crossAxisCount: _getColumns(state.viewMode),
horizontal: 16,
vertical: 16,
)
: EdgeInsets.zero,
child: Grid(
mainAxisSpacing: isMobile ? 8 : 16,
crossAxisSpacing: 16,
crossAxisCount: columns,
children: [
for (final profile in state.profiles)
GridItem(
child: ProfileItem(
profile: profile,
groupValue: state.currentProfileId,
onChanged: globalState.appController.changeProfile,
),
),
],
),
), ),
); );
}, },
@@ -128,188 +162,118 @@ class ProfilesFragment extends StatelessWidget {
} }
} }
class ProfileItem extends StatefulWidget { class ProfileItem extends StatelessWidget {
final Profile profile; final Profile profile;
final String? groupValue; final String? groupValue;
final CommonPopupMenu commonPopupMenu;
final void Function(String? value) onChanged; final void Function(String? value) onChanged;
const ProfileItem({ const ProfileItem({
super.key, super.key,
required this.profile, required this.profile,
required this.commonPopupMenu,
required this.groupValue, required this.groupValue,
required this.onChanged, required this.onChanged,
}); });
String _getLastUpdateTimeDifference(DateTime lastDateTime) {
final currentDateTime = DateTime.now();
final difference = currentDateTime.difference(lastDateTime);
final days = difference.inDays;
if (days >= 365) {
return "${(days / 365).floor()} ${appLocalizations.years}${appLocalizations.ago}";
}
if (days >= 30) {
return "${(days / 30).floor()} ${appLocalizations.months}${appLocalizations.ago}";
}
if (days >= 1) {
return "$days ${appLocalizations.days}${appLocalizations.ago}";
}
final hours = difference.inHours;
if (hours >= 1) {
return "$hours ${appLocalizations.hours}${appLocalizations.ago}";
}
final minutes = difference.inMinutes;
if (minutes >= 1) {
return "$minutes ${appLocalizations.minutes}${appLocalizations.ago}";
}
return appLocalizations.just;
}
@override @override
State<ProfileItem> createState() => _ProfileItemState(); Widget build(BuildContext context) {
} String useShow;
String totalShow;
class _ProfileItemState extends State<ProfileItem> { double progress;
final isUpdating = ValueNotifier<bool>(false); final userInfo = profile.userInfo;
if (userInfo == null) {
_handleDeleteProfile(String id) async { useShow = "Infinite";
globalState.appController.deleteProfile(id); totalShow = "Infinite";
} progress = 1;
} else {
_handleUpdateProfile(String id) async { final use = userInfo.upload + userInfo.download;
isUpdating.value = true; final total = userInfo.total;
await globalState.safeRun<void>(() async { useShow = TrafficValue(value: use).show;
await globalState.appController.updateProfile(id); totalShow = TrafficValue(value: total).show;
}); progress = total == 0 ? 0.0 : use / total;
isUpdating.value = false; }
} return ListItem.radio(
horizontalTitleGap: 16,
_handleShowEditExtendPage( delegate: RadioDelegate<String?>(
Profile profile, value: profile.id,
) { groupValue: groupValue,
showExtendPage( onChanged: onChanged,
context,
body: EditProfile(
profile: profile.copyWith(),
context: context,
), ),
title: "${appLocalizations.edit}${appLocalizations.profile}", padding: const EdgeInsets.symmetric(horizontal: 16),
); trailing: commonPopupMenu,
} title: Column(
mainAxisSize: MainAxisSize.min,
_buildTitle(Profile profile) {
final textTheme = context.textTheme;
final userInfo = profile.userInfo ?? UserInfo();
final use = userInfo.upload + userInfo.download;
final total = userInfo.total;
final useShow = TrafficValue(value: use).show;
final totalShow = TrafficValue(value: total).show;
final progress = total == 0 ? 0.0 : use / total;
final expireShow = userInfo.expire == 0
? "长期有效"
: DateTime.fromMillisecondsSinceEpoch(userInfo.expire * 1000).show;
return Container(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Row( Flexible(
mainAxisSize: MainAxisSize.max, child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.max,
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Text( children: [
profile.label ?? profile.id, Flexible(
style: textTheme.titleMedium, child: Text(
maxLines: 1, profile.label ?? profile.id,
overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.titleMedium,
), maxLines: 1,
Text( overflow: TextOverflow.ellipsis,
profile.lastUpdateDate?.lastUpdateTimeDesc ?? '', ),
style: textTheme.labelMedium?.toLight(), ),
), Flexible(
], child: Text(
profile.lastUpdateDate != null
? _getLastUpdateTimeDifference(profile.lastUpdateDate!)
: '',
style: Theme.of(context).textTheme.labelMedium?.toLight(),
),
),
],
),
), ),
Column( Flexible(
mainAxisSize: MainAxisSize.min, child: Container(
crossAxisAlignment: CrossAxisAlignment.start, margin: const EdgeInsets.symmetric(
mainAxisAlignment: MainAxisAlignment.center, vertical: 8,
children: [
Container(
margin: const EdgeInsets.symmetric(
vertical: 8,
),
child: LinearProgressIndicator(
minHeight: 6,
value: progress,
),
), ),
Text( child: LinearProgressIndicator(
"$useShow / $totalShow", minHeight: 6,
style: textTheme.labelMedium?.toLight(), value: progress,
), ),
const SizedBox( ),
height: 2, ),
), Flexible(
Row( child: Text(
children: [ "$useShow / $totalShow",
Text( style: Theme.of(context).textTheme.labelMedium?.toLight(),
"到期时间:", ),
style: textTheme.labelMedium?.toLighter(),
),
const SizedBox(
width: 4,
),
Text(
expireShow,
style: textTheme.labelMedium?.toLighter(),
),
],
)
],
), ),
], ],
), ),
); );
} }
@override
Widget build(BuildContext context) {
final profile = widget.profile;
final groupValue = widget.groupValue;
final onChanged = widget.onChanged;
return Selector<AppState, ViewMode>(
selector: (_, appState) => appState.viewMode,
builder: (_, viewMode, child) {
if (viewMode == ViewMode.mobile) {
return child!;
}
return CommonCard(
child: child!,
);
},
child: ListItem.radio(
key: Key(profile.id),
horizontalTitleGap: 16,
delegate: RadioDelegate<String?>(
value: profile.id,
groupValue: groupValue,
onChanged: onChanged,
),
padding: const EdgeInsets.symmetric(horizontal: 16),
trailing: CommonPopupMenu<ProfileActions>(
items: [
CommonPopupMenuItem(
action: ProfileActions.edit,
label: appLocalizations.edit,
iconData: Icons.edit,
),
if (profile.type == ProfileType.url)
CommonPopupMenuItem(
action: ProfileActions.update,
label: appLocalizations.update,
iconData: Icons.sync,
),
CommonPopupMenuItem(
action: ProfileActions.delete,
label: appLocalizations.delete,
iconData: Icons.delete,
),
],
onSelected: (ProfileActions? action) async {
switch (action) {
case ProfileActions.edit:
_handleShowEditExtendPage(profile);
break;
case ProfileActions.delete:
_handleDeleteProfile(profile.id);
break;
case ProfileActions.update:
_handleUpdateProfile(profile.id);
break;
case null:
break;
}
},
),
title: _buildTitle(profile),
tileTitleAlignment: ListTileTitleAlignment.titleHeight,
),
);
}
} }

View File

@@ -1,4 +1,3 @@
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/state.dart'; import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -51,9 +50,6 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
selectedValue: proxiesSortType, selectedValue: proxiesSortType,
); );
}, },
),
const SizedBox(
width: 8,
) )
]; ];
}); });
@@ -61,69 +57,72 @@ class _ProxiesFragmentState extends State<ProxiesFragment>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<AppState, bool>( return DelayTestButtonContainer(
selector: (_, appState) => appState.currentLabel == 'proxies', child: Selector<AppState, bool>(
builder: (_, isCurrent, child) { selector: (_, appState) => appState.currentLabel == 'proxies',
if (isCurrent) { builder: (_, isCurrent, child) {
_initActions(); if (isCurrent) {
} _initActions();
return child!;
},
child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>(
selector: (_, appState, config, clashConfig) {
final currentGroups = appState.currentGroups;
final groupNames = currentGroups.map((e) => e.name).toList();
return ProxiesSelectorState(
groupNames: groupNames,
);
},
shouldRebuild: (prev, next) {
if (prev.groupNames.length != next.groupNames.length) {
_tabController?.dispose();
_tabController = null;
} }
return prev != next; return child!;
}, },
builder: (_, state, __) { child: Selector3<AppState, Config, ClashConfig, ProxiesSelectorState>(
_tabController ??= TabController( selector: (_, appState, config, clashConfig) {
length: state.groupNames.length, final currentGroups = appState.currentGroups;
vsync: this, final groupNames = currentGroups.map((e) => e.name).toList();
); return ProxiesSelectorState(
return Column( groupNames: groupNames,
mainAxisAlignment: MainAxisAlignment.start, );
crossAxisAlignment: CrossAxisAlignment.start, },
children: [ shouldRebuild: (prev, next) {
TabBar( if (prev.groupNames.length != next.groupNames.length) {
controller: _tabController, _tabController?.dispose();
padding: const EdgeInsets.symmetric(horizontal: 16), _tabController = null;
dividerColor: Colors.transparent, }
isScrollable: true, return prev != next;
tabAlignment: TabAlignment.start, },
overlayColor: const WidgetStatePropertyAll(Colors.transparent), builder: (_, state, __) {
tabs: [ _tabController ??= TabController(
for (final groupName in state.groupNames) length: state.groupNames.length,
Tab( vsync: this,
text: groupName, );
), return Column(
], mainAxisAlignment: MainAxisAlignment.start,
), crossAxisAlignment: CrossAxisAlignment.start,
Expanded( children: [
child: TabBarView( TabBar(
controller: _tabController, controller: _tabController,
children: [ padding: const EdgeInsets.symmetric(horizontal: 16),
dividerColor: Colors.transparent,
isScrollable: true,
tabAlignment: TabAlignment.start,
overlayColor:
const WidgetStatePropertyAll(Colors.transparent),
tabs: [
for (final groupName in state.groupNames) for (final groupName in state.groupNames)
KeepContainer( Tab(
key: ObjectKey(groupName), text: groupName,
child: ProxiesTabView(
groupName: groupName,
),
), ),
], ],
), ),
) Expanded(
], child: TabBarView(
); controller: _tabController,
}, children: [
for (final groupName in state.groupNames)
KeepContainer(
key: ObjectKey(groupName),
child: ProxiesTabView(
groupName: groupName,
),
),
],
),
)
],
);
},
),
), ),
); );
} }
@@ -197,18 +196,6 @@ class ProxiesTabView extends StatelessWidget {
} }
} }
_delayTest(List<Proxy> proxies) async {
for (final proxy in proxies) {
globalState.appController.setDelay(
Delay(name: proxy.name, value: 0),
);
clashCore.getDelay(proxy.name).then((delay) {
globalState.appController.setDelay(delay);
});
}
await Future.delayed(httpTimeoutDuration + moreDuration);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector2<AppState, Config, ProxiesTabViewSelectorState>( return Selector2<AppState, Config, ProxiesTabViewSelectorState>(
@@ -226,32 +213,25 @@ class ProxiesTabView extends StatelessWidget {
state.group.all, state.group.all,
state.proxiesSortType, state.proxiesSortType,
); );
return DelayTestButtonContainer( return Align(
onClick: () async { alignment: Alignment.topCenter,
await _delayTest( child: GridView.builder(
state.group.all, padding: const EdgeInsets.all(16),
); gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
}, crossAxisCount: _getColumns(state.viewMode),
child: Align( mainAxisSpacing: 8,
alignment: Alignment.topCenter, crossAxisSpacing: 8,
child: GridView.builder( mainAxisExtent: _getItemHeight(context),
padding: const EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _getColumns(state.viewMode),
mainAxisSpacing: 8,
crossAxisSpacing: 8,
mainAxisExtent: _getItemHeight(context),
),
itemCount: proxies.length,
itemBuilder: (_, index) {
final proxy = proxies[index];
return ProxyCard(
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
);
},
), ),
itemCount: proxies.length,
itemBuilder: (_, index) {
final proxy = proxies[index];
return ProxyCard(
key: ValueKey('$groupName.${proxy.name}'),
proxy: proxy,
groupName: groupName,
);
},
), ),
); );
}, },
@@ -404,12 +384,10 @@ class ProxyCard extends StatelessWidget {
class DelayTestButtonContainer extends StatefulWidget { class DelayTestButtonContainer extends StatefulWidget {
final Widget child; final Widget child;
final Future Function() onClick;
const DelayTestButtonContainer({ const DelayTestButtonContainer({
super.key, super.key,
required this.child, required this.child,
required this.onClick,
}); });
@override @override
@@ -421,11 +399,15 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late AnimationController _controller; late AnimationController _controller;
late Animation<double> _scale; late Animation<double> _scale;
late Animation<double> _opacity;
_healthcheck() async { _healthcheck() async {
if (globalState.healthcheckLock) return;
_controller.forward(); _controller.forward();
await widget.onClick(); globalState.appController.healthcheck();
_controller.reverse(); Future.delayed(httpTimeoutDuration + moreDuration, () {
_controller.reverse();
});
} }
@override @override
@@ -434,7 +416,7 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
duration: const Duration( duration: const Duration(
milliseconds: 600, milliseconds: 300,
), ),
); );
_scale = Tween<double>( _scale = Tween<double>(
@@ -446,6 +428,20 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
curve: const Interval( curve: const Interval(
0, 0,
1, 1,
curve: Curves.elasticInOut,
),
),
);
_opacity = Tween<double>(
begin: 1.0,
end: 0.0,
).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(
0,
1,
curve: Curves.easeIn,
), ),
), ),
); );
@@ -459,7 +455,6 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_controller.reverse();
return FloatLayout( return FloatLayout(
floatingWidget: FloatWrapper( floatingWidget: FloatWrapper(
child: AnimatedBuilder( child: AnimatedBuilder(
@@ -470,7 +465,10 @@ class _DelayTestButtonContainerState extends State<DelayTestButtonContainer>
height: 56, height: 56,
child: Transform.scale( child: Transform.scale(
scale: _scale.value, scale: _scale.value,
child: child, child: Opacity(
opacity: _opacity.value,
child: child!,
),
), ),
); );
}, },

View File

@@ -1,172 +0,0 @@
import 'dart:io';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/ffi.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' hide context;
@immutable
class GeoItem {
final String label;
final String fileName;
const GeoItem({
required this.label,
required this.fileName,
});
}
class Resources extends StatefulWidget {
const Resources({super.key});
@override
State<Resources> createState() => _ResourcesState();
}
class _ResourcesState extends State<Resources> {
_updateExternalProvider(
String providerName,
String providerType,
) async {
final commonScaffoldState = context.commonScaffoldState;
await commonScaffoldState?.loadingRun(() async {
final message = await clashCore.updateExternalProvider(
providerName: providerName,
providerType: providerType,
);
if (message.isNotEmpty) throw message;
});
setState(() {});
}
Future<DateTime> _getGeoFileLastModified(String fileName) async {
final homePath = await appPath.getHomeDirPath();
return await File(join(homePath, fileName)).lastModified();
}
Widget _buildExternalProviderSection() {
return FutureBuilder<List<ExternalProvider>>(
future: () async {
await Future.delayed(const Duration(milliseconds: 200));
return await clashCore.getExternalProviders();
}(),
builder: (_, snapshot) {
return Center(
child: FadeBox(
key: const Key("external_providers"),
child: snapshot.data == null || snapshot.data!.isEmpty
? Container()
: Section(
title: appLocalizations.externalResources,
child: Column(
children: [
for (final externalProvider in snapshot.data!)
ListItem(
title: Text(externalProvider.name),
subtitle: Text(
"${externalProvider.type} (${externalProvider.vehicleType})",
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
externalProvider.updateAt.lastUpdateTimeDesc,
style: context.textTheme.bodyMedium,
),
const Padding(
padding: EdgeInsets.only(left: 12,right: 4),
child: VerticalDivider(
endIndent: 2,
width: 4,
indent: 2,
),
),
externalProvider.vehicleType == "HTTP"
? IconButton(
icon: const Icon(Icons.sync),
onPressed: () {
_updateExternalProvider(
externalProvider.name,
externalProvider.type,
);
},
)
: Container(),
],
),
)
],
),
),
),
);
},
);
}
Widget _buildGeoDataSection() {
const geoItems = <GeoItem>[
GeoItem(label: "GeoIp", fileName: mmdbFileName),
GeoItem(label: "GeoSite", fileName: geoSiteFileName),
GeoItem(label: "ASN", fileName: asnFileName),
];
return Section(
title: appLocalizations.geoData,
child: Column(
children: [
for (final geoItem in geoItems)
ListItem(
title: Text(geoItem.label),
subtitle: FutureBuilder<DateTime>(
future: () async {
await Future.delayed(const Duration(milliseconds: 200));
return await _getGeoFileLastModified(geoItem.fileName);
}(),
builder: (_, snapshot) {
return Container(
alignment: Alignment.centerLeft,
height: 24,
child: FadeBox(
key: Key("fade_box_${geoItem.label}"),
child: snapshot.data == null
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: Text(
snapshot.data!.lastUpdateTimeDesc,
),
),
);
},
),
trailing: IconButton(
icon: const Icon(Icons.sync),
onPressed: () {
_updateExternalProvider(
geoItem.fileName,
geoItem.label,
);
},
),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return ListView(
children: [
_buildGeoDataSection(),
_buildExternalProviderSection(),
],
);
}
}

View File

@@ -9,8 +9,6 @@
"tools": "Tools", "tools": "Tools",
"logs": "Logs", "logs": "Logs",
"logsDesc": "Log capture records", "logsDesc": "Log capture records",
"resources": "Resources",
"resourcesDesc": "External resource related info",
"trafficUsage": "Traffic usage", "trafficUsage": "Traffic usage",
"coreInfo": "Core info", "coreInfo": "Core info",
"nullCoreInfoDesc": "Unable to obtain core info", "nullCoreInfoDesc": "Unable to obtain core info",
@@ -153,12 +151,5 @@
"checkUpdate": "Check for updates", "checkUpdate": "Check for updates",
"discoverNewVersion": "Discover the new version", "discoverNewVersion": "Discover the new version",
"checkUpdateError": "The current application is already the latest version", "checkUpdateError": "The current application is already the latest version",
"goDownload": "Go to download", "goDownload": "Go to download"
"unknown": "Unknown",
"geoData": "GeoData",
"externalResources": "External resources",
"checking": "Checking...",
"country": "Country",
"checkError": "Check error",
"ipCheckTimeout": "Ip check timeout"
} }

View File

@@ -9,8 +9,6 @@
"tools": "工具", "tools": "工具",
"logs": "日志", "logs": "日志",
"logsDesc": "日志捕获记录", "logsDesc": "日志捕获记录",
"resources": "资源",
"resourcesDesc": "外部资源相关信息",
"trafficUsage": "流量统计", "trafficUsage": "流量统计",
"coreInfo": "内核信息", "coreInfo": "内核信息",
"nullCoreInfoDesc": "无法获取内核信息", "nullCoreInfoDesc": "无法获取内核信息",
@@ -153,12 +151,5 @@
"checkUpdate": "检查更新", "checkUpdate": "检查更新",
"discoverNewVersion": "发现新版本", "discoverNewVersion": "发现新版本",
"checkUpdateError": "当前应用已经是最新版了", "checkUpdateError": "当前应用已经是最新版了",
"goDownload": "前往下载", "goDownload": "前往下载"
"unknown": "未知",
"geoData": "地理数据",
"externalResources": "外部资源",
"checking": "检测中...",
"country": "区域",
"checkError": "检测失败",
"ipCheckTimeout": "Ip检测超时"
} }

View File

@@ -76,12 +76,10 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Cancel filter system app"), MessageLookupByLibrary.simpleMessage("Cancel filter system app"),
"cancelSelectAll": "cancelSelectAll":
MessageLookupByLibrary.simpleMessage("Cancel select all"), MessageLookupByLibrary.simpleMessage("Cancel select all"),
"checkError": MessageLookupByLibrary.simpleMessage("Check error"),
"checkUpdate": "checkUpdate":
MessageLookupByLibrary.simpleMessage("Check for updates"), MessageLookupByLibrary.simpleMessage("Check for updates"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage( "checkUpdateError": MessageLookupByLibrary.simpleMessage(
"The current application is already the latest version"), "The current application is already the latest version"),
"checking": MessageLookupByLibrary.simpleMessage("Checking..."),
"compatible": "compatible":
MessageLookupByLibrary.simpleMessage("Compatibility mode"), MessageLookupByLibrary.simpleMessage("Compatibility mode"),
"compatibleDesc": MessageLookupByLibrary.simpleMessage( "compatibleDesc": MessageLookupByLibrary.simpleMessage(
@@ -90,7 +88,6 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"), "connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"),
"core": MessageLookupByLibrary.simpleMessage("Core"), "core": MessageLookupByLibrary.simpleMessage("Core"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"), "coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
"country": MessageLookupByLibrary.simpleMessage("Country"),
"create": MessageLookupByLibrary.simpleMessage("Create"), "create": MessageLookupByLibrary.simpleMessage("Create"),
"dark": MessageLookupByLibrary.simpleMessage("Dark"), "dark": MessageLookupByLibrary.simpleMessage("Dark"),
"dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"), "dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"),
@@ -112,21 +109,16 @@ class MessageLookup extends MessageLookupByLibrary {
"edit": MessageLookupByLibrary.simpleMessage("Edit"), "edit": MessageLookupByLibrary.simpleMessage("Edit"),
"en": MessageLookupByLibrary.simpleMessage("English"), "en": MessageLookupByLibrary.simpleMessage("English"),
"exit": MessageLookupByLibrary.simpleMessage("Exit"), "exit": MessageLookupByLibrary.simpleMessage("Exit"),
"externalResources":
MessageLookupByLibrary.simpleMessage("External resources"),
"file": MessageLookupByLibrary.simpleMessage("File"), "file": MessageLookupByLibrary.simpleMessage("File"),
"fileDesc": "fileDesc":
MessageLookupByLibrary.simpleMessage("Directly upload profile"), MessageLookupByLibrary.simpleMessage("Directly upload profile"),
"filterSystemApp": "filterSystemApp":
MessageLookupByLibrary.simpleMessage("Filter system app"), MessageLookupByLibrary.simpleMessage("Filter system app"),
"geoData": MessageLookupByLibrary.simpleMessage("GeoData"),
"global": MessageLookupByLibrary.simpleMessage("Global"), "global": MessageLookupByLibrary.simpleMessage("Global"),
"goDownload": MessageLookupByLibrary.simpleMessage("Go to download"), "goDownload": MessageLookupByLibrary.simpleMessage("Go to download"),
"hours": MessageLookupByLibrary.simpleMessage("Hours"), "hours": MessageLookupByLibrary.simpleMessage("Hours"),
"importFromURL": "importFromURL":
MessageLookupByLibrary.simpleMessage("Import from URL"), MessageLookupByLibrary.simpleMessage("Import from URL"),
"ipCheckTimeout":
MessageLookupByLibrary.simpleMessage("Ip check timeout"),
"just": MessageLookupByLibrary.simpleMessage("Just"), "just": MessageLookupByLibrary.simpleMessage("Just"),
"language": MessageLookupByLibrary.simpleMessage("Language"), "language": MessageLookupByLibrary.simpleMessage("Language"),
"light": MessageLookupByLibrary.simpleMessage("Light"), "light": MessageLookupByLibrary.simpleMessage("Light"),
@@ -207,9 +199,6 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Only recovery profiles"), MessageLookupByLibrary.simpleMessage("Only recovery profiles"),
"recoverySuccess": "recoverySuccess":
MessageLookupByLibrary.simpleMessage("Recovery success"), MessageLookupByLibrary.simpleMessage("Recovery success"),
"resources": MessageLookupByLibrary.simpleMessage("Resources"),
"resourcesDesc": MessageLookupByLibrary.simpleMessage(
"External resource related info"),
"rule": MessageLookupByLibrary.simpleMessage("Rule"), "rule": MessageLookupByLibrary.simpleMessage("Rule"),
"save": MessageLookupByLibrary.simpleMessage("Save"), "save": MessageLookupByLibrary.simpleMessage("Save"),
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"), "selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
@@ -240,7 +229,6 @@ class MessageLookup extends MessageLookupByLibrary {
"unableToUpdateCurrentProfileDesc": "unableToUpdateCurrentProfileDesc":
MessageLookupByLibrary.simpleMessage( MessageLookupByLibrary.simpleMessage(
"unable to update current profile"), "unable to update current profile"),
"unknown": MessageLookupByLibrary.simpleMessage("Unknown"),
"update": MessageLookupByLibrary.simpleMessage("Update"), "update": MessageLookupByLibrary.simpleMessage("Update"),
"upload": MessageLookupByLibrary.simpleMessage("Upload"), "upload": MessageLookupByLibrary.simpleMessage("Upload"),
"url": MessageLookupByLibrary.simpleMessage("URL"), "url": MessageLookupByLibrary.simpleMessage("URL"),

View File

@@ -63,10 +63,8 @@ class MessageLookup extends MessageLookupByLibrary {
"cancelFilterSystemApp": "cancelFilterSystemApp":
MessageLookupByLibrary.simpleMessage("取消过滤系统应用"), MessageLookupByLibrary.simpleMessage("取消过滤系统应用"),
"cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"), "cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"),
"checkError": MessageLookupByLibrary.simpleMessage("检测失败"),
"checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"), "checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"), "checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"),
"checking": MessageLookupByLibrary.simpleMessage("检测中..."),
"compatible": MessageLookupByLibrary.simpleMessage("兼容模式"), "compatible": MessageLookupByLibrary.simpleMessage("兼容模式"),
"compatibleDesc": "compatibleDesc":
MessageLookupByLibrary.simpleMessage("开启将失去部分应用能力获得全量的Clash的支持"), MessageLookupByLibrary.simpleMessage("开启将失去部分应用能力获得全量的Clash的支持"),
@@ -74,7 +72,6 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"), "connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
"core": MessageLookupByLibrary.simpleMessage("内核"), "core": MessageLookupByLibrary.simpleMessage("内核"),
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"), "coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
"country": MessageLookupByLibrary.simpleMessage("区域"),
"create": MessageLookupByLibrary.simpleMessage("创建"), "create": MessageLookupByLibrary.simpleMessage("创建"),
"dark": MessageLookupByLibrary.simpleMessage("深色"), "dark": MessageLookupByLibrary.simpleMessage("深色"),
"dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"), "dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"),
@@ -93,16 +90,13 @@ class MessageLookup extends MessageLookupByLibrary {
"edit": MessageLookupByLibrary.simpleMessage("编辑"), "edit": MessageLookupByLibrary.simpleMessage("编辑"),
"en": MessageLookupByLibrary.simpleMessage("英语"), "en": MessageLookupByLibrary.simpleMessage("英语"),
"exit": MessageLookupByLibrary.simpleMessage("退出"), "exit": MessageLookupByLibrary.simpleMessage("退出"),
"externalResources": MessageLookupByLibrary.simpleMessage("外部资源"),
"file": MessageLookupByLibrary.simpleMessage("文件"), "file": MessageLookupByLibrary.simpleMessage("文件"),
"fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"), "fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"),
"filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"), "filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"),
"geoData": MessageLookupByLibrary.simpleMessage("地理数据"),
"global": MessageLookupByLibrary.simpleMessage("全局"), "global": MessageLookupByLibrary.simpleMessage("全局"),
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"), "goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
"hours": MessageLookupByLibrary.simpleMessage("小时"), "hours": MessageLookupByLibrary.simpleMessage("小时"),
"importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"), "importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"),
"ipCheckTimeout": MessageLookupByLibrary.simpleMessage("Ip检测超时"),
"just": MessageLookupByLibrary.simpleMessage("刚刚"), "just": MessageLookupByLibrary.simpleMessage("刚刚"),
"language": MessageLookupByLibrary.simpleMessage("语言"), "language": MessageLookupByLibrary.simpleMessage("语言"),
"light": MessageLookupByLibrary.simpleMessage("浅色"), "light": MessageLookupByLibrary.simpleMessage("浅色"),
@@ -167,8 +161,6 @@ class MessageLookup extends MessageLookupByLibrary {
"recoveryDesc": MessageLookupByLibrary.simpleMessage("从WebDAV恢复数据"), "recoveryDesc": MessageLookupByLibrary.simpleMessage("从WebDAV恢复数据"),
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"), "recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"),
"recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"), "recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"),
"resources": MessageLookupByLibrary.simpleMessage("资源"),
"resourcesDesc": MessageLookupByLibrary.simpleMessage("外部资源相关信息"),
"rule": MessageLookupByLibrary.simpleMessage("规则"), "rule": MessageLookupByLibrary.simpleMessage("规则"),
"save": MessageLookupByLibrary.simpleMessage("保存"), "save": MessageLookupByLibrary.simpleMessage("保存"),
"selectAll": MessageLookupByLibrary.simpleMessage("全选"), "selectAll": MessageLookupByLibrary.simpleMessage("全选"),
@@ -195,7 +187,6 @@ class MessageLookup extends MessageLookupByLibrary {
"tunDesc": MessageLookupByLibrary.simpleMessage("仅在管理员模式生效"), "tunDesc": MessageLookupByLibrary.simpleMessage("仅在管理员模式生效"),
"unableToUpdateCurrentProfileDesc": "unableToUpdateCurrentProfileDesc":
MessageLookupByLibrary.simpleMessage("无法更新当前配置文件"), MessageLookupByLibrary.simpleMessage("无法更新当前配置文件"),
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
"update": MessageLookupByLibrary.simpleMessage("更新"), "update": MessageLookupByLibrary.simpleMessage("更新"),
"upload": MessageLookupByLibrary.simpleMessage("上传"), "upload": MessageLookupByLibrary.simpleMessage("上传"),
"url": MessageLookupByLibrary.simpleMessage("URL"), "url": MessageLookupByLibrary.simpleMessage("URL"),

View File

@@ -150,26 +150,6 @@ class AppLocalizations {
); );
} }
/// `Resources`
String get resources {
return Intl.message(
'Resources',
name: 'resources',
desc: '',
args: [],
);
}
/// `External resource related info`
String get resourcesDesc {
return Intl.message(
'External resource related info',
name: 'resourcesDesc',
desc: '',
args: [],
);
}
/// `Traffic usage` /// `Traffic usage`
String get trafficUsage { String get trafficUsage {
return Intl.message( return Intl.message(
@@ -1599,76 +1579,6 @@ class AppLocalizations {
args: [], args: [],
); );
} }
/// `Unknown`
String get unknown {
return Intl.message(
'Unknown',
name: 'unknown',
desc: '',
args: [],
);
}
/// `GeoData`
String get geoData {
return Intl.message(
'GeoData',
name: 'geoData',
desc: '',
args: [],
);
}
/// `External resources`
String get externalResources {
return Intl.message(
'External resources',
name: 'externalResources',
desc: '',
args: [],
);
}
/// `Checking...`
String get checking {
return Intl.message(
'Checking...',
name: 'checking',
desc: '',
args: [],
);
}
/// `Country`
String get country {
return Intl.message(
'Country',
name: 'country',
desc: '',
args: [],
);
}
/// `Check error`
String get checkError {
return Intl.message(
'Check error',
name: 'checkError',
desc: '',
args: [],
);
}
/// `Ip check timeout`
String get ipCheckTimeout {
return Intl.message(
'Ip check timeout',
name: 'ipCheckTimeout',
desc: '',
args: [],
);
}
} }
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> { class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -21,6 +21,7 @@ Future<void> main() async {
mode: clashConfig.mode, mode: clashConfig.mode,
isCompatible: config.isCompatible, isCompatible: config.isCompatible,
selectedMap: config.currentSelectedMap, selectedMap: config.currentSelectedMap,
viewWidth: other.getViewWidth(),
); );
await globalState.init( await globalState.init(
appState: appState, appState: appState,

View File

@@ -32,6 +32,7 @@ class AppState with ChangeNotifier {
AppState({ AppState({
required Mode mode, required Mode mode,
double? viewWidth,
required bool isCompatible, required bool isCompatible,
required SelectedMap selectedMap, required SelectedMap selectedMap,
}) : _navigationItems = [], }) : _navigationItems = [],
@@ -39,7 +40,7 @@ class AppState with ChangeNotifier {
_currentLabel = "dashboard", _currentLabel = "dashboard",
_traffics = [], _traffics = [],
_logs = [], _logs = [],
_viewWidth = 0, _viewWidth = viewWidth ?? 0,
_selectedMap = selectedMap, _selectedMap = selectedMap,
_sortNum = 0, _sortNum = 0,
_mode = mode, _mode = mode,
@@ -89,8 +90,6 @@ class AppState with ChangeNotifier {
} }
} }
bool get isStart => _runTime != null;
int? get runTime => _runTime; int? get runTime => _runTime;
set runTime(int? value) { set runTime(int? value) {
@@ -168,9 +167,6 @@ class AppState with ChangeNotifier {
addLog(Log log) { addLog(Log log) {
_logs.add(log); _logs.add(log);
if (_logs.length > 60) {
_logs = _logs.sublist(_logs.length - 60);
}
notifyListeners(); notifyListeners();
} }
@@ -183,6 +179,7 @@ class AppState with ChangeNotifier {
} }
} }
List<Group> get groups => _groups; List<Group> get groups => _groups;
set groups(List<Group> value) { set groups(List<Group> value) {
@@ -256,7 +253,11 @@ class AppState with ChangeNotifier {
} }
} }
ViewMode get viewMode => other.getViewMode(_viewWidth); ViewMode get viewMode {
if (_viewWidth <= maxMobileWidth) return ViewMode.mobile;
if (_viewWidth <= maxLaptopWidth) return ViewMode.laptop;
return ViewMode.desktop;
}
DelayMap get delayMap { DelayMap get delayMap {
return _delayMap; return _delayMap;

View File

@@ -16,7 +16,7 @@ class Tun with _$Tun {
const factory Tun({ const factory Tun({
@Default(false) bool enable, @Default(false) bool enable,
@Default(appName) String device, @Default(appName) String device,
@Default(TunStack.mixed) TunStack stack, @Default(TunStack.gvisor) TunStack stack,
@JsonKey(name: "dns-hijack") @Default(["any:53"]) List<String> dnsHijack, @JsonKey(name: "dns-hijack") @Default(["any:53"]) List<String> dnsHijack,
}) = _Tun; }) = _Tun;

24
lib/models/common.dart Normal file
View File

@@ -0,0 +1,24 @@
import 'package:fl_clash/enum/enum.dart';
class Result<T> {
String? message;
ResultType type;
T? data;
Result({
this.message,
required this.type,
this.data,
});
Result.success([this.data]) : type = ResultType.success,
message = null;
Result.error([this.message]) : type = ResultType.error,
data = null;
@override
String toString() {
return 'Result{message: $message, type: $type, data: $data}';
}
}

View File

@@ -50,7 +50,7 @@ class Config extends ChangeNotifier {
_autoRun = false, _autoRun = false,
_themeMode = ThemeMode.system, _themeMode = ThemeMode.system,
_openLog = false, _openLog = false,
_isCompatible = true, _isCompatible = false,
_primaryColor = defaultPrimaryColor.value, _primaryColor = defaultPrimaryColor.value,
_proxiesSortType = ProxiesSortType.none, _proxiesSortType = ProxiesSortType.none,
_isMinimizeOnExit = true, _isMinimizeOnExit = true,
@@ -90,7 +90,7 @@ class Config extends ChangeNotifier {
_setProfile(Profile profile) { _setProfile(Profile profile) {
final List<Profile> profilesTemp = List.from(_profiles); final List<Profile> profilesTemp = List.from(_profiles);
final index = final index =
profilesTemp.indexWhere((element) => element.id == profile.id); profilesTemp.indexWhere((element) => element.id == profile.id);
final updateProfile = profile.copyWith( final updateProfile = profile.copyWith(
label: _getLabel(profile.label, profile.id), label: _getLabel(profile.label, profile.id),
); );
@@ -281,7 +281,7 @@ class Config extends ChangeNotifier {
} }
} }
@JsonKey(defaultValue: true) @JsonKey(defaultValue: false)
bool get isCompatible { bool get isCompatible {
return _isCompatible; return _isCompatible;
} }

View File

@@ -75,16 +75,3 @@ class Process with _$Process {
factory Process.fromJson(Map<String, Object?> json) => factory Process.fromJson(Map<String, Object?> json) =>
_$ProcessFromJson(json); _$ProcessFromJson(json);
} }
@freezed
class ExternalProvider with _$ExternalProvider {
const factory ExternalProvider({
required String name,
required String type,
@JsonKey(name: "vehicle-type") required String vehicleType,
@JsonKey(name: "update-at") required DateTime updateAt,
}) = _ExternalProvider;
factory ExternalProvider.fromJson(Map<String, Object?> json) =>
_$ExternalProviderFromJson(json);
}

View File

@@ -135,7 +135,7 @@ class _$TunImpl implements _Tun {
const _$TunImpl( const _$TunImpl(
{this.enable = false, {this.enable = false,
this.device = appName, this.device = appName,
this.stack = TunStack.mixed, this.stack = TunStack.gvisor,
@JsonKey(name: "dns-hijack") @JsonKey(name: "dns-hijack")
final List<String> dnsHijack = const ["any:53"]}) final List<String> dnsHijack = const ["any:53"]})
: _dnsHijack = dnsHijack; : _dnsHijack = dnsHijack;

View File

@@ -79,7 +79,7 @@ _$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
enable: json['enable'] as bool? ?? false, enable: json['enable'] as bool? ?? false,
device: json['device'] as String? ?? appName, device: json['device'] as String? ?? appName,
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ?? stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
TunStack.mixed, TunStack.gvisor,
dnsHijack: (json['dns-hijack'] as List<dynamic>?) dnsHijack: (json['dns-hijack'] as List<dynamic>?)
?.map((e) => e as String) ?.map((e) => e as String)
.toList() ?? .toList() ??

View File

@@ -31,7 +31,7 @@ Config _$ConfigFromJson(Map<String, dynamic> json) => Config()
? null ? null
: DAV.fromJson(json['dav'] as Map<String, dynamic>) : DAV.fromJson(json['dav'] as Map<String, dynamic>)
..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true ..isAnimateToPage = json['isAnimateToPage'] as bool? ?? true
..isCompatible = json['isCompatible'] as bool? ?? true ..isCompatible = json['isCompatible'] as bool? ?? false
..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true; ..autoCheckUpdate = json['autoCheckUpdate'] as bool? ?? true;
Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{ Map<String, dynamic> _$ConfigToJson(Config instance) => <String, dynamic>{

View File

@@ -1029,214 +1029,3 @@ abstract class _Process implements Process {
_$$ProcessImplCopyWith<_$ProcessImpl> get copyWith => _$$ProcessImplCopyWith<_$ProcessImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
ExternalProvider _$ExternalProviderFromJson(Map<String, dynamic> json) {
return _ExternalProvider.fromJson(json);
}
/// @nodoc
mixin _$ExternalProvider {
String get name => throw _privateConstructorUsedError;
String get type => throw _privateConstructorUsedError;
@JsonKey(name: "vehicle-type")
String get vehicleType => throw _privateConstructorUsedError;
@JsonKey(name: "update-at")
DateTime get updateAt => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ExternalProviderCopyWith<ExternalProvider> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ExternalProviderCopyWith<$Res> {
factory $ExternalProviderCopyWith(
ExternalProvider value, $Res Function(ExternalProvider) then) =
_$ExternalProviderCopyWithImpl<$Res, ExternalProvider>;
@useResult
$Res call(
{String name,
String type,
@JsonKey(name: "vehicle-type") String vehicleType,
@JsonKey(name: "update-at") DateTime updateAt});
}
/// @nodoc
class _$ExternalProviderCopyWithImpl<$Res, $Val extends ExternalProvider>
implements $ExternalProviderCopyWith<$Res> {
_$ExternalProviderCopyWithImpl(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? name = null,
Object? type = null,
Object? vehicleType = null,
Object? updateAt = null,
}) {
return _then(_value.copyWith(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as String,
vehicleType: null == vehicleType
? _value.vehicleType
: vehicleType // ignore: cast_nullable_to_non_nullable
as String,
updateAt: null == updateAt
? _value.updateAt
: updateAt // ignore: cast_nullable_to_non_nullable
as DateTime,
) as $Val);
}
}
/// @nodoc
abstract class _$$ExternalProviderImplCopyWith<$Res>
implements $ExternalProviderCopyWith<$Res> {
factory _$$ExternalProviderImplCopyWith(_$ExternalProviderImpl value,
$Res Function(_$ExternalProviderImpl) then) =
__$$ExternalProviderImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String name,
String type,
@JsonKey(name: "vehicle-type") String vehicleType,
@JsonKey(name: "update-at") DateTime updateAt});
}
/// @nodoc
class __$$ExternalProviderImplCopyWithImpl<$Res>
extends _$ExternalProviderCopyWithImpl<$Res, _$ExternalProviderImpl>
implements _$$ExternalProviderImplCopyWith<$Res> {
__$$ExternalProviderImplCopyWithImpl(_$ExternalProviderImpl _value,
$Res Function(_$ExternalProviderImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? name = null,
Object? type = null,
Object? vehicleType = null,
Object? updateAt = null,
}) {
return _then(_$ExternalProviderImpl(
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as String,
vehicleType: null == vehicleType
? _value.vehicleType
: vehicleType // ignore: cast_nullable_to_non_nullable
as String,
updateAt: null == updateAt
? _value.updateAt
: updateAt // ignore: cast_nullable_to_non_nullable
as DateTime,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ExternalProviderImpl implements _ExternalProvider {
const _$ExternalProviderImpl(
{required this.name,
required this.type,
@JsonKey(name: "vehicle-type") required this.vehicleType,
@JsonKey(name: "update-at") required this.updateAt});
factory _$ExternalProviderImpl.fromJson(Map<String, dynamic> json) =>
_$$ExternalProviderImplFromJson(json);
@override
final String name;
@override
final String type;
@override
@JsonKey(name: "vehicle-type")
final String vehicleType;
@override
@JsonKey(name: "update-at")
final DateTime updateAt;
@override
String toString() {
return 'ExternalProvider(name: $name, type: $type, vehicleType: $vehicleType, updateAt: $updateAt)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ExternalProviderImpl &&
(identical(other.name, name) || other.name == name) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.vehicleType, vehicleType) ||
other.vehicleType == vehicleType) &&
(identical(other.updateAt, updateAt) ||
other.updateAt == updateAt));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, name, type, vehicleType, updateAt);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$ExternalProviderImplCopyWith<_$ExternalProviderImpl> get copyWith =>
__$$ExternalProviderImplCopyWithImpl<_$ExternalProviderImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ExternalProviderImplToJson(
this,
);
}
}
abstract class _ExternalProvider implements ExternalProvider {
const factory _ExternalProvider(
{required final String name,
required final String type,
@JsonKey(name: "vehicle-type") required final String vehicleType,
@JsonKey(name: "update-at") required final DateTime updateAt}) =
_$ExternalProviderImpl;
factory _ExternalProvider.fromJson(Map<String, dynamic> json) =
_$ExternalProviderImpl.fromJson;
@override
String get name;
@override
String get type;
@override
@JsonKey(name: "vehicle-type")
String get vehicleType;
@override
@JsonKey(name: "update-at")
DateTime get updateAt;
@override
@JsonKey(ignore: true)
_$$ExternalProviderImplCopyWith<_$ExternalProviderImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -94,21 +94,3 @@ Map<String, dynamic> _$$ProcessImplToJson(_$ProcessImpl instance) =>
'source': instance.source, 'source': instance.source,
'target': instance.target, 'target': instance.target,
}; };
_$ExternalProviderImpl _$$ExternalProviderImplFromJson(
Map<String, dynamic> json) =>
_$ExternalProviderImpl(
name: json['name'] as String,
type: json['type'] as String,
vehicleType: json['vehicle-type'] as String,
updateAt: DateTime.parse(json['update-at'] as String),
);
Map<String, dynamic> _$$ExternalProviderImplToJson(
_$ExternalProviderImpl instance) =>
<String, dynamic>{
'name': instance.name,
'type': instance.type,
'vehicle-type': instance.vehicleType,
'update-at': instance.updateAt.toIso8601String(),
};

View File

@@ -20,7 +20,6 @@ mixin _$NavigationItem {
String get label => throw _privateConstructorUsedError; String get label => throw _privateConstructorUsedError;
String? get description => throw _privateConstructorUsedError; String? get description => throw _privateConstructorUsedError;
Widget get fragment => throw _privateConstructorUsedError; Widget get fragment => throw _privateConstructorUsedError;
bool get keep => throw _privateConstructorUsedError;
String? get path => throw _privateConstructorUsedError; String? get path => throw _privateConstructorUsedError;
List<NavigationItemMode> get modes => throw _privateConstructorUsedError; List<NavigationItemMode> get modes => throw _privateConstructorUsedError;
@@ -40,7 +39,6 @@ abstract class $NavigationItemCopyWith<$Res> {
String label, String label,
String? description, String? description,
Widget fragment, Widget fragment,
bool keep,
String? path, String? path,
List<NavigationItemMode> modes}); List<NavigationItemMode> modes});
} }
@@ -62,7 +60,6 @@ class _$NavigationItemCopyWithImpl<$Res, $Val extends NavigationItem>
Object? label = null, Object? label = null,
Object? description = freezed, Object? description = freezed,
Object? fragment = null, Object? fragment = null,
Object? keep = null,
Object? path = freezed, Object? path = freezed,
Object? modes = null, Object? modes = null,
}) { }) {
@@ -83,10 +80,6 @@ class _$NavigationItemCopyWithImpl<$Res, $Val extends NavigationItem>
? _value.fragment ? _value.fragment
: fragment // ignore: cast_nullable_to_non_nullable : fragment // ignore: cast_nullable_to_non_nullable
as Widget, as Widget,
keep: null == keep
? _value.keep
: keep // ignore: cast_nullable_to_non_nullable
as bool,
path: freezed == path path: freezed == path
? _value.path ? _value.path
: path // ignore: cast_nullable_to_non_nullable : path // ignore: cast_nullable_to_non_nullable
@@ -112,7 +105,6 @@ abstract class _$$NavigationItemImplCopyWith<$Res>
String label, String label,
String? description, String? description,
Widget fragment, Widget fragment,
bool keep,
String? path, String? path,
List<NavigationItemMode> modes}); List<NavigationItemMode> modes});
} }
@@ -132,7 +124,6 @@ class __$$NavigationItemImplCopyWithImpl<$Res>
Object? label = null, Object? label = null,
Object? description = freezed, Object? description = freezed,
Object? fragment = null, Object? fragment = null,
Object? keep = null,
Object? path = freezed, Object? path = freezed,
Object? modes = null, Object? modes = null,
}) { }) {
@@ -153,10 +144,6 @@ class __$$NavigationItemImplCopyWithImpl<$Res>
? _value.fragment ? _value.fragment
: fragment // ignore: cast_nullable_to_non_nullable : fragment // ignore: cast_nullable_to_non_nullable
as Widget, as Widget,
keep: null == keep
? _value.keep
: keep // ignore: cast_nullable_to_non_nullable
as bool,
path: freezed == path path: freezed == path
? _value.path ? _value.path
: path // ignore: cast_nullable_to_non_nullable : path // ignore: cast_nullable_to_non_nullable
@@ -177,7 +164,6 @@ class _$NavigationItemImpl implements _NavigationItem {
required this.label, required this.label,
this.description, this.description,
required this.fragment, required this.fragment,
this.keep = true,
this.path, this.path,
final List<NavigationItemMode> modes = const [ final List<NavigationItemMode> modes = const [
NavigationItemMode.mobile, NavigationItemMode.mobile,
@@ -194,9 +180,6 @@ class _$NavigationItemImpl implements _NavigationItem {
@override @override
final Widget fragment; final Widget fragment;
@override @override
@JsonKey()
final bool keep;
@override
final String? path; final String? path;
final List<NavigationItemMode> _modes; final List<NavigationItemMode> _modes;
@override @override
@@ -209,7 +192,7 @@ class _$NavigationItemImpl implements _NavigationItem {
@override @override
String toString() { String toString() {
return 'NavigationItem(icon: $icon, label: $label, description: $description, fragment: $fragment, keep: $keep, path: $path, modes: $modes)'; return 'NavigationItem(icon: $icon, label: $label, description: $description, fragment: $fragment, path: $path, modes: $modes)';
} }
@override @override
@@ -223,14 +206,13 @@ class _$NavigationItemImpl implements _NavigationItem {
other.description == description) && other.description == description) &&
(identical(other.fragment, fragment) || (identical(other.fragment, fragment) ||
other.fragment == fragment) && other.fragment == fragment) &&
(identical(other.keep, keep) || other.keep == keep) &&
(identical(other.path, path) || other.path == path) && (identical(other.path, path) || other.path == path) &&
const DeepCollectionEquality().equals(other._modes, _modes)); const DeepCollectionEquality().equals(other._modes, _modes));
} }
@override @override
int get hashCode => Object.hash(runtimeType, icon, label, description, int get hashCode => Object.hash(runtimeType, icon, label, description,
fragment, keep, path, const DeepCollectionEquality().hash(_modes)); fragment, path, const DeepCollectionEquality().hash(_modes));
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@@ -246,7 +228,6 @@ abstract class _NavigationItem implements NavigationItem {
required final String label, required final String label,
final String? description, final String? description,
required final Widget fragment, required final Widget fragment,
final bool keep,
final String? path, final String? path,
final List<NavigationItemMode> modes}) = _$NavigationItemImpl; final List<NavigationItemMode> modes}) = _$NavigationItemImpl;
@@ -259,8 +240,6 @@ abstract class _NavigationItem implements NavigationItem {
@override @override
Widget get fragment; Widget get fragment;
@override @override
bool get keep;
@override
String? get path; String? get path;
@override @override
List<NavigationItemMode> get modes; List<NavigationItemMode> get modes;

View File

@@ -157,30 +157,34 @@ abstract class _StartButtonSelectorState implements StartButtonSelectorState {
} }
/// @nodoc /// @nodoc
mixin _$CheckIpSelectorState { mixin _$UpdateCurrentDelaySelectorState {
String? get currentProxyName => throw _privateConstructorUsedError;
bool get isCurrent => throw _privateConstructorUsedError;
int? get delay => throw _privateConstructorUsedError;
bool get isInit => throw _privateConstructorUsedError; bool get isInit => throw _privateConstructorUsedError;
bool get isStart => throw _privateConstructorUsedError;
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
$CheckIpSelectorStateCopyWith<CheckIpSelectorState> get copyWith => $UpdateCurrentDelaySelectorStateCopyWith<UpdateCurrentDelaySelectorState>
throw _privateConstructorUsedError; get copyWith => throw _privateConstructorUsedError;
} }
/// @nodoc /// @nodoc
abstract class $CheckIpSelectorStateCopyWith<$Res> { abstract class $UpdateCurrentDelaySelectorStateCopyWith<$Res> {
factory $CheckIpSelectorStateCopyWith(CheckIpSelectorState value, factory $UpdateCurrentDelaySelectorStateCopyWith(
$Res Function(CheckIpSelectorState) then) = UpdateCurrentDelaySelectorState value,
_$CheckIpSelectorStateCopyWithImpl<$Res, CheckIpSelectorState>; $Res Function(UpdateCurrentDelaySelectorState) then) =
_$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res,
UpdateCurrentDelaySelectorState>;
@useResult @useResult
$Res call({bool isInit, bool isStart, Map<String, String> selectedMap}); $Res call(
{String? currentProxyName, bool isCurrent, int? delay, bool isInit});
} }
/// @nodoc /// @nodoc
class _$CheckIpSelectorStateCopyWithImpl<$Res, class _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res,
$Val extends CheckIpSelectorState> $Val extends UpdateCurrentDelaySelectorState>
implements $CheckIpSelectorStateCopyWith<$Res> { implements $UpdateCurrentDelaySelectorStateCopyWith<$Res> {
_$CheckIpSelectorStateCopyWithImpl(this._value, this._then); _$UpdateCurrentDelaySelectorStateCopyWithImpl(this._value, this._then);
// ignore: unused_field // ignore: unused_field
final $Val _value; final $Val _value;
@@ -190,136 +194,154 @@ class _$CheckIpSelectorStateCopyWithImpl<$Res,
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? currentProxyName = freezed,
Object? isCurrent = null,
Object? delay = freezed,
Object? isInit = null, Object? isInit = null,
Object? isStart = null,
Object? selectedMap = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
currentProxyName: freezed == currentProxyName
? _value.currentProxyName
: currentProxyName // ignore: cast_nullable_to_non_nullable
as String?,
isCurrent: null == isCurrent
? _value.isCurrent
: isCurrent // ignore: cast_nullable_to_non_nullable
as bool,
delay: freezed == delay
? _value.delay
: delay // ignore: cast_nullable_to_non_nullable
as int?,
isInit: null == isInit isInit: null == isInit
? _value.isInit ? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable : isInit // ignore: cast_nullable_to_non_nullable
as bool, as bool,
isStart: null == isStart
? _value.isStart
: isStart // ignore: cast_nullable_to_non_nullable
as bool,
selectedMap: null == selectedMap
? _value.selectedMap
: selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
) as $Val); ) as $Val);
} }
} }
/// @nodoc /// @nodoc
abstract class _$$CheckIpSelectorStateImplCopyWith<$Res> abstract class _$$UpdateCurrentDelaySelectorStateImplCopyWith<$Res>
implements $CheckIpSelectorStateCopyWith<$Res> { implements $UpdateCurrentDelaySelectorStateCopyWith<$Res> {
factory _$$CheckIpSelectorStateImplCopyWith(_$CheckIpSelectorStateImpl value, factory _$$UpdateCurrentDelaySelectorStateImplCopyWith(
$Res Function(_$CheckIpSelectorStateImpl) then) = _$UpdateCurrentDelaySelectorStateImpl value,
__$$CheckIpSelectorStateImplCopyWithImpl<$Res>; $Res Function(_$UpdateCurrentDelaySelectorStateImpl) then) =
__$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({bool isInit, bool isStart, Map<String, String> selectedMap}); $Res call(
{String? currentProxyName, bool isCurrent, int? delay, bool isInit});
} }
/// @nodoc /// @nodoc
class __$$CheckIpSelectorStateImplCopyWithImpl<$Res> class __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<$Res>
extends _$CheckIpSelectorStateCopyWithImpl<$Res, _$CheckIpSelectorStateImpl> extends _$UpdateCurrentDelaySelectorStateCopyWithImpl<$Res,
implements _$$CheckIpSelectorStateImplCopyWith<$Res> { _$UpdateCurrentDelaySelectorStateImpl>
__$$CheckIpSelectorStateImplCopyWithImpl(_$CheckIpSelectorStateImpl _value, implements _$$UpdateCurrentDelaySelectorStateImplCopyWith<$Res> {
$Res Function(_$CheckIpSelectorStateImpl) _then) __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl(
_$UpdateCurrentDelaySelectorStateImpl _value,
$Res Function(_$UpdateCurrentDelaySelectorStateImpl) _then)
: super(_value, _then); : super(_value, _then);
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? currentProxyName = freezed,
Object? isCurrent = null,
Object? delay = freezed,
Object? isInit = null, Object? isInit = null,
Object? isStart = null,
Object? selectedMap = null,
}) { }) {
return _then(_$CheckIpSelectorStateImpl( return _then(_$UpdateCurrentDelaySelectorStateImpl(
currentProxyName: freezed == currentProxyName
? _value.currentProxyName
: currentProxyName // ignore: cast_nullable_to_non_nullable
as String?,
isCurrent: null == isCurrent
? _value.isCurrent
: isCurrent // ignore: cast_nullable_to_non_nullable
as bool,
delay: freezed == delay
? _value.delay
: delay // ignore: cast_nullable_to_non_nullable
as int?,
isInit: null == isInit isInit: null == isInit
? _value.isInit ? _value.isInit
: isInit // ignore: cast_nullable_to_non_nullable : isInit // ignore: cast_nullable_to_non_nullable
as bool, as bool,
isStart: null == isStart
? _value.isStart
: isStart // ignore: cast_nullable_to_non_nullable
as bool,
selectedMap: null == selectedMap
? _value._selectedMap
: selectedMap // ignore: cast_nullable_to_non_nullable
as Map<String, String>,
)); ));
} }
} }
/// @nodoc /// @nodoc
class _$CheckIpSelectorStateImpl implements _CheckIpSelectorState { class _$UpdateCurrentDelaySelectorStateImpl
const _$CheckIpSelectorStateImpl( implements _UpdateCurrentDelaySelectorState {
{required this.isInit, const _$UpdateCurrentDelaySelectorStateImpl(
required this.isStart, {required this.currentProxyName,
required final Map<String, String> selectedMap}) required this.isCurrent,
: _selectedMap = selectedMap; required this.delay,
required this.isInit});
@override
final String? currentProxyName;
@override
final bool isCurrent;
@override
final int? delay;
@override @override
final bool isInit; final bool isInit;
@override
final bool isStart;
final Map<String, String> _selectedMap;
@override
Map<String, String> get selectedMap {
if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_selectedMap);
}
@override @override
String toString() { String toString() {
return 'CheckIpSelectorState(isInit: $isInit, isStart: $isStart, selectedMap: $selectedMap)'; return 'UpdateCurrentDelaySelectorState(currentProxyName: $currentProxyName, isCurrent: $isCurrent, delay: $delay, isInit: $isInit)';
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$CheckIpSelectorStateImpl && other is _$UpdateCurrentDelaySelectorStateImpl &&
(identical(other.isInit, isInit) || other.isInit == isInit) && (identical(other.currentProxyName, currentProxyName) ||
(identical(other.isStart, isStart) || other.isStart == isStart) && other.currentProxyName == currentProxyName) &&
const DeepCollectionEquality() (identical(other.isCurrent, isCurrent) ||
.equals(other._selectedMap, _selectedMap)); other.isCurrent == isCurrent) &&
(identical(other.delay, delay) || other.delay == delay) &&
(identical(other.isInit, isInit) || other.isInit == isInit));
} }
@override @override
int get hashCode => Object.hash(runtimeType, isInit, isStart, int get hashCode =>
const DeepCollectionEquality().hash(_selectedMap)); Object.hash(runtimeType, currentProxyName, isCurrent, delay, isInit);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl> _$$UpdateCurrentDelaySelectorStateImplCopyWith<
get copyWith => _$UpdateCurrentDelaySelectorStateImpl>
__$$CheckIpSelectorStateImplCopyWithImpl<_$CheckIpSelectorStateImpl>( get copyWith => __$$UpdateCurrentDelaySelectorStateImplCopyWithImpl<
this, _$identity); _$UpdateCurrentDelaySelectorStateImpl>(this, _$identity);
} }
abstract class _CheckIpSelectorState implements CheckIpSelectorState { abstract class _UpdateCurrentDelaySelectorState
const factory _CheckIpSelectorState( implements UpdateCurrentDelaySelectorState {
{required final bool isInit, const factory _UpdateCurrentDelaySelectorState(
required final bool isStart, {required final String? currentProxyName,
required final Map<String, String> selectedMap}) = required final bool isCurrent,
_$CheckIpSelectorStateImpl; required final int? delay,
required final bool isInit}) = _$UpdateCurrentDelaySelectorStateImpl;
@override
String? get currentProxyName;
@override
bool get isCurrent;
@override
int? get delay;
@override @override
bool get isInit; bool get isInit;
@override @override
bool get isStart;
@override
Map<String, String> get selectedMap;
@override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$CheckIpSelectorStateImplCopyWith<_$CheckIpSelectorStateImpl> _$$UpdateCurrentDelaySelectorStateImplCopyWith<
_$UpdateCurrentDelaySelectorStateImpl>
get copyWith => throw _privateConstructorUsedError; get copyWith => throw _privateConstructorUsedError;
} }

View File

@@ -1,70 +0,0 @@
class IpInfo {
final String ip;
final String countryCode;
const IpInfo({
required this.ip,
required this.countryCode,
});
static IpInfo fromIpInfoIoJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country": final String country,
} =>
IpInfo(
ip: ip,
countryCode: country,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpApiCoJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpSbJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
static IpInfo fromIpwhoIsJson(Map<String, dynamic> json) {
return switch (json) {
{
"ip": final String ip,
"country_code": final String countryCode,
} =>
IpInfo(
ip: ip,
countryCode: countryCode,
),
_ => throw const FormatException("invalid json"),
};
}
@override
String toString() {
return 'IpInfo{ip: $ip, countryCode: $countryCode}';
}
}

View File

@@ -9,8 +9,8 @@ export 'log.dart';
export 'system_color_scheme.dart'; export 'system_color_scheme.dart';
export 'connection.dart'; export 'connection.dart';
export 'package.dart'; export 'package.dart';
export 'common.dart';
export 'ffi.dart'; export 'ffi.dart';
export 'selector.dart'; export 'selector.dart';
export 'navigation.dart'; export 'navigation.dart';
export 'dav.dart'; export 'dav.dart';
export 'ip.dart';

View File

@@ -11,7 +11,6 @@ class NavigationItem with _$NavigationItem {
required String label, required String label,
final String? description, final String? description,
required Widget fragment, required Widget fragment,
@Default(true) bool keep,
String? path, String? path,
@Default([NavigationItemMode.mobile, NavigationItemMode.desktop]) @Default([NavigationItemMode.mobile, NavigationItemMode.desktop])
List<NavigationItemMode> modes, List<NavigationItemMode> modes,

View File

@@ -5,8 +5,11 @@ import 'dart:typed_data';
import 'package:fl_clash/clash/core.dart'; import 'package:fl_clash/clash/core.dart';
import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/models.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'common.dart';
part 'generated/profile.g.dart'; part 'generated/profile.g.dart';
typedef SelectedMap = Map<String, String>; typedef SelectedMap = Map<String, String>;
@@ -16,17 +19,16 @@ class UserInfo {
int upload; int upload;
int download; int download;
int total; int total;
int expire; int? expire;
UserInfo({ UserInfo({
int? upload, int? upload,
int? download, int? download,
int? total, int? total,
int? expire, this.expire,
}) : upload = upload ?? 0, }) : upload = upload ?? 0,
download = download ?? 0, download = download ?? 0,
total = total ?? 0, total = total ?? 0;
expire = expire ?? 0;
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return _$UserInfoToJson(this); return _$UserInfoToJson(this);
@@ -38,10 +40,10 @@ class UserInfo {
factory UserInfo.formHString(String? info) { factory UserInfo.formHString(String? info) {
if (info == null) return UserInfo(); if (info == null) return UserInfo();
final list = info.split(";"); var list = info.split(";");
Map<String, int?> map = {}; Map<String, int?> map = {};
for (final i in list) { for (var i in list) {
final keyValue = i.trim().split("="); var keyValue = i.trim().split("=");
map[keyValue[0]] = int.tryParse(keyValue[1]); map[keyValue[0]] = int.tryParse(keyValue[1]);
} }
return UserInfo( return UserInfo(
@@ -81,29 +83,44 @@ class Profile {
Duration? autoUpdateDuration, Duration? autoUpdateDuration,
this.autoUpdate = true, this.autoUpdate = true,
}) : id = id ?? DateTime.now().millisecondsSinceEpoch.toString(), }) : id = id ?? DateTime.now().millisecondsSinceEpoch.toString(),
autoUpdateDuration = autoUpdateDuration ?? defaultUpdateDuration, autoUpdateDuration =
autoUpdateDuration ?? defaultUpdateDuration,
selectedMap = selectedMap ?? {}; selectedMap = selectedMap ?? {};
ProfileType get type => ProfileType get type => url == null ? ProfileType.file : ProfileType.url;
url == null || url?.isEmpty == true ? ProfileType.file : ProfileType.url;
Future<void> checkAndUpdate() async { Future<Result<bool>> checkAndUpdate() async {
final isExists = await check(); final isExists = await check();
if (!isExists) { if(!isExists){
if (url != null) { if(url != null){
return await update(); return await update();
} }
return Result.error();
} }
return Result.success();
} }
Future<void> update() async { Future<Result<bool>> update() async {
final response = await request.getFileResponseForUrl(url!); if (url == null) {
final disposition = response.headers.value("content-disposition"); return Result.error(
appLocalizations.unableToUpdateCurrentProfileDesc,
);
}
final responseResult = await Request.getFileResponseForUrl(url!);
final response = responseResult.data;
if (responseResult.type != ResultType.success || response == null) {
return Result.error(responseResult.message);
}
final disposition = response.headers['content-disposition'];
label ??= other.getFileNameForDisposition(disposition) ?? id; label ??= other.getFileNameForDisposition(disposition) ?? id;
final userinfo = response.headers.value('subscription-userinfo'); final userinfo = response.headers['subscription-userinfo'];
userInfo = UserInfo.formHString(userinfo); userInfo = UserInfo.formHString(userinfo);
await saveFile(response.data); final saveResult = await saveFile(response.bodyBytes);
if (saveResult.type == ResultType.error) {
return Result.error(saveResult.message);
}
lastUpdateDate = DateTime.now(); lastUpdateDate = DateTime.now();
return Result.success();
} }
Future<bool> check() async { Future<bool> check() async {
@@ -111,10 +128,10 @@ class Profile {
return await File(profilePath!).exists(); return await File(profilePath!).exists();
} }
Future<void> saveFile(Uint8List bytes) async { Future<Result<void>> saveFile(Uint8List bytes) async {
final message = await clashCore.validateConfig(utf8.decode(bytes)); final isValidate = clashCore.validateConfig(utf8.decode(bytes));
if (message.isNotEmpty) { if (!isValidate) {
throw message; return Result.error(appLocalizations.profileParseErrorDesc);
} }
final path = await appPath.getProfilePath(id); final path = await appPath.getProfilePath(id);
final file = File(path!); final file = File(path!);
@@ -124,6 +141,7 @@ class Profile {
} }
await file.writeAsBytes(bytes); await file.writeAsBytes(bytes);
lastUpdateDate = DateTime.now(); lastUpdateDate = DateTime.now();
return Result.success();
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {

View File

@@ -1,7 +1,10 @@
import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'config.dart';
import 'navigation.dart';
import 'profile.dart';
import 'proxy.dart';
part 'generated/selector.freezed.dart'; part 'generated/selector.freezed.dart';
@@ -14,12 +17,13 @@ class StartButtonSelectorState with _$StartButtonSelectorState {
} }
@freezed @freezed
class CheckIpSelectorState with _$CheckIpSelectorState { class UpdateCurrentDelaySelectorState with _$UpdateCurrentDelaySelectorState {
const factory CheckIpSelectorState({ const factory UpdateCurrentDelaySelectorState({
required String? currentProxyName,
required bool isCurrent,
required int? delay,
required bool isInit, required bool isInit,
required bool isStart, }) = _UpdateCurrentDelaySelectorState;
required SelectedMap selectedMap,
}) = _CheckIpSelectorState;
} }
@freezed @freezed
@@ -49,23 +53,24 @@ class ApplicationSelectorState with _$ApplicationSelectorState {
} }
@freezed @freezed
class TrayContainerSelectorState with _$TrayContainerSelectorState { class TrayContainerSelectorState with _$TrayContainerSelectorState{
const factory TrayContainerSelectorState({ const factory TrayContainerSelectorState({
required Mode mode, required Mode mode,
required bool autoLaunch, required bool autoLaunch,
required bool isRun, required bool isRun,
required String? locale, required String? locale,
}) = _TrayContainerSelectorState; })=_TrayContainerSelectorState;
} }
@freezed @freezed
class UpdateNavigationsSelector with _$UpdateNavigationsSelector { class UpdateNavigationsSelector with _$UpdateNavigationsSelector{
const factory UpdateNavigationsSelector({ const factory UpdateNavigationsSelector({
required bool openLogs, required bool openLogs,
required bool hasProxies, required bool hasProxies,
}) = _UpdateNavigationsSelector; }) = _UpdateNavigationsSelector;
} }
@freezed @freezed
class HomeSelectorState with _$HomeSelectorState { class HomeSelectorState with _$HomeSelectorState {
const factory HomeSelectorState({ const factory HomeSelectorState({
@@ -84,21 +89,21 @@ class HomeBodySelectorState with _$HomeBodySelectorState {
} }
@freezed @freezed
class ProxiesCardSelectorState with _$ProxiesCardSelectorState { class ProxiesCardSelectorState with _$ProxiesCardSelectorState{
const factory ProxiesCardSelectorState({ const factory ProxiesCardSelectorState({
required bool isSelected, required bool isSelected,
}) = _ProxiesCardSelectorState; }) = _ProxiesCardSelectorState;
} }
@freezed @freezed
class ProxiesSelectorState with _$ProxiesSelectorState { class ProxiesSelectorState with _$ProxiesSelectorState{
const factory ProxiesSelectorState({ const factory ProxiesSelectorState({
required List<String> groupNames, required List<String> groupNames,
}) = _ProxiesSelectorState; }) = _ProxiesSelectorState;
} }
@freezed @freezed
class ProxiesTabViewSelectorState with _$ProxiesTabViewSelectorState { class ProxiesTabViewSelectorState with _$ProxiesTabViewSelectorState{
const factory ProxiesTabViewSelectorState({ const factory ProxiesTabViewSelectorState({
required ProxiesSortType proxiesSortType, required ProxiesSortType proxiesSortType,
required num sortNum, required num sortNum,
@@ -120,4 +125,4 @@ class PackageListSelectorState with _$PackageListSelectorState {
required AccessControl accessControl, required AccessControl accessControl,
required bool isAccessControl, required bool isAccessControl,
}) = _PackageListSelectorState; }) = _PackageListSelectorState;
} }

View File

@@ -1,4 +1,3 @@
import 'package:fl_clash/common/common.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/state.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -57,65 +56,54 @@ class HomePage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return PopContainer( return PopContainer(
child: LayoutBuilder( child: Selector2<AppState, Config, HomeSelectorState>(
builder: (_, container) { selector: (_, appState, config) => HomeSelectorState(
final appController = globalState.appController; currentLabel: appState.currentLabel,
final maxWidth = container.maxWidth; navigationItems: appState.currentNavigationItems,
if (appController.appState.viewWidth != maxWidth) { viewMode: appState.viewMode,
globalState.appController.updateViewWidth(maxWidth); locale: config.locale,
),
builder: (_, state, child) {
final viewMode = state.viewMode;
final navigationItems = state.navigationItems;
final currentLabel = state.currentLabel;
final index = navigationItems.lastIndexWhere(
(element) => element.label == currentLabel,
);
final currentIndex = index == -1 ? 0 : index;
final navigationBar = _getNavigationBar(
viewMode: viewMode,
navigationItems: navigationItems,
currentIndex: currentIndex,
);
final bottomNavigationBar =
viewMode == ViewMode.mobile ? navigationBar : null;
Widget body;
if (viewMode != ViewMode.mobile) {
body = Row(
children: [
navigationBar,
Expanded(
flex: 1,
child: child!,
)
],
);
} else {
body = child!;
} }
return Selector2<AppState, Config, HomeSelectorState>( return CommonScaffold(
selector: (_, appState, config) { key: globalState.homeScaffoldKey,
return HomeSelectorState( title: Intl.message(
currentLabel: appState.currentLabel, currentLabel,
navigationItems: appState.currentNavigationItems,
viewMode: other.getViewMode(maxWidth),
locale: config.locale,
);
},
builder: (_, state, child) {
final viewMode = state.viewMode;
final navigationItems = state.navigationItems;
final currentLabel = state.currentLabel;
final index = navigationItems.lastIndexWhere(
(element) => element.label == currentLabel,
);
final currentIndex = index == -1 ? 0 : index;
final navigationBar = _getNavigationBar(
viewMode: viewMode,
navigationItems: navigationItems,
currentIndex: currentIndex,
);
final bottomNavigationBar =
viewMode == ViewMode.mobile ? navigationBar : null;
Widget body;
if (viewMode != ViewMode.mobile) {
body = Row(
children: [
navigationBar,
Expanded(
flex: 1,
child: child!,
)
],
);
} else {
body = child!;
}
return CommonScaffold(
key: globalState.homeScaffoldKey,
title: Intl.message(
currentLabel,
),
body: body,
bottomNavigationBar: bottomNavigationBar,
);
},
child: const HomeBody(
key: Key("home_boy"),
), ),
body: body,
bottomNavigationBar: bottomNavigationBar,
); );
}, },
child: const HomeBody(
key: Key("home_boy"),
),
), ),
); );
} }
@@ -132,7 +120,7 @@ class HomeBody extends StatelessWidget {
final currentIndex = index == -1 ? 0 : index; final currentIndex = index == -1 ? 0 : index;
if (globalState.pageController != null) { if (globalState.pageController != null) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
globalState.appController.toPage(currentIndex, hasAnimate: true); globalState.appController.toPage(currentIndex);
}); });
} else { } else {
globalState.pageController = PageController( globalState.pageController = PageController(
@@ -158,7 +146,6 @@ class HomeBody extends StatelessWidget {
itemBuilder: (_, index) { itemBuilder: (_, index) {
final navigationItem = navigationItems[index]; final navigationItem = navigationItems[index];
return KeepContainer( return KeepContainer(
keep: navigationItem.keep,
key: Key(navigationItem.label), key: Key(navigationItem.label),
child: navigationItem.fragment, child: navigationItem.fragment,
); );

View File

@@ -70,7 +70,7 @@ class Proxy extends ProxyPlatform {
}); });
} }
bool get isStart => startTime != null && startTime!.isBeforeNow; bool get isStart => startTime != null && startTime!.isBeforeNow();
startAfterHook(int? fd) { startAfterHook(int? fd) {
if (!isStart && fd != null) { if (!isStart && fd != null) {

View File

@@ -13,6 +13,7 @@ import 'common/common.dart';
class GlobalState { class GlobalState {
Timer? timer; Timer? timer;
Function? healthcheckLockDebounce;
Timer? groupsUpdateTimer; Timer? groupsUpdateTimer;
Function? updateCurrentDelayDebounce; Function? updateCurrentDelayDebounce;
PageController? pageController; PageController? pageController;
@@ -21,6 +22,7 @@ class GlobalState {
late AppController appController; late AppController appController;
GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey(); GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
List<Function> updateFunctionLists = []; List<Function> updateFunctionLists = [];
bool healthcheckLock = false;
startListenUpdate() { startListenUpdate() {
if (timer != null && timer!.isActive == true) return; if (timer != null && timer!.isActive == true) return;
@@ -36,7 +38,7 @@ class GlobalState {
timer?.cancel(); timer?.cancel();
} }
Future<void> updateClashConfig({ Future<String> updateClashConfig({
required ClashConfig clashConfig, required ClashConfig clashConfig,
required Config config, required Config config,
bool isPatch = true, bool isPatch = true,
@@ -44,13 +46,12 @@ class GlobalState {
final profilePath = await appPath.getProfilePath(config.currentProfileId); final profilePath = await appPath.getProfilePath(config.currentProfileId);
await config.currentProfile?.checkAndUpdate(); await config.currentProfile?.checkAndUpdate();
debugPrint("update config"); debugPrint("update config");
final res = await clashCore.updateConfig(UpdateConfigParams( return clashCore.updateConfig(UpdateConfigParams(
profilePath: profilePath, profilePath: profilePath,
config: clashConfig, config: clashConfig,
isPatch: isPatch, isPatch: isPatch,
isCompatible: config.isCompatible, isCompatible: config.isCompatible,
)); ));
if (res.isNotEmpty) throw res;
} }
updateCoreVersionInfo(AppState appState) { updateCoreVersionInfo(AppState appState) {
@@ -75,16 +76,17 @@ class GlobalState {
stopListenUpdate(); stopListenUpdate();
} }
Future<void> applyProfile({ applyProfile({
required AppState appState, required AppState appState,
required Config config, required Config config,
required ClashConfig clashConfig, required ClashConfig clashConfig,
}) async { }) async {
await updateClashConfig( final res = await updateClashConfig(
clashConfig: clashConfig, clashConfig: clashConfig,
config: config, config: config,
isPatch: false, isPatch: false,
); );
if (res.isNotEmpty) return Result.error(res);
await updateGroups(appState); await updateGroups(appState);
changeProxy( changeProxy(
appState: appState, appState: appState,
@@ -119,17 +121,19 @@ class GlobalState {
required Config config, required Config config,
required ClashConfig clashConfig, required ClashConfig clashConfig,
}) { }) {
if (config.profiles.isEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) {
stopSystemProxy(); if (config.profiles.isEmpty) {
return; stopSystemProxy();
} return;
config.currentSelectedMap.forEach((key, value) { }
clashCore.changeProxy( config.currentSelectedMap.forEach((key, value) {
ChangeProxyParams( clashCore.changeProxy(
groupName: key, ChangeProxyParams(
proxyName: value, groupName: key,
), proxyName: value,
); ),
);
});
}); });
} }
@@ -163,7 +167,9 @@ class GlobalState {
title: Text(title), title: Text(title),
content: Container( content: Container(
width: 300, width: 300,
constraints: const BoxConstraints(maxHeight: 200), constraints: const BoxConstraints(
maxHeight: 200
),
child: SingleChildScrollView( child: SingleChildScrollView(
child: RichText( child: RichText(
overflow: TextOverflow.visible, overflow: TextOverflow.visible,
@@ -251,22 +257,18 @@ class GlobalState {
); );
} }
Future<T?> safeRun<T>( void updateCurrentDelay(
FutureOr<T> Function() futureFunction, { String? proxyName,
String? title, ) {
}) async { updateCurrentDelayDebounce ??= debounce<Function(String?)>((proxyName) {
try { if (proxyName != null) {
final res = await futureFunction(); debugPrint("[delay]=====> $proxyName");
return res; clashCore.delay(
} catch (e) { proxyName,
showMessage( );
title: title ?? appLocalizations.tip, }
message: TextSpan( });
text: e.toString(), updateCurrentDelayDebounce!([proxyName]);
),
);
return null;
}
} }
} }

View File

@@ -38,8 +38,16 @@ class _ClashMessageContainerState extends State<ClashMessageContainer>
@override @override
void onDelay(Delay delay) { void onDelay(Delay delay) {
globalState.healthcheckLock = true;
final appController = globalState.appController; final appController = globalState.appController;
appController.setDelay(delay); appController.setDelay(delay);
globalState.healthcheckLockDebounce ??= debounce<Function()>(
() async {
globalState.healthcheckLock = false;
},
milliseconds: 5000,
);
globalState.healthcheckLockDebounce!();
super.onDelay(delay); super.onDelay(delay);
} }

View File

@@ -2,13 +2,8 @@ import 'package:flutter/material.dart';
class KeepContainer extends StatefulWidget { class KeepContainer extends StatefulWidget {
final Widget child; final Widget child;
final bool keep;
const KeepContainer({ const KeepContainer({super.key, required this.child});
super.key,
required this.child,
this.keep = true,
});
@override @override
State<KeepContainer> createState() => _KeepContainerState(); State<KeepContainer> createState() => _KeepContainerState();
@@ -16,6 +11,7 @@ class KeepContainer extends StatefulWidget {
class _KeepContainerState extends State<KeepContainer> class _KeepContainerState extends State<KeepContainer>
with AutomaticKeepAliveClientMixin { with AutomaticKeepAliveClientMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
@@ -23,5 +19,5 @@ class _KeepContainerState extends State<KeepContainer>
} }
@override @override
bool get wantKeepAlive => widget.keep; bool get wantKeepAlive => true;
} }

View File

@@ -71,7 +71,6 @@ class ListItem<T> extends StatelessWidget {
final Widget title; final Widget title;
final Widget? subtitle; final Widget? subtitle;
final EdgeInsets padding; final EdgeInsets padding;
final ListTileTitleAlignment tileTitleAlignment;
final bool? prue; final bool? prue;
final Widget? trailing; final Widget? trailing;
final Delegate delegate; final Delegate delegate;
@@ -88,7 +87,6 @@ class ListItem<T> extends StatelessWidget {
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.onTab, this.onTab,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : delegate = const Delegate(); }) : delegate = const Delegate();
const ListItem.open({ const ListItem.open({
@@ -101,7 +99,6 @@ class ListItem<T> extends StatelessWidget {
required OpenDelegate this.delegate, required OpenDelegate this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTab = null; }) : onTab = null;
const ListItem.next({ const ListItem.next({
@@ -114,7 +111,6 @@ class ListItem<T> extends StatelessWidget {
required NextDelegate this.delegate, required NextDelegate this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTab = null; }) : onTab = null;
const ListItem.checkbox({ const ListItem.checkbox({
@@ -126,7 +122,6 @@ class ListItem<T> extends StatelessWidget {
required CheckboxDelegate this.delegate, required CheckboxDelegate this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : trailing = null, }) : trailing = null,
onTab = null; onTab = null;
@@ -139,7 +134,6 @@ class ListItem<T> extends StatelessWidget {
required SwitchDelegate this.delegate, required SwitchDelegate this.delegate,
this.horizontalTitleGap, this.horizontalTitleGap,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : trailing = null, }) : trailing = null,
onTab = null; onTab = null;
@@ -152,7 +146,6 @@ class ListItem<T> extends StatelessWidget {
required RadioDelegate<T> this.delegate, required RadioDelegate<T> this.delegate,
this.horizontalTitleGap = 8, this.horizontalTitleGap = 8,
this.prue, this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : leading = null, }) : leading = null,
onTab = null; onTab = null;
@@ -200,7 +193,6 @@ class ListItem<T> extends StatelessWidget {
horizontalTitleGap: horizontalTitleGap, horizontalTitleGap: horizontalTitleGap,
title: title, title: title,
subtitle: subtitle, subtitle: subtitle,
titleAlignment: tileTitleAlignment,
onTap: onTab, onTap: onTab,
trailing: trailing ?? this.trailing, trailing: trailing ?? this.trailing,
contentPadding: padding, contentPadding: padding,

View File

@@ -49,7 +49,6 @@ class CommonScaffold extends StatefulWidget {
class CommonScaffoldState extends State<CommonScaffold> { class CommonScaffoldState extends State<CommonScaffold> {
final ValueNotifier<List<Widget>> _actions = ValueNotifier([]); final ValueNotifier<List<Widget>> _actions = ValueNotifier([]);
final ValueNotifier<Widget?> _floatingActionButton = ValueNotifier(null);
final ValueNotifier<bool> _loading = ValueNotifier(false); final ValueNotifier<bool> _loading = ValueNotifier(false);
@@ -59,16 +58,11 @@ class CommonScaffoldState extends State<CommonScaffold> {
} }
} }
set floatingActionButton(Widget? actions) {
if (_floatingActionButton.value != actions) {
_floatingActionButton.value = actions;
}
}
Future<T?> loadingRun<T>( Future<T?> loadingRun<T>(
Future<T> Function() futureFunction, { Future<T> Function() futureFunction, {
String? title, String? title,
}) async { }) async {
if (_loading.value == true) return null;
_loading.value = true; _loading.value = true;
try { try {
final res = await futureFunction(); final res = await futureFunction();
@@ -91,7 +85,6 @@ class CommonScaffoldState extends State<CommonScaffold> {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (oldWidget.title != widget.title) { if (oldWidget.title != widget.title) {
_actions.value = []; _actions.value = [];
_floatingActionButton.value = null;
} }
} }
@@ -116,13 +109,6 @@ class CommonScaffoldState extends State<CommonScaffold> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _platformContainer( return _platformContainer(
child: Scaffold( child: Scaffold(
floatingActionButton: ValueListenableBuilder(
valueListenable: _floatingActionButton,
builder: (_, floatingActionButton, __) {
return floatingActionButton ?? Container();
},
),
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
appBar: PreferredSize( appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight), preferredSize: const Size.fromHeight(kToolbarHeight),
child: Stack( child: Stack(

View File

@@ -27,6 +27,11 @@ class _WindowContainerState extends State<WindowContainer>
windowManager.addListener(this); windowManager.addListener(this);
} }
@override
void onWindowResize() {
globalState.appController.updateViewWidth();
}
@override @override
void onWindowClose() async { void onWindowClose() async {
await globalState.appController.handleBackOrExit(); await globalState.appController.handleBackOrExit();

View File

@@ -37,10 +37,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.6.1" version: "3.5.1"
args: args:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -85,10 +85,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build_daemon name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "4.0.2" version: "4.0.1"
build_resolvers: build_resolvers:
dependency: transitive dependency: transitive
description: description:
@@ -101,18 +101,18 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.11" version: "2.4.9"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "7.3.1" version: "7.3.0"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@@ -193,14 +193,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
country_flags:
dependency: "direct main"
description:
name: country_flags
sha256: dbc4f76e7c801619b2d841023e0327191ba00663f1f1b4770394e9bc6632c444
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
@@ -226,7 +218,7 @@ packages:
source: hosted source: hosted
version: "2.3.6" version: "2.3.6"
dio: dio:
dependency: "direct main" dependency: transitive
description: description:
name: dio name: dio
sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5"
@@ -277,10 +269,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: file_picker name: file_picker
sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a" sha256: "1bbf65dd997458a08b531042ec3794112a6c39c07c37ff22113d2e7e4f81d4e4"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "8.0.3" version: "6.2.1"
file_selector_linux: file_selector_linux:
dependency: transitive dependency: transitive
description: description:
@@ -343,10 +335,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e sha256: "592dc01a18961a51c24ae5d963b724b2b7fa4a95c100fe8eb6ca8a5a4732cadf"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.0.20" version: "2.0.18"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@@ -361,10 +353,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: freezed name: freezed
sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 sha256: "91bce569d4805ea5bad6619a3e8690df8ad062a235165af4c0c5d928dda15eaf"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.5.2" version: "2.5.1"
freezed_annotation: freezed_annotation:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -406,7 +398,7 @@ packages:
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
http: http:
dependency: transitive dependency: "direct main"
description: description:
name: http name: http
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
@@ -433,42 +425,42 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: image name: image
sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "4.2.0" version: "4.1.7"
image_picker: image_picker:
dependency: "direct main" dependency: "direct main"
description: description:
name: image_picker name: image_picker
sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" sha256: "33974eca2e87e8b4e3727f1b94fa3abcb25afe80b6bc2c4d449a0e150aedf720"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.2" version: "1.1.1"
image_picker_android: image_picker_android:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: "4161e1f843d8480d2e9025ee22411778c3c9eb7e40076dcf2da23d8242b7b51c" sha256: "79455f6cff4cbef583b2b524bbf0d4ec424e5959f4d464e36ef5323715b98370"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.8.12+3" version: "0.8.12"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
name: image_picker_for_web name: image_picker_for_web
sha256: "5d6eb13048cd47b60dbf1a5495424dea226c5faf3950e20bf8120a58efb5b5f3" sha256: "6a1704fdd75022272e7e7a897a9068e9c2ff3cd6a66820bf3ded810633eac954"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.0.4" version: "3.0.3"
image_picker_ios: image_picker_ios:
dependency: transitive dependency: transitive
description: description:
name: image_picker_ios name: image_picker_ios
sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" sha256: cb0db0ec0d3e2cd49674f2e6053be25ccdb959832607c1cbd215dd6cf10fb0dd
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.8.12" version: "0.8.11"
image_picker_linux: image_picker_linux:
dependency: transitive dependency: transitive
description: description:
@@ -517,22 +509,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
jovial_misc:
dependency: transitive
description:
name: jovial_misc
sha256: f6e64f789ee311025bb367be9c9afe9759f76dd8209070b7f38e735b5f529eb1
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.8.5"
jovial_svg:
dependency: transitive
description:
name: jovial_svg
sha256: "000cafe183bdeba28f76d65bf93fc9b636d6f7eaa7e2a33e80c4345a28866ea8"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.21"
js: js:
dependency: transitive dependency: transitive
description: description:
@@ -705,18 +681,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514" sha256: "51f0d2c554cfbc9d6a312ab35152fc77e2f0b758ce9f1a444a3a1e5b8f3c6b7f"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.2.5" version: "2.2.3"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
name: path_provider_foundation name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.0" version: "2.3.2"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@@ -765,14 +741,6 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.9.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@@ -808,10 +776,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: pubspec_parse name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.0" version: "1.2.3"
quiver: quiver:
dependency: transitive dependency: transitive
description: description:
@@ -840,18 +808,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.2.3" version: "2.2.1"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_foundation name: shared_preferences_foundation
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.4.0" version: "2.3.5"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
@@ -896,10 +864,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shelf_web_socket name: shelf_web_socket
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.0.0" version: "1.0.4"
shortid: shortid:
dependency: transitive dependency: transitive
description: description:
@@ -997,10 +965,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: tray_manager name: tray_manager
sha256: c9a63fd88bd3546287a7eb8ccc978d707eef82c775397af17dda3a4f4c039e64 sha256: e0ac9a88b2700f366b8629b97e8663b6ef450a2f169560a685dc167bfe9c9c29
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.3" version: "0.2.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@@ -1013,26 +981,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.3.0" version: "6.2.6"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: ceb2625f0c24ade6ef6778d1de0b2e44f2db71fded235eb52295247feba8c5cf sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.3.3" version: "6.3.0"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89" sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "6.3.0" version: "6.2.5"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@@ -1045,10 +1013,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_macos name: url_launcher_macos
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.2.0" version: "3.1.0"
url_launcher_platform_interface: url_launcher_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -1061,10 +1029,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.3.1" version: "2.3.0"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
@@ -1105,22 +1073,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.5.1" version: "0.5.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "24301d8c293ce6fe327ffe6f59d8fd8834735f0ec36e4fd383ec7ff8a64aa078"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.5"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
name: web_socket_channel name: web_socket_channel
sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276 sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "3.0.0" version: "2.4.5"
webdav_client: webdav_client:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1133,26 +1093,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "5.5.1" version: "5.5.0"
win32_registry: win32_registry:
dependency: "direct main" dependency: "direct main"
description: description:
name: win32_registry name: win32_registry
sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a"
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.3" version: "1.1.2"
window_manager: window_manager:
dependency: "direct main" dependency: "direct main"
description: description:
name: window_manager name: window_manager
sha256: "8699323b30da4cdbe2aa2e7c9de567a6abd8a97d9a5c850a3c86dcd0b34bbfbf" sha256: b3c895bdf936c77b83c5254bec2e6b3f066710c1f89c38b20b8acc382b525494
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.3.9" version: "0.3.8"
windows_single_instance: windows_single_instance:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -1202,5 +1162,5 @@ packages:
source: hosted source: hosted
version: "0.2.3" version: "0.2.3"
sdks: sdks:
dart: ">=3.4.0 <4.0.0" dart: ">=3.3.0 <4.0.0"
flutter: ">=3.22.0" flutter: ">=3.19.0"

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.8.16 version: 0.8.8
environment: environment:
sdk: '>=3.1.0 <4.0.0' sdk: '>=3.1.0 <4.0.0'
@@ -23,7 +23,8 @@ dependencies:
launch_at_startup: ^0.2.2 launch_at_startup: ^0.2.2
windows_single_instance: ^1.0.1 windows_single_instance: ^1.0.1
json_annotation: ^4.9.0 json_annotation: ^4.9.0
file_picker: ^8.0.3 http: ^1.2.0
file_picker: ^6.1.1
mobile_scanner: 5.0.1 mobile_scanner: 5.0.1
app_links: ^3.5.0 app_links: ^3.5.0
win32_registry: ^1.1.2 win32_registry: ^1.1.2
@@ -33,12 +34,10 @@ dependencies:
package_info_plus: ^7.0.0 package_info_plus: ^7.0.0
url_launcher: ^6.2.6 url_launcher: ^6.2.6
freezed_annotation: ^2.4.1 freezed_annotation: ^2.4.1
image_picker: ^1.1.2 image_picker: ^1.1.1
zxing2: ^0.2.3 zxing2: ^0.2.3
image: ^4.1.7 image: ^4.1.7
webdav_client: ^1.2.2 webdav_client: ^1.2.2
dio: ^5.4.3+1
country_flags: ^2.2.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
@@ -52,7 +51,7 @@ dev_dependencies:
flutter: flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- assets/data/ - assets/data/geoip.metadb
- assets/images/ - assets/images/
ffigen: ffigen:
name: "ClashFFI" name: "ClashFFI"

View File

@@ -1,4 +1,5 @@
// ignore_for_file: avoid_print // ignore_for_file: avoid_print
void main() async { void main() async {
String input = """ String input = """