Compare commits
1 Commits
v0.8.89
...
v0.8.81-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8c0519ffe |
21
.github/workflows/build.yaml
vendored
21
.github/workflows/build.yaml
vendored
@@ -4,6 +4,8 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
env:
|
||||
IS_STABLE: ${{ !contains(github.ref, '-') }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -74,7 +76,7 @@ jobs:
|
||||
run: flutter pub get
|
||||
|
||||
- name: Setup
|
||||
run: dart setup.dart ${{ matrix.platform }} ${{ matrix.arch && format('--arch {0}', matrix.arch) }}
|
||||
run: dart setup.dart ${{ matrix.platform }} ${{ matrix.arch && format('--arch {0}', matrix.arch) }} ${{ env.IS_STABLE == 'true' && '--env stable' || '' }}
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -88,14 +90,13 @@ jobs:
|
||||
needs: [ build ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
if: ${{ !contains(github.ref, '+') }}
|
||||
uses: actions/checkout@v4
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: refs/heads/main
|
||||
|
||||
- name: Generate
|
||||
if: ${{ !contains(github.ref, '+') }}
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
run: |
|
||||
tags=($(git tag --merged $(git rev-parse HEAD) --sort=-creatordate))
|
||||
preTag=$(grep -oP '^## \K.*' CHANGELOG.md | head -n 1)
|
||||
@@ -127,7 +128,7 @@ jobs:
|
||||
cat NEW_CHANGELOG.md > CHANGELOG.md
|
||||
|
||||
- name: Commit
|
||||
if: ${{ !contains(github.ref, '+') }}
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
run: |
|
||||
git add CHANGELOG.md
|
||||
if ! git diff --cached --quiet; then
|
||||
@@ -206,7 +207,7 @@ jobs:
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
python release.py
|
||||
python release_telegram.py
|
||||
|
||||
- name: Patch release.md
|
||||
run: |
|
||||
@@ -214,21 +215,21 @@ jobs:
|
||||
sed "s|VERSION|$version|g" ./.github/release_template.md >> release.md
|
||||
|
||||
- name: Release
|
||||
if: ${{ !contains(github.ref, '+') }}
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: ./dist/*
|
||||
body_path: './release.md'
|
||||
|
||||
- name: Create Fdroid Source Dir
|
||||
if: ${{ !contains(github.ref, '+') }}
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
run: |
|
||||
mkdir -p ./tmp
|
||||
cp ./dist/*android-arm64-v8a* ./tmp/ || true
|
||||
echo "Files copied successfully"
|
||||
|
||||
- name: Push to fdroid repo
|
||||
if: ${{ !contains(github.ref, '+') }}
|
||||
if: ${{ env.IS_STABLE == 'true' }}
|
||||
uses: cpina/github-action-push-to-another-repository@v1.7.2
|
||||
env:
|
||||
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
|
||||
@@ -238,7 +239,7 @@ jobs:
|
||||
destination-repository-name: FlClash-fdroid-repo
|
||||
user-name: 'github-actions[bot]'
|
||||
user-email: 'github-actions[bot]@users.noreply.github.com'
|
||||
target-branch: action-pr
|
||||
target-branch: main
|
||||
commit-message: Update from ${{ github.ref_name }}
|
||||
target-directory: /tmp/
|
||||
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,7 +1,7 @@
|
||||
[submodule "core/Clash.Meta"]
|
||||
path = core/Clash.Meta
|
||||
url = git@github.com:chen08209/Clash.Meta.git
|
||||
branch = FlClash-Alpha
|
||||
branch = FlClash
|
||||
[submodule "plugins/flutter_distributor"]
|
||||
path = plugins/flutter_distributor
|
||||
url = git@github.com:chen08209/flutter_distributor.git
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
|
||||
analyzer:
|
||||
plugins:
|
||||
- custom_lint
|
||||
|
||||
@@ -33,7 +33,7 @@ def isRelease = defStoreFile.exists() && defStorePassword != null && defKeyAlias
|
||||
|
||||
android {
|
||||
namespace "com.follow.clash"
|
||||
compileSdkVersion 34
|
||||
compileSdkVersion 35
|
||||
ndkVersion "27.1.12297006"
|
||||
|
||||
compileOptions {
|
||||
@@ -63,7 +63,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "com.follow.clash"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
targetSdkVersion 35
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
||||
@@ -10,14 +10,12 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
|
||||
tools:ignore="SystemPermissionTypo" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
@@ -64,7 +62,9 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<meta-data android:name="io.flutter.embedding.android.EnableImpeller" android:value="false" />
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||
android:value="false" />
|
||||
|
||||
<activity
|
||||
android:name=".TempActivity"
|
||||
@@ -87,7 +87,6 @@
|
||||
<service
|
||||
android:name=".services.FlClashTileService"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:icon="@drawable/ic_stat_name"
|
||||
android:label="FlClash"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||
@@ -125,7 +124,7 @@
|
||||
<service
|
||||
android:name=".services.FlClashVpnService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService" />
|
||||
@@ -138,7 +137,7 @@
|
||||
<service
|
||||
android:name=".services.FlClashService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="specialUse">
|
||||
android:foregroundServiceType="dataSync">
|
||||
<property
|
||||
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="service" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.follow.clash;
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context;
|
||||
import android.content.Context
|
||||
|
||||
class FlClashApplication : Application() {
|
||||
companion object {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.follow.clash
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.follow.clash.plugins.AppPlugin
|
||||
import com.follow.clash.plugins.ServicePlugin
|
||||
import com.follow.clash.plugins.TilePlugin
|
||||
import com.follow.clash.plugins.VpnPlugin
|
||||
import io.flutter.FlutterInjector
|
||||
|
||||
@@ -291,16 +291,18 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
private fun getPackages(): List<Package> {
|
||||
val packageManager = FlClashApplication.getAppContext().packageManager
|
||||
if (packages.isNotEmpty()) return packages
|
||||
packageManager?.getInstalledPackages(PackageManager.GET_META_DATA)?.filter {
|
||||
it.packageName != FlClashApplication.getAppContext().packageName
|
||||
|| it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
|
||||
|| it.packageName == "android"
|
||||
packageManager?.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_PERMISSIONS)
|
||||
?.filter {
|
||||
it.packageName != FlClashApplication.getAppContext().packageName && (
|
||||
it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
|
||||
|| it.packageName == "android"
|
||||
)
|
||||
|
||||
}?.map {
|
||||
}?.map {
|
||||
Package(
|
||||
packageName = it.packageName,
|
||||
label = it.applicationInfo.loadLabel(packageManager).toString(),
|
||||
isSystem = (it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 1,
|
||||
label = it.applicationInfo?.loadLabel(packageManager).toString(),
|
||||
isSystem = (it.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM)) == 1,
|
||||
lastUpdateTime = it.lastUpdateTime
|
||||
)
|
||||
}?.let { packages.addAll(it) }
|
||||
@@ -353,7 +355,7 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
}
|
||||
|
||||
suspend fun getText(text: String): String? {
|
||||
return withContext(Dispatchers.Default){
|
||||
return withContext(Dispatchers.Default) {
|
||||
channel.awaitResult<String>("getText", text)
|
||||
}
|
||||
}
|
||||
@@ -391,31 +393,33 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
|
||||
}.forEach {
|
||||
if (it.name.matches(chinaAppRegex)) return true
|
||||
}
|
||||
ZipFile(File(packageInfo.applicationInfo.publicSourceDir)).use {
|
||||
for (packageEntry in it.entries()) {
|
||||
if (packageEntry.name.startsWith("firebase-")) return false
|
||||
}
|
||||
for (packageEntry in it.entries()) {
|
||||
if (!(packageEntry.name.startsWith("classes") && packageEntry.name.endsWith(
|
||||
".dex"
|
||||
))
|
||||
) {
|
||||
continue
|
||||
packageInfo.applicationInfo?.publicSourceDir?.let {
|
||||
ZipFile(File(it)).use {
|
||||
for (packageEntry in it.entries()) {
|
||||
if (packageEntry.name.startsWith("firebase-")) return false
|
||||
}
|
||||
if (packageEntry.size > 15000000) {
|
||||
return true
|
||||
}
|
||||
val input = it.getInputStream(packageEntry).buffered()
|
||||
val dexFile = try {
|
||||
DexBackedDexFile.fromInputStream(null, input)
|
||||
} catch (e: Exception) {
|
||||
return false
|
||||
}
|
||||
for (clazz in dexFile.classes) {
|
||||
val clazzName =
|
||||
clazz.type.substring(1, clazz.type.length - 1).replace("/", ".")
|
||||
.replace("$", ".")
|
||||
if (clazzName.matches(chinaAppRegex)) return true
|
||||
for (packageEntry in it.entries()) {
|
||||
if (!(packageEntry.name.startsWith("classes") && packageEntry.name.endsWith(
|
||||
".dex"
|
||||
))
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if (packageEntry.size > 15000000) {
|
||||
return true
|
||||
}
|
||||
val input = it.getInputStream(packageEntry).buffered()
|
||||
val dexFile = try {
|
||||
DexBackedDexFile.fromInputStream(null, input)
|
||||
} catch (e: Exception) {
|
||||
return false
|
||||
}
|
||||
for (clazz in dexFile.classes) {
|
||||
val clazzName =
|
||||
clazz.type.substring(1, clazz.type.length - 1).replace("/", ".")
|
||||
.replace("$", ".")
|
||||
if (clazzName.matches(chinaAppRegex)) return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.follow.clash.plugins
|
||||
|
||||
import com.follow.clash.FlClashApplication
|
||||
import com.follow.clash.GlobalState
|
||||
import com.follow.clash.models.VpnOptions
|
||||
import com.google.gson.Gson
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.core.content.getSystemService
|
||||
import com.follow.clash.FlClashApplication
|
||||
import com.follow.clash.GlobalState
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
@@ -87,6 +87,7 @@ class FlClashService : Service(), BaseServiceInterface {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getNotificationBuilder(): NotificationCompat.Builder {
|
||||
return notificationBuilderDeferred.await()
|
||||
}
|
||||
@@ -100,7 +101,8 @@ class FlClashService : Service(), BaseServiceInterface {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ForegroundServiceType", "WrongConstant")
|
||||
|
||||
@SuppressLint("ForegroundServiceType")
|
||||
override suspend fun startForeground(title: String, content: String) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
@@ -116,7 +118,11 @@ class FlClashService : Service(), BaseServiceInterface {
|
||||
.setContentTitle(title)
|
||||
.setContentText(content).build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||
try {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} catch (_: Exception) {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
|
||||
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
import android.net.ProxyInfo
|
||||
import android.net.VpnService
|
||||
import android.os.Binder
|
||||
@@ -45,9 +45,16 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
addAddress(cidr.address, cidr.prefixLength)
|
||||
val routeAddress = options.getIpv4RouteAddress()
|
||||
if (routeAddress.isNotEmpty()) {
|
||||
routeAddress.forEach { i ->
|
||||
Log.d("addRoute4", "address: ${i.address} prefixLength:${i.prefixLength}")
|
||||
addRoute(i.address, i.prefixLength)
|
||||
try {
|
||||
routeAddress.forEach { i ->
|
||||
Log.d(
|
||||
"addRoute4",
|
||||
"address: ${i.address} prefixLength:${i.prefixLength}"
|
||||
)
|
||||
addRoute(i.address, i.prefixLength)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
addRoute("0.0.0.0", 0)
|
||||
}
|
||||
} else {
|
||||
addRoute("0.0.0.0", 0)
|
||||
@@ -58,9 +65,16 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
addAddress(cidr.address, cidr.prefixLength)
|
||||
val routeAddress = options.getIpv6RouteAddress()
|
||||
if (routeAddress.isNotEmpty()) {
|
||||
routeAddress.forEach { i ->
|
||||
Log.d("addRoute6", "address: ${i.address} prefixLength:${i.prefixLength}")
|
||||
addRoute(i.address, i.prefixLength)
|
||||
try {
|
||||
routeAddress.forEach { i ->
|
||||
Log.d(
|
||||
"addRoute6",
|
||||
"address: ${i.address} prefixLength:${i.prefixLength}"
|
||||
)
|
||||
addRoute(i.address, i.prefixLength)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
addRoute("::", 0)
|
||||
}
|
||||
} else {
|
||||
addRoute("::", 0)
|
||||
@@ -165,7 +179,7 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
return notificationBuilderDeferred.await()
|
||||
}
|
||||
|
||||
@SuppressLint("ForegroundServiceType", "WrongConstant")
|
||||
@SuppressLint("ForegroundServiceType")
|
||||
override suspend fun startForeground(title: String, content: String) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
@@ -182,7 +196,11 @@ class FlClashVpnService : VpnService(), BaseServiceInterface {
|
||||
.setContentText(content)
|
||||
.build()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
|
||||
try {
|
||||
startForeground(notificationId, notification, FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} catch (_: Exception) {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
} else {
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx4G
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
kotlin_version=1.9.22
|
||||
agp_version=8.2.1
|
||||
agp_version=8.9.1
|
||||
|
||||
@@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
|
||||
|
||||
|
||||
BIN
assets/fonts/JetBrainsMono-Regular.ttf
Normal file
BIN
assets/fonts/JetBrainsMono-Regular.ttf
Normal file
Binary file not shown.
Submodule core/Clash.Meta updated: 76b0d7e8bc...f19dad529f
@@ -28,8 +28,12 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
func splitByComma(s string) interface{} {
|
||||
parts := strings.Split(s, ",")
|
||||
func splitByMultipleSeparators(s string) interface{} {
|
||||
isSeparator := func(r rune) bool {
|
||||
return r == ',' || r == ' ' || r == ';'
|
||||
}
|
||||
|
||||
parts := strings.FieldsFunc(s, isSeparator)
|
||||
if len(parts) > 1 {
|
||||
return parts
|
||||
}
|
||||
@@ -168,18 +172,18 @@ func decorationConfig(profileId string, cfg config.RawConfig) *config.RawConfig
|
||||
return prof
|
||||
}
|
||||
|
||||
func genHosts(hosts, patchHosts map[string]any) {
|
||||
func attachHosts(hosts, patchHosts map[string]any) {
|
||||
for k, v := range patchHosts {
|
||||
if str, ok := v.(string); ok {
|
||||
hosts[k] = splitByComma(str)
|
||||
hosts[k] = splitByMultipleSeparators(str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func modPatchDns(dns *config.RawDNS) {
|
||||
func updatePatchDns(dns config.RawDNS) {
|
||||
for pair := dns.NameServerPolicy.Oldest(); pair != nil; pair = pair.Next() {
|
||||
if str, ok := pair.Value.(string); ok {
|
||||
dns.NameServerPolicy.Set(pair.Key, splitByComma(str))
|
||||
dns.NameServerPolicy.Set(pair.Key, splitByMultipleSeparators(str))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,26 +195,25 @@ func trimArr(arr []string) (r []string) {
|
||||
return
|
||||
}
|
||||
|
||||
func overrideRules(rules *[]string) {
|
||||
var target = ""
|
||||
for _, line := range *rules {
|
||||
func overrideRules(rules, patchRules []string) []string {
|
||||
target := ""
|
||||
for _, line := range rules {
|
||||
rule := trimArr(strings.Split(line, ","))
|
||||
l := len(rule)
|
||||
if l != 2 {
|
||||
return
|
||||
if len(rule) != 2 {
|
||||
continue
|
||||
}
|
||||
if strings.ToUpper(rule[0]) == "MATCH" {
|
||||
if strings.EqualFold(rule[0], "MATCH") {
|
||||
target = rule[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
if target == "" {
|
||||
return
|
||||
return rules
|
||||
}
|
||||
var rulesExt = lo.Map(ips, func(ip string, index int) string {
|
||||
return fmt.Sprintf("DOMAIN %s %s", ip, target)
|
||||
rulesExt := lo.Map(ips, func(ip string, _ int) string {
|
||||
return fmt.Sprintf("DOMAIN,%s,%s", ip, target)
|
||||
})
|
||||
*rules = append(rulesExt, *rules...)
|
||||
return append(append(rulesExt, patchRules...), rules...)
|
||||
}
|
||||
|
||||
func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig) {
|
||||
@@ -244,16 +247,20 @@ func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfi
|
||||
for idx := range targetConfig.ProxyGroup {
|
||||
targetConfig.ProxyGroup[idx]["url"] = ""
|
||||
}
|
||||
genHosts(targetConfig.Hosts, patchConfig.Hosts)
|
||||
attachHosts(targetConfig.Hosts, patchConfig.Hosts)
|
||||
if configParams.OverrideDns {
|
||||
modPatchDns(&patchConfig.DNS)
|
||||
updatePatchDns(patchConfig.DNS)
|
||||
targetConfig.DNS = patchConfig.DNS
|
||||
} else {
|
||||
if targetConfig.DNS.Enable == false {
|
||||
targetConfig.DNS.Enable = true
|
||||
}
|
||||
}
|
||||
overrideRules(&targetConfig.Rule)
|
||||
if configParams.OverrideRule {
|
||||
targetConfig.Rule = overrideRules(patchConfig.Rule, []string{})
|
||||
} else {
|
||||
targetConfig.Rule = overrideRules(targetConfig.Rule, patchConfig.Rule)
|
||||
}
|
||||
}
|
||||
|
||||
func patchConfig() {
|
||||
@@ -267,6 +274,7 @@ func patchConfig() {
|
||||
dialer.DefaultInterface.Store(general.Interface)
|
||||
adapter.UnifiedDelay.Store(general.UnifiedDelay)
|
||||
tunnel.SetMode(general.Mode)
|
||||
tunnel.UpdateRules(currentConfig.Rules, currentConfig.SubRules, currentConfig.RuleProviders)
|
||||
log.SetLevel(general.LogLevel)
|
||||
resolver.DisableIPv6 = !general.IPv6
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ type ConfigExtendedParams struct {
|
||||
SelectedMap map[string]string `json:"selected-map"`
|
||||
TestURL *string `json:"test-url"`
|
||||
OverrideDns bool `json:"override-dns"`
|
||||
OverrideRule bool `json:"override-rule"`
|
||||
}
|
||||
|
||||
type GenerateConfigParams struct {
|
||||
|
||||
11
core/go.mod
11
core/go.mod
@@ -21,7 +21,7 @@ require (
|
||||
github.com/coreos/go-iptables v0.8.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/enfein/mieru/v3 v3.11.2 // indirect
|
||||
github.com/enfein/mieru/v3 v3.13.0 // indirect
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
|
||||
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
|
||||
@@ -50,21 +50,22 @@ require (
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab // indirect
|
||||
github.com/metacubex/bart v0.19.0 // indirect
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 // indirect
|
||||
github.com/metacubex/chacha v0.1.1 // indirect
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b // indirect
|
||||
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect
|
||||
github.com/metacubex/randv2 v0.2.0 // indirect
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 // indirect
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.5 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04 // indirect
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 // indirect
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect
|
||||
github.com/metacubex/utls v1.6.6 // indirect
|
||||
github.com/metacubex/utls v1.6.8-alpha.4 // indirect
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
|
||||
@@ -74,7 +75,7 @@ require (
|
||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.0 // indirect
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/sagernet/cors v1.2.1 // indirect
|
||||
|
||||
22
core/go.sum
22
core/go.sum
@@ -28,8 +28,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/enfein/mieru/v3 v3.11.2 h1:06KyGbXiiGz2nSHLJDOOkztAVY3cRr3wBMOpYxPotTo=
|
||||
github.com/enfein/mieru/v3 v3.11.2/go.mod h1:XvVfNsM78lUMSlJJKXJZ0Hn3lAB2o/ETXTbb84x5egw=
|
||||
github.com/enfein/mieru/v3 v3.13.0 h1:eGyxLGkb+lut9ebmx+BGwLJ5UMbEc/wGIYO0AXEKy98=
|
||||
github.com/enfein/mieru/v3 v3.13.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=
|
||||
github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
|
||||
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
|
||||
@@ -97,14 +97,16 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4=
|
||||
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI=
|
||||
github.com/metacubex/bart v0.19.0 h1:XQ9AJeI+WO+phRPkUOoflAFwlqDJnm5BPQpixciJQBY=
|
||||
github.com/metacubex/bart v0.19.0/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
|
||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
|
||||
github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c=
|
||||
github.com/metacubex/chacha v0.1.1/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
|
||||
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a h1:cZ6oNVrsmsi3SNlnSnRio4zOgtQq+/XidwsaNgKICcg=
|
||||
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a/go.mod h1:xBw/SYJPgUMPQ1tklV/brGn2nxhfr3BnvBzNlyi4Nic=
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b h1:RUh4OdVPz/jDrM9MQ2ySuqu2aeBqcA8rtfWUYLZ8RtI=
|
||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
|
||||
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/jBDS/kCYjz/x+0BCOKfd2VODYevyeIt+Ds=
|
||||
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
|
||||
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
|
||||
@@ -117,16 +119,16 @@ github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJ
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8/go.mod h1:X3x88XtJpBxG0W0/ECOJL6Ib0SJ3xdniAkU/6/RMWU0=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhDpb9no4+gdXPo=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q=
|
||||
github.com/metacubex/sing-tun v0.4.5 h1:kWSyQzuzHI40r50OFBczfWIDvMBMy1RIk+JsXeBPRB0=
|
||||
github.com/metacubex/sing-tun v0.4.5/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04 h1:B211C+i/I8CWf4I/BaAV0mmkEHrDBJ0XR9EWxjPbFEg=
|
||||
github.com/metacubex/sing-tun v0.4.6-0.20250312042506-6d3b4dc05c04/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 h1:zZp5uct9+/0Hb1jKGyqDjCU4/72t43rs7qOq3Rc9oU8=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||
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/metacubex/utls v1.6.8-alpha.4 h1:5EvsCHxDNneaOtAyc8CztoNSpmonLvkvuGs01lIeeEI=
|
||||
github.com/metacubex/utls v1.6.8-alpha.4/go.mod h1:MEZ5WO/VLKYs/s/dOzEK/mlXOQxc04ESeLzRgjmLYtk=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ=
|
||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
@@ -153,8 +155,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.0 h1:i+cMcpEDY1BkNm7lPDkCtE4oElsYLn+EKF8kAu2vXT4=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
|
||||
@@ -61,16 +61,6 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
_autoUpdateGroupTask();
|
||||
_autoUpdateProfilesTask();
|
||||
globalState.appController = AppController(context, ref);
|
||||
globalState.measure = Measure.of(context);
|
||||
// ref.listenManual(themeSettingProvider.select((state) => state.fontFamily),
|
||||
// (prev, next) {
|
||||
// if (prev != next) {
|
||||
// globalState.measure = Measure.of(
|
||||
// context,
|
||||
// fontFamily: next.value,
|
||||
// );
|
||||
// }
|
||||
// }, fireImmediately: true);
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final currentContext = globalState.navigatorKey.currentContext;
|
||||
if (currentContext != null) {
|
||||
@@ -98,7 +88,7 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
});
|
||||
}
|
||||
|
||||
_buildPlatformWrap(Widget child) {
|
||||
_buildPlatformState(Widget child) {
|
||||
if (system.isDesktop) {
|
||||
return WindowManager(
|
||||
child: TrayManager(
|
||||
@@ -117,18 +107,7 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
);
|
||||
}
|
||||
|
||||
_buildPage(Widget page) {
|
||||
if (system.isDesktop) {
|
||||
return WindowHeaderContainer(
|
||||
child: page,
|
||||
);
|
||||
}
|
||||
return VpnManager(
|
||||
child: page,
|
||||
);
|
||||
}
|
||||
|
||||
_buildWrap(Widget child) {
|
||||
_buildState(Widget child) {
|
||||
return AppStateManager(
|
||||
child: ClashManager(
|
||||
child: ConnectivityManager(
|
||||
@@ -142,6 +121,25 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
);
|
||||
}
|
||||
|
||||
_buildPlatformApp(Widget child) {
|
||||
if (system.isDesktop) {
|
||||
return WindowHeaderContainer(
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return VpnManager(
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
_buildApp(Widget child) {
|
||||
return MessageManager(
|
||||
child: ThemeManager(
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_updateSystemColorSchemes(
|
||||
ColorScheme? lightDynamic,
|
||||
ColorScheme? darkDynamic,
|
||||
@@ -157,8 +155,8 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
return _buildPlatformWrap(
|
||||
_buildWrap(
|
||||
return _buildPlatformState(
|
||||
_buildState(
|
||||
Consumer(
|
||||
builder: (_, ref, child) {
|
||||
final locale =
|
||||
@@ -168,6 +166,7 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
builder: (lightDynamic, darkDynamic) {
|
||||
_updateSystemColorSchemes(lightDynamic, darkDynamic);
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
navigatorKey: globalState.navigatorKey,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
@@ -176,14 +175,9 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
GlobalWidgetsLocalizations.delegate
|
||||
],
|
||||
builder: (_, child) {
|
||||
return MessageManager(
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
globalState.appController.updateViewWidth(
|
||||
container.maxWidth,
|
||||
);
|
||||
return _buildPage(child!);
|
||||
},
|
||||
return AppEnvManager(
|
||||
child: _buildPlatformApp(
|
||||
_buildApp(child!),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -240,7 +240,7 @@ class ClashCore {
|
||||
if (res.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return ClashConfigSnippet.fromJson(json.decode(res));
|
||||
return Isolate.run(() => ClashConfigSnippet.fromJson(json.decode(res)));
|
||||
}
|
||||
|
||||
resetTraffic() {
|
||||
|
||||
@@ -1,20 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension ColorExtension on Color {
|
||||
Color get toLight {
|
||||
return withOpacity(0.8);
|
||||
Color get opacity80 {
|
||||
return withAlpha(204);
|
||||
}
|
||||
|
||||
Color get toLighter {
|
||||
return withOpacity(0.6);
|
||||
Color get opacity60 {
|
||||
return withAlpha(153);
|
||||
}
|
||||
|
||||
Color get toSoft {
|
||||
return withOpacity(0.15);
|
||||
Color get opacity50 {
|
||||
return withAlpha(128);
|
||||
}
|
||||
|
||||
Color get toLittle {
|
||||
return withOpacity(0.03);
|
||||
Color get opacity38 {
|
||||
return withAlpha(97);
|
||||
}
|
||||
|
||||
Color get opacity30 {
|
||||
return withAlpha(77);
|
||||
}
|
||||
|
||||
Color get opacity15 {
|
||||
return withAlpha(38);
|
||||
}
|
||||
|
||||
Color get opacity10 {
|
||||
return withAlpha(15);
|
||||
}
|
||||
|
||||
Color get opacity3 {
|
||||
return withAlpha(76);
|
||||
}
|
||||
|
||||
Color get opacity0 {
|
||||
return withAlpha(0);
|
||||
}
|
||||
|
||||
Color darken([double amount = .1]) {
|
||||
|
||||
@@ -21,6 +21,11 @@ const baseInfoEdgeInsets = EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 16,
|
||||
);
|
||||
|
||||
double textScaleFactor = min(
|
||||
WidgetsBinding.instance.platformDispatcher.textScaleFactor,
|
||||
1.2,
|
||||
);
|
||||
const httpTimeoutDuration = Duration(milliseconds: 5000);
|
||||
const moreDuration = Duration(milliseconds: 100);
|
||||
const animateDuration = Duration(milliseconds: 100);
|
||||
@@ -46,7 +51,7 @@ const defaultExternalController = "127.0.0.1:9090";
|
||||
const maxMobileWidth = 600;
|
||||
const maxLaptopWidth = 840;
|
||||
const defaultTestUrl = "https://www.gstatic.com/generate_204";
|
||||
final filter = ImageFilter.blur(
|
||||
final commonFilter = ImageFilter.blur(
|
||||
sigmaX: 5,
|
||||
sigmaY: 5,
|
||||
tileMode: TileMode.mirror,
|
||||
@@ -76,7 +81,7 @@ const viewModeColumnsMap = {
|
||||
const defaultPrimaryColor = Colors.brown;
|
||||
|
||||
double getWidgetHeight(num lines) {
|
||||
return max(lines * 84 + (lines - 1) * 16, 0);
|
||||
return max(lines * 84 * textScaleFactor + (lines - 1) * 16, 0);
|
||||
}
|
||||
|
||||
final mainIsolate = "FlClashMainIsolate";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
class Debouncer {
|
||||
final Map<dynamic, Timer> _operations = {};
|
||||
final Map<dynamic, Timer?> _operations = {};
|
||||
|
||||
call(
|
||||
dynamic tag,
|
||||
@@ -28,14 +28,15 @@ class Debouncer {
|
||||
|
||||
cancel(dynamic tag) {
|
||||
_operations[tag]?.cancel();
|
||||
_operations[tag] = null;
|
||||
}
|
||||
}
|
||||
|
||||
class Throttler {
|
||||
final Map<dynamic, Timer> _operations = {};
|
||||
final Map<dynamic, Timer?> _operations = {};
|
||||
|
||||
call(
|
||||
String tag,
|
||||
dynamic tag,
|
||||
Function func, {
|
||||
List<dynamic>? args,
|
||||
Duration duration = const Duration(milliseconds: 600),
|
||||
@@ -60,6 +61,7 @@ class Throttler {
|
||||
|
||||
cancel(dynamic tag) {
|
||||
_operations[tag]?.cancel();
|
||||
_operations[tag] = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,3 +65,12 @@ extension DoubleListExt on List<double> {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extension MapExt<K, V> on Map<K, V> {
|
||||
getCacheValue(K key, V defaultValue) {
|
||||
if (this[key] == null) {
|
||||
this[key] = defaultValue;
|
||||
}
|
||||
return this[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class FixedList<T> {
|
||||
}
|
||||
|
||||
class FixedMap<K, V> {
|
||||
final int maxSize;
|
||||
int maxSize;
|
||||
final Map<K, V> _map = {};
|
||||
final Queue<K> _queue = Queue<K>();
|
||||
|
||||
@@ -45,6 +45,7 @@ class FixedMap<K, V> {
|
||||
}
|
||||
_map[key] = value;
|
||||
_queue.add(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
clear() {
|
||||
@@ -52,8 +53,13 @@ class FixedMap<K, V> {
|
||||
_queue.clear();
|
||||
}
|
||||
|
||||
updateMaxSize(int size){
|
||||
maxSize = size;
|
||||
}
|
||||
|
||||
V? get(K key) => _map[key];
|
||||
|
||||
|
||||
bool containsKey(K key) => _map.containsKey(key);
|
||||
|
||||
int get length => _map.length;
|
||||
|
||||
@@ -5,13 +5,11 @@ import 'package:flutter/material.dart';
|
||||
class Measure {
|
||||
final TextScaler _textScale;
|
||||
final BuildContext context;
|
||||
final String? _fontFamily;
|
||||
|
||||
Measure.of(this.context, {String? fontFamily})
|
||||
Measure.of(this.context)
|
||||
: _textScale = TextScaler.linear(
|
||||
WidgetsBinding.instance.platformDispatcher.textScaleFactor,
|
||||
),
|
||||
_fontFamily = fontFamily ?? "";
|
||||
textScaleFactor,
|
||||
);
|
||||
|
||||
Size computeTextSize(
|
||||
Text text, {
|
||||
@@ -20,9 +18,7 @@ class Measure {
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: text.data,
|
||||
style: text.style?.copyWith(
|
||||
fontFamily: _fontFamily,
|
||||
),
|
||||
style: text.style,
|
||||
),
|
||||
maxLines: text.maxLines,
|
||||
textScaler: _textScale,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'context.dart';
|
||||
@@ -29,8 +30,14 @@ mixin PageMixin<T extends StatefulWidget> on State<T> {
|
||||
final commonScaffoldState = context.commonScaffoldState;
|
||||
commonScaffoldState?.actions = actions;
|
||||
commonScaffoldState?.floatingActionButton = floatingActionButton;
|
||||
commonScaffoldState?.onSearch = onSearch;
|
||||
commonScaffoldState?.onKeywordsUpdate = onKeywordsUpdate;
|
||||
commonScaffoldState?.updateSearchState(
|
||||
(_) => onSearch != null
|
||||
? AppBarSearchState(
|
||||
onSearch: onSearch!,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class CommonRoute<T> extends MaterialPageRoute<T> {
|
||||
Duration get transitionDuration => const Duration(milliseconds: 500);
|
||||
|
||||
@override
|
||||
Duration get reverseTransitionDuration => const Duration(milliseconds: 250);
|
||||
Duration get reverseTransitionDuration => const Duration(milliseconds: 500);
|
||||
}
|
||||
|
||||
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
|
||||
@@ -194,7 +194,7 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
|
||||
_primaryPositionCurve = CurvedAnimation(
|
||||
parent: widget.primaryRouteAnimation,
|
||||
curve: Curves.fastEaseInToSlowEaseOut,
|
||||
reverseCurve: Curves.easeInOut,
|
||||
reverseCurve: Curves.fastEaseInToSlowEaseOut.flipped,
|
||||
);
|
||||
_secondaryPositionCurve = CurvedAnimation(
|
||||
parent: widget.secondaryRouteAnimation,
|
||||
@@ -218,9 +218,8 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
|
||||
begin: const _CommonEdgeShadowDecoration(),
|
||||
end: _CommonEdgeShadowDecoration(
|
||||
<Color>[
|
||||
widget.context.colorScheme.inverseSurface.withOpacity(
|
||||
0.06,
|
||||
),
|
||||
widget.context.colorScheme.inverseSurface
|
||||
.withValues(alpha: 0.02),
|
||||
Colors.transparent,
|
||||
],
|
||||
),
|
||||
@@ -274,7 +273,7 @@ class _CommonEdgeShadowPainter extends BoxPainter {
|
||||
return;
|
||||
}
|
||||
|
||||
final double shadowWidth = 0.03 * configuration.size!.width;
|
||||
final double shadowWidth = 1 * configuration.size!.width;
|
||||
final double shadowHeight = configuration.size!.height;
|
||||
final double bandWidth = shadowWidth / (colors.length - 1);
|
||||
|
||||
|
||||
@@ -241,11 +241,6 @@ class Other {
|
||||
return "${appName}_${DateTime.now().show}.log";
|
||||
}
|
||||
|
||||
Size getScreenSize() {
|
||||
final view = WidgetsBinding.instance.platformDispatcher.views.first;
|
||||
return view.physicalSize / view.devicePixelRatio;
|
||||
}
|
||||
|
||||
Future<String?> getLocalIpAddress() async {
|
||||
List<NetworkInterface> interfaces = await NetworkInterface.list(
|
||||
includeLoopback: false,
|
||||
|
||||
@@ -14,15 +14,13 @@ class Protocol {
|
||||
|
||||
void register(String scheme) {
|
||||
String protocolRegKey = 'Software\\Classes\\$scheme';
|
||||
RegistryValue protocolRegValue = const RegistryValue(
|
||||
RegistryValue protocolRegValue = RegistryValue.string(
|
||||
'URL Protocol',
|
||||
RegistryValueType.string,
|
||||
'',
|
||||
);
|
||||
String protocolCmdRegKey = 'shell\\open\\command';
|
||||
RegistryValue protocolCmdRegValue = RegistryValue(
|
||||
RegistryValue protocolCmdRegValue = RegistryValue.string(
|
||||
'',
|
||||
RegistryValueType.string,
|
||||
'"${Platform.resolvedExecutable}" "%1"',
|
||||
);
|
||||
final regKey = Registry.currentUser.createKey(protocolRegKey);
|
||||
@@ -31,4 +29,4 @@ class Protocol {
|
||||
}
|
||||
}
|
||||
|
||||
final protocol = Protocol();
|
||||
final protocol = Protocol();
|
||||
|
||||
@@ -22,7 +22,7 @@ class Render {
|
||||
}
|
||||
|
||||
pause() {
|
||||
debouncer.call(
|
||||
throttler.call(
|
||||
DebounceTag.renderPause,
|
||||
_pause,
|
||||
duration: Duration(seconds: 5),
|
||||
@@ -30,11 +30,11 @@ class Render {
|
||||
}
|
||||
|
||||
resume() {
|
||||
debouncer.cancel(DebounceTag.renderPause);
|
||||
throttler.cancel(DebounceTag.renderPause);
|
||||
_resume();
|
||||
}
|
||||
|
||||
void _pause() {
|
||||
void _pause() async {
|
||||
if (_isPaused) return;
|
||||
_isPaused = true;
|
||||
_beginFrame = _dispatcher.onBeginFrame;
|
||||
|
||||
@@ -83,13 +83,19 @@ class Request {
|
||||
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,
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
final response = await Dio()
|
||||
.get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
)
|
||||
.timeout(
|
||||
Duration(
|
||||
seconds: 30,
|
||||
),
|
||||
);
|
||||
if (response.statusCode != 200 || response.data == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/widgets/scroll.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BaseScrollBehavior extends MaterialScrollBehavior {
|
||||
@@ -16,8 +17,6 @@ class BaseScrollBehavior extends MaterialScrollBehavior {
|
||||
};
|
||||
}
|
||||
|
||||
class BaseScrollBehavior2 extends ScrollBehavior {}
|
||||
|
||||
class HiddenBarScrollBehavior extends BaseScrollBehavior {
|
||||
@override
|
||||
Widget buildScrollbar(
|
||||
@@ -36,8 +35,7 @@ class ShowBarScrollBehavior extends BaseScrollBehavior {
|
||||
Widget child,
|
||||
ScrollableDetails details,
|
||||
) {
|
||||
return Scrollbar(
|
||||
interactive: true,
|
||||
return CommonAutoHiddenScrollBar(
|
||||
controller: details.controller,
|
||||
child: child,
|
||||
);
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'color.dart';
|
||||
|
||||
extension TextStyleExtension on TextStyle {
|
||||
TextStyle get toLight => copyWith(color: color?.toLight);
|
||||
TextStyle get toLight => copyWith(color: color?.opacity80);
|
||||
|
||||
TextStyle get toLighter => copyWith(color: color?.toLighter);
|
||||
TextStyle get toLighter => copyWith(color: color?.opacity60);
|
||||
|
||||
TextStyle get toSoftBold => copyWith(fontWeight: FontWeight.w500);
|
||||
|
||||
TextStyle get toBold => copyWith(fontWeight: FontWeight.bold);
|
||||
|
||||
TextStyle get toJetBrainsMono => copyWith(
|
||||
fontFamily: FontFamily.jetBrainsMono.value,
|
||||
);
|
||||
|
||||
TextStyle adjustSize(int size) => copyWith(
|
||||
fontSize: fontSize! + size,
|
||||
);
|
||||
|
||||
39
lib/common/theme.dart
Normal file
39
lib/common/theme.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CommonTheme {
|
||||
final BuildContext context;
|
||||
final Map<String, Color> _colorMap;
|
||||
|
||||
CommonTheme.of(this.context) : _colorMap = {};
|
||||
|
||||
Color get darkenSecondaryContainer {
|
||||
return _colorMap.getCacheValue(
|
||||
"darkenSecondaryContainer",
|
||||
context.colorScheme.secondaryContainer.blendDarken(context, factor: 0.1),
|
||||
);
|
||||
}
|
||||
|
||||
Color get darkenSecondaryContainerLighter {
|
||||
return _colorMap.getCacheValue(
|
||||
"darkenSecondaryContainerLighter",
|
||||
context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.1)
|
||||
.opacity60,
|
||||
);
|
||||
}
|
||||
|
||||
Color get darken2SecondaryContainer {
|
||||
return _colorMap.getCacheValue(
|
||||
"darken2SecondaryContainer",
|
||||
context.colorScheme.secondaryContainer.blendDarken(context, factor: 0.2),
|
||||
);
|
||||
}
|
||||
|
||||
Color get darken3PrimaryContainer {
|
||||
return _colorMap.getCacheValue(
|
||||
"darken3PrimaryContainer",
|
||||
context.colorScheme.primaryContainer.blendDarken(context, factor: 0.3),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,12 @@ class Window {
|
||||
await windowManager.ensureInitialized();
|
||||
WindowOptions windowOptions = WindowOptions(
|
||||
size: Size(props.width, props.height),
|
||||
minimumSize: const Size(380, 500),
|
||||
minimumSize: const Size(380, 400),
|
||||
);
|
||||
if (!Platform.isMacOS || version > 10) {
|
||||
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
|
||||
}
|
||||
if(!Platform.isMacOS){
|
||||
if (!Platform.isMacOS) {
|
||||
final left = props.left ?? 0;
|
||||
final top = props.top ?? 0;
|
||||
final right = left + props.width;
|
||||
@@ -36,7 +36,7 @@ class Window {
|
||||
} else {
|
||||
final displays = await screenRetriever.getAllDisplays();
|
||||
final isPositionValid = displays.any(
|
||||
(display) {
|
||||
(display) {
|
||||
final displayBounds = Rect.fromLTWH(
|
||||
display.visiblePosition!.dx,
|
||||
display.visiblePosition!.dy,
|
||||
@@ -69,8 +69,10 @@ class Window {
|
||||
await windowManager.setSkipTaskbar(false);
|
||||
}
|
||||
|
||||
Future<bool> isVisible() async {
|
||||
return await windowManager.isVisible();
|
||||
Future<bool> get isVisible async {
|
||||
final value = await windowManager.isVisible();
|
||||
commonPrint.log("window visible check: $value");
|
||||
return value;
|
||||
}
|
||||
|
||||
close() async {
|
||||
|
||||
@@ -10,12 +10,14 @@ import 'package:fl_clash/common/archive.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'common/common.dart';
|
||||
import 'fragments/profiles/override_profile.dart';
|
||||
import 'models/models.dart';
|
||||
|
||||
class AppController {
|
||||
@@ -242,6 +244,7 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> updateClashConfig([bool? isPatch]) async {
|
||||
commonPrint.log("update clash patch: ${isPatch ?? false}");
|
||||
final commonScaffoldState = globalState.homeScaffoldKey.currentState;
|
||||
if (commonScaffoldState?.mounted != true) return;
|
||||
await commonScaffoldState?.loadingRun(() async {
|
||||
@@ -414,6 +417,9 @@ class AppController {
|
||||
Map<String, dynamic>? data,
|
||||
bool handleError = false,
|
||||
}) async {
|
||||
if(globalState.isPre){
|
||||
return;
|
||||
}
|
||||
if (data != null) {
|
||||
final tagName = data['tag_name'];
|
||||
final body = data['body'];
|
||||
@@ -520,37 +526,12 @@ class AppController {
|
||||
_ref.read(delayDataSourceProvider.notifier).setDelay(delay);
|
||||
}
|
||||
|
||||
toPage(
|
||||
int index, {
|
||||
bool hasAnimate = false,
|
||||
}) {
|
||||
final navigations = _ref.read(currentNavigationsStateProvider).value;
|
||||
if (index > navigations.length - 1) {
|
||||
return;
|
||||
}
|
||||
_ref.read(currentPageLabelProvider.notifier).value =
|
||||
navigations[index].label;
|
||||
final isAnimateToPage = _ref.read(appSettingProvider).isAnimateToPage;
|
||||
final isMobile =
|
||||
_ref.read(viewWidthProvider.notifier).viewMode == ViewMode.mobile;
|
||||
if (isAnimateToPage && isMobile || hasAnimate) {
|
||||
globalState.pageController?.animateToPage(
|
||||
index,
|
||||
duration: kTabScrollDuration,
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
} else {
|
||||
globalState.pageController?.jumpToPage(index);
|
||||
}
|
||||
toPage(PageLabel pageLabel) {
|
||||
_ref.read(currentPageLabelProvider.notifier).value = pageLabel;
|
||||
}
|
||||
|
||||
toProfiles() {
|
||||
final index = _ref.read(currentNavigationsStateProvider).value.indexWhere(
|
||||
(element) => element.label == PageLabel.profiles,
|
||||
);
|
||||
if (index != -1) {
|
||||
toPage(index);
|
||||
}
|
||||
toPage(PageLabel.profiles);
|
||||
}
|
||||
|
||||
initLink() {
|
||||
@@ -587,17 +568,8 @@ class AppController {
|
||||
Future<bool> showDisclaimer() async {
|
||||
return await globalState.showCommonDialog<bool>(
|
||||
dismissible: false,
|
||||
child: AlertDialog(
|
||||
title: Text(appLocalizations.disclaimer),
|
||||
content: Container(
|
||||
width: dialogCommonWidth,
|
||||
constraints: const BoxConstraints(maxHeight: 200),
|
||||
child: SingleChildScrollView(
|
||||
child: SelectableText(
|
||||
appLocalizations.disclaimerDesc,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: CommonDialog(
|
||||
title: appLocalizations.disclaimer,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -615,6 +587,9 @@ class AppController {
|
||||
child: Text(appLocalizations.agree),
|
||||
)
|
||||
],
|
||||
child: SelectableText(
|
||||
appLocalizations.disclaimerDesc,
|
||||
),
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
@@ -680,9 +655,9 @@ class AppController {
|
||||
addProfileFormURL(url);
|
||||
}
|
||||
|
||||
updateViewWidth(double width) {
|
||||
updateViewSize(Size size) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_ref.read(viewWidthProvider.notifier).value = width;
|
||||
_ref.read(viewSizeProvider.notifier).value = size;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -741,10 +716,18 @@ class AppController {
|
||||
final providersPath = await appPath.getProvidersPath(profileId);
|
||||
return await Isolate.run(() async {
|
||||
if (profilePath != null) {
|
||||
await File(profilePath).delete(recursive: true);
|
||||
final profileFile = File(profilePath);
|
||||
final isExists = await profileFile.exists();
|
||||
if (isExists) {
|
||||
profileFile.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
if (providersPath != null) {
|
||||
await File(providersPath).delete(recursive: true);
|
||||
final providersFileDir = File(providersPath);
|
||||
final isExists = await providersFileDir.exists();
|
||||
if (isExists) {
|
||||
providersFileDir.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -798,10 +781,10 @@ class AppController {
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(mode: mode),
|
||||
);
|
||||
// if (mode == Mode.global) {
|
||||
// updateCurrentGroupName(GroupName.GLOBAL.name);
|
||||
// }
|
||||
// addCheckIpNumDebounce();
|
||||
if (mode == Mode.global) {
|
||||
updateCurrentGroupName(GroupName.GLOBAL.name);
|
||||
}
|
||||
addCheckIpNumDebounce();
|
||||
}
|
||||
|
||||
updateAutoLaunch() {
|
||||
@@ -813,7 +796,7 @@ class AppController {
|
||||
}
|
||||
|
||||
updateVisible() async {
|
||||
final visible = await window?.isVisible();
|
||||
final visible = await window?.isVisible;
|
||||
if (visible != null && !visible) {
|
||||
window?.show();
|
||||
} else {
|
||||
@@ -836,6 +819,38 @@ class AppController {
|
||||
);
|
||||
}
|
||||
|
||||
handleAddOrUpdate(WidgetRef ref, [Rule? rule]) async {
|
||||
final res = await globalState.showCommonDialog<Rule>(
|
||||
child: AddRuleDialog(
|
||||
rule: rule,
|
||||
snippet: ref.read(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) => state.snippet!,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (res == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) {
|
||||
final model = state.copyWith.overrideData!(
|
||||
rule: state.overrideData!.rule.updateRules(
|
||||
(rules) {
|
||||
final index = rules.indexWhere((item) => item.id == res.id);
|
||||
if (index == -1) {
|
||||
return List.from([res, ...rules]);
|
||||
}
|
||||
return List.from(rules)..[index] = res;
|
||||
},
|
||||
),
|
||||
);
|
||||
return model;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> exportLogs() async {
|
||||
final logsRaw = _ref.read(logsProvider).list.map(
|
||||
(item) => item.toString(),
|
||||
|
||||
@@ -220,11 +220,12 @@ enum ProxiesIconStyle {
|
||||
|
||||
enum FontFamily {
|
||||
twEmoji("Twemoji"),
|
||||
jetBrainsMono("JetBrainsMono"),
|
||||
icon("Icons");
|
||||
|
||||
final String? value;
|
||||
final String value;
|
||||
|
||||
const FontFamily([this.value]);
|
||||
const FontFamily(this.value);
|
||||
}
|
||||
|
||||
enum RouteMode {
|
||||
@@ -384,3 +385,65 @@ enum PageLabel {
|
||||
resources,
|
||||
connections,
|
||||
}
|
||||
|
||||
enum RuleAction {
|
||||
DOMAIN("DOMAIN"),
|
||||
DOMAIN_SUFFIX("DOMAIN-SUFFIX"),
|
||||
DOMAIN_KEYWORD("DOMAIN-KEYWORD"),
|
||||
DOMAIN_REGEX("DOMAIN-REGEX"),
|
||||
GEOSITE("GEOSITE"),
|
||||
IP_CIDR("IP-CIDR"),
|
||||
IP_CIDR6("IP-CIDR6"),
|
||||
IP_SUFFIX("IP-SUFFIX"),
|
||||
IP_ASN("IP-ASN"),
|
||||
GEOIP("GEOIP"),
|
||||
SRC_GEOIP("SRC-GEOIP"),
|
||||
SRC_IP_ASN("SRC-IP-ASN"),
|
||||
SRC_IP_CIDR("SRC-IP-CIDR"),
|
||||
SRC_IP_SUFFIX("SRC-IP-SUFFIX"),
|
||||
DST_PORT("DST-PORT"),
|
||||
SRC_PORT("SRC-PORT"),
|
||||
IN_PORT("IN-PORT"),
|
||||
IN_TYPE("IN-TYPE"),
|
||||
IN_USER("IN-USER"),
|
||||
IN_NAME("IN-NAME"),
|
||||
PROCESS_PATH("PROCESS-PATH"),
|
||||
PROCESS_PATH_REGEX("PROCESS-PATH-REGEX"),
|
||||
PROCESS_NAME("PROCESS-NAME"),
|
||||
PROCESS_NAME_REGEX("PROCESS-NAME-REGEX"),
|
||||
UID("UID"),
|
||||
NETWORK("NETWORK"),
|
||||
DSCP("DSCP"),
|
||||
RULE_SET("RULE-SET"),
|
||||
AND("AND"),
|
||||
OR("OR"),
|
||||
NOT("NOT"),
|
||||
SUB_RULE("SUB-RULE"),
|
||||
MATCH("MATCH");
|
||||
|
||||
final String value;
|
||||
|
||||
const RuleAction(this.value);
|
||||
}
|
||||
|
||||
extension RuleActionExt on RuleAction {
|
||||
bool get hasParams => [
|
||||
RuleAction.GEOIP,
|
||||
RuleAction.IP_ASN,
|
||||
RuleAction.SRC_IP_ASN,
|
||||
RuleAction.IP_CIDR,
|
||||
RuleAction.IP_CIDR6,
|
||||
RuleAction.IP_SUFFIX,
|
||||
RuleAction.RULE_SET,
|
||||
].contains(this);
|
||||
}
|
||||
|
||||
enum OverrideRuleType {
|
||||
override,
|
||||
added,
|
||||
}
|
||||
|
||||
enum RuleTarget {
|
||||
DIRECT,
|
||||
REJECT,
|
||||
}
|
||||
|
||||
@@ -150,9 +150,17 @@ class _AccessFragmentState extends ConsumerState<AccessFragment> {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
final res = await showSheet<int>(
|
||||
title: appLocalizations.proxiesSetting,
|
||||
context: context,
|
||||
body: AccessControlPanel(),
|
||||
props: SheetProps(
|
||||
isScrollControlled: true,
|
||||
),
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: AccessControlPanel(),
|
||||
title: appLocalizations.proxiesSetting,
|
||||
);
|
||||
},
|
||||
);
|
||||
if (res == 1) {
|
||||
_intelligentSelected();
|
||||
@@ -763,17 +771,19 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
..._buildModeSetting(),
|
||||
..._buildSortSetting(),
|
||||
..._buildSourceSetting(),
|
||||
..._buildActionSetting(),
|
||||
],
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
..._buildModeSetting(),
|
||||
..._buildSortSetting(),
|
||||
..._buildSourceSetting(),
|
||||
..._buildActionSetting(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -276,8 +276,8 @@ class ApplicationSettingFragment extends StatelessWidget {
|
||||
AutoRunItem(),
|
||||
if (Platform.isAndroid) ...[
|
||||
HiddenItem(),
|
||||
AnimateTabItem(),
|
||||
],
|
||||
AnimateTabItem(),
|
||||
OpenLogsItem(),
|
||||
CloseConnectionsItem(),
|
||||
UsageItem(),
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:fl_clash/widgets/fade_box.dart';
|
||||
import 'package:fl_clash/widgets/list.dart';
|
||||
import 'package:fl_clash/widgets/text.dart';
|
||||
@@ -174,7 +175,7 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
future: client!.pingCompleter.future,
|
||||
builder: (_, snapshot) {
|
||||
return Center(
|
||||
child: FadeBox(
|
||||
child: FadeThroughBox(
|
||||
child: snapshot.connectionState ==
|
||||
ConnectionState.waiting
|
||||
? const SizedBox(
|
||||
@@ -275,30 +276,27 @@ class _RecoveryOptionsDialogState extends State<RecoveryOptionsDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(appLocalizations.recovery),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
return CommonDialog(
|
||||
title: appLocalizations.recovery,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 16,
|
||||
),
|
||||
content: SizedBox(
|
||||
width: 250,
|
||||
child: Wrap(
|
||||
children: [
|
||||
ListItem(
|
||||
onTap: () {
|
||||
_handleOnTab(RecoveryOption.onlyProfiles);
|
||||
},
|
||||
title: Text(appLocalizations.recoveryProfiles),
|
||||
),
|
||||
ListItem(
|
||||
onTap: () {
|
||||
_handleOnTab(RecoveryOption.all);
|
||||
},
|
||||
title: Text(appLocalizations.recoveryAll),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Wrap(
|
||||
children: [
|
||||
ListItem(
|
||||
onTap: () {
|
||||
_handleOnTab(RecoveryOption.onlyProfiles);
|
||||
},
|
||||
title: Text(appLocalizations.recoveryProfiles),
|
||||
),
|
||||
ListItem(
|
||||
onTap: () {
|
||||
_handleOnTab(RecoveryOption.all);
|
||||
},
|
||||
title: Text(appLocalizations.recoveryAll),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -351,78 +349,8 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(appLocalizations.webDAVConfiguration),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: SizedBox(
|
||||
width: dialogCommonWidth,
|
||||
child: Wrap(
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: uriController,
|
||||
maxLines: 5,
|
||||
minLines: 1,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.link),
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: appLocalizations.address,
|
||||
helperText: appLocalizations.addressHelp,
|
||||
),
|
||||
validator: (String? value) {
|
||||
if (value == null || value.isEmpty || !value.isUrl) {
|
||||
return appLocalizations.addressTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
controller: userController,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.account_circle),
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: appLocalizations.account,
|
||||
),
|
||||
validator: (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations.accountTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _obscureController,
|
||||
builder: (_, obscure, __) {
|
||||
return TextFormField(
|
||||
controller: passwordController,
|
||||
obscureText: obscure,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.password),
|
||||
border: const OutlineInputBorder(),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
obscure ? Icons.visibility : Icons.visibility_off,
|
||||
),
|
||||
onPressed: () {
|
||||
_obscureController.value = !obscure;
|
||||
},
|
||||
),
|
||||
labelText: appLocalizations.password,
|
||||
),
|
||||
validator: (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations.passwordTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
return CommonDialog(
|
||||
title: appLocalizations.webDAVConfiguration,
|
||||
actions: [
|
||||
if (widget.dav != null)
|
||||
TextButton(
|
||||
@@ -434,6 +362,73 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
|
||||
child: Text(appLocalizations.save),
|
||||
)
|
||||
],
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Wrap(
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: uriController,
|
||||
maxLines: 5,
|
||||
minLines: 1,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.link),
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: appLocalizations.address,
|
||||
helperText: appLocalizations.addressHelp,
|
||||
),
|
||||
validator: (String? value) {
|
||||
if (value == null || value.isEmpty || !value.isUrl) {
|
||||
return appLocalizations.addressTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
controller: userController,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.account_circle),
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: appLocalizations.account,
|
||||
),
|
||||
validator: (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations.accountTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _obscureController,
|
||||
builder: (_, obscure, __) {
|
||||
return TextFormField(
|
||||
controller: passwordController,
|
||||
obscureText: obscure,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.password),
|
||||
border: const OutlineInputBorder(),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
obscure ? Icons.visibility : Icons.visibility_off,
|
||||
),
|
||||
onPressed: () {
|
||||
_obscureController.value = !obscure;
|
||||
},
|
||||
),
|
||||
labelText: appLocalizations.password,
|
||||
),
|
||||
validator: (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations.passwordTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,13 @@ import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/fragments/config/dns.dart';
|
||||
import 'package:fl_clash/fragments/config/general.dart';
|
||||
import 'package:fl_clash/fragments/config/network.dart';
|
||||
import 'package:fl_clash/models/clash_config.dart';
|
||||
import 'package:fl_clash/providers/config.dart' show patchClashConfigProvider;
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../state.dart';
|
||||
|
||||
class ConfigFragment extends StatefulWidget {
|
||||
const ConfigFragment({super.key});
|
||||
@@ -16,18 +21,6 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> items = [
|
||||
ListItem.open(
|
||||
title: Text(appLocalizations.network),
|
||||
subtitle: Text(appLocalizations.networkDesc),
|
||||
leading: const Icon(Icons.vpn_key),
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.network,
|
||||
isScaffold: true,
|
||||
isBlur: false,
|
||||
extendPageWidth: 360,
|
||||
widget: const NetworkListView(),
|
||||
),
|
||||
),
|
||||
ListItem.open(
|
||||
title: Text(appLocalizations.general),
|
||||
subtitle: Text(appLocalizations.generalDesc),
|
||||
@@ -37,20 +30,52 @@ class _ConfigFragmentState extends State<ConfigFragment> {
|
||||
widget: generateListView(
|
||||
generalItems,
|
||||
),
|
||||
isBlur: false,
|
||||
extendPageWidth: 360,
|
||||
blur: false,
|
||||
),
|
||||
),
|
||||
ListItem.open(
|
||||
title: Text(appLocalizations.network),
|
||||
subtitle: Text(appLocalizations.networkDesc),
|
||||
leading: const Icon(Icons.vpn_key),
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.network,
|
||||
blur: false,
|
||||
widget: const NetworkListView(),
|
||||
),
|
||||
),
|
||||
ListItem.open(
|
||||
title: const Text("DNS"),
|
||||
subtitle: Text(appLocalizations.dnsDesc),
|
||||
leading: const Icon(Icons.dns),
|
||||
delegate: const OpenDelegate(
|
||||
delegate: OpenDelegate(
|
||||
title: "DNS",
|
||||
widget: DnsListView(),
|
||||
isScaffold: true,
|
||||
isBlur: false,
|
||||
extendPageWidth: 360,
|
||||
action: Consumer(builder: (_, ref, __) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
final res = await globalState.showMessage(
|
||||
title: appLocalizations.reset,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.resetTip,
|
||||
),
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
dns: defaultDns,
|
||||
),
|
||||
);
|
||||
},
|
||||
tooltip: appLocalizations.reset,
|
||||
icon: const Icon(
|
||||
Icons.replay,
|
||||
),
|
||||
);
|
||||
}),
|
||||
widget: const DnsListView(),
|
||||
blur: false,
|
||||
),
|
||||
)
|
||||
];
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
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/providers/config.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@@ -205,7 +203,7 @@ class FakeIpFilterItem extends StatelessWidget {
|
||||
return ListItem.open(
|
||||
title: Text(appLocalizations.fakeipFilter),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: appLocalizations.fakeipFilter,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
@@ -213,7 +211,7 @@ class FakeIpFilterItem extends StatelessWidget {
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fakeIpFilter),
|
||||
);
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: appLocalizations.fakeipFilter,
|
||||
items: fakeIpFilter,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -227,7 +225,6 @@ class FakeIpFilterItem extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -242,14 +239,14 @@ class DefaultNameserverItem extends StatelessWidget {
|
||||
title: Text(appLocalizations.defaultNameserver),
|
||||
subtitle: Text(appLocalizations.defaultNameserverDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: appLocalizations.defaultNameserver,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final defaultNameserver = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.defaultNameserver),
|
||||
);
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: appLocalizations.defaultNameserver,
|
||||
items: defaultNameserver,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -262,7 +259,6 @@ class DefaultNameserverItem extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -278,13 +274,13 @@ class NameserverItem extends StatelessWidget {
|
||||
subtitle: Text(appLocalizations.nameserverDesc),
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.nameserver,
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final nameserver = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.nameserver),
|
||||
);
|
||||
return ListPage(
|
||||
title: "域名服务器",
|
||||
return ListInputPage(
|
||||
title: appLocalizations.nameserver,
|
||||
items: nameserver,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
@@ -296,7 +292,6 @@ class NameserverItem extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -357,28 +352,27 @@ class NameserverPolicyItem extends StatelessWidget {
|
||||
title: Text(appLocalizations.nameserverPolicy),
|
||||
subtitle: Text(appLocalizations.nameserverPolicyDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: appLocalizations.nameserverPolicy,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final nameserverPolicy = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.nameserverPolicy),
|
||||
);
|
||||
return ListPage(
|
||||
return MapInputPage(
|
||||
title: appLocalizations.nameserverPolicy,
|
||||
items: nameserverPolicy.entries,
|
||||
map: nameserverPolicy,
|
||||
titleBuilder: (item) => Text(item.key),
|
||||
subtitleBuilder: (item) => Text(item.value),
|
||||
onChange: (items) {
|
||||
onChange: (value) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.dns(
|
||||
nameserverPolicy: Map.fromEntries(items),
|
||||
nameserverPolicy: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -393,7 +387,7 @@ class ProxyServerNameserverItem extends StatelessWidget {
|
||||
title: Text(appLocalizations.proxyNameserver),
|
||||
subtitle: Text(appLocalizations.proxyNameserverDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: appLocalizations.proxyNameserver,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
@@ -401,7 +395,7 @@ class ProxyServerNameserverItem extends StatelessWidget {
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.proxyServerNameserver),
|
||||
);
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: appLocalizations.proxyNameserver,
|
||||
items: proxyServerNameserver,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -415,7 +409,6 @@ class ProxyServerNameserverItem extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -430,13 +423,13 @@ class FallbackItem extends StatelessWidget {
|
||||
title: Text(appLocalizations.fallback),
|
||||
subtitle: Text(appLocalizations.fallbackDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: appLocalizations.fallback,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final fallback = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.fallback),
|
||||
);
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: appLocalizations.fallback,
|
||||
items: fallback,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -449,7 +442,6 @@ class FallbackItem extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -518,14 +510,14 @@ class GeositeItem extends StatelessWidget {
|
||||
return ListItem.open(
|
||||
title: const Text("Geosite"),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: "Geosite",
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final geosite = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fallbackFilter.geosite),
|
||||
);
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: "Geosite",
|
||||
items: geosite,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -538,7 +530,6 @@ class GeositeItem extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -552,14 +543,14 @@ class IpcidrItem extends StatelessWidget {
|
||||
return ListItem.open(
|
||||
title: Text(appLocalizations.ipcidr),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: appLocalizations.ipcidr,
|
||||
widget: Consumer(builder: (_, ref, ___) {
|
||||
final ipcidr = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fallbackFilter.ipcidr),
|
||||
);
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: appLocalizations.ipcidr,
|
||||
items: ipcidr,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -572,7 +563,6 @@ class IpcidrItem extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -586,14 +576,14 @@ class DomainItem extends StatelessWidget {
|
||||
return ListItem.open(
|
||||
title: Text(appLocalizations.domain),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: appLocalizations.domain,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final domain = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fallbackFilter.domain),
|
||||
);
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: appLocalizations.domain,
|
||||
items: domain,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -606,7 +596,6 @@ class DomainItem extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -671,39 +660,8 @@ const dnsItems = <Widget>[
|
||||
class DnsListView extends ConsumerWidget {
|
||||
const DnsListView({super.key});
|
||||
|
||||
_initActions(BuildContext context, WidgetRef ref) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
context.commonScaffoldState?.actions = [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
final res = await globalState.showMessage(
|
||||
title: appLocalizations.reset,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.resetTip,
|
||||
),
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
dns: defaultDns,
|
||||
),
|
||||
);
|
||||
},
|
||||
tooltip: appLocalizations.reset,
|
||||
icon: const Icon(
|
||||
Icons.replay,
|
||||
),
|
||||
)
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
_initActions(context, ref);
|
||||
return generateListView(
|
||||
dnsItems,
|
||||
);
|
||||
|
||||
@@ -199,28 +199,27 @@ class HostsItem extends StatelessWidget {
|
||||
title: const Text("Hosts"),
|
||||
subtitle: Text(appLocalizations.hostsDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
blur: false,
|
||||
title: "Hosts",
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final hosts = ref
|
||||
.watch(patchClashConfigProvider.select((state) => state.hosts));
|
||||
return ListPage(
|
||||
return MapInputPage(
|
||||
title: "Hosts",
|
||||
items: hosts.entries,
|
||||
map: hosts,
|
||||
titleBuilder: (item) => Text(item.key),
|
||||
subtitleBuilder: (item) => Text(item.value),
|
||||
onChange: (items) {
|
||||
onChange: (value) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
hosts: Map.fromEntries(items),
|
||||
hosts: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,15 +224,14 @@ class BypassDomainItem extends StatelessWidget {
|
||||
title: Text(appLocalizations.bypassDomain),
|
||||
subtitle: Text(appLocalizations.bypassDomainDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
isScaffold: true,
|
||||
blur: false,
|
||||
title: appLocalizations.bypassDomain,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
_initActions(context, ref);
|
||||
final bypassDomain = ref.watch(
|
||||
networkSettingProvider.select((state) => state.bypassDomain));
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: appLocalizations.bypassDomain,
|
||||
items: bypassDomain,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -246,7 +245,6 @@ class BypassDomainItem extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -298,14 +296,14 @@ class RouteAddressItem extends ConsumerWidget {
|
||||
title: Text(appLocalizations.routeAddress),
|
||||
subtitle: Text(appLocalizations.routeAddressDesc),
|
||||
delegate: OpenDelegate(
|
||||
isBlur: false,
|
||||
isScaffold: true,
|
||||
blur: false,
|
||||
maxWidth: 360,
|
||||
title: appLocalizations.routeAddress,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final routeAddress = ref.watch(patchClashConfigProvider
|
||||
.select((state) => state.tun.routeAddress));
|
||||
return ListPage(
|
||||
return ListInputPage(
|
||||
title: appLocalizations.routeAddress,
|
||||
items: routeAddress,
|
||||
titleBuilder: (item) => Text(item),
|
||||
@@ -319,7 +317,6 @@ class RouteAddressItem extends ConsumerWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ class _ConnectionsFragmentState extends ConsumerState<ConnectionsFragment>
|
||||
return ConnectionItem(
|
||||
key: Key(connection.id),
|
||||
connection: connection,
|
||||
onClick: (value) {
|
||||
onClickKeyword: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
trailing: IconButton(
|
||||
|
||||
@@ -9,40 +9,15 @@ import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class FindProcessBuilder extends StatelessWidget {
|
||||
final Widget Function(bool value) builder;
|
||||
|
||||
const FindProcessBuilder({
|
||||
super.key,
|
||||
required this.builder,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final value = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) =>
|
||||
state.findProcessMode == FindProcessMode.always &&
|
||||
Platform.isAndroid,
|
||||
),
|
||||
);
|
||||
return builder(value);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionItem extends StatelessWidget {
|
||||
class ConnectionItem extends ConsumerWidget {
|
||||
final Connection connection;
|
||||
final Function(String)? onClick;
|
||||
final Function(String)? onClickKeyword;
|
||||
final Widget? trailing;
|
||||
|
||||
const ConnectionItem({
|
||||
super.key,
|
||||
required this.connection,
|
||||
this.onClick,
|
||||
this.onClickKeyword,
|
||||
this.trailing,
|
||||
});
|
||||
|
||||
@@ -59,7 +34,14 @@ class ConnectionItem extends StatelessWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final value = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) =>
|
||||
state.findProcessMode == FindProcessMode.always &&
|
||||
Platform.isAndroid,
|
||||
),
|
||||
);
|
||||
final title = Text(
|
||||
connection.desc,
|
||||
style: context.textTheme.bodyLarge,
|
||||
@@ -86,70 +68,143 @@ class ConnectionItem extends StatelessWidget {
|
||||
CommonChip(
|
||||
label: chain,
|
||||
onPressed: () {
|
||||
if (onClick == null) return;
|
||||
onClick!(chain);
|
||||
if (onClickKeyword == null) return;
|
||||
onClickKeyword!(chain);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
if (!Platform.isAndroid) {
|
||||
return ListItem(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
),
|
||||
tileTitleAlignment: ListTileTitleAlignment.titleHeight,
|
||||
title: title,
|
||||
subtitle: subTitle,
|
||||
trailing: trailing,
|
||||
);
|
||||
}
|
||||
return FindProcessBuilder(
|
||||
builder: (bool value) {
|
||||
final leading = value
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
if (onClick == null) return;
|
||||
final process = connection.metadata.process;
|
||||
if (process.isEmpty) return;
|
||||
onClick!(process);
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: FutureBuilder<ImageProvider?>(
|
||||
future: _getPackageIcon(connection),
|
||||
builder: (_, snapshot) {
|
||||
if (!snapshot.hasData && snapshot.data == null) {
|
||||
return Container();
|
||||
} else {
|
||||
return Image(
|
||||
image: snapshot.data!,
|
||||
gaplessPlayback: true,
|
||||
width: 48,
|
||||
height: 48,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
: null;
|
||||
return CommonPopupBox(
|
||||
targetBuilder: (open) {
|
||||
return ListItem(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 4,
|
||||
),
|
||||
tileTitleAlignment: ListTileTitleAlignment.titleHeight,
|
||||
leading: leading,
|
||||
leading: value
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
if (onClickKeyword == null) return;
|
||||
final process = connection.metadata.process;
|
||||
if (process.isEmpty) return;
|
||||
onClickKeyword!(process);
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 4),
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: FutureBuilder<ImageProvider?>(
|
||||
future: _getPackageIcon(connection),
|
||||
builder: (_, snapshot) {
|
||||
if (!snapshot.hasData && snapshot.data == null) {
|
||||
return Container();
|
||||
} else {
|
||||
return Image(
|
||||
image: snapshot.data!,
|
||||
gaplessPlayback: true,
|
||||
width: 48,
|
||||
height: 48,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
title: title,
|
||||
subtitle: subTitle,
|
||||
trailing: trailing,
|
||||
);
|
||||
// return InkWell(
|
||||
// child: GestureDetector(
|
||||
// onLongPressStart: (details) {
|
||||
// if (!system.isDesktop) {
|
||||
// return;
|
||||
// }
|
||||
// open(
|
||||
// offset: details.localPosition.translate(
|
||||
// 0,
|
||||
// -12,
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// onSecondaryTapDown: (details) {
|
||||
// if (!system.isDesktop) {
|
||||
// return;
|
||||
// }
|
||||
// open(
|
||||
// offset: details.localPosition.translate(
|
||||
// 0,
|
||||
// -12,
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// child: ListItem(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 16,
|
||||
// vertical: 4,
|
||||
// ),
|
||||
// tileTitleAlignment: ListTileTitleAlignment.titleHeight,
|
||||
// leading: value
|
||||
// ? GestureDetector(
|
||||
// onTap: () {
|
||||
// if (onClickKeyword == null) return;
|
||||
// final process = connection.metadata.process;
|
||||
// if (process.isEmpty) return;
|
||||
// onClickKeyword!(process);
|
||||
// },
|
||||
// child: Container(
|
||||
// margin: const EdgeInsets.only(top: 4),
|
||||
// width: 48,
|
||||
// height: 48,
|
||||
// child: FutureBuilder<ImageProvider?>(
|
||||
// future: _getPackageIcon(connection),
|
||||
// builder: (_, snapshot) {
|
||||
// if (!snapshot.hasData && snapshot.data == null) {
|
||||
// return Container();
|
||||
// } else {
|
||||
// return Image(
|
||||
// image: snapshot.data!,
|
||||
// gaplessPlayback: true,
|
||||
// width: 48,
|
||||
// height: 48,
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// : null,
|
||||
// title: title,
|
||||
// subtitle: subTitle,
|
||||
// trailing: trailing,
|
||||
// ),
|
||||
// ),
|
||||
// onTap: () {},
|
||||
// );
|
||||
},
|
||||
popup: CommonPopupMenu(
|
||||
minWidth: 160,
|
||||
items: [
|
||||
PopupMenuItemData(
|
||||
label: "编辑规则",
|
||||
onPressed: () {
|
||||
// _handleShowEditExtendPage(context);
|
||||
},
|
||||
),
|
||||
PopupMenuItemData(
|
||||
label: "设置直连",
|
||||
onPressed: () {},
|
||||
),
|
||||
PopupMenuItemData(
|
||||
label: "一键屏蔽",
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
@@ -20,6 +22,7 @@ class RequestsFragment extends ConsumerStatefulWidget {
|
||||
|
||||
class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
with PageMixin {
|
||||
final GlobalKey<CacheItemExtentListViewState> _key = GlobalKey();
|
||||
final _requestsStateNotifier =
|
||||
ValueNotifier<ConnectionsState>(const ConnectionsState());
|
||||
List<Connection> _requests = [];
|
||||
@@ -28,8 +31,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
initialScrollOffset: _preOffset != 0 ? _preOffset : double.maxFinite,
|
||||
);
|
||||
|
||||
final FixedMap<String, double?> _cacheDynamicHeightMap = FixedMap(1000);
|
||||
|
||||
double _currentMaxWidth = 0;
|
||||
|
||||
@override
|
||||
@@ -78,10 +79,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
}
|
||||
|
||||
double _calcCacheHeight(Connection item) {
|
||||
final cacheHeight = _cacheDynamicHeightMap.get(item.id);
|
||||
if (cacheHeight != null) {
|
||||
return cacheHeight;
|
||||
}
|
||||
final size = globalState.measure.computeTextSize(
|
||||
Text(
|
||||
item.desc,
|
||||
@@ -102,14 +99,13 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
final lines = (chainSize.height / baseHeight).round();
|
||||
final computerHeight =
|
||||
size.height + chainSize.height + 24 + 24 * (lines - 1);
|
||||
_cacheDynamicHeightMap.put(item.id, computerHeight);
|
||||
return computerHeight;
|
||||
}
|
||||
|
||||
_handleTryClearCache(double maxWidth) {
|
||||
if (_currentMaxWidth != maxWidth) {
|
||||
_currentMaxWidth = maxWidth;
|
||||
_cacheDynamicHeightMap.clear();
|
||||
_key.currentState?.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +114,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
_requestsStateNotifier.dispose();
|
||||
_scrollController.dispose();
|
||||
_currentMaxWidth = 0;
|
||||
_cacheDynamicHeightMap.clear();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -143,9 +138,19 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
return FindProcessBuilder(builder: (value) {
|
||||
_handleTryClearCache(constraints.maxWidth - 40 - (value ? 60 : 0));
|
||||
return ValueListenableBuilder<ConnectionsState>(
|
||||
return Consumer(
|
||||
builder: (_, ref, child) {
|
||||
final value = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) =>
|
||||
state.findProcessMode == FindProcessMode.always &&
|
||||
Platform.isAndroid,
|
||||
),
|
||||
);
|
||||
_handleTryClearCache(constraints.maxWidth - 40 - (value ? 60 : 0));
|
||||
return child!;
|
||||
},
|
||||
child: ValueListenableBuilder<ConnectionsState>(
|
||||
valueListenable: _requestsStateNotifier,
|
||||
builder: (_, state, __) {
|
||||
final connections = state.list;
|
||||
@@ -159,7 +164,7 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
(connection) => ConnectionItem(
|
||||
key: Key(connection.id),
|
||||
connection: connection,
|
||||
onClick: (value) {
|
||||
onClickKeyword: (value) {
|
||||
context.commonScaffoldState?.addKeyword(value);
|
||||
},
|
||||
),
|
||||
@@ -179,12 +184,13 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
},
|
||||
child: CommonScrollBar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
child: CacheItemExtentListView(
|
||||
key: _key,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemExtentBuilder: (index, __) {
|
||||
itemExtentBuilder: (index) {
|
||||
final widget = items[index];
|
||||
if (widget.runtimeType == Divider) {
|
||||
return 0;
|
||||
@@ -199,13 +205,21 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
|
||||
return items[index];
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
final widget = items[index];
|
||||
if (widget.runtimeType == Divider) {
|
||||
return "divider";
|
||||
}
|
||||
final connection = connections[(index / 2).floor()];
|
||||
return connection.id;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class IntranetIP extends StatelessWidget {
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final localIp = ref.watch(localIpProvider);
|
||||
return FadeBox(
|
||||
return FadeThroughBox(
|
||||
child: localIp != null
|
||||
? TooltipText(
|
||||
text: Text(
|
||||
|
||||
@@ -39,7 +39,7 @@ class _MemoryInfoState extends State<MemoryInfo> {
|
||||
_memoryInfoStateNotifier.value = TrafficValue(
|
||||
value: clashLib != null ? rss : await clashCore.getMemory() + rss,
|
||||
);
|
||||
timer = Timer(Duration(seconds: 5), () async {
|
||||
timer = Timer(Duration(seconds: 2), () async {
|
||||
_updateMemory();
|
||||
});
|
||||
});
|
||||
@@ -47,13 +47,8 @@ class _MemoryInfoState extends State<MemoryInfo> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final darkenLighter = context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.1)
|
||||
.toLighter;
|
||||
final darken = context.colorScheme.secondaryContainer
|
||||
.blendDarken(context, factor: 0.1);
|
||||
return SizedBox(
|
||||
height: getWidgetHeight(2),
|
||||
height: getWidgetHeight(1),
|
||||
child: CommonCard(
|
||||
info: Info(
|
||||
iconData: Icons.memory,
|
||||
@@ -76,43 +71,94 @@ class _MemoryInfoState extends State<MemoryInfo> {
|
||||
children: [
|
||||
Text(
|
||||
trafficValue.showValue,
|
||||
style: context.textTheme.titleLarge?.toLight,
|
||||
style:
|
||||
context.textTheme.bodyMedium?.toLight.adjustSize(1),
|
||||
),
|
||||
SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
Text(
|
||||
trafficValue.showUnit,
|
||||
style: context.textTheme.titleLarge?.toLight,
|
||||
style:
|
||||
context.textTheme.bodyMedium?.toLight.adjustSize(1),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Flexible(
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: WaveView(
|
||||
waveAmplitude: 12.0,
|
||||
waveFrequency: 0.35,
|
||||
waveColor: darkenLighter,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: WaveView(
|
||||
waveAmplitude: 12.0,
|
||||
waveFrequency: 0.9,
|
||||
waveColor: darken,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class AnimatedCounter extends StatefulWidget {
|
||||
// final double value;
|
||||
// final TextStyle? style;
|
||||
//
|
||||
// const AnimatedCounter({
|
||||
// super.key,
|
||||
// required this.value,
|
||||
// this.style,
|
||||
// });
|
||||
//
|
||||
// @override
|
||||
// State<AnimatedCounter> createState() => _AnimatedCounterState();
|
||||
// }
|
||||
//
|
||||
// class _AnimatedCounterState extends State<AnimatedCounter> {
|
||||
// late double _previousValue;
|
||||
// late double _currentValue;
|
||||
//
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _previousValue = widget.value;
|
||||
// _currentValue = widget.value;
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// void didUpdateWidget(AnimatedCounter oldWidget) {
|
||||
// super.didUpdateWidget(oldWidget);
|
||||
// if (oldWidget.value != widget.value) {
|
||||
// // if (_previousValue == _currentValue) {
|
||||
// // _previousValue = widget.value;
|
||||
// // _currentValue = widget.value;
|
||||
// // return;
|
||||
// // }
|
||||
// _currentValue = widget.value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// void dispose() {
|
||||
// super.dispose();
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Text(
|
||||
// _currentValue.fixed(decimals: 1),
|
||||
// style: widget.style,
|
||||
// );
|
||||
// return TweenAnimationBuilder(
|
||||
// tween: Tween(
|
||||
// begin: _previousValue,
|
||||
// end: _currentValue,
|
||||
// ),
|
||||
// onEnd: () {
|
||||
// _previousValue = _currentValue;
|
||||
// },
|
||||
// duration: Duration(seconds: 6),
|
||||
// curve: Curves.easeOut,
|
||||
// builder: (_, value, ___) {
|
||||
// return Text(
|
||||
// value.fixed(decimals: 1),
|
||||
// style: widget.style,
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -12,7 +12,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
final _networkDetectionState = ValueNotifier<NetworkDetectionState>(
|
||||
const NetworkDetectionState(
|
||||
isTesting: true,
|
||||
isTesting: false,
|
||||
isLoading: true,
|
||||
ipInfo: null,
|
||||
),
|
||||
);
|
||||
@@ -28,7 +29,6 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
bool? _preIsStart;
|
||||
Timer? _setTimeoutTimer;
|
||||
CancelToken? cancelToken;
|
||||
Completer? checkedCompleter;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -37,11 +37,14 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
_startCheck();
|
||||
}
|
||||
});
|
||||
if (!_networkDetectionState.value.isTesting &&
|
||||
_networkDetectionState.value.isLoading) {
|
||||
_startCheck();
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_startCheck() async {
|
||||
await checkedCompleter?.future;
|
||||
if (cancelToken != null) {
|
||||
cancelToken!.cancel();
|
||||
cancelToken = null;
|
||||
@@ -64,7 +67,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
}
|
||||
_clearSetTimeoutTimer();
|
||||
_networkDetectionState.value = _networkDetectionState.value.copyWith(
|
||||
isTesting: true,
|
||||
isLoading: true,
|
||||
ipInfo: null,
|
||||
);
|
||||
_preIsStart = isStart;
|
||||
@@ -74,16 +77,16 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
}
|
||||
cancelToken = CancelToken();
|
||||
try {
|
||||
_networkDetectionState.value = _networkDetectionState.value.copyWith(
|
||||
isTesting: true,
|
||||
);
|
||||
final ipInfo = await request.checkIp(cancelToken: cancelToken);
|
||||
_networkDetectionState.value = _networkDetectionState.value.copyWith(
|
||||
isTesting: false,
|
||||
);
|
||||
if (ipInfo != null) {
|
||||
checkedCompleter = Completer();
|
||||
checkedCompleter?.complete(
|
||||
Future.delayed(
|
||||
Duration(milliseconds: 3000),
|
||||
),
|
||||
);
|
||||
_networkDetectionState.value = _networkDetectionState.value.copyWith(
|
||||
isTesting: false,
|
||||
isLoading: false,
|
||||
ipInfo: ipInfo,
|
||||
);
|
||||
return;
|
||||
@@ -91,14 +94,14 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
_clearSetTimeoutTimer();
|
||||
_setTimeoutTimer = Timer(const Duration(milliseconds: 300), () {
|
||||
_networkDetectionState.value = _networkDetectionState.value.copyWith(
|
||||
isTesting: false,
|
||||
isLoading: false,
|
||||
ipInfo: null,
|
||||
);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.toString() == "cancelled") {
|
||||
_networkDetectionState.value = _networkDetectionState.value.copyWith(
|
||||
isTesting: true,
|
||||
isLoading: true,
|
||||
ipInfo: null,
|
||||
);
|
||||
}
|
||||
@@ -136,7 +139,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
valueListenable: _networkDetectionState,
|
||||
builder: (_, state, __) {
|
||||
final ipInfo = state.ipInfo;
|
||||
final isTesting = state.isTesting;
|
||||
final isLoading = state.isLoading;
|
||||
return CommonCard(
|
||||
onPressed: () {},
|
||||
child: Column(
|
||||
@@ -218,7 +221,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
),
|
||||
child: SizedBox(
|
||||
height: globalState.measure.bodyMediumHeight + 2,
|
||||
child: FadeBox(
|
||||
child: FadeThroughBox(
|
||||
child: ipInfo != null
|
||||
? TooltipText(
|
||||
text: Text(
|
||||
@@ -229,8 +232,8 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
)
|
||||
: FadeBox(
|
||||
child: isTesting == false && ipInfo == null
|
||||
: FadeThroughBox(
|
||||
child: isLoading == false && ipInfo == null
|
||||
? Text(
|
||||
"timeout",
|
||||
style: context.textTheme.bodyMedium
|
||||
|
||||
@@ -41,7 +41,7 @@ class _NetworkSpeedState extends State<NetworkSpeed> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = context.colorScheme.onSurfaceVariant.toLight;
|
||||
final color = context.colorScheme.onSurfaceVariant.opacity80;
|
||||
return SizedBox(
|
||||
height: getWidgetHeight(2),
|
||||
child: CommonCard(
|
||||
|
||||
@@ -38,7 +38,7 @@ class OutboundMode extends StatelessWidget {
|
||||
for (final item in Mode.values)
|
||||
Flexible(
|
||||
child: ListItem.radio(
|
||||
prue: true,
|
||||
dense: true,
|
||||
horizontalTitleGap: 4,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
|
||||
@@ -16,13 +16,20 @@ class TUNButton extends StatelessWidget {
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
body: generateListView(generateSection(
|
||||
items: [
|
||||
if (system.isDesktop) const TUNItem(),
|
||||
const TunStackItem(),
|
||||
],
|
||||
)),
|
||||
title: appLocalizations.tun,
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: generateListView(
|
||||
generateSection(
|
||||
items: [
|
||||
if (system.isDesktop) const TUNItem(),
|
||||
const TunStackItem(),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: appLocalizations.tun,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
info: Info(
|
||||
@@ -89,15 +96,20 @@ class SystemProxyButton extends StatelessWidget {
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
context: context,
|
||||
body: generateListView(
|
||||
generateSection(
|
||||
items: [
|
||||
SystemProxyItem(),
|
||||
BypassDomainItem(),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: appLocalizations.systemProxy,
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: generateListView(
|
||||
generateSection(
|
||||
items: [
|
||||
SystemProxyItem(),
|
||||
BypassDomainItem(),
|
||||
],
|
||||
),
|
||||
),
|
||||
title: appLocalizations.systemProxy,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
info: Info(
|
||||
|
||||
@@ -51,10 +51,8 @@ class TrafficUsage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final primaryColor =
|
||||
context.colorScheme.surfaceContainer.blendDarken(context, factor: 0.2);
|
||||
final secondaryColor =
|
||||
context.colorScheme.primaryContainer.blendDarken(context, factor: 0.3);
|
||||
final primaryColor = globalState.theme.darken3PrimaryContainer;
|
||||
final secondaryColor = globalState.theme.darken2SecondaryContainer;
|
||||
return SizedBox(
|
||||
height: getWidgetHeight(2),
|
||||
child: CommonCard(
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/card.dart';
|
||||
import 'package:fl_clash/widgets/dialog.dart';
|
||||
import 'package:fl_clash/widgets/list.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -156,9 +157,28 @@ class _HotKeyRecorderState extends State<HotKeyRecorder> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(IntlExt.actionMessage((widget.hotKeyAction.action.name))),
|
||||
content: ValueListenableBuilder(
|
||||
return CommonDialog(
|
||||
title: IntlExt.actionMessage(widget.hotKeyAction.action.name),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_handleRemove();
|
||||
},
|
||||
child: Text(appLocalizations.remove),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_handleConfirm();
|
||||
},
|
||||
child: Text(
|
||||
appLocalizations.confirm,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: hotKeyActionNotifier,
|
||||
builder: (_, hotKeyAction, ___) {
|
||||
final key = hotKeyAction.key;
|
||||
@@ -191,25 +211,6 @@ class _HotKeyRecorderState extends State<HotKeyRecorder> {
|
||||
);
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_handleRemove();
|
||||
},
|
||||
child: Text(appLocalizations.remove),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_handleConfirm();
|
||||
},
|
||||
child: Text(
|
||||
appLocalizations.confirm,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
final _scrollController = ScrollController(
|
||||
initialScrollOffset: _preOffset != 0 ? _preOffset : double.maxFinite,
|
||||
);
|
||||
final FixedMap<String, double?> _cacheDynamicHeightMap = FixedMap(1000);
|
||||
double _currentMaxWidth = 0;
|
||||
final GlobalKey<CacheItemExtentListViewState> _key = GlobalKey();
|
||||
|
||||
List<Log> _logs = [];
|
||||
|
||||
@@ -90,14 +90,13 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
void dispose() {
|
||||
_logsStateNotifier.dispose();
|
||||
_scrollController.dispose();
|
||||
_cacheDynamicHeightMap.clear();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_handleTryClearCache(double maxWidth) {
|
||||
if (_currentMaxWidth != maxWidth) {
|
||||
_currentMaxWidth = maxWidth;
|
||||
_cacheDynamicHeightMap.clear();
|
||||
_key.currentState?.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,27 +115,19 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
);
|
||||
}
|
||||
|
||||
double _calcCacheHeight(String text) {
|
||||
final cacheHeight = _cacheDynamicHeightMap.get(text);
|
||||
if (cacheHeight != null) {
|
||||
return cacheHeight;
|
||||
}
|
||||
final size = globalState.measure.computeTextSize(
|
||||
Text(
|
||||
text,
|
||||
style: globalState.appController.context.textTheme.bodyLarge,
|
||||
),
|
||||
maxWidth: _currentMaxWidth,
|
||||
);
|
||||
_cacheDynamicHeightMap.put(text, size.height);
|
||||
return size.height;
|
||||
}
|
||||
|
||||
double _getItemHeight(Log log) {
|
||||
final measure = globalState.measure;
|
||||
final bodySmallHeight = measure.bodySmallHeight;
|
||||
final bodyMediumHeight = measure.bodyMediumHeight;
|
||||
final height = _calcCacheHeight(log.payload ?? "");
|
||||
final height = globalState.measure
|
||||
.computeTextSize(
|
||||
Text(
|
||||
log.payload ?? "",
|
||||
style: globalState.appController.context.textTheme.bodyLarge,
|
||||
),
|
||||
maxWidth: _currentMaxWidth,
|
||||
)
|
||||
.height;
|
||||
return height + bodySmallHeight + 8 + bodyMediumHeight + 40;
|
||||
}
|
||||
|
||||
@@ -196,7 +187,8 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
},
|
||||
child: CommonScrollBar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
child: CacheItemExtentListView(
|
||||
key: _key,
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
physics: NextClampingScrollPhysics(),
|
||||
@@ -204,7 +196,7 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
itemBuilder: (_, index) {
|
||||
return items[index];
|
||||
},
|
||||
itemExtentBuilder: (index, __) {
|
||||
itemExtentBuilder: (index) {
|
||||
final item = items[index];
|
||||
if (item.runtimeType == Divider) {
|
||||
return 0;
|
||||
@@ -213,6 +205,14 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
|
||||
return _getItemHeight(log);
|
||||
},
|
||||
itemCount: items.length,
|
||||
keyBuilder: (int index) {
|
||||
final item = items[index];
|
||||
if (item.runtimeType == Divider) {
|
||||
return "divider";
|
||||
}
|
||||
final log = logs[(index / 2).floor()];
|
||||
return log.payload ?? "";
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -272,11 +272,3 @@ class LogItem extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NoGlowScrollBehavior extends ScrollBehavior {
|
||||
@override
|
||||
Widget buildOverscrollIndicator(
|
||||
BuildContext context, Widget child, ScrollableDetails details) {
|
||||
return child; // 禁用过度滚动效果
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,19 +50,19 @@ class AddProfile extends StatelessWidget {
|
||||
return ListView(
|
||||
children: [
|
||||
ListItem(
|
||||
leading: const Icon(Icons.qr_code),
|
||||
leading: const Icon(Icons.qr_code_sharp),
|
||||
title: Text(appLocalizations.qrcode),
|
||||
subtitle: Text(appLocalizations.qrcodeDesc),
|
||||
onTap: _toScan,
|
||||
),
|
||||
ListItem(
|
||||
leading: const Icon(Icons.upload_file),
|
||||
leading: const Icon(Icons.upload_file_sharp),
|
||||
title: Text(appLocalizations.file),
|
||||
subtitle: Text(appLocalizations.fileDesc),
|
||||
onTap: _handleAddProfileFormFile,
|
||||
),
|
||||
ListItem(
|
||||
leading: const Icon(Icons.cloud_download),
|
||||
leading: const Icon(Icons.cloud_download_sharp),
|
||||
title: Text(appLocalizations.url),
|
||||
subtitle: Text(appLocalizations.urlDesc),
|
||||
onTap: _toAdd,
|
||||
@@ -90,9 +90,15 @@ class _URLFormDialogState extends State<URLFormDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(appLocalizations.importFromURL),
|
||||
content: SizedBox(
|
||||
return CommonDialog(
|
||||
title: appLocalizations.importFromURL,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _handleAddProfileFormURL,
|
||||
child: Text(appLocalizations.submit),
|
||||
)
|
||||
],
|
||||
child: SizedBox(
|
||||
width: 300,
|
||||
child: Wrap(
|
||||
runSpacing: 16,
|
||||
@@ -109,12 +115,6 @@ class _URLFormDialogState extends State<URLFormDialog> {
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _handleAddProfileFormURL,
|
||||
child: Text(appLocalizations.submit),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ class _EditProfileState extends State<EditProfile> {
|
||||
ValueListenableBuilder<FileInfo?>(
|
||||
valueListenable: fileInfoNotifier,
|
||||
builder: (_, fileInfo, __) {
|
||||
return FadeBox(
|
||||
return FadeThroughBox(
|
||||
child: fileInfo == null
|
||||
? Container()
|
||||
: ListItem(
|
||||
@@ -324,15 +324,13 @@ class _EditProfileState extends State<EditProfile> {
|
||||
},
|
||||
),
|
||||
];
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, __) {
|
||||
if (didPop) return;
|
||||
return CommonPopScope(
|
||||
onPop: () {
|
||||
if (fileData == null) {
|
||||
Navigator.of(context).pop();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
_handleBack();
|
||||
return false;
|
||||
},
|
||||
child: FloatLayout(
|
||||
floatingWidget: FloatWrapper(
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
import 'package:fl_clash/clash/core.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/card.dart';
|
||||
import 'package:fl_clash/widgets/scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class GenProfile extends StatefulWidget {
|
||||
final String profileId;
|
||||
|
||||
const GenProfile({
|
||||
super.key,
|
||||
required this.profileId,
|
||||
});
|
||||
|
||||
@override
|
||||
State<GenProfile> createState() => _GenProfileState();
|
||||
}
|
||||
|
||||
class _GenProfileState extends State<GenProfile> {
|
||||
final _currentClashConfigNotifier = ValueNotifier<ClashConfigSnippet?>(null);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initCurrentClashConfig();
|
||||
}
|
||||
|
||||
_initCurrentClashConfig() async {
|
||||
final currentProfileId = globalState.config.currentProfileId;
|
||||
if (currentProfileId == null) {
|
||||
return;
|
||||
}
|
||||
_currentClashConfigNotifier.value =
|
||||
await clashCore.getProfile(currentProfileId);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonScaffold(
|
||||
body: ValueListenableBuilder(
|
||||
valueListenable: _currentClashConfigNotifier,
|
||||
builder: (_, clashConfig, ___) {
|
||||
if (clashConfig == null) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
return Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverGrid.builder(
|
||||
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 100,
|
||||
mainAxisExtent: 50,
|
||||
mainAxisSpacing: 8,
|
||||
crossAxisSpacing: 8,
|
||||
),
|
||||
itemCount: clashConfig.proxyGroups.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return CommonCard(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
clashConfig.proxyGroups[index].name,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SliverList.builder(
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final rule = clashConfig.rule[index];
|
||||
return Text(
|
||||
rule,
|
||||
);
|
||||
},
|
||||
itemCount: clashConfig.rule.length,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
title: "自定义",
|
||||
);
|
||||
}
|
||||
}
|
||||
957
lib/fragments/profiles/override_profile.dart
Normal file
957
lib/fragments/profiles/override_profile.dart
Normal file
@@ -0,0 +1,957 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fl_clash/clash/core.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/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class OverrideProfile extends StatefulWidget {
|
||||
final String profileId;
|
||||
|
||||
const OverrideProfile({
|
||||
super.key,
|
||||
required this.profileId,
|
||||
});
|
||||
|
||||
@override
|
||||
State<OverrideProfile> createState() => _OverrideProfileState();
|
||||
}
|
||||
|
||||
class _OverrideProfileState extends State<OverrideProfile> {
|
||||
final GlobalKey<CacheItemExtentListViewState> _ruleListKey = GlobalKey();
|
||||
final _controller = ScrollController();
|
||||
double _currentMaxWidth = 0;
|
||||
|
||||
_initState(WidgetRef ref) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Future.delayed(Duration(milliseconds: 300), () async {
|
||||
final snippet = await clashCore.getProfile(widget.profileId);
|
||||
final overrideData = ref.read(
|
||||
getProfileOverrideDataProvider(widget.profileId),
|
||||
);
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
snippet: snippet,
|
||||
overrideData: overrideData,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_handleSave(WidgetRef ref, OverrideData overrideData) {
|
||||
ref.read(profilesProvider.notifier).updateProfile(
|
||||
widget.profileId,
|
||||
(state) => state.copyWith(
|
||||
overrideData: overrideData,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_handleDelete(WidgetRef ref) async {
|
||||
final res = await globalState.showMessage(
|
||||
title: appLocalizations.tip,
|
||||
message: TextSpan(text: appLocalizations.deleteRuleTip),
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
final selectedRules = ref.read(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) => state.selectedRules,
|
||||
),
|
||||
);
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) {
|
||||
final overrideRule = state.overrideData!.rule.updateRules(
|
||||
(rules) => List.from(
|
||||
rules.where(
|
||||
(item) => !selectedRules.contains(item.id),
|
||||
),
|
||||
),
|
||||
);
|
||||
return state.copyWith.overrideData!(
|
||||
rule: overrideRule,
|
||||
);
|
||||
},
|
||||
);
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) => state.copyWith(isEdit: false, selectedRules: {}),
|
||||
);
|
||||
}
|
||||
|
||||
_handleTryClearCache(double maxWidth) {
|
||||
if (_currentMaxWidth != maxWidth) {
|
||||
_currentMaxWidth = maxWidth;
|
||||
_ruleListKey.currentState?.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
_buildContent() {
|
||||
return Consumer(
|
||||
builder: (_, ref, child) {
|
||||
final isInit = ref.watch(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) => state.snippet != null && state.overrideData != null,
|
||||
),
|
||||
);
|
||||
if (!isInit) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
return FadeBox(
|
||||
child: !isInit
|
||||
? Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: child!,
|
||||
);
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
_handleTryClearCache(constraints.maxWidth - 104);
|
||||
return CommonAutoHiddenScrollBar(
|
||||
controller: _controller,
|
||||
child: CustomScrollView(
|
||||
controller: _controller,
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: OverrideSwitch(),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 8,
|
||||
right: 8,
|
||||
),
|
||||
child: RuleTitle(
|
||||
profileId: widget.profileId,
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 0),
|
||||
sliver: RuleContent(
|
||||
maxWidth: _currentMaxWidth,
|
||||
ruleListKey: _ruleListKey,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
profileOverrideStateProvider.overrideWith(() => ProfileOverrideState()),
|
||||
],
|
||||
child: Consumer(
|
||||
builder: (_, ref, child) {
|
||||
_initState(ref);
|
||||
return child!;
|
||||
},
|
||||
child: Consumer(
|
||||
builder: (_, ref, ___) {
|
||||
final vm2 = ref.watch(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) => VM2(
|
||||
a: state.isEdit,
|
||||
b: state.selectedRules.length,
|
||||
),
|
||||
),
|
||||
);
|
||||
final isEdit = vm2.a;
|
||||
final editCount = vm2.b;
|
||||
return CommonScaffold(
|
||||
title: appLocalizations.override,
|
||||
body: _buildContent(),
|
||||
actions: [
|
||||
if (!isEdit)
|
||||
Consumer(
|
||||
builder: (_, ref, child) {
|
||||
final overrideData = ref.watch(
|
||||
getProfileOverrideDataProvider(widget.profileId));
|
||||
final newOverrideData = ref.watch(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) => state.overrideData,
|
||||
),
|
||||
);
|
||||
final equals = overrideData == newOverrideData;
|
||||
if (equals || newOverrideData == null) {
|
||||
return SizedBox();
|
||||
}
|
||||
return CommonPopScope(
|
||||
onPop: () async {
|
||||
if (equals) {
|
||||
return true;
|
||||
}
|
||||
final res = await globalState.showMessage(
|
||||
message: TextSpan(
|
||||
text: appLocalizations.saveChanges,
|
||||
),
|
||||
confirmText: appLocalizations.save,
|
||||
);
|
||||
if (!context.mounted || res != true) {
|
||||
return true;
|
||||
}
|
||||
_handleSave(ref, newOverrideData);
|
||||
return true;
|
||||
},
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
final res = await globalState.showMessage(
|
||||
message: TextSpan(
|
||||
text: appLocalizations.saveTip,
|
||||
),
|
||||
confirmText: appLocalizations.tip,
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
_handleSave(ref, newOverrideData);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.save,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (editCount == 1)
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
final rule = ref.read(profileOverrideStateProvider.select(
|
||||
(state) {
|
||||
return state.overrideData?.rule.rules.firstWhere(
|
||||
(item) => item.id == state.selectedRules.first,
|
||||
);
|
||||
},
|
||||
));
|
||||
if (rule == null) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.handleAddOrUpdate(
|
||||
ref,
|
||||
rule,
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.edit,
|
||||
),
|
||||
),
|
||||
if (editCount > 0)
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_handleDelete(ref);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.delete,
|
||||
),
|
||||
)
|
||||
],
|
||||
appBarEditState: AppBarEditState(
|
||||
isEdit: isEdit,
|
||||
editCount: editCount,
|
||||
onExit: () {
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
isEdit: false,
|
||||
selectedRules: {},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OverrideSwitch extends ConsumerWidget {
|
||||
const OverrideSwitch({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final enable = ref.watch(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) => state.overrideData?.enable,
|
||||
),
|
||||
);
|
||||
return CommonCard(
|
||||
onPressed: () {},
|
||||
type: CommonCardType.filled,
|
||||
radius: 18,
|
||||
child: ListItem.switchItem(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
),
|
||||
title: Text(appLocalizations.enableOverride),
|
||||
delegate: SwitchDelegate(
|
||||
value: enable ?? false,
|
||||
onChanged: (value) {
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) => state.copyWith.overrideData!(
|
||||
enable: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RuleTitle extends ConsumerWidget {
|
||||
final String profileId;
|
||||
|
||||
const RuleTitle({
|
||||
super.key,
|
||||
required this.profileId,
|
||||
});
|
||||
|
||||
_handleChangeType(WidgetRef ref, isOverrideRule) {
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) => state.copyWith.overrideData!.rule(
|
||||
type: isOverrideRule
|
||||
? OverrideRuleType.added
|
||||
: OverrideRuleType.override,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final vm3 = ref.watch(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) {
|
||||
final overrideRule = state.overrideData?.rule;
|
||||
return VM3(
|
||||
a: state.isEdit,
|
||||
b: state.selectedRules.containsAll(
|
||||
overrideRule?.rules.map((item) => item.id).toSet() ?? {},
|
||||
),
|
||||
c: overrideRule?.type == OverrideRuleType.override,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
final isEdit = vm3.a;
|
||||
final isSelectAll = vm3.b;
|
||||
final isOverrideRule = vm3.c;
|
||||
return FilledButtonTheme(
|
||||
data: FilledButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStatePropertyAll(EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
)),
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
),
|
||||
child: IconButtonTheme(
|
||||
data: IconButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStatePropertyAll(EdgeInsets.zero),
|
||||
visualDensity: VisualDensity.compact,
|
||||
iconSize: WidgetStatePropertyAll(20),
|
||||
),
|
||||
),
|
||||
child: ListHeader(
|
||||
title: appLocalizations.rule,
|
||||
subTitle: isOverrideRule
|
||||
? appLocalizations.overrideOriginRules
|
||||
: appLocalizations.addedOriginRules,
|
||||
space: 8,
|
||||
actions: [
|
||||
if (!isEdit)
|
||||
IconButton.filledTonal(
|
||||
icon: Icon(
|
||||
isOverrideRule ? Icons.edit_document : Icons.note_add,
|
||||
),
|
||||
onPressed: () {
|
||||
_handleChangeType(
|
||||
ref,
|
||||
isOverrideRule,
|
||||
);
|
||||
},
|
||||
),
|
||||
!isEdit
|
||||
? FilledButton.tonal(
|
||||
onPressed: () {
|
||||
globalState.appController.handleAddOrUpdate(ref);
|
||||
},
|
||||
child: Text(appLocalizations.add),
|
||||
)
|
||||
: isSelectAll
|
||||
? FilledButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(profileOverrideStateProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
selectedRules: {},
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(appLocalizations.selectAll),
|
||||
)
|
||||
: FilledButton.tonal(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(profileOverrideStateProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
selectedRules: state.overrideData?.rule.rules
|
||||
.map((item) => item.id)
|
||||
.toSet() ??
|
||||
{},
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(appLocalizations.selectAll),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RuleContent extends ConsumerWidget {
|
||||
final Key ruleListKey;
|
||||
final double maxWidth;
|
||||
|
||||
const RuleContent({
|
||||
super.key,
|
||||
required this.ruleListKey,
|
||||
required this.maxWidth,
|
||||
});
|
||||
|
||||
Widget _proxyDecorator(
|
||||
Widget child,
|
||||
int index,
|
||||
Animation<double> animation,
|
||||
) {
|
||||
return AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (_, Widget? child) {
|
||||
final double animValue = Curves.easeInOut.transform(animation.value);
|
||||
final double scale = lerpDouble(1, 1.02, animValue)!;
|
||||
return Transform.scale(
|
||||
scale: scale,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItem(Rule rule, int index) {
|
||||
return Consumer(
|
||||
builder: (context, ref, ___) {
|
||||
final vm2 = ref.watch(profileOverrideStateProvider.select(
|
||||
(item) => VM2(
|
||||
a: item.isEdit,
|
||||
b: item.selectedRules.contains(rule.id),
|
||||
),
|
||||
));
|
||||
final isEdit = vm2.a;
|
||||
final isSelected = vm2.b;
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? context.colorScheme.secondaryContainer.opacity80
|
||||
: context.colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: ListTile(
|
||||
minTileHeight: 0,
|
||||
minVerticalPadding: 0,
|
||||
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
trailing: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: !isEdit
|
||||
? ReorderableDragStartListener(
|
||||
index: index,
|
||||
child: const Icon(Icons.drag_handle),
|
||||
)
|
||||
: CommonCheckBox(
|
||||
value: isSelected,
|
||||
isCircle: true,
|
||||
onChanged: (_) {
|
||||
_handleSelect(ref, rule);
|
||||
},
|
||||
),
|
||||
),
|
||||
title: Text(rule.value),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_handleSelect(WidgetRef ref, ruleId) {
|
||||
if (!ref.read(profileOverrideStateProvider).isEdit) {
|
||||
return;
|
||||
}
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) {
|
||||
final newSelectedRules = Set<String>.from(state.selectedRules);
|
||||
if (newSelectedRules.contains(ruleId)) {
|
||||
newSelectedRules.remove(ruleId);
|
||||
} else {
|
||||
newSelectedRules.add(ruleId);
|
||||
}
|
||||
return state.copyWith(
|
||||
selectedRules: newSelectedRules,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final vm2 = ref.watch(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) {
|
||||
final overrideRule = state.overrideData?.rule;
|
||||
return VM2(
|
||||
a: overrideRule?.rules ?? [],
|
||||
b: overrideRule?.type ?? OverrideRuleType.added,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
final rules = vm2.a;
|
||||
final type = vm2.b;
|
||||
if (rules.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 300,
|
||||
child: Center(
|
||||
child: type == OverrideRuleType.added
|
||||
? Text(
|
||||
appLocalizations.noData,
|
||||
)
|
||||
: FilledButton(
|
||||
onPressed: () {
|
||||
final rules = ref.read(
|
||||
profileOverrideStateProvider.select(
|
||||
(state) => state.snippet?.rule ?? [],
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(profileOverrideStateProvider.notifier)
|
||||
.updateState(
|
||||
(state) {
|
||||
return state.copyWith.overrideData!.rule(
|
||||
overrideRules: rules,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Text(appLocalizations.getOriginRules),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return CacheItemExtentSliverReorderableList(
|
||||
key: ruleListKey,
|
||||
itemBuilder: (context, index) {
|
||||
final rule = rules[index];
|
||||
return GestureDetector(
|
||||
key: ObjectKey(rule),
|
||||
child: _buildItem(
|
||||
rule,
|
||||
index,
|
||||
),
|
||||
onTap: () {
|
||||
_handleSelect(ref, rule.id);
|
||||
},
|
||||
onLongPress: () {
|
||||
if (ref.read(profileOverrideStateProvider).isEdit) {
|
||||
return;
|
||||
}
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
isEdit: true,
|
||||
selectedRules: {
|
||||
rule.id,
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
proxyDecorator: _proxyDecorator,
|
||||
itemCount: rules.length,
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final newRules = List<Rule>.from(rules);
|
||||
final item = newRules.removeAt(oldIndex);
|
||||
newRules.insert(newIndex, item);
|
||||
ref.read(profileOverrideStateProvider.notifier).updateState(
|
||||
(state) => state.copyWith.overrideData!(
|
||||
rule: state.overrideData!.rule.updateRules((_) => newRules),
|
||||
),
|
||||
);
|
||||
},
|
||||
keyBuilder: (int index) {
|
||||
return rules[index].value;
|
||||
},
|
||||
itemExtentBuilder: (index) {
|
||||
final rule = rules[index];
|
||||
return 40 +
|
||||
globalState.measure
|
||||
.computeTextSize(
|
||||
Text(
|
||||
rule.value,
|
||||
style: context.textTheme.bodyMedium?.toJetBrainsMono,
|
||||
),
|
||||
maxWidth: maxWidth,
|
||||
)
|
||||
.height;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddRuleDialog extends StatefulWidget {
|
||||
final ClashConfigSnippet snippet;
|
||||
final Rule? rule;
|
||||
|
||||
const AddRuleDialog({
|
||||
super.key,
|
||||
required this.snippet,
|
||||
this.rule,
|
||||
});
|
||||
|
||||
@override
|
||||
State<AddRuleDialog> createState() => _AddRuleDialogState();
|
||||
}
|
||||
|
||||
class _AddRuleDialogState extends State<AddRuleDialog> {
|
||||
late RuleAction _ruleAction;
|
||||
final _ruleTargetController = TextEditingController();
|
||||
final _contentController = TextEditingController();
|
||||
final _ruleProviderController = TextEditingController();
|
||||
final _subRuleController = TextEditingController();
|
||||
bool _noResolve = false;
|
||||
bool _src = false;
|
||||
List<DropdownMenuEntry> _targetItems = [];
|
||||
List<DropdownMenuEntry> _ruleProviderItems = [];
|
||||
List<DropdownMenuEntry> _subRuleItems = [];
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_initState();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
_initState() {
|
||||
_targetItems = [
|
||||
...widget.snippet.proxyGroups.map(
|
||||
(item) => DropdownMenuEntry(
|
||||
value: item.name,
|
||||
label: item.name,
|
||||
),
|
||||
),
|
||||
...RuleTarget.values.map(
|
||||
(item) => DropdownMenuEntry(
|
||||
value: item.name,
|
||||
label: item.name,
|
||||
),
|
||||
),
|
||||
];
|
||||
_ruleProviderItems = [
|
||||
...widget.snippet.ruleProvider.map(
|
||||
(item) => DropdownMenuEntry(
|
||||
value: item.name,
|
||||
label: item.name,
|
||||
),
|
||||
),
|
||||
];
|
||||
_subRuleItems = [
|
||||
...widget.snippet.subRules.map(
|
||||
(item) => DropdownMenuEntry(
|
||||
value: item.name,
|
||||
label: item.name,
|
||||
),
|
||||
),
|
||||
];
|
||||
if (widget.rule != null) {
|
||||
final parsedRule = ParsedRule.parseString(widget.rule!.value);
|
||||
_ruleAction = parsedRule.ruleAction;
|
||||
_contentController.text = parsedRule.content ?? "";
|
||||
_ruleTargetController.text = parsedRule.ruleTarget ?? "";
|
||||
_ruleProviderController.text = parsedRule.ruleProvider ?? "";
|
||||
_subRuleController.text = parsedRule.subRule ?? "";
|
||||
_noResolve = parsedRule.noResolve;
|
||||
_src = parsedRule.src;
|
||||
return;
|
||||
}
|
||||
_ruleAction = RuleAction.values.first;
|
||||
if (_targetItems.isNotEmpty) {
|
||||
_ruleTargetController.text = _targetItems.first.value;
|
||||
}
|
||||
if (_ruleProviderItems.isNotEmpty) {
|
||||
_ruleProviderController.text = _ruleProviderItems.first.value;
|
||||
}
|
||||
if (_subRuleItems.isNotEmpty) {
|
||||
_subRuleController.text = _subRuleItems.first.value;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(AddRuleDialog oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.rule != widget.rule) {
|
||||
_initState();
|
||||
}
|
||||
}
|
||||
|
||||
_handleSubmit() {
|
||||
final res = _formKey.currentState?.validate();
|
||||
if (res == false) {
|
||||
return;
|
||||
}
|
||||
final parsedRule = ParsedRule(
|
||||
ruleAction: _ruleAction,
|
||||
content: _contentController.text,
|
||||
ruleProvider: _ruleProviderController.text,
|
||||
ruleTarget: _ruleTargetController.text,
|
||||
subRule: _subRuleController.text,
|
||||
noResolve: _noResolve,
|
||||
src: _src,
|
||||
);
|
||||
final rule = widget.rule != null
|
||||
? widget.rule!.copyWith(value: parsedRule.value)
|
||||
: Rule.value(
|
||||
parsedRule.value,
|
||||
);
|
||||
Navigator.of(context).pop(rule);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CommonDialog(
|
||||
title: appLocalizations.addRule,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: _handleSubmit,
|
||||
child: Text(
|
||||
appLocalizations.confirm,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: DropdownMenuTheme(
|
||||
data: DropdownMenuThemeData(
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
border: OutlineInputBorder(),
|
||||
labelStyle: context.textTheme.bodyLarge
|
||||
?.copyWith(overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
FilledButton.tonal(
|
||||
onPressed: () async {
|
||||
_ruleAction =
|
||||
await globalState.showCommonDialog<RuleAction>(
|
||||
child: OptionsDialog<RuleAction>(
|
||||
title: appLocalizations.ruleName,
|
||||
options: RuleAction.values,
|
||||
textBuilder: (item) => item.value,
|
||||
value: _ruleAction,
|
||||
),
|
||||
) ??
|
||||
_ruleAction;
|
||||
setState(() {});
|
||||
},
|
||||
child: Text(_ruleAction.name),
|
||||
),
|
||||
SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
_ruleAction == RuleAction.RULE_SET
|
||||
? FormField(
|
||||
validator: (_) {
|
||||
if (_ruleProviderController.text.isEmpty) {
|
||||
return appLocalizations.ruleProviderEmptyTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
builder: (field) {
|
||||
return DropdownMenu(
|
||||
expandedInsets: EdgeInsets.zero,
|
||||
controller: _ruleProviderController,
|
||||
label: Text(appLocalizations.ruleProviders),
|
||||
menuHeight: 250,
|
||||
errorText: field.errorText,
|
||||
dropdownMenuEntries: _ruleProviderItems,
|
||||
);
|
||||
},
|
||||
)
|
||||
: TextFormField(
|
||||
controller: _contentController,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: appLocalizations.content,
|
||||
),
|
||||
validator: (_) {
|
||||
if (_contentController.text.isEmpty) {
|
||||
return appLocalizations.contentEmptyTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
_ruleAction == RuleAction.SUB_RULE
|
||||
? FormField(
|
||||
validator: (_) {
|
||||
if (_subRuleController.text.isEmpty) {
|
||||
return appLocalizations.subRuleEmptyTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
builder: (filed) {
|
||||
return DropdownMenu(
|
||||
width: 200,
|
||||
controller: _subRuleController,
|
||||
label: Text(appLocalizations.subRule),
|
||||
menuHeight: 250,
|
||||
dropdownMenuEntries: _subRuleItems,
|
||||
);
|
||||
},
|
||||
)
|
||||
: FormField<String>(
|
||||
validator: (_) {
|
||||
if (_ruleTargetController.text.isEmpty) {
|
||||
return appLocalizations.ruleTargetEmptyTip;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
builder: (filed) {
|
||||
return DropdownMenu(
|
||||
controller: _ruleTargetController,
|
||||
initialSelection: filed.value,
|
||||
label: Text(appLocalizations.ruleTarget),
|
||||
width: 200,
|
||||
menuHeight: 250,
|
||||
enableFilter: true,
|
||||
dropdownMenuEntries: _targetItems,
|
||||
errorText: filed.errorText,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (_ruleAction.hasParams) ...[
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
children: [
|
||||
CommonCard(
|
||||
radius: 8,
|
||||
isSelected: _src,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 8),
|
||||
child: Text(
|
||||
appLocalizations.sourceIp,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_src = !_src;
|
||||
});
|
||||
},
|
||||
),
|
||||
CommonCard(
|
||||
radius: 8,
|
||||
isSelected: _noResolve,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 8),
|
||||
child: Text(
|
||||
appLocalizations.noResolve,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_noResolve = !_noResolve;
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:ui';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/fragments/profiles/edit_profile.dart';
|
||||
import 'package:fl_clash/fragments/profiles/override_profile.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
@@ -11,7 +12,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'add_profile.dart';
|
||||
import 'gen_profile.dart';
|
||||
|
||||
class ProfilesFragment extends StatefulWidget {
|
||||
const ProfilesFragment({super.key});
|
||||
@@ -24,12 +24,17 @@ class _ProfilesFragmentState extends State<ProfilesFragment> with PageMixin {
|
||||
Function? applyConfigDebounce;
|
||||
|
||||
_handleShowAddExtendPage() {
|
||||
showExtendPage(
|
||||
showExtend(
|
||||
globalState.navigatorKey.currentState!.context,
|
||||
body: AddProfile(
|
||||
context: globalState.navigatorKey.currentState!.context,
|
||||
),
|
||||
title: "${appLocalizations.add}${appLocalizations.profile}",
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: AddProfile(
|
||||
context: globalState.navigatorKey.currentState!.context,
|
||||
),
|
||||
title: "${appLocalizations.add}${appLocalizations.profile}",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,12 +86,13 @@ class _ProfilesFragmentState extends State<ProfilesFragment> with PageMixin {
|
||||
onPressed: () {
|
||||
final profiles = globalState.config.profiles;
|
||||
showSheet(
|
||||
title: appLocalizations.profilesSort,
|
||||
context: context,
|
||||
body: SizedBox(
|
||||
height: 400,
|
||||
child: ReorderableProfiles(profiles: profiles),
|
||||
),
|
||||
builder: (_, type) {
|
||||
return ReorderableProfilesSheet(
|
||||
type: type,
|
||||
profiles: profiles,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.sort),
|
||||
@@ -205,13 +211,18 @@ class ProfileItem extends StatelessWidget {
|
||||
}
|
||||
|
||||
_handleShowEditExtendPage(BuildContext context) {
|
||||
showExtendPage(
|
||||
showExtend(
|
||||
context,
|
||||
body: EditProfile(
|
||||
profile: profile,
|
||||
context: context,
|
||||
),
|
||||
title: "${appLocalizations.edit}${appLocalizations.profile}",
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: EditProfile(
|
||||
profile: profile,
|
||||
context: context,
|
||||
),
|
||||
title: "${appLocalizations.edit}${appLocalizations.profile}",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -277,7 +288,7 @@ class ProfileItem extends StatelessWidget {
|
||||
_handlePushGenProfilePage(BuildContext context, String id) {
|
||||
BaseNavigator.push(
|
||||
context,
|
||||
GenProfile(
|
||||
OverrideProfile(
|
||||
profileId: id,
|
||||
),
|
||||
);
|
||||
@@ -285,7 +296,6 @@ class ProfileItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final key = GlobalKey<CommonPopupBoxState>();
|
||||
return CommonCard(
|
||||
isSelected: profile.id == groupValue,
|
||||
onPressed: () {
|
||||
@@ -298,17 +308,16 @@ class ProfileItem extends StatelessWidget {
|
||||
trailing: SizedBox(
|
||||
height: 40,
|
||||
width: 40,
|
||||
child: FadeBox(
|
||||
child: FadeThroughBox(
|
||||
child: profile.isUpdating
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: CommonPopupBox(
|
||||
key: key,
|
||||
popup: CommonPopupMenu(
|
||||
items: [
|
||||
ActionItemData(
|
||||
PopupMenuItemData(
|
||||
icon: Icons.edit_outlined,
|
||||
label: appLocalizations.edit,
|
||||
onPressed: () {
|
||||
@@ -316,52 +325,47 @@ class ProfileItem extends StatelessWidget {
|
||||
},
|
||||
),
|
||||
if (profile.type == ProfileType.url) ...[
|
||||
ActionItemData(
|
||||
PopupMenuItemData(
|
||||
icon: Icons.sync_alt_sharp,
|
||||
label: appLocalizations.sync,
|
||||
onPressed: () {
|
||||
updateProfile();
|
||||
},
|
||||
),
|
||||
// ActionItemData(
|
||||
// icon: Icons.copy,
|
||||
// label: appLocalizations.copyLink,
|
||||
// onPressed: () {
|
||||
// _handleCopyLink(context);
|
||||
// },
|
||||
// ),
|
||||
],
|
||||
// ActionItemData(
|
||||
// icon: Icons.extension_outlined,
|
||||
// label: "自定义",
|
||||
// onPressed: () {
|
||||
// _handlePushGenProfilePage(context, profile.id);
|
||||
// },
|
||||
// ),
|
||||
ActionItemData(
|
||||
PopupMenuItemData(
|
||||
icon: Icons.extension_outlined,
|
||||
label: appLocalizations.override,
|
||||
onPressed: () {
|
||||
_handlePushGenProfilePage(context, profile.id);
|
||||
},
|
||||
),
|
||||
PopupMenuItemData(
|
||||
icon: Icons.file_copy_outlined,
|
||||
label: appLocalizations.exportFile,
|
||||
onPressed: () {
|
||||
_handleExportFile(context);
|
||||
},
|
||||
),
|
||||
ActionItemData(
|
||||
PopupMenuItemData(
|
||||
icon: Icons.delete_outlined,
|
||||
iconSize: 20,
|
||||
label: appLocalizations.delete,
|
||||
onPressed: () {
|
||||
_handleDeleteProfile(context);
|
||||
},
|
||||
type: ActionType.danger,
|
||||
type: PopupMenuItemType.danger,
|
||||
),
|
||||
],
|
||||
),
|
||||
target: IconButton(
|
||||
onPressed: () {
|
||||
key.currentState?.pop();
|
||||
},
|
||||
icon: Icon(Icons.more_vert),
|
||||
),
|
||||
targetBuilder: (open) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
open();
|
||||
},
|
||||
icon: Icon(Icons.more_vert),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -397,19 +401,22 @@ class ProfileItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class ReorderableProfiles extends StatefulWidget {
|
||||
class ReorderableProfilesSheet extends StatefulWidget {
|
||||
final List<Profile> profiles;
|
||||
final SheetType type;
|
||||
|
||||
const ReorderableProfiles({
|
||||
const ReorderableProfilesSheet({
|
||||
super.key,
|
||||
required this.profiles,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ReorderableProfiles> createState() => _ReorderableProfilesState();
|
||||
State<ReorderableProfilesSheet> createState() =>
|
||||
_ReorderableProfilesSheetState();
|
||||
}
|
||||
|
||||
class _ReorderableProfilesState extends State<ReorderableProfiles> {
|
||||
class _ReorderableProfilesSheetState extends State<ReorderableProfilesSheet> {
|
||||
late List<Profile> profiles;
|
||||
|
||||
@override
|
||||
@@ -453,74 +460,61 @@ class _ReorderableProfilesState extends State<ReorderableProfiles> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ReorderableListView.builder(
|
||||
buildDefaultDragHandles: false,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
proxyDecorator: proxyDecorator,
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
setState(() {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final profile = profiles.removeAt(oldIndex);
|
||||
profiles.insert(newIndex, profile);
|
||||
});
|
||||
},
|
||||
itemBuilder: (_, index) {
|
||||
final profile = profiles[index];
|
||||
return Container(
|
||||
key: Key(profile.id),
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: CommonCard(
|
||||
type: CommonCardType.filled,
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
right: 16,
|
||||
left: 16,
|
||||
),
|
||||
title: Text(profile.label ?? profile.id),
|
||||
trailing: ReorderableDragStartListener(
|
||||
index: index,
|
||||
child: const Icon(Icons.drag_handle),
|
||||
),
|
||||
return AdaptiveSheetScaffold(
|
||||
type: widget.type,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
globalState.appController.setProfiles(profiles);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.save,
|
||||
),
|
||||
)
|
||||
],
|
||||
body: Padding(
|
||||
padding: EdgeInsets.only(bottom: 32),
|
||||
child: ReorderableListView.builder(
|
||||
buildDefaultDragHandles: false,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
),
|
||||
proxyDecorator: proxyDecorator,
|
||||
onReorder: (oldIndex, newIndex) {
|
||||
setState(() {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
final profile = profiles.removeAt(oldIndex);
|
||||
profiles.insert(newIndex, profile);
|
||||
});
|
||||
},
|
||||
itemBuilder: (_, index) {
|
||||
final profile = profiles[index];
|
||||
return Container(
|
||||
key: Key(profile.id),
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: CommonCard(
|
||||
type: CommonCardType.filled,
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
right: 16,
|
||||
left: 16,
|
||||
),
|
||||
title: Text(profile.label ?? profile.id),
|
||||
trailing: ReorderableDragStartListener(
|
||||
index: index,
|
||||
child: const Icon(Icons.drag_handle),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: profiles.length,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 16,
|
||||
horizontal: 24,
|
||||
),
|
||||
child: FilledButton.tonal(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
globalState.appController.setProfiles(profiles);
|
||||
},
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(vertical: 8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
appLocalizations.confirm,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: profiles.length,
|
||||
),
|
||||
],
|
||||
),
|
||||
title: appLocalizations.profilesSort,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ class ProxyCard extends StatelessWidget {
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color:
|
||||
context.textTheme.bodySmall?.color?.toLight,
|
||||
context.textTheme.bodySmall?.color?.opacity80,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -221,7 +221,7 @@ class _ProxyDesc extends ConsumerWidget {
|
||||
desc,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: context.textTheme.bodySmall?.color?.toLight,
|
||||
color: context.textTheme.bodySmall?.color?.opacity80,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -467,10 +467,6 @@ class _ListHeaderState extends State<ListHeader>
|
||||
return CommonCard(
|
||||
enterAnimated: widget.enterAnimated,
|
||||
key: widget.key,
|
||||
borderSide: WidgetStatePropertyAll(BorderSide.none),
|
||||
backgroundColor: WidgetStatePropertyAll(
|
||||
context.colorScheme.surfaceContainer,
|
||||
),
|
||||
radius: 14,
|
||||
type: CommonCardType.filled,
|
||||
child: Padding(
|
||||
@@ -556,6 +552,7 @@ class _ListHeaderState extends State<ListHeader>
|
||||
children: [
|
||||
if (isExpand) ...[
|
||||
IconButton(
|
||||
visualDensity: VisualDensity.standard,
|
||||
onPressed: () {
|
||||
widget.onScrollToSelected(groupName);
|
||||
},
|
||||
@@ -565,12 +562,13 @@ class _ListHeaderState extends State<ListHeader>
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _delayTest,
|
||||
visualDensity: VisualDensity.standard,
|
||||
icon: const Icon(
|
||||
Icons.network_ping,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
width: 6,
|
||||
),
|
||||
],
|
||||
AnimatedBuilder(
|
||||
|
||||
@@ -13,8 +13,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
typedef UpdatingMap = Map<String, bool>;
|
||||
|
||||
class ProvidersView extends ConsumerStatefulWidget {
|
||||
final SheetType type;
|
||||
|
||||
const ProvidersView({
|
||||
super.key,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -22,25 +25,6 @@ class ProvidersView extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _ProvidersViewState extends ConsumerState<ProvidersView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
globalState.appController.updateProviders();
|
||||
context.commonScaffoldState?.actions = [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_updateProviders();
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.sync,
|
||||
),
|
||||
)
|
||||
];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
_updateProviders() async {
|
||||
final providers = ref.read(providersProvider);
|
||||
@@ -102,10 +86,24 @@ class _ProvidersViewState extends ConsumerState<ProvidersView> {
|
||||
title: appLocalizations.ruleProviders,
|
||||
items: ruleProviders,
|
||||
);
|
||||
return generateListView([
|
||||
...proxySection,
|
||||
...ruleSection,
|
||||
]);
|
||||
return AdaptiveSheetScaffold(
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_updateProviders();
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.sync,
|
||||
),
|
||||
)
|
||||
],
|
||||
type: widget.type,
|
||||
body: generateListView([
|
||||
...proxySection,
|
||||
...ruleSection,
|
||||
]),
|
||||
title: appLocalizations.providers,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +220,7 @@ class ProviderItem extends StatelessWidget {
|
||||
trailing: SizedBox(
|
||||
height: 48,
|
||||
width: 48,
|
||||
child: FadeBox(
|
||||
child: FadeThroughBox(
|
||||
child: provider.isUpdating
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
|
||||
@@ -28,12 +28,13 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
|
||||
if (_hasProviders)
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
showExtendPage(
|
||||
isScaffold: true,
|
||||
extendPageWidth: 360,
|
||||
showExtend(
|
||||
context,
|
||||
body: const ProvidersView(),
|
||||
title: appLocalizations.providers,
|
||||
builder: (_, type) {
|
||||
return ProvidersView(
|
||||
type: type,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(
|
||||
@@ -51,11 +52,15 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
|
||||
)
|
||||
: IconButton(
|
||||
onPressed: () {
|
||||
showExtendPage(
|
||||
showExtend(
|
||||
context,
|
||||
extendPageWidth: 360,
|
||||
title: appLocalizations.iconConfiguration,
|
||||
body: _IconConfigView(),
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: const _IconConfigView(),
|
||||
title: appLocalizations.iconConfiguration,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(
|
||||
@@ -65,9 +70,17 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
showSheet(
|
||||
title: appLocalizations.proxiesSetting,
|
||||
context: context,
|
||||
body: const ProxiesSetting(),
|
||||
props: SheetProps(
|
||||
isScrollControlled: true,
|
||||
),
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: const ProxiesSetting(),
|
||||
title: appLocalizations.proxiesSetting,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(
|
||||
@@ -128,13 +141,11 @@ class _IconConfigView extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final iconMap =
|
||||
ref.watch(proxiesStyleSettingProvider.select((state) => state.iconMap));
|
||||
final entries = iconMap.entries.toList();
|
||||
return ListPage(
|
||||
return MapInputPage(
|
||||
title: appLocalizations.iconConfiguration,
|
||||
items: entries,
|
||||
map: iconMap,
|
||||
keyLabel: appLocalizations.regExp,
|
||||
valueLabel: appLocalizations.icon,
|
||||
keyBuilder: (item) => Key(item.key),
|
||||
titleBuilder: (item) => Text(item.key),
|
||||
leadingBuilder: (item) => Container(
|
||||
decoration: BoxDecoration(
|
||||
@@ -151,10 +162,10 @@ class _IconConfigView extends ConsumerWidget {
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
onChange: (entries) {
|
||||
onChange: (value) {
|
||||
ref.read(proxiesStyleSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
iconMap: Map.fromEntries(entries),
|
||||
iconMap: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -248,8 +248,8 @@ class ProxiesSetting extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32),
|
||||
return SingleChildScrollView(
|
||||
padding: EdgeInsets.only(bottom: 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -268,6 +268,7 @@ class ProxiesSetting extends StatelessWidget {
|
||||
return Container();
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
..._buildGroupStyleSetting(),
|
||||
|
||||
@@ -49,7 +49,7 @@ class ProxiesTabFragmentState extends ConsumerState<ProxiesTabFragment>
|
||||
_buildMoreButton() {
|
||||
return Consumer(
|
||||
builder: (_, ref, ___) {
|
||||
final isMobileView = ref.watch(viewWidthProvider.notifier).isMobileView;
|
||||
final isMobileView = ref.watch(isMobileViewProvider);
|
||||
return IconButton(
|
||||
onPressed: _showMoreMenu,
|
||||
icon: isMobileView
|
||||
@@ -67,42 +67,48 @@ class ProxiesTabFragmentState extends ConsumerState<ProxiesTabFragment>
|
||||
_showMoreMenu() {
|
||||
showSheet(
|
||||
context: context,
|
||||
width: 380,
|
||||
isScrollControlled: false,
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final state = ref.watch(proxiesSelectorStateProvider);
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (final groupName in state.groupNames)
|
||||
SettingTextCard(
|
||||
groupName,
|
||||
onPressed: () {
|
||||
final index = state.groupNames.indexWhere(
|
||||
(item) => item == groupName,
|
||||
);
|
||||
if (index == -1) return;
|
||||
_tabController?.animateTo(index);
|
||||
globalState.appController
|
||||
.updateCurrentGroupName(groupName);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
isSelected: groupName == state.currentGroupName,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
props: SheetProps(
|
||||
isScrollControlled: false,
|
||||
),
|
||||
title: appLocalizations.proxyGroup,
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final state = ref.watch(proxiesSelectorStateProvider);
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
for (final groupName in state.groupNames)
|
||||
SettingTextCard(
|
||||
groupName,
|
||||
onPressed: () {
|
||||
final index = state.groupNames.indexWhere(
|
||||
(item) => item == groupName,
|
||||
);
|
||||
if (index == -1) return;
|
||||
_tabController?.animateTo(index);
|
||||
globalState.appController
|
||||
.updateCurrentGroupName(groupName);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
isSelected: groupName == state.currentGroupName,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
title: appLocalizations.proxyGroup,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -238,7 +244,7 @@ class ProxiesTabFragmentState extends ConsumerState<ProxiesTabFragment>
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
context.colorScheme.surface.withOpacity(0.1),
|
||||
context.colorScheme.surface.opacity10,
|
||||
context.colorScheme.surface,
|
||||
],
|
||||
stops: const [
|
||||
@@ -319,32 +325,35 @@ class ProxyGroupViewState extends ConsumerState<ProxyGroupView> {
|
||||
);
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: GridView.builder(
|
||||
child: CommonAutoHiddenScrollBar(
|
||||
controller: _controller,
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 96,
|
||||
child: GridView.builder(
|
||||
controller: _controller,
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 96,
|
||||
),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: columns,
|
||||
mainAxisSpacing: 8,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisExtent: getItemHeight(proxyCardType),
|
||||
),
|
||||
itemCount: sortedProxies.length,
|
||||
itemBuilder: (_, index) {
|
||||
final proxy = sortedProxies[index];
|
||||
return ProxyCard(
|
||||
testUrl: state.testUrl,
|
||||
groupType: state.groupType,
|
||||
type: proxyCardType,
|
||||
key: ValueKey('$groupName.${proxy.name}'),
|
||||
proxy: proxy,
|
||||
groupName: groupName,
|
||||
);
|
||||
},
|
||||
),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: columns,
|
||||
mainAxisSpacing: 8,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisExtent: getItemHeight(proxyCardType),
|
||||
),
|
||||
itemCount: sortedProxies.length,
|
||||
itemBuilder: (_, index) {
|
||||
final proxy = sortedProxies[index];
|
||||
return ProxyCard(
|
||||
testUrl: state.testUrl,
|
||||
groupType: state.groupType,
|
||||
type: proxyCardType,
|
||||
key: ValueKey('$groupName.${proxy.name}'),
|
||||
proxy: proxy,
|
||||
groupName: groupName,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ class _GeoDataListItemState extends State<GeoDataListItem> {
|
||||
builder: (_, snapshot) {
|
||||
return SizedBox(
|
||||
height: 24,
|
||||
child: FadeBox(
|
||||
child: FadeThroughBox(
|
||||
key: Key("fade_box_${geoItem.label}"),
|
||||
child: snapshot.data == null
|
||||
? const SizedBox(
|
||||
@@ -248,7 +248,7 @@ class _GeoDataListItemState extends State<GeoDataListItem> {
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: isUpdating,
|
||||
builder: (_, isUpdating, ___) {
|
||||
return FadeBox(
|
||||
return FadeThroughBox(
|
||||
child: isUpdating
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
@@ -299,24 +299,8 @@ class _UpdateGeoUrlFormDialogState extends State<UpdateGeoUrlFormDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(widget.title),
|
||||
content: SizedBox(
|
||||
width: 300,
|
||||
child: Wrap(
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
TextField(
|
||||
maxLines: 5,
|
||||
minLines: 1,
|
||||
controller: urlController,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
return CommonDialog(
|
||||
title: widget.title,
|
||||
actions: [
|
||||
if (widget.defaultValue != null &&
|
||||
urlController.value.text != widget.defaultValue) ...[
|
||||
@@ -333,6 +317,19 @@ class _UpdateGeoUrlFormDialogState extends State<UpdateGeoUrlFormDialog> {
|
||||
child: Text(appLocalizations.submit),
|
||||
)
|
||||
],
|
||||
child: Wrap(
|
||||
runSpacing: 16,
|
||||
children: [
|
||||
TextField(
|
||||
maxLines: 5,
|
||||
minLines: 1,
|
||||
controller: urlController,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,12 +291,12 @@ class _PrimaryColorItem extends ConsumerWidget {
|
||||
itemBuilder: (_, index) {
|
||||
final color = primaryColors[index];
|
||||
return ColorSchemeBox(
|
||||
isSelected: color?.value == primaryColor,
|
||||
isSelected: color?.toARGB32() == primaryColor,
|
||||
primaryColor: color,
|
||||
onPressed: () {
|
||||
ref.read(themeSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
primaryColor: color?.value,
|
||||
primaryColor: color?.toARGB32(),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -35,7 +35,6 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
|
||||
delegate: OpenDelegate(
|
||||
title: Intl.message(navigationItem.label.name),
|
||||
widget: navigationItem.fragment,
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -65,7 +64,7 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _getSettingList() {
|
||||
_getSettingList() {
|
||||
return generateSection(
|
||||
title: appLocalizations.settings,
|
||||
items: [
|
||||
@@ -75,7 +74,7 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
|
||||
if (system.isDesktop) _HotkeyItem(),
|
||||
if (Platform.isWindows) _LoopbackItem(),
|
||||
if (Platform.isAndroid) _AccessItem(),
|
||||
_OverrideItem(),
|
||||
_ConfigItem(),
|
||||
_SettingItem(),
|
||||
],
|
||||
);
|
||||
@@ -155,7 +154,6 @@ class _ThemeItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.theme,
|
||||
widget: const ThemeFragment(),
|
||||
extendPageWidth: 360,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -231,15 +229,15 @@ class _AccessItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _OverrideItem extends StatelessWidget {
|
||||
const _OverrideItem();
|
||||
class _ConfigItem extends StatelessWidget {
|
||||
const _ConfigItem();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListItem.open(
|
||||
leading: const Icon(Icons.edit),
|
||||
title: Text(appLocalizations.override),
|
||||
subtitle: Text(appLocalizations.overrideDesc),
|
||||
title: Text(appLocalizations.basicConfig),
|
||||
subtitle: Text(appLocalizations.basicConfigDesc),
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.override,
|
||||
widget: const ConfigFragment(),
|
||||
|
||||
@@ -123,7 +123,6 @@
|
||||
"project": "Project",
|
||||
"core": "Core",
|
||||
"tabAnimation": "Tab animation",
|
||||
"tabAnimationDesc": "When enabled, the home tab will add a toggle animation",
|
||||
"desc": "A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.",
|
||||
"startVpn": "Starting VPN...",
|
||||
"stopVpn": "Stopping VPN...",
|
||||
@@ -181,7 +180,6 @@
|
||||
"requests": "Requests",
|
||||
"requestsDesc": "View recently request records",
|
||||
"findProcessMode": "Find process",
|
||||
"findProcessModeDesc": "There is a risk of flashback after opening",
|
||||
"init": "Init",
|
||||
"infiniteTime": "Long term effective",
|
||||
"expirationTime": "Expiration time",
|
||||
@@ -249,7 +247,6 @@
|
||||
"stop": "Stop",
|
||||
"appDesc": "Processing app related settings",
|
||||
"vpnDesc": "Modify VPN related settings",
|
||||
"generalDesc": "Overwrite general settings",
|
||||
"dnsDesc": "Update DNS related settings",
|
||||
"key": "Key",
|
||||
"value": "Value",
|
||||
@@ -346,5 +343,34 @@
|
||||
"exportFile": "Export file",
|
||||
"cacheCorrupt": "The cache is corrupt. Do you want to clear it?",
|
||||
"detectionTip": "Relying on third-party api is for reference only",
|
||||
"listen": "Listen"
|
||||
"listen": "Listen",
|
||||
"keyExists": "The current key already exists",
|
||||
"valueExists": "The current value already exists",
|
||||
"undo": "undo",
|
||||
"redo": "redo",
|
||||
"none": "none",
|
||||
"basicConfig": "Basic configuration",
|
||||
"basicConfigDesc": "Modify the basic configuration globally",
|
||||
"selectedCountTitle": "{count} items have been selected",
|
||||
"addRule": "Add rule",
|
||||
"ruleProviderEmptyTip": "Rule provider cannot be empty",
|
||||
"ruleName": "Rule name",
|
||||
"content": "Content",
|
||||
"contentEmptyTip": "Content cannot be empty",
|
||||
"subRule": "Sub rule",
|
||||
"subRuleEmptyTip": "Sub rule content cannot be empty",
|
||||
"ruleTarget": "Rule target",
|
||||
"ruleTargetEmptyTip": "Rule target cannot be empty",
|
||||
"sourceIp": "Source IP",
|
||||
"noResolve": "No resolve IP",
|
||||
"getOriginRules": "Get original rules",
|
||||
"overrideOriginRules": "Override the original rule",
|
||||
"addedOriginRules": "Attach on the original rules",
|
||||
"enableOverride": "Enable override",
|
||||
"deleteRuleTip": "Are you sure you want to delete the selected rule?",
|
||||
"saveChanges": "Do you want to save the changes?",
|
||||
"generalDesc": "Modify general settings",
|
||||
"findProcessModeDesc": "There is a certain performance loss after opening",
|
||||
"tabAnimationDesc": "Effective only in mobile view",
|
||||
"saveTip": "Are you sure you want to save?"
|
||||
}
|
||||
@@ -123,7 +123,6 @@
|
||||
"project": "プロジェクト",
|
||||
"core": "コア",
|
||||
"tabAnimation": "タブアニメーション",
|
||||
"tabAnimationDesc": "有効化するとホームタブに切り替えアニメーションを追加",
|
||||
"desc": "ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
|
||||
"startVpn": "VPNを開始中...",
|
||||
"stopVpn": "VPNを停止中...",
|
||||
@@ -181,7 +180,6 @@
|
||||
"requests": "リクエスト",
|
||||
"requestsDesc": "最近のリクエスト記録を表示",
|
||||
"findProcessMode": "プロセス検出",
|
||||
"findProcessModeDesc": "有効化するとフラッシュバックのリスクあり",
|
||||
"init": "初期化",
|
||||
"infiniteTime": "長期有効",
|
||||
"expirationTime": "有効期限",
|
||||
@@ -249,7 +247,6 @@
|
||||
"stop": "停止",
|
||||
"appDesc": "アプリ関連設定の処理",
|
||||
"vpnDesc": "VPN関連設定の変更",
|
||||
"generalDesc": "一般設定の上書き",
|
||||
"dnsDesc": "DNS関連設定の更新",
|
||||
"key": "キー",
|
||||
"value": "値",
|
||||
@@ -346,5 +343,34 @@
|
||||
"exportFile": "ファイルをエクスポート",
|
||||
"cacheCorrupt": "キャッシュが破損しています。クリアしますか?",
|
||||
"detectionTip": "サードパーティAPIに依存(参考値)",
|
||||
"listen": "リスン"
|
||||
"listen": "リスン",
|
||||
"keyExists": "現在のキーは既に存在します",
|
||||
"valueExists": "現在の値は既に存在します",
|
||||
"undo": "元に戻す",
|
||||
"redo": "やり直す",
|
||||
"none": "なし",
|
||||
"basicConfig": "基本設定",
|
||||
"basicConfigDesc": "基本設定をグローバルに変更",
|
||||
"selectedCountTitle": "{count} 項目が選択されています",
|
||||
"addRule": "ルールを追加",
|
||||
"ruleProviderEmptyTip": "ルールプロバイダーは必須です",
|
||||
"ruleName": "ルール名",
|
||||
"content": "内容",
|
||||
"contentEmptyTip": "内容は必須です",
|
||||
"subRule": "サブルール",
|
||||
"subRuleEmptyTip": "サブルールの内容は必須です",
|
||||
"ruleTarget": "ルール対象",
|
||||
"ruleTargetEmptyTip": "ルール対象は必須です",
|
||||
"sourceIp": "送信元IP",
|
||||
"noResolve": "IPを解決しない",
|
||||
"getOriginRules": "元のルールを取得",
|
||||
"overrideOriginRules": "元のルールを上書き",
|
||||
"addedOriginRules": "元のルールに追加",
|
||||
"enableOverride": "上書きを有効化",
|
||||
"deleteRuleTip": "選択したルールを削除しますか?",
|
||||
"saveChanges": "変更を保存しますか?",
|
||||
"generalDesc": "一般設定を変更",
|
||||
"findProcessModeDesc": "有効化するとパフォーマンスが若干低下します",
|
||||
"tabAnimationDesc": "モバイル表示でのみ有効",
|
||||
"saveTip": "保存してもよろしいですか?"
|
||||
}
|
||||
@@ -123,7 +123,6 @@
|
||||
"project": "Проект",
|
||||
"core": "Ядро",
|
||||
"tabAnimation": "Анимация вкладок",
|
||||
"tabAnimationDesc": "При включении домашняя вкладка добавит анимацию переключения",
|
||||
"desc": "Многоплатформенный прокси-клиент на основе ClashMeta, простой и удобный в использовании, с открытым исходным кодом и без рекламы.",
|
||||
"startVpn": "Запуск VPN...",
|
||||
"stopVpn": "Остановка VPN...",
|
||||
@@ -181,7 +180,6 @@
|
||||
"requests": "Запросы",
|
||||
"requestsDesc": "Просмотр последних записей запросов",
|
||||
"findProcessMode": "Режим поиска процесса",
|
||||
"findProcessModeDesc": "Есть риск сбоя после включения",
|
||||
"init": "Инициализация",
|
||||
"infiniteTime": "Долгосрочное действие",
|
||||
"expirationTime": "Время истечения",
|
||||
@@ -249,7 +247,6 @@
|
||||
"stop": "Стоп",
|
||||
"appDesc": "Обработка настроек, связанных с приложением",
|
||||
"vpnDesc": "Изменение настроек, связанных с VPN",
|
||||
"generalDesc": "Переопределение общих настроек",
|
||||
"dnsDesc": "Обновление настроек, связанных с DNS",
|
||||
"key": "Ключ",
|
||||
"value": "Значение",
|
||||
@@ -346,5 +343,34 @@
|
||||
"exportFile": "Экспорт файла",
|
||||
"cacheCorrupt": "Кэш поврежден. Хотите очистить его?",
|
||||
"detectionTip": "Опирается на сторонний API, только для справки",
|
||||
"listen": "Слушать"
|
||||
"listen": "Слушать",
|
||||
"keyExists": "Текущий ключ уже существует",
|
||||
"valueExists": "Текущее значение уже существует",
|
||||
"undo": "Отменить",
|
||||
"redo": "Повторить",
|
||||
"none": "Нет",
|
||||
"basicConfig": "Базовая конфигурация",
|
||||
"basicConfigDesc": "Глобальное изменение базовых настроек",
|
||||
"selectedCountTitle": "Выбрано {count} элементов",
|
||||
"addRule": "Добавить правило",
|
||||
"ruleProviderEmptyTip": "Поставщик правил не может быть пустым",
|
||||
"ruleName": "Название правила",
|
||||
"content": "Содержание",
|
||||
"contentEmptyTip": "Содержание не может быть пустым",
|
||||
"subRule": "Подправило",
|
||||
"subRuleEmptyTip": "Содержание подправила не может быть пустым",
|
||||
"ruleTarget": "Цель правила",
|
||||
"ruleTargetEmptyTip": "Цель правила не может быть пустой",
|
||||
"sourceIp": "Исходный IP",
|
||||
"noResolve": "Не разрешать IP",
|
||||
"getOriginRules": "Получить оригинальные правила",
|
||||
"overrideOriginRules": "Переопределить оригинальное правило",
|
||||
"addedOriginRules": "Добавить к оригинальным правилам",
|
||||
"enableOverride": "Включить переопределение",
|
||||
"deleteRuleTip": "Вы уверены, что хотите удалить выбранное правило?",
|
||||
"saveChanges": "Сохранить изменения?",
|
||||
"generalDesc": "Изменение общих настроек",
|
||||
"findProcessModeDesc": "При включении возможны небольшие потери производительности",
|
||||
"tabAnimationDesc": "Действительно только в мобильном виде",
|
||||
"saveTip": "Вы уверены, что хотите сохранить?"
|
||||
}
|
||||
@@ -123,7 +123,6 @@
|
||||
"project": "项目",
|
||||
"core": "内核",
|
||||
"tabAnimation": "选项卡动画",
|
||||
"tabAnimationDesc": "开启后,主页选项卡将添加切换动画",
|
||||
"desc": "基于ClashMeta的多平台代理客户端,简单易用,开源无广告。",
|
||||
"startVpn": "正在启动VPN...",
|
||||
"stopVpn": "正在停止VPN...",
|
||||
@@ -169,7 +168,7 @@
|
||||
"externalControllerDesc": "开启后将可以通过9090端口控制Clash内核",
|
||||
"ipv6Desc": "开启后将可以接收IPv6流量",
|
||||
"app": "应用",
|
||||
"general": "基础",
|
||||
"general": "常规",
|
||||
"vpnSystemProxyDesc": "为VpnService附加HTTP代理",
|
||||
"systemProxyDesc": "设置系统代理",
|
||||
"unifiedDelay": "统一延迟",
|
||||
@@ -181,7 +180,6 @@
|
||||
"requests": "请求",
|
||||
"requestsDesc": "查看最近请求记录",
|
||||
"findProcessMode": "查找进程",
|
||||
"findProcessModeDesc": "开启后存在闪退风险",
|
||||
"init": "初始化",
|
||||
"infiniteTime": "长期有效",
|
||||
"expirationTime": "到期时间",
|
||||
@@ -249,7 +247,6 @@
|
||||
"stop": "暂停",
|
||||
"appDesc": "处理应用相关设置",
|
||||
"vpnDesc": "修改VPN相关设置",
|
||||
"generalDesc": "覆写基础设置",
|
||||
"dnsDesc": "更新DNS相关设置",
|
||||
"key": "键",
|
||||
"value": "值",
|
||||
@@ -346,5 +343,34 @@
|
||||
"exportFile": "导出文件",
|
||||
"cacheCorrupt": "缓存已损坏,是否清空?",
|
||||
"detectionTip": "依赖第三方api,仅供参考",
|
||||
"listen": "监听"
|
||||
"listen": "监听",
|
||||
"keyExists": "当前键已存在",
|
||||
"valueExists": "当前值已存在",
|
||||
"undo": "撤销",
|
||||
"redo": "重做",
|
||||
"none": "无",
|
||||
"basicConfig": "基本配置",
|
||||
"basicConfigDesc": "全局修改基本配置",
|
||||
"selectedCountTitle": "已选择 {count} 项",
|
||||
"addRule": "添加规则",
|
||||
"ruleProviderEmptyTip": "规则提供者不能为空",
|
||||
"ruleName": "规则名称",
|
||||
"content": "内容",
|
||||
"contentEmptyTip": "内容不能为空",
|
||||
"subRule": "子规则",
|
||||
"subRuleEmptyTip": "子规则内容不能为空",
|
||||
"ruleTarget": "规则目标",
|
||||
"ruleTargetEmptyTip": "规则目标不能为空",
|
||||
"sourceIp": "源IP",
|
||||
"noResolve": "不解析IP",
|
||||
"getOriginRules": "获取原始规则",
|
||||
"overrideOriginRules": "覆盖原始规则",
|
||||
"addedOriginRules": "附加到原始规则",
|
||||
"enableOverride": "启用覆写",
|
||||
"deleteRuleTip": "确定要删除选中的规则吗?",
|
||||
"saveChanges": "是否保存更改?",
|
||||
"generalDesc": "修改通用设置",
|
||||
"findProcessModeDesc": "开启后会有一定性能损耗",
|
||||
"tabAnimationDesc": "仅在移动视图中有效",
|
||||
"saveTip": "确定要保存吗?"
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'en';
|
||||
|
||||
static String m0(count) => "${count} items have been selected";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"about": MessageLookupByLibrary.simpleMessage("About"),
|
||||
@@ -44,6 +46,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"action_tun": MessageLookupByLibrary.simpleMessage("TUN"),
|
||||
"action_view": MessageLookupByLibrary.simpleMessage("Show/Hide"),
|
||||
"add": MessageLookupByLibrary.simpleMessage("Add"),
|
||||
"addRule": MessageLookupByLibrary.simpleMessage("Add rule"),
|
||||
"addedOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Attach on the original rules",
|
||||
),
|
||||
"address": MessageLookupByLibrary.simpleMessage("Address"),
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage(
|
||||
"WebDAV server address",
|
||||
@@ -114,6 +120,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Sync data via WebDAV or file",
|
||||
),
|
||||
"backupSuccess": MessageLookupByLibrary.simpleMessage("Backup success"),
|
||||
"basicConfig": MessageLookupByLibrary.simpleMessage("Basic configuration"),
|
||||
"basicConfigDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Modify the basic configuration globally",
|
||||
),
|
||||
"bind": MessageLookupByLibrary.simpleMessage("Bind"),
|
||||
"blacklistMode": MessageLookupByLibrary.simpleMessage("Blacklist mode"),
|
||||
"bypassDomain": MessageLookupByLibrary.simpleMessage("Bypass domain"),
|
||||
@@ -149,6 +159,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"View current connections data",
|
||||
),
|
||||
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity:"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("Content"),
|
||||
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Content cannot be empty",
|
||||
),
|
||||
"copy": MessageLookupByLibrary.simpleMessage("Copy"),
|
||||
"copyEnvVar": MessageLookupByLibrary.simpleMessage(
|
||||
"Copying environment variables",
|
||||
@@ -177,6 +191,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Sure you want to delete the current profile?",
|
||||
),
|
||||
"deleteRuleTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure you want to delete the selected rule?",
|
||||
),
|
||||
"desc": MessageLookupByLibrary.simpleMessage(
|
||||
"A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.",
|
||||
),
|
||||
@@ -205,6 +222,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"download": MessageLookupByLibrary.simpleMessage("Download"),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
|
||||
"en": MessageLookupByLibrary.simpleMessage("English"),
|
||||
"enableOverride": MessageLookupByLibrary.simpleMessage("Enable override"),
|
||||
"entries": MessageLookupByLibrary.simpleMessage(" entries"),
|
||||
"exclude": MessageLookupByLibrary.simpleMessage("Hidden from recent tasks"),
|
||||
"excludeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -243,13 +261,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"findProcessMode": MessageLookupByLibrary.simpleMessage("Find process"),
|
||||
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"There is a risk of flashback after opening",
|
||||
"There is a certain performance loss after opening",
|
||||
),
|
||||
"fontFamily": MessageLookupByLibrary.simpleMessage("FontFamily"),
|
||||
"fourColumns": MessageLookupByLibrary.simpleMessage("Four columns"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("General"),
|
||||
"generalDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Overwrite general settings",
|
||||
"Modify general settings",
|
||||
),
|
||||
"geoData": MessageLookupByLibrary.simpleMessage("GeoData"),
|
||||
"geodataLoader": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -259,6 +277,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Enabling will use the Geo low memory loader",
|
||||
),
|
||||
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip code"),
|
||||
"getOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Get original rules",
|
||||
),
|
||||
"global": MessageLookupByLibrary.simpleMessage("Global"),
|
||||
"go": MessageLookupByLibrary.simpleMessage("Go"),
|
||||
"goDownload": MessageLookupByLibrary.simpleMessage("Go to download"),
|
||||
@@ -302,6 +323,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Tcp keep alive interval",
|
||||
),
|
||||
"key": MessageLookupByLibrary.simpleMessage("Key"),
|
||||
"keyExists": MessageLookupByLibrary.simpleMessage(
|
||||
"The current key already exists",
|
||||
),
|
||||
"language": MessageLookupByLibrary.simpleMessage("Language"),
|
||||
"layout": MessageLookupByLibrary.simpleMessage("Layout"),
|
||||
"light": MessageLookupByLibrary.simpleMessage("Light"),
|
||||
@@ -366,6 +390,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Please create a profile or add a valid profile",
|
||||
),
|
||||
"noResolve": MessageLookupByLibrary.simpleMessage("No resolve IP"),
|
||||
"none": MessageLookupByLibrary.simpleMessage("none"),
|
||||
"notEmpty": MessageLookupByLibrary.simpleMessage("Cannot be empty"),
|
||||
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
|
||||
"The current proxy group cannot be selected.",
|
||||
@@ -407,6 +433,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Turning it on will override the DNS options in the profile",
|
||||
),
|
||||
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Override the original rule",
|
||||
),
|
||||
"password": MessageLookupByLibrary.simpleMessage("Password"),
|
||||
"passwordTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Password cannot be empty",
|
||||
@@ -483,6 +512,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Only recovery profiles",
|
||||
),
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage("Recovery success"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("redo"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("RegExp"),
|
||||
"remote": MessageLookupByLibrary.simpleMessage("Remote"),
|
||||
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -517,12 +547,27 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"routeMode_config": MessageLookupByLibrary.simpleMessage("Use config"),
|
||||
"ru": MessageLookupByLibrary.simpleMessage("Russian"),
|
||||
"rule": MessageLookupByLibrary.simpleMessage("Rule"),
|
||||
"ruleName": MessageLookupByLibrary.simpleMessage("Rule name"),
|
||||
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Rule provider cannot be empty",
|
||||
),
|
||||
"ruleProviders": MessageLookupByLibrary.simpleMessage("Rule providers"),
|
||||
"ruleTarget": MessageLookupByLibrary.simpleMessage("Rule target"),
|
||||
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Rule target cannot be empty",
|
||||
),
|
||||
"save": MessageLookupByLibrary.simpleMessage("Save"),
|
||||
"saveChanges": MessageLookupByLibrary.simpleMessage(
|
||||
"Do you want to save the changes?",
|
||||
),
|
||||
"saveTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure you want to save?",
|
||||
),
|
||||
"search": MessageLookupByLibrary.simpleMessage("Search"),
|
||||
"seconds": MessageLookupByLibrary.simpleMessage("Seconds"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
|
||||
"selected": MessageLookupByLibrary.simpleMessage("Selected"),
|
||||
"selectedCountTitle": m0,
|
||||
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
|
||||
"show": MessageLookupByLibrary.simpleMessage("Show"),
|
||||
"shrink": MessageLookupByLibrary.simpleMessage("Shrink"),
|
||||
@@ -533,6 +578,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"size": MessageLookupByLibrary.simpleMessage("Size"),
|
||||
"sort": MessageLookupByLibrary.simpleMessage("Sort"),
|
||||
"source": MessageLookupByLibrary.simpleMessage("Source"),
|
||||
"sourceIp": MessageLookupByLibrary.simpleMessage("Source IP"),
|
||||
"stackMode": MessageLookupByLibrary.simpleMessage("Stack mode"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("Standard"),
|
||||
"start": MessageLookupByLibrary.simpleMessage("Start"),
|
||||
@@ -544,6 +590,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"stop": MessageLookupByLibrary.simpleMessage("Stop"),
|
||||
"stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."),
|
||||
"style": MessageLookupByLibrary.simpleMessage("Style"),
|
||||
"subRule": MessageLookupByLibrary.simpleMessage("Sub rule"),
|
||||
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Sub rule content cannot be empty",
|
||||
),
|
||||
"submit": MessageLookupByLibrary.simpleMessage("Submit"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("Sync"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("System"),
|
||||
@@ -555,7 +605,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tab": MessageLookupByLibrary.simpleMessage("Tab"),
|
||||
"tabAnimation": MessageLookupByLibrary.simpleMessage("Tab animation"),
|
||||
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"When enabled, the home tab will add a toggle animation",
|
||||
"Effective only in mobile view",
|
||||
),
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP concurrent"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -583,6 +633,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"unable to update current profile",
|
||||
),
|
||||
"undo": MessageLookupByLibrary.simpleMessage("undo"),
|
||||
"unifiedDelay": MessageLookupByLibrary.simpleMessage("Unified delay"),
|
||||
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Remove extra delays such as handshaking",
|
||||
@@ -597,6 +648,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"),
|
||||
"useSystemHosts": MessageLookupByLibrary.simpleMessage("Use system hosts"),
|
||||
"value": MessageLookupByLibrary.simpleMessage("Value"),
|
||||
"valueExists": MessageLookupByLibrary.simpleMessage(
|
||||
"The current value already exists",
|
||||
),
|
||||
"view": MessageLookupByLibrary.simpleMessage("View"),
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Modify VPN related settings",
|
||||
|
||||
@@ -20,6 +20,8 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'ja';
|
||||
|
||||
static String m0(count) => "${count} 項目が選択されています";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"about": MessageLookupByLibrary.simpleMessage("について"),
|
||||
@@ -42,6 +44,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"action_tun": MessageLookupByLibrary.simpleMessage("TUN"),
|
||||
"action_view": MessageLookupByLibrary.simpleMessage("表示/非表示"),
|
||||
"add": MessageLookupByLibrary.simpleMessage("追加"),
|
||||
"addRule": MessageLookupByLibrary.simpleMessage("ルールを追加"),
|
||||
"addedOriginRules": MessageLookupByLibrary.simpleMessage("元のルールに追加"),
|
||||
"address": MessageLookupByLibrary.simpleMessage("アドレス"),
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAVサーバーアドレス"),
|
||||
"addressTip": MessageLookupByLibrary.simpleMessage("有効なWebDAVアドレスを入力"),
|
||||
@@ -82,6 +86,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"WebDAVまたはファイルでデータを同期",
|
||||
),
|
||||
"backupSuccess": MessageLookupByLibrary.simpleMessage("バックアップ成功"),
|
||||
"basicConfig": MessageLookupByLibrary.simpleMessage("基本設定"),
|
||||
"basicConfigDesc": MessageLookupByLibrary.simpleMessage("基本設定をグローバルに変更"),
|
||||
"bind": MessageLookupByLibrary.simpleMessage("バインド"),
|
||||
"blacklistMode": MessageLookupByLibrary.simpleMessage("ブラックリストモード"),
|
||||
"bypassDomain": MessageLookupByLibrary.simpleMessage("バイパスドメイン"),
|
||||
@@ -109,6 +115,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"connections": MessageLookupByLibrary.simpleMessage("接続"),
|
||||
"connectionsDesc": MessageLookupByLibrary.simpleMessage("現在の接続データを表示"),
|
||||
"connectivity": MessageLookupByLibrary.simpleMessage("接続性:"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("内容"),
|
||||
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容は必須です"),
|
||||
"copy": MessageLookupByLibrary.simpleMessage("コピー"),
|
||||
"copyEnvVar": MessageLookupByLibrary.simpleMessage("環境変数をコピー"),
|
||||
"copyLink": MessageLookupByLibrary.simpleMessage("リンクをコピー"),
|
||||
@@ -133,6 +141,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
|
||||
"現在のプロファイルを削除しますか?",
|
||||
),
|
||||
"deleteRuleTip": MessageLookupByLibrary.simpleMessage("選択したルールを削除しますか?"),
|
||||
"desc": MessageLookupByLibrary.simpleMessage(
|
||||
"ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
|
||||
),
|
||||
@@ -151,6 +160,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"download": MessageLookupByLibrary.simpleMessage("ダウンロード"),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("編集"),
|
||||
"en": MessageLookupByLibrary.simpleMessage("英語"),
|
||||
"enableOverride": MessageLookupByLibrary.simpleMessage("上書きを有効化"),
|
||||
"entries": MessageLookupByLibrary.simpleMessage(" エントリ"),
|
||||
"exclude": MessageLookupByLibrary.simpleMessage("最近のタスクから非表示"),
|
||||
"excludeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -181,18 +191,19 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"filterSystemApp": MessageLookupByLibrary.simpleMessage("システムアプリを除外"),
|
||||
"findProcessMode": MessageLookupByLibrary.simpleMessage("プロセス検出"),
|
||||
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"有効化するとフラッシュバックのリスクあり",
|
||||
"有効化するとパフォーマンスが若干低下します",
|
||||
),
|
||||
"fontFamily": MessageLookupByLibrary.simpleMessage("フォントファミリー"),
|
||||
"fourColumns": MessageLookupByLibrary.simpleMessage("4列"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("一般"),
|
||||
"generalDesc": MessageLookupByLibrary.simpleMessage("一般設定の上書き"),
|
||||
"generalDesc": MessageLookupByLibrary.simpleMessage("一般設定を変更"),
|
||||
"geoData": MessageLookupByLibrary.simpleMessage("地域データ"),
|
||||
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低メモリモード"),
|
||||
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"有効化するとGeo低メモリローダーを使用",
|
||||
),
|
||||
"geoipCode": MessageLookupByLibrary.simpleMessage("GeoIPコード"),
|
||||
"getOriginRules": MessageLookupByLibrary.simpleMessage("元のルールを取得"),
|
||||
"global": MessageLookupByLibrary.simpleMessage("グローバル"),
|
||||
"go": MessageLookupByLibrary.simpleMessage("移動"),
|
||||
"goDownload": MessageLookupByLibrary.simpleMessage("ダウンロードへ"),
|
||||
@@ -222,6 +233,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"TCPキープアライブ間隔",
|
||||
),
|
||||
"key": MessageLookupByLibrary.simpleMessage("キー"),
|
||||
"keyExists": MessageLookupByLibrary.simpleMessage("現在のキーは既に存在します"),
|
||||
"language": MessageLookupByLibrary.simpleMessage("言語"),
|
||||
"layout": MessageLookupByLibrary.simpleMessage("レイアウト"),
|
||||
"light": MessageLookupByLibrary.simpleMessage("ライト"),
|
||||
@@ -270,6 +282,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"プロファイルを作成するか、有効なプロファイルを追加してください",
|
||||
),
|
||||
"noResolve": MessageLookupByLibrary.simpleMessage("IPを解決しない"),
|
||||
"none": MessageLookupByLibrary.simpleMessage("なし"),
|
||||
"notEmpty": MessageLookupByLibrary.simpleMessage("空欄不可"),
|
||||
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
|
||||
"現在のプロキシグループは選択できません",
|
||||
@@ -299,6 +313,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"有効化するとプロファイルのDNS設定を上書き",
|
||||
),
|
||||
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("元のルールを上書き"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("パスワード"),
|
||||
"passwordTip": MessageLookupByLibrary.simpleMessage("パスワードは必須です"),
|
||||
"paste": MessageLookupByLibrary.simpleMessage("貼り付け"),
|
||||
@@ -359,6 +374,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"recoveryAll": MessageLookupByLibrary.simpleMessage("全データ復元"),
|
||||
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("プロファイルのみ復元"),
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage("復元成功"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("やり直す"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("正規表現"),
|
||||
"remote": MessageLookupByLibrary.simpleMessage("リモート"),
|
||||
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -387,12 +403,21 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"routeMode_config": MessageLookupByLibrary.simpleMessage("設定を使用"),
|
||||
"ru": MessageLookupByLibrary.simpleMessage("ロシア語"),
|
||||
"rule": MessageLookupByLibrary.simpleMessage("ルール"),
|
||||
"ruleName": MessageLookupByLibrary.simpleMessage("ルール名"),
|
||||
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"ルールプロバイダーは必須です",
|
||||
),
|
||||
"ruleProviders": MessageLookupByLibrary.simpleMessage("ルールプロバイダー"),
|
||||
"ruleTarget": MessageLookupByLibrary.simpleMessage("ルール対象"),
|
||||
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage("ルール対象は必須です"),
|
||||
"save": MessageLookupByLibrary.simpleMessage("保存"),
|
||||
"saveChanges": MessageLookupByLibrary.simpleMessage("変更を保存しますか?"),
|
||||
"saveTip": MessageLookupByLibrary.simpleMessage("保存してもよろしいですか?"),
|
||||
"search": MessageLookupByLibrary.simpleMessage("検索"),
|
||||
"seconds": MessageLookupByLibrary.simpleMessage("秒"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("すべて選択"),
|
||||
"selected": MessageLookupByLibrary.simpleMessage("選択済み"),
|
||||
"selectedCountTitle": m0,
|
||||
"settings": MessageLookupByLibrary.simpleMessage("設定"),
|
||||
"show": MessageLookupByLibrary.simpleMessage("表示"),
|
||||
"shrink": MessageLookupByLibrary.simpleMessage("縮小"),
|
||||
@@ -401,6 +426,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"size": MessageLookupByLibrary.simpleMessage("サイズ"),
|
||||
"sort": MessageLookupByLibrary.simpleMessage("並び替え"),
|
||||
"source": MessageLookupByLibrary.simpleMessage("ソース"),
|
||||
"sourceIp": MessageLookupByLibrary.simpleMessage("送信元IP"),
|
||||
"stackMode": MessageLookupByLibrary.simpleMessage("スタックモード"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("標準"),
|
||||
"start": MessageLookupByLibrary.simpleMessage("開始"),
|
||||
@@ -410,6 +436,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"stop": MessageLookupByLibrary.simpleMessage("停止"),
|
||||
"stopVpn": MessageLookupByLibrary.simpleMessage("VPNを停止中..."),
|
||||
"style": MessageLookupByLibrary.simpleMessage("スタイル"),
|
||||
"subRule": MessageLookupByLibrary.simpleMessage("サブルール"),
|
||||
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage("サブルールの内容は必須です"),
|
||||
"submit": MessageLookupByLibrary.simpleMessage("送信"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("同期"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("システム"),
|
||||
@@ -420,9 +448,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"tab": MessageLookupByLibrary.simpleMessage("タブ"),
|
||||
"tabAnimation": MessageLookupByLibrary.simpleMessage("タブアニメーション"),
|
||||
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"有効化するとホームタブに切り替えアニメーションを追加",
|
||||
),
|
||||
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage("モバイル表示でのみ有効"),
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP並列処理"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("TCP並列処理を許可"),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("URLテスト"),
|
||||
@@ -443,6 +469,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"現在のプロファイルを更新できません",
|
||||
),
|
||||
"undo": MessageLookupByLibrary.simpleMessage("元に戻す"),
|
||||
"unifiedDelay": MessageLookupByLibrary.simpleMessage("統一遅延"),
|
||||
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"ハンドシェイクなどの余分な遅延を削除",
|
||||
@@ -455,6 +482,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"useHosts": MessageLookupByLibrary.simpleMessage("ホストを使用"),
|
||||
"useSystemHosts": MessageLookupByLibrary.simpleMessage("システムホストを使用"),
|
||||
"value": MessageLookupByLibrary.simpleMessage("値"),
|
||||
"valueExists": MessageLookupByLibrary.simpleMessage("現在の値は既に存在します"),
|
||||
"view": MessageLookupByLibrary.simpleMessage("表示"),
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage("VPN関連設定の変更"),
|
||||
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -20,6 +20,8 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'ru';
|
||||
|
||||
static String m0(count) => "Выбрано ${count} элементов";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"about": MessageLookupByLibrary.simpleMessage("О программе"),
|
||||
@@ -44,6 +46,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"action_tun": MessageLookupByLibrary.simpleMessage("TUN"),
|
||||
"action_view": MessageLookupByLibrary.simpleMessage("Показать/Скрыть"),
|
||||
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
|
||||
"addRule": MessageLookupByLibrary.simpleMessage("Добавить правило"),
|
||||
"addedOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Добавить к оригинальным правилам",
|
||||
),
|
||||
"address": MessageLookupByLibrary.simpleMessage("Адрес"),
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage("Адрес сервера WebDAV"),
|
||||
"addressTip": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -114,6 +120,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"backupSuccess": MessageLookupByLibrary.simpleMessage(
|
||||
"Резервное копирование успешно",
|
||||
),
|
||||
"basicConfig": MessageLookupByLibrary.simpleMessage("Базовая конфигурация"),
|
||||
"basicConfigDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Глобальное изменение базовых настроек",
|
||||
),
|
||||
"bind": MessageLookupByLibrary.simpleMessage("Привязать"),
|
||||
"blacklistMode": MessageLookupByLibrary.simpleMessage(
|
||||
"Режим черного списка",
|
||||
@@ -155,6 +165,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Просмотр текущих данных о соединениях",
|
||||
),
|
||||
"connectivity": MessageLookupByLibrary.simpleMessage("Связь:"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("Содержание"),
|
||||
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Содержание не может быть пустым",
|
||||
),
|
||||
"copy": MessageLookupByLibrary.simpleMessage("Копировать"),
|
||||
"copyEnvVar": MessageLookupByLibrary.simpleMessage(
|
||||
"Копирование переменных окружения",
|
||||
@@ -185,6 +199,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы уверены, что хотите удалить текущий профиль?",
|
||||
),
|
||||
"deleteRuleTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы уверены, что хотите удалить выбранное правило?",
|
||||
),
|
||||
"desc": MessageLookupByLibrary.simpleMessage(
|
||||
"Многоплатформенный прокси-клиент на основе ClashMeta, простой и удобный в использовании, с открытым исходным кодом и без рекламы.",
|
||||
),
|
||||
@@ -215,6 +232,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"download": MessageLookupByLibrary.simpleMessage("Скачивание"),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("Редактировать"),
|
||||
"en": MessageLookupByLibrary.simpleMessage("Английский"),
|
||||
"enableOverride": MessageLookupByLibrary.simpleMessage(
|
||||
"Включить переопределение",
|
||||
),
|
||||
"entries": MessageLookupByLibrary.simpleMessage(" записей"),
|
||||
"exclude": MessageLookupByLibrary.simpleMessage(
|
||||
"Скрыть из последних задач",
|
||||
@@ -259,13 +279,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Режим поиска процесса",
|
||||
),
|
||||
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Есть риск сбоя после включения",
|
||||
"При включении возможны небольшие потери производительности",
|
||||
),
|
||||
"fontFamily": MessageLookupByLibrary.simpleMessage("Семейство шрифтов"),
|
||||
"fourColumns": MessageLookupByLibrary.simpleMessage("Четыре столбца"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("Общие"),
|
||||
"generalDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Переопределение общих настроек",
|
||||
"Изменение общих настроек",
|
||||
),
|
||||
"geoData": MessageLookupByLibrary.simpleMessage("Геоданные"),
|
||||
"geodataLoader": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -275,6 +295,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Включение будет использовать загрузчик геоданных с низким потреблением памяти",
|
||||
),
|
||||
"geoipCode": MessageLookupByLibrary.simpleMessage("Код Geoip"),
|
||||
"getOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Получить оригинальные правила",
|
||||
),
|
||||
"global": MessageLookupByLibrary.simpleMessage("Глобальный"),
|
||||
"go": MessageLookupByLibrary.simpleMessage("Перейти"),
|
||||
"goDownload": MessageLookupByLibrary.simpleMessage("Перейти к загрузке"),
|
||||
@@ -322,6 +345,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Интервал поддержания TCP-соединения",
|
||||
),
|
||||
"key": MessageLookupByLibrary.simpleMessage("Ключ"),
|
||||
"keyExists": MessageLookupByLibrary.simpleMessage(
|
||||
"Текущий ключ уже существует",
|
||||
),
|
||||
"language": MessageLookupByLibrary.simpleMessage("Язык"),
|
||||
"layout": MessageLookupByLibrary.simpleMessage("Макет"),
|
||||
"light": MessageLookupByLibrary.simpleMessage("Светлый"),
|
||||
@@ -392,6 +418,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Пожалуйста, создайте профиль или добавьте действительный профиль",
|
||||
),
|
||||
"noResolve": MessageLookupByLibrary.simpleMessage("Не разрешать IP"),
|
||||
"none": MessageLookupByLibrary.simpleMessage("Нет"),
|
||||
"notEmpty": MessageLookupByLibrary.simpleMessage("Не может быть пустым"),
|
||||
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Текущая группа прокси не может быть выбрана.",
|
||||
@@ -435,6 +463,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Включение переопределит настройки DNS в профиле",
|
||||
),
|
||||
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
|
||||
"Переопределить оригинальное правило",
|
||||
),
|
||||
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
|
||||
"passwordTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Пароль не может быть пустым",
|
||||
@@ -517,6 +548,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage(
|
||||
"Восстановление успешно",
|
||||
),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("Повторить"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("Регулярное выражение"),
|
||||
"remote": MessageLookupByLibrary.simpleMessage("Удаленный"),
|
||||
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -555,12 +587,25 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"ru": MessageLookupByLibrary.simpleMessage("Русский"),
|
||||
"rule": MessageLookupByLibrary.simpleMessage("Правило"),
|
||||
"ruleName": MessageLookupByLibrary.simpleMessage("Название правила"),
|
||||
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Поставщик правил не может быть пустым",
|
||||
),
|
||||
"ruleProviders": MessageLookupByLibrary.simpleMessage("Провайдеры правил"),
|
||||
"ruleTarget": MessageLookupByLibrary.simpleMessage("Цель правила"),
|
||||
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Цель правила не может быть пустой",
|
||||
),
|
||||
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
|
||||
"saveChanges": MessageLookupByLibrary.simpleMessage("Сохранить изменения?"),
|
||||
"saveTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы уверены, что хотите сохранить?",
|
||||
),
|
||||
"search": MessageLookupByLibrary.simpleMessage("Поиск"),
|
||||
"seconds": MessageLookupByLibrary.simpleMessage("Секунд"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("Выбрать все"),
|
||||
"selected": MessageLookupByLibrary.simpleMessage("Выбрано"),
|
||||
"selectedCountTitle": m0,
|
||||
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
|
||||
"show": MessageLookupByLibrary.simpleMessage("Показать"),
|
||||
"shrink": MessageLookupByLibrary.simpleMessage("Сжать"),
|
||||
@@ -571,6 +616,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"size": MessageLookupByLibrary.simpleMessage("Размер"),
|
||||
"sort": MessageLookupByLibrary.simpleMessage("Сортировка"),
|
||||
"source": MessageLookupByLibrary.simpleMessage("Источник"),
|
||||
"sourceIp": MessageLookupByLibrary.simpleMessage("Исходный IP"),
|
||||
"stackMode": MessageLookupByLibrary.simpleMessage("Режим стека"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("Стандартный"),
|
||||
"start": MessageLookupByLibrary.simpleMessage("Старт"),
|
||||
@@ -582,6 +628,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"stop": MessageLookupByLibrary.simpleMessage("Стоп"),
|
||||
"stopVpn": MessageLookupByLibrary.simpleMessage("Остановка VPN..."),
|
||||
"style": MessageLookupByLibrary.simpleMessage("Стиль"),
|
||||
"subRule": MessageLookupByLibrary.simpleMessage("Подправило"),
|
||||
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Содержание подправила не может быть пустым",
|
||||
),
|
||||
"submit": MessageLookupByLibrary.simpleMessage("Отправить"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("Синхронизация"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("Система"),
|
||||
@@ -593,7 +643,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tab": MessageLookupByLibrary.simpleMessage("Вкладка"),
|
||||
"tabAnimation": MessageLookupByLibrary.simpleMessage("Анимация вкладок"),
|
||||
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"При включении домашняя вкладка добавит анимацию переключения",
|
||||
"Действительно только в мобильном виде",
|
||||
),
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP параллелизм"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -623,6 +673,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"невозможно обновить текущий профиль",
|
||||
),
|
||||
"undo": MessageLookupByLibrary.simpleMessage("Отменить"),
|
||||
"unifiedDelay": MessageLookupByLibrary.simpleMessage(
|
||||
"Унифицированная задержка",
|
||||
),
|
||||
@@ -641,6 +692,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Использовать системные hosts",
|
||||
),
|
||||
"value": MessageLookupByLibrary.simpleMessage("Значение"),
|
||||
"valueExists": MessageLookupByLibrary.simpleMessage(
|
||||
"Текущее значение уже существует",
|
||||
),
|
||||
"view": MessageLookupByLibrary.simpleMessage("Просмотр"),
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Изменение настроек, связанных с VPN",
|
||||
|
||||
@@ -20,6 +20,8 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'zh_CN';
|
||||
|
||||
static String m0(count) => "已选择 ${count} 项";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"about": MessageLookupByLibrary.simpleMessage("关于"),
|
||||
@@ -40,6 +42,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"action_tun": MessageLookupByLibrary.simpleMessage("虚拟网卡"),
|
||||
"action_view": MessageLookupByLibrary.simpleMessage("显示/隐藏"),
|
||||
"add": MessageLookupByLibrary.simpleMessage("添加"),
|
||||
"addRule": MessageLookupByLibrary.simpleMessage("添加规则"),
|
||||
"addedOriginRules": MessageLookupByLibrary.simpleMessage("附加到原始规则"),
|
||||
"address": MessageLookupByLibrary.simpleMessage("地址"),
|
||||
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAV服务器地址"),
|
||||
"addressTip": MessageLookupByLibrary.simpleMessage("请输入有效的WebDAV地址"),
|
||||
@@ -76,6 +80,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"通过WebDAV或者文件同步数据",
|
||||
),
|
||||
"backupSuccess": MessageLookupByLibrary.simpleMessage("备份成功"),
|
||||
"basicConfig": MessageLookupByLibrary.simpleMessage("基本配置"),
|
||||
"basicConfigDesc": MessageLookupByLibrary.simpleMessage("全局修改基本配置"),
|
||||
"bind": MessageLookupByLibrary.simpleMessage("绑定"),
|
||||
"blacklistMode": MessageLookupByLibrary.simpleMessage("黑名单模式"),
|
||||
"bypassDomain": MessageLookupByLibrary.simpleMessage("排除域名"),
|
||||
@@ -99,6 +105,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"connections": MessageLookupByLibrary.simpleMessage("连接"),
|
||||
"connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"),
|
||||
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
|
||||
"content": MessageLookupByLibrary.simpleMessage("内容"),
|
||||
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容不能为空"),
|
||||
"copy": MessageLookupByLibrary.simpleMessage("复制"),
|
||||
"copyEnvVar": MessageLookupByLibrary.simpleMessage("复制环境变量"),
|
||||
"copyLink": MessageLookupByLibrary.simpleMessage("复制链接"),
|
||||
@@ -119,6 +127,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("删除"),
|
||||
"deleteProfileTip": MessageLookupByLibrary.simpleMessage("确定要删除当前配置吗?"),
|
||||
"deleteRuleTip": MessageLookupByLibrary.simpleMessage("确定要删除选中的规则吗?"),
|
||||
"desc": MessageLookupByLibrary.simpleMessage(
|
||||
"基于ClashMeta的多平台代理客户端,简单易用,开源无广告。",
|
||||
),
|
||||
@@ -137,6 +146,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"download": MessageLookupByLibrary.simpleMessage("下载"),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
|
||||
"en": MessageLookupByLibrary.simpleMessage("英语"),
|
||||
"enableOverride": MessageLookupByLibrary.simpleMessage("启用覆写"),
|
||||
"entries": MessageLookupByLibrary.simpleMessage("个条目"),
|
||||
"exclude": MessageLookupByLibrary.simpleMessage("从最近任务中隐藏"),
|
||||
"excludeDesc": MessageLookupByLibrary.simpleMessage("应用在后台时,从最近任务中隐藏应用"),
|
||||
@@ -162,15 +172,16 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"fileIsUpdate": MessageLookupByLibrary.simpleMessage("文件有修改,是否保存修改"),
|
||||
"filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"),
|
||||
"findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"),
|
||||
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后存在闪退风险"),
|
||||
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后会有一定性能损耗"),
|
||||
"fontFamily": MessageLookupByLibrary.simpleMessage("字体"),
|
||||
"fourColumns": MessageLookupByLibrary.simpleMessage("四列"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("基础"),
|
||||
"generalDesc": MessageLookupByLibrary.simpleMessage("覆写基础设置"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("常规"),
|
||||
"generalDesc": MessageLookupByLibrary.simpleMessage("修改通用设置"),
|
||||
"geoData": MessageLookupByLibrary.simpleMessage("地理数据"),
|
||||
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"),
|
||||
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"),
|
||||
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip代码"),
|
||||
"getOriginRules": MessageLookupByLibrary.simpleMessage("获取原始规则"),
|
||||
"global": MessageLookupByLibrary.simpleMessage("全局"),
|
||||
"go": MessageLookupByLibrary.simpleMessage("前往"),
|
||||
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
|
||||
@@ -196,6 +207,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"just": MessageLookupByLibrary.simpleMessage("刚刚"),
|
||||
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"),
|
||||
"key": MessageLookupByLibrary.simpleMessage("键"),
|
||||
"keyExists": MessageLookupByLibrary.simpleMessage("当前键已存在"),
|
||||
"language": MessageLookupByLibrary.simpleMessage("语言"),
|
||||
"layout": MessageLookupByLibrary.simpleMessage("布局"),
|
||||
"light": MessageLookupByLibrary.simpleMessage("浅色"),
|
||||
@@ -238,6 +250,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noNetwork": MessageLookupByLibrary.simpleMessage("无网络"),
|
||||
"noProxy": MessageLookupByLibrary.simpleMessage("暂无代理"),
|
||||
"noProxyDesc": MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"),
|
||||
"noResolve": MessageLookupByLibrary.simpleMessage("不解析IP"),
|
||||
"none": MessageLookupByLibrary.simpleMessage("无"),
|
||||
"notEmpty": MessageLookupByLibrary.simpleMessage("不能为空"),
|
||||
"notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"),
|
||||
"nullConnectionsDesc": MessageLookupByLibrary.simpleMessage("暂无连接"),
|
||||
@@ -261,6 +275,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"overrideDesc": MessageLookupByLibrary.simpleMessage("覆写代理相关配置"),
|
||||
"overrideDns": MessageLookupByLibrary.simpleMessage("覆写DNS"),
|
||||
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage("开启后将覆盖配置中的DNS选项"),
|
||||
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("覆盖原始规则"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("密码"),
|
||||
"passwordTip": MessageLookupByLibrary.simpleMessage("密码不能为空"),
|
||||
"paste": MessageLookupByLibrary.simpleMessage("粘贴"),
|
||||
@@ -313,6 +328,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"recoveryAll": MessageLookupByLibrary.simpleMessage("恢复所有数据"),
|
||||
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"),
|
||||
"recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"),
|
||||
"redo": MessageLookupByLibrary.simpleMessage("重做"),
|
||||
"regExp": MessageLookupByLibrary.simpleMessage("正则"),
|
||||
"remote": MessageLookupByLibrary.simpleMessage("远程"),
|
||||
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到WebDAV"),
|
||||
@@ -335,12 +351,19 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"routeMode_config": MessageLookupByLibrary.simpleMessage("使用配置"),
|
||||
"ru": MessageLookupByLibrary.simpleMessage("俄语"),
|
||||
"rule": MessageLookupByLibrary.simpleMessage("规则"),
|
||||
"ruleName": MessageLookupByLibrary.simpleMessage("规则名称"),
|
||||
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage("规则提供者不能为空"),
|
||||
"ruleProviders": MessageLookupByLibrary.simpleMessage("规则提供者"),
|
||||
"ruleTarget": MessageLookupByLibrary.simpleMessage("规则目标"),
|
||||
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage("规则目标不能为空"),
|
||||
"save": MessageLookupByLibrary.simpleMessage("保存"),
|
||||
"saveChanges": MessageLookupByLibrary.simpleMessage("是否保存更改?"),
|
||||
"saveTip": MessageLookupByLibrary.simpleMessage("确定要保存吗?"),
|
||||
"search": MessageLookupByLibrary.simpleMessage("搜索"),
|
||||
"seconds": MessageLookupByLibrary.simpleMessage("秒"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
|
||||
"selected": MessageLookupByLibrary.simpleMessage("已选择"),
|
||||
"selectedCountTitle": m0,
|
||||
"settings": MessageLookupByLibrary.simpleMessage("设置"),
|
||||
"show": MessageLookupByLibrary.simpleMessage("显示"),
|
||||
"shrink": MessageLookupByLibrary.simpleMessage("紧凑"),
|
||||
@@ -349,6 +372,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"size": MessageLookupByLibrary.simpleMessage("尺寸"),
|
||||
"sort": MessageLookupByLibrary.simpleMessage("排序"),
|
||||
"source": MessageLookupByLibrary.simpleMessage("来源"),
|
||||
"sourceIp": MessageLookupByLibrary.simpleMessage("源IP"),
|
||||
"stackMode": MessageLookupByLibrary.simpleMessage("栈模式"),
|
||||
"standard": MessageLookupByLibrary.simpleMessage("标准"),
|
||||
"start": MessageLookupByLibrary.simpleMessage("启动"),
|
||||
@@ -358,6 +382,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"stop": MessageLookupByLibrary.simpleMessage("暂停"),
|
||||
"stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."),
|
||||
"style": MessageLookupByLibrary.simpleMessage("风格"),
|
||||
"subRule": MessageLookupByLibrary.simpleMessage("子规则"),
|
||||
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage("子规则内容不能为空"),
|
||||
"submit": MessageLookupByLibrary.simpleMessage("提交"),
|
||||
"sync": MessageLookupByLibrary.simpleMessage("同步"),
|
||||
"system": MessageLookupByLibrary.simpleMessage("系统"),
|
||||
@@ -366,9 +392,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"systemProxyDesc": MessageLookupByLibrary.simpleMessage("设置系统代理"),
|
||||
"tab": MessageLookupByLibrary.simpleMessage("标签页"),
|
||||
"tabAnimation": MessageLookupByLibrary.simpleMessage("选项卡动画"),
|
||||
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"开启后,主页选项卡将添加切换动画",
|
||||
),
|
||||
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage("仅在移动视图中有效"),
|
||||
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"),
|
||||
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"),
|
||||
"testUrl": MessageLookupByLibrary.simpleMessage("测速链接"),
|
||||
@@ -389,6 +413,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"无法更新当前配置文件",
|
||||
),
|
||||
"undo": MessageLookupByLibrary.simpleMessage("撤销"),
|
||||
"unifiedDelay": MessageLookupByLibrary.simpleMessage("统一延迟"),
|
||||
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage("去除握手等额外延迟"),
|
||||
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
||||
@@ -399,6 +424,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"),
|
||||
"useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"),
|
||||
"value": MessageLookupByLibrary.simpleMessage("值"),
|
||||
"valueExists": MessageLookupByLibrary.simpleMessage("当前值已存在"),
|
||||
"view": MessageLookupByLibrary.simpleMessage("查看"),
|
||||
"vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"),
|
||||
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
|
||||
|
||||
@@ -945,16 +945,6 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `When enabled, the home tab will add a toggle animation`
|
||||
String get tabAnimationDesc {
|
||||
return Intl.message(
|
||||
'When enabled, the home tab will add a toggle animation',
|
||||
name: 'tabAnimationDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.`
|
||||
String get desc {
|
||||
return Intl.message(
|
||||
@@ -1435,16 +1425,6 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `There is a risk of flashback after opening`
|
||||
String get findProcessModeDesc {
|
||||
return Intl.message(
|
||||
'There is a risk of flashback after opening',
|
||||
name: 'findProcessModeDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Init`
|
||||
String get init {
|
||||
return Intl.message('Init', name: 'init', desc: '', args: []);
|
||||
@@ -1930,16 +1910,6 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Overwrite general settings`
|
||||
String get generalDesc {
|
||||
return Intl.message(
|
||||
'Overwrite general settings',
|
||||
name: 'generalDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Update DNS related settings`
|
||||
String get dnsDesc {
|
||||
return Intl.message(
|
||||
@@ -2694,6 +2664,246 @@ class AppLocalizations {
|
||||
String get listen {
|
||||
return Intl.message('Listen', name: 'listen', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `The current key already exists`
|
||||
String get keyExists {
|
||||
return Intl.message(
|
||||
'The current key already exists',
|
||||
name: 'keyExists',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `The current value already exists`
|
||||
String get valueExists {
|
||||
return Intl.message(
|
||||
'The current value already exists',
|
||||
name: 'valueExists',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `undo`
|
||||
String get undo {
|
||||
return Intl.message('undo', name: 'undo', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `redo`
|
||||
String get redo {
|
||||
return Intl.message('redo', name: 'redo', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `none`
|
||||
String get none {
|
||||
return Intl.message('none', name: 'none', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Basic configuration`
|
||||
String get basicConfig {
|
||||
return Intl.message(
|
||||
'Basic configuration',
|
||||
name: 'basicConfig',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Modify the basic configuration globally`
|
||||
String get basicConfigDesc {
|
||||
return Intl.message(
|
||||
'Modify the basic configuration globally',
|
||||
name: 'basicConfigDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `{count} items have been selected`
|
||||
String selectedCountTitle(Object count) {
|
||||
return Intl.message(
|
||||
'$count items have been selected',
|
||||
name: 'selectedCountTitle',
|
||||
desc: '',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Add rule`
|
||||
String get addRule {
|
||||
return Intl.message('Add rule', name: 'addRule', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Rule provider cannot be empty`
|
||||
String get ruleProviderEmptyTip {
|
||||
return Intl.message(
|
||||
'Rule provider cannot be empty',
|
||||
name: 'ruleProviderEmptyTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Rule name`
|
||||
String get ruleName {
|
||||
return Intl.message('Rule name', name: 'ruleName', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Content`
|
||||
String get content {
|
||||
return Intl.message('Content', name: 'content', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Content cannot be empty`
|
||||
String get contentEmptyTip {
|
||||
return Intl.message(
|
||||
'Content cannot be empty',
|
||||
name: 'contentEmptyTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Sub rule`
|
||||
String get subRule {
|
||||
return Intl.message('Sub rule', name: 'subRule', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Sub rule content cannot be empty`
|
||||
String get subRuleEmptyTip {
|
||||
return Intl.message(
|
||||
'Sub rule content cannot be empty',
|
||||
name: 'subRuleEmptyTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Rule target`
|
||||
String get ruleTarget {
|
||||
return Intl.message('Rule target', name: 'ruleTarget', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Rule target cannot be empty`
|
||||
String get ruleTargetEmptyTip {
|
||||
return Intl.message(
|
||||
'Rule target cannot be empty',
|
||||
name: 'ruleTargetEmptyTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Source IP`
|
||||
String get sourceIp {
|
||||
return Intl.message('Source IP', name: 'sourceIp', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `No resolve IP`
|
||||
String get noResolve {
|
||||
return Intl.message('No resolve IP', name: 'noResolve', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Get original rules`
|
||||
String get getOriginRules {
|
||||
return Intl.message(
|
||||
'Get original rules',
|
||||
name: 'getOriginRules',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Override the original rule`
|
||||
String get overrideOriginRules {
|
||||
return Intl.message(
|
||||
'Override the original rule',
|
||||
name: 'overrideOriginRules',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Attach on the original rules`
|
||||
String get addedOriginRules {
|
||||
return Intl.message(
|
||||
'Attach on the original rules',
|
||||
name: 'addedOriginRules',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Enable override`
|
||||
String get enableOverride {
|
||||
return Intl.message(
|
||||
'Enable override',
|
||||
name: 'enableOverride',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Are you sure you want to delete the selected rule?`
|
||||
String get deleteRuleTip {
|
||||
return Intl.message(
|
||||
'Are you sure you want to delete the selected rule?',
|
||||
name: 'deleteRuleTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Do you want to save the changes?`
|
||||
String get saveChanges {
|
||||
return Intl.message(
|
||||
'Do you want to save the changes?',
|
||||
name: 'saveChanges',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Modify general settings`
|
||||
String get generalDesc {
|
||||
return Intl.message(
|
||||
'Modify general settings',
|
||||
name: 'generalDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `There is a certain performance loss after opening`
|
||||
String get findProcessModeDesc {
|
||||
return Intl.message(
|
||||
'There is a certain performance loss after opening',
|
||||
name: 'findProcessModeDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Effective only in mobile view`
|
||||
String get tabAnimationDesc {
|
||||
return Intl.message(
|
||||
'Effective only in mobile view',
|
||||
name: 'tabAnimationDesc',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Are you sure you want to save?`
|
||||
String get saveTip {
|
||||
return Intl.message(
|
||||
'Are you sure you want to save?',
|
||||
name: 'saveTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@@ -27,6 +27,7 @@ Future<void> main() async {
|
||||
await globalState.initApp(version);
|
||||
await android?.init();
|
||||
await window?.init(version);
|
||||
globalState.isPre = const String.fromEnvironment("APP_ENV") != 'stable';
|
||||
HttpOverrides.global = FlClashHttpOverrides();
|
||||
runApp(ProviderScope(
|
||||
child: const Application(),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -19,6 +21,7 @@ class _AppStateManagerState extends State<AppStateManager>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@@ -56,3 +59,24 @@ class _AppStateManagerState extends State<AppStateManager>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppEnvManager extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const AppEnvManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (globalState.isPre) {
|
||||
return Banner(
|
||||
message: 'PRE',
|
||||
location: BannerLocation.topEnd,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,5 @@ export 'app_state_manager.dart';
|
||||
export 'vpn_manager.dart';
|
||||
export 'proxy_manager.dart';
|
||||
export 'connectivity_manager.dart';
|
||||
export 'message_manager.dart';
|
||||
export 'message_manager.dart';
|
||||
export 'theme_manager.dart';
|
||||
38
lib/manager/theme_manager.dart
Normal file
38
lib/manager/theme_manager.dart
Normal file
@@ -0,0 +1,38 @@
|
||||
import 'package:fl_clash/common/constant.dart';
|
||||
import 'package:fl_clash/common/measure.dart';
|
||||
import 'package:fl_clash/common/theme.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ThemeManager extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const ThemeManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
globalState.measure = Measure.of(context);
|
||||
globalState.theme = CommonTheme.of(context);
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaler: TextScaler.linear(
|
||||
textScaleFactor,
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
globalState.appController.updateViewSize(
|
||||
Size(
|
||||
container.maxWidth,
|
||||
container.maxHeight,
|
||||
),
|
||||
);
|
||||
return child;
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
@@ -24,6 +25,7 @@ class WindowManager extends ConsumerStatefulWidget {
|
||||
|
||||
class _WindowContainerState extends ConsumerState<WindowManager>
|
||||
with WindowListener, WindowExtListener {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
@@ -271,7 +273,7 @@ class _WindowHeaderState extends State<WindowHeader> {
|
||||
_updateMaximized();
|
||||
},
|
||||
child: Container(
|
||||
color: context.colorScheme.secondary.toSoft,
|
||||
color: context.colorScheme.secondary.opacity15,
|
||||
alignment: Alignment.centerLeft,
|
||||
height: kHeaderHeight,
|
||||
),
|
||||
|
||||
@@ -18,7 +18,7 @@ class AppState with _$AppState {
|
||||
@Default([]) List<Package> packages,
|
||||
@Default(ColorSchemes()) ColorSchemes colorSchemes,
|
||||
@Default(0) int sortNum,
|
||||
required double viewWidth,
|
||||
required Size viewSize,
|
||||
@Default({}) DelayMap delayMap,
|
||||
@Default([]) List<Group> groups,
|
||||
@Default(0) int checkIpNum,
|
||||
@@ -35,7 +35,7 @@ class AppState with _$AppState {
|
||||
}
|
||||
|
||||
extension AppStateExt on AppState {
|
||||
ViewMode get viewMode => other.getViewMode(viewWidth);
|
||||
ViewMode get viewMode => other.getViewMode(viewSize.width);
|
||||
|
||||
bool get isStart => runTime != null;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import '../enum/enum.dart';
|
||||
|
||||
part 'generated/clash_config.freezed.dart';
|
||||
|
||||
part 'generated/clash_config.g.dart';
|
||||
|
||||
typedef HostsMap = Map<String, String>;
|
||||
@@ -122,7 +123,7 @@ class ProxyGroup with _$ProxyGroup {
|
||||
String? filter,
|
||||
@JsonKey(name: "expected-filter") String? excludeFilter,
|
||||
@JsonKey(name: "exclude-type") String? excludeType,
|
||||
@JsonKey(name: "expected-status") int? expectedStatus,
|
||||
@JsonKey(name: "expected-status") dynamic expectedStatus,
|
||||
bool? hidden,
|
||||
String? icon,
|
||||
}) = _ProxyGroup;
|
||||
@@ -131,6 +132,16 @@ class ProxyGroup with _$ProxyGroup {
|
||||
_$ProxyGroupFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RuleProvider with _$RuleProvider {
|
||||
const factory RuleProvider({
|
||||
required String name,
|
||||
}) = _RuleProvider;
|
||||
|
||||
factory RuleProvider.fromJson(Map<String, Object?> json) =>
|
||||
_$RuleProviderFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Tun with _$Tun {
|
||||
const factory Tun({
|
||||
@@ -273,11 +284,136 @@ class GeoXUrl with _$GeoXUrl {
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ParsedRule with _$ParsedRule {
|
||||
const factory ParsedRule({
|
||||
required RuleAction ruleAction,
|
||||
String? content,
|
||||
String? ruleTarget,
|
||||
String? ruleProvider,
|
||||
String? subRule,
|
||||
@Default(false) bool noResolve,
|
||||
@Default(false) bool src,
|
||||
}) = _ParsedRule;
|
||||
|
||||
factory ParsedRule.parseString(String value) {
|
||||
final splits = value.split(",");
|
||||
final shortSplits = splits
|
||||
.where(
|
||||
(item) => !item.contains("src") && !item.contains("no-resolve"),
|
||||
)
|
||||
.toList();
|
||||
final ruleAction = RuleAction.values.firstWhere(
|
||||
(item) => item.value == shortSplits.first,
|
||||
orElse: () => RuleAction.DOMAIN,
|
||||
);
|
||||
String? subRule;
|
||||
String? ruleTarget;
|
||||
|
||||
if (ruleAction == RuleAction.SUB_RULE) {
|
||||
subRule = shortSplits.last;
|
||||
} else {
|
||||
ruleTarget = shortSplits.last;
|
||||
}
|
||||
|
||||
String? content;
|
||||
String? ruleProvider;
|
||||
|
||||
if (ruleAction == RuleAction.RULE_SET) {
|
||||
ruleProvider = shortSplits.sublist(1, shortSplits.length - 1).join(",");
|
||||
} else {
|
||||
content = shortSplits.sublist(1, shortSplits.length - 1).join(",");
|
||||
}
|
||||
|
||||
return ParsedRule(
|
||||
ruleAction: ruleAction,
|
||||
content: content,
|
||||
src: splits.contains("src"),
|
||||
ruleProvider: ruleProvider,
|
||||
noResolve: splits.contains("no-resolve"),
|
||||
subRule: subRule,
|
||||
ruleTarget: ruleTarget,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ParsedRuleExt on ParsedRule {
|
||||
String get value {
|
||||
return [
|
||||
ruleAction.value,
|
||||
ruleAction == RuleAction.RULE_SET ? ruleProvider : content,
|
||||
ruleAction == RuleAction.SUB_RULE ? subRule : ruleTarget,
|
||||
if (ruleAction.hasParams) ...[
|
||||
if (src) "src",
|
||||
if (noResolve) "no-resolve",
|
||||
]
|
||||
].join(",");
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Rule with _$Rule {
|
||||
const factory Rule({
|
||||
required String id,
|
||||
required String value,
|
||||
}) = _Rule;
|
||||
|
||||
factory Rule.value(String value) {
|
||||
return Rule(
|
||||
value: value,
|
||||
id: other.uuidV4,
|
||||
);
|
||||
}
|
||||
|
||||
factory Rule.fromJson(Map<String, Object?> json) => _$RuleFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SubRule with _$SubRule {
|
||||
const factory SubRule({
|
||||
required String name,
|
||||
}) = _SubRule;
|
||||
|
||||
factory SubRule.fromJson(Map<String, Object?> json) =>
|
||||
_$SubRuleFromJson(json);
|
||||
}
|
||||
|
||||
_genRule(List<dynamic>? rules) {
|
||||
if (rules == null) {
|
||||
return [];
|
||||
}
|
||||
return rules
|
||||
.map(
|
||||
(item) => Rule.value(item),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
|
||||
return json.entries.map((entry) => RuleProvider(name: entry.key)).toList();
|
||||
}
|
||||
|
||||
List<SubRule> _genSubRules(Map<String, dynamic> json) {
|
||||
return json.entries
|
||||
.map(
|
||||
(entry) => SubRule(
|
||||
name: entry.key,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ClashConfigSnippet with _$ClashConfigSnippet {
|
||||
const factory ClashConfigSnippet({
|
||||
@Default([]) @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
|
||||
@Default([]) List<String> rule,
|
||||
@JsonKey(fromJson: _genRule) @Default([]) List<Rule> rule,
|
||||
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
|
||||
@Default([])
|
||||
List<RuleProvider> ruleProvider,
|
||||
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
|
||||
@Default([])
|
||||
List<SubRule> subRules,
|
||||
}) = _ClashConfigSnippet;
|
||||
|
||||
factory ClashConfigSnippet.fromJson(Map<String, Object?> json) =>
|
||||
|
||||
@@ -382,7 +382,7 @@ extension ColorSchemesExt on ColorSchemes {
|
||||
);
|
||||
}
|
||||
return lightColorScheme != null
|
||||
? ColorScheme.fromSeed(seedColor: lightColorScheme!.primary)
|
||||
? ColorScheme.fromSeed(seedColor: lightColorScheme!.primary,dynamicSchemeVariant: DynamicSchemeVariant.vibrant)
|
||||
: ColorScheme.fromSeed(seedColor: defaultPrimaryColor);
|
||||
}
|
||||
}
|
||||
@@ -481,13 +481,13 @@ class Field with _$Field {
|
||||
}) = _Field;
|
||||
}
|
||||
|
||||
enum ActionType {
|
||||
enum PopupMenuItemType {
|
||||
primary,
|
||||
danger,
|
||||
}
|
||||
|
||||
class ActionItemData {
|
||||
const ActionItemData({
|
||||
class PopupMenuItemData {
|
||||
const PopupMenuItemData({
|
||||
this.icon,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
@@ -497,7 +497,7 @@ class ActionItemData {
|
||||
|
||||
final double? iconSize;
|
||||
final String label;
|
||||
final VoidCallback onPressed;
|
||||
final VoidCallback? onPressed;
|
||||
final IconData? icon;
|
||||
final ActionType? type;
|
||||
final PopupMenuItemType? type;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ const defaultProxiesStyle = ProxiesStyle();
|
||||
const defaultWindowProps = WindowProps();
|
||||
const defaultAccessControl = AccessControl();
|
||||
final defaultThemeProps = ThemeProps().copyWith(
|
||||
primaryColor: defaultPrimaryColor.value,
|
||||
primaryColor: defaultPrimaryColor.toARGB32(),
|
||||
themeMode: ThemeMode.dark,
|
||||
);
|
||||
|
||||
@@ -121,7 +121,7 @@ extension AccessControlExt on AccessControl {
|
||||
@freezed
|
||||
class WindowProps with _$WindowProps {
|
||||
const factory WindowProps({
|
||||
@Default(900) double width,
|
||||
@Default(750) double width,
|
||||
@Default(600) double height,
|
||||
double? top,
|
||||
double? left,
|
||||
|
||||
@@ -62,6 +62,7 @@ class ConfigExtendedParams with _$ConfigExtendedParams {
|
||||
@JsonKey(name: "is-patch") required bool isPatch,
|
||||
@JsonKey(name: "selected-map") required SelectedMap selectedMap,
|
||||
@JsonKey(name: "override-dns") required bool overrideDns,
|
||||
@JsonKey(name: "override-rule") required bool overrideRule,
|
||||
@JsonKey(name: "test-url") required String testUrl,
|
||||
}) = _ConfigExtendedParams;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ mixin _$AppState {
|
||||
List<Package> get packages => throw _privateConstructorUsedError;
|
||||
ColorSchemes get colorSchemes => throw _privateConstructorUsedError;
|
||||
int get sortNum => throw _privateConstructorUsedError;
|
||||
double get viewWidth => throw _privateConstructorUsedError;
|
||||
Size get viewSize => throw _privateConstructorUsedError;
|
||||
Map<String, Map<String, int?>> get delayMap =>
|
||||
throw _privateConstructorUsedError;
|
||||
List<Group> get groups => throw _privateConstructorUsedError;
|
||||
@@ -54,7 +54,7 @@ abstract class $AppStateCopyWith<$Res> {
|
||||
List<Package> packages,
|
||||
ColorSchemes colorSchemes,
|
||||
int sortNum,
|
||||
double viewWidth,
|
||||
Size viewSize,
|
||||
Map<String, Map<String, int?>> delayMap,
|
||||
List<Group> groups,
|
||||
int checkIpNum,
|
||||
@@ -91,7 +91,7 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
|
||||
Object? packages = null,
|
||||
Object? colorSchemes = null,
|
||||
Object? sortNum = null,
|
||||
Object? viewWidth = null,
|
||||
Object? viewSize = null,
|
||||
Object? delayMap = null,
|
||||
Object? groups = null,
|
||||
Object? checkIpNum = null,
|
||||
@@ -126,10 +126,10 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
|
||||
? _value.sortNum
|
||||
: sortNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
viewWidth: null == viewWidth
|
||||
? _value.viewWidth
|
||||
: viewWidth // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
viewSize: null == viewSize
|
||||
? _value.viewSize
|
||||
: viewSize // ignore: cast_nullable_to_non_nullable
|
||||
as Size,
|
||||
delayMap: null == delayMap
|
||||
? _value.delayMap
|
||||
: delayMap // ignore: cast_nullable_to_non_nullable
|
||||
@@ -206,7 +206,7 @@ abstract class _$$AppStateImplCopyWith<$Res>
|
||||
List<Package> packages,
|
||||
ColorSchemes colorSchemes,
|
||||
int sortNum,
|
||||
double viewWidth,
|
||||
Size viewSize,
|
||||
Map<String, Map<String, int?>> delayMap,
|
||||
List<Group> groups,
|
||||
int checkIpNum,
|
||||
@@ -242,7 +242,7 @@ class __$$AppStateImplCopyWithImpl<$Res>
|
||||
Object? packages = null,
|
||||
Object? colorSchemes = null,
|
||||
Object? sortNum = null,
|
||||
Object? viewWidth = null,
|
||||
Object? viewSize = null,
|
||||
Object? delayMap = null,
|
||||
Object? groups = null,
|
||||
Object? checkIpNum = null,
|
||||
@@ -277,10 +277,10 @@ class __$$AppStateImplCopyWithImpl<$Res>
|
||||
? _value.sortNum
|
||||
: sortNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
viewWidth: null == viewWidth
|
||||
? _value.viewWidth
|
||||
: viewWidth // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
viewSize: null == viewSize
|
||||
? _value.viewSize
|
||||
: viewSize // ignore: cast_nullable_to_non_nullable
|
||||
as Size,
|
||||
delayMap: null == delayMap
|
||||
? _value._delayMap
|
||||
: delayMap // ignore: cast_nullable_to_non_nullable
|
||||
@@ -342,7 +342,7 @@ class _$AppStateImpl implements _AppState {
|
||||
final List<Package> packages = const [],
|
||||
this.colorSchemes = const ColorSchemes(),
|
||||
this.sortNum = 0,
|
||||
required this.viewWidth,
|
||||
required this.viewSize,
|
||||
final Map<String, Map<String, int?>> delayMap = const {},
|
||||
final List<Group> groups = const [],
|
||||
this.checkIpNum = 0,
|
||||
@@ -382,7 +382,7 @@ class _$AppStateImpl implements _AppState {
|
||||
@JsonKey()
|
||||
final int sortNum;
|
||||
@override
|
||||
final double viewWidth;
|
||||
final Size viewSize;
|
||||
final Map<String, Map<String, int?>> _delayMap;
|
||||
@override
|
||||
@JsonKey()
|
||||
@@ -432,7 +432,7 @@ class _$AppStateImpl implements _AppState {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppState(isInit: $isInit, pageLabel: $pageLabel, packages: $packages, colorSchemes: $colorSchemes, sortNum: $sortNum, viewWidth: $viewWidth, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic)';
|
||||
return 'AppState(isInit: $isInit, pageLabel: $pageLabel, packages: $packages, colorSchemes: $colorSchemes, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -447,8 +447,8 @@ class _$AppStateImpl implements _AppState {
|
||||
(identical(other.colorSchemes, colorSchemes) ||
|
||||
other.colorSchemes == colorSchemes) &&
|
||||
(identical(other.sortNum, sortNum) || other.sortNum == sortNum) &&
|
||||
(identical(other.viewWidth, viewWidth) ||
|
||||
other.viewWidth == viewWidth) &&
|
||||
(identical(other.viewSize, viewSize) ||
|
||||
other.viewSize == viewSize) &&
|
||||
const DeepCollectionEquality().equals(other._delayMap, _delayMap) &&
|
||||
const DeepCollectionEquality().equals(other._groups, _groups) &&
|
||||
(identical(other.checkIpNum, checkIpNum) ||
|
||||
@@ -477,7 +477,7 @@ class _$AppStateImpl implements _AppState {
|
||||
const DeepCollectionEquality().hash(_packages),
|
||||
colorSchemes,
|
||||
sortNum,
|
||||
viewWidth,
|
||||
viewSize,
|
||||
const DeepCollectionEquality().hash(_delayMap),
|
||||
const DeepCollectionEquality().hash(_groups),
|
||||
checkIpNum,
|
||||
@@ -507,7 +507,7 @@ abstract class _AppState implements AppState {
|
||||
final List<Package> packages,
|
||||
final ColorSchemes colorSchemes,
|
||||
final int sortNum,
|
||||
required final double viewWidth,
|
||||
required final Size viewSize,
|
||||
final Map<String, Map<String, int?>> delayMap,
|
||||
final List<Group> groups,
|
||||
final int checkIpNum,
|
||||
@@ -532,7 +532,7 @@ abstract class _AppState implements AppState {
|
||||
@override
|
||||
int get sortNum;
|
||||
@override
|
||||
double get viewWidth;
|
||||
Size get viewSize;
|
||||
@override
|
||||
Map<String, Map<String, int?>> get delayMap;
|
||||
@override
|
||||
|
||||
@@ -37,7 +37,7 @@ mixin _$ProxyGroup {
|
||||
@JsonKey(name: "exclude-type")
|
||||
String? get excludeType => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "expected-status")
|
||||
int? get expectedStatus => throw _privateConstructorUsedError;
|
||||
dynamic get expectedStatus => throw _privateConstructorUsedError;
|
||||
bool? get hidden => throw _privateConstructorUsedError;
|
||||
String? get icon => throw _privateConstructorUsedError;
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class $ProxyGroupCopyWith<$Res> {
|
||||
String? filter,
|
||||
@JsonKey(name: "expected-filter") String? excludeFilter,
|
||||
@JsonKey(name: "exclude-type") String? excludeType,
|
||||
@JsonKey(name: "expected-status") int? expectedStatus,
|
||||
@JsonKey(name: "expected-status") dynamic expectedStatus,
|
||||
bool? hidden,
|
||||
String? icon});
|
||||
}
|
||||
@@ -158,7 +158,7 @@ class _$ProxyGroupCopyWithImpl<$Res, $Val extends ProxyGroup>
|
||||
expectedStatus: freezed == expectedStatus
|
||||
? _value.expectedStatus
|
||||
: expectedStatus // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
as dynamic,
|
||||
hidden: freezed == hidden
|
||||
? _value.hidden
|
||||
: hidden // ignore: cast_nullable_to_non_nullable
|
||||
@@ -192,7 +192,7 @@ abstract class _$$ProxyGroupImplCopyWith<$Res>
|
||||
String? filter,
|
||||
@JsonKey(name: "expected-filter") String? excludeFilter,
|
||||
@JsonKey(name: "exclude-type") String? excludeType,
|
||||
@JsonKey(name: "expected-status") int? expectedStatus,
|
||||
@JsonKey(name: "expected-status") dynamic expectedStatus,
|
||||
bool? hidden,
|
||||
String? icon});
|
||||
}
|
||||
@@ -278,7 +278,7 @@ class __$$ProxyGroupImplCopyWithImpl<$Res>
|
||||
expectedStatus: freezed == expectedStatus
|
||||
? _value.expectedStatus
|
||||
: expectedStatus // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
as dynamic,
|
||||
hidden: freezed == hidden
|
||||
? _value.hidden
|
||||
: hidden // ignore: cast_nullable_to_non_nullable
|
||||
@@ -362,7 +362,7 @@ class _$ProxyGroupImpl implements _ProxyGroup {
|
||||
final String? excludeType;
|
||||
@override
|
||||
@JsonKey(name: "expected-status")
|
||||
final int? expectedStatus;
|
||||
final dynamic expectedStatus;
|
||||
@override
|
||||
final bool? hidden;
|
||||
@override
|
||||
@@ -394,8 +394,8 @@ class _$ProxyGroupImpl implements _ProxyGroup {
|
||||
other.excludeFilter == excludeFilter) &&
|
||||
(identical(other.excludeType, excludeType) ||
|
||||
other.excludeType == excludeType) &&
|
||||
(identical(other.expectedStatus, expectedStatus) ||
|
||||
other.expectedStatus == expectedStatus) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.expectedStatus, expectedStatus) &&
|
||||
(identical(other.hidden, hidden) || other.hidden == hidden) &&
|
||||
(identical(other.icon, icon) || other.icon == icon));
|
||||
}
|
||||
@@ -416,7 +416,7 @@ class _$ProxyGroupImpl implements _ProxyGroup {
|
||||
filter,
|
||||
excludeFilter,
|
||||
excludeType,
|
||||
expectedStatus,
|
||||
const DeepCollectionEquality().hash(expectedStatus),
|
||||
hidden,
|
||||
icon);
|
||||
|
||||
@@ -451,7 +451,7 @@ abstract class _ProxyGroup implements ProxyGroup {
|
||||
final String? filter,
|
||||
@JsonKey(name: "expected-filter") final String? excludeFilter,
|
||||
@JsonKey(name: "exclude-type") final String? excludeType,
|
||||
@JsonKey(name: "expected-status") final int? expectedStatus,
|
||||
@JsonKey(name: "expected-status") final dynamic expectedStatus,
|
||||
final bool? hidden,
|
||||
final String? icon}) = _$ProxyGroupImpl;
|
||||
|
||||
@@ -488,7 +488,7 @@ abstract class _ProxyGroup implements ProxyGroup {
|
||||
String? get excludeType;
|
||||
@override
|
||||
@JsonKey(name: "expected-status")
|
||||
int? get expectedStatus;
|
||||
dynamic get expectedStatus;
|
||||
@override
|
||||
bool? get hidden;
|
||||
@override
|
||||
@@ -502,6 +502,156 @@ abstract class _ProxyGroup implements ProxyGroup {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
RuleProvider _$RuleProviderFromJson(Map<String, dynamic> json) {
|
||||
return _RuleProvider.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$RuleProvider {
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this RuleProvider to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of RuleProvider
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$RuleProviderCopyWith<RuleProvider> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $RuleProviderCopyWith<$Res> {
|
||||
factory $RuleProviderCopyWith(
|
||||
RuleProvider value, $Res Function(RuleProvider) then) =
|
||||
_$RuleProviderCopyWithImpl<$Res, RuleProvider>;
|
||||
@useResult
|
||||
$Res call({String name});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$RuleProviderCopyWithImpl<$Res, $Val extends RuleProvider>
|
||||
implements $RuleProviderCopyWith<$Res> {
|
||||
_$RuleProviderCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of RuleProvider
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$RuleProviderImplCopyWith<$Res>
|
||||
implements $RuleProviderCopyWith<$Res> {
|
||||
factory _$$RuleProviderImplCopyWith(
|
||||
_$RuleProviderImpl value, $Res Function(_$RuleProviderImpl) then) =
|
||||
__$$RuleProviderImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String name});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$RuleProviderImplCopyWithImpl<$Res>
|
||||
extends _$RuleProviderCopyWithImpl<$Res, _$RuleProviderImpl>
|
||||
implements _$$RuleProviderImplCopyWith<$Res> {
|
||||
__$$RuleProviderImplCopyWithImpl(
|
||||
_$RuleProviderImpl _value, $Res Function(_$RuleProviderImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of RuleProvider
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
}) {
|
||||
return _then(_$RuleProviderImpl(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$RuleProviderImpl implements _RuleProvider {
|
||||
const _$RuleProviderImpl({required this.name});
|
||||
|
||||
factory _$RuleProviderImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$RuleProviderImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'RuleProvider(name: $name)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$RuleProviderImpl &&
|
||||
(identical(other.name, name) || other.name == name));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, name);
|
||||
|
||||
/// Create a copy of RuleProvider
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$RuleProviderImplCopyWith<_$RuleProviderImpl> get copyWith =>
|
||||
__$$RuleProviderImplCopyWithImpl<_$RuleProviderImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$RuleProviderImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _RuleProvider implements RuleProvider {
|
||||
const factory _RuleProvider({required final String name}) =
|
||||
_$RuleProviderImpl;
|
||||
|
||||
factory _RuleProvider.fromJson(Map<String, dynamic> json) =
|
||||
_$RuleProviderImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get name;
|
||||
|
||||
/// Create a copy of RuleProvider
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$RuleProviderImplCopyWith<_$RuleProviderImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Tun _$TunFromJson(Map<String, dynamic> json) {
|
||||
return _Tun.fromJson(json);
|
||||
}
|
||||
@@ -1834,6 +1984,571 @@ abstract class _GeoXUrl implements GeoXUrl {
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$ParsedRule {
|
||||
RuleAction get ruleAction => throw _privateConstructorUsedError;
|
||||
String? get content => throw _privateConstructorUsedError;
|
||||
String? get ruleTarget => throw _privateConstructorUsedError;
|
||||
String? get ruleProvider => throw _privateConstructorUsedError;
|
||||
String? get subRule => throw _privateConstructorUsedError;
|
||||
bool get noResolve => throw _privateConstructorUsedError;
|
||||
bool get src => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of ParsedRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$ParsedRuleCopyWith<ParsedRule> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $ParsedRuleCopyWith<$Res> {
|
||||
factory $ParsedRuleCopyWith(
|
||||
ParsedRule value, $Res Function(ParsedRule) then) =
|
||||
_$ParsedRuleCopyWithImpl<$Res, ParsedRule>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{RuleAction ruleAction,
|
||||
String? content,
|
||||
String? ruleTarget,
|
||||
String? ruleProvider,
|
||||
String? subRule,
|
||||
bool noResolve,
|
||||
bool src});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$ParsedRuleCopyWithImpl<$Res, $Val extends ParsedRule>
|
||||
implements $ParsedRuleCopyWith<$Res> {
|
||||
_$ParsedRuleCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of ParsedRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? ruleAction = null,
|
||||
Object? content = freezed,
|
||||
Object? ruleTarget = freezed,
|
||||
Object? ruleProvider = freezed,
|
||||
Object? subRule = freezed,
|
||||
Object? noResolve = null,
|
||||
Object? src = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
ruleAction: null == ruleAction
|
||||
? _value.ruleAction
|
||||
: ruleAction // ignore: cast_nullable_to_non_nullable
|
||||
as RuleAction,
|
||||
content: freezed == content
|
||||
? _value.content
|
||||
: content // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
ruleTarget: freezed == ruleTarget
|
||||
? _value.ruleTarget
|
||||
: ruleTarget // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
ruleProvider: freezed == ruleProvider
|
||||
? _value.ruleProvider
|
||||
: ruleProvider // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
subRule: freezed == subRule
|
||||
? _value.subRule
|
||||
: subRule // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
noResolve: null == noResolve
|
||||
? _value.noResolve
|
||||
: noResolve // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
src: null == src
|
||||
? _value.src
|
||||
: src // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$ParsedRuleImplCopyWith<$Res>
|
||||
implements $ParsedRuleCopyWith<$Res> {
|
||||
factory _$$ParsedRuleImplCopyWith(
|
||||
_$ParsedRuleImpl value, $Res Function(_$ParsedRuleImpl) then) =
|
||||
__$$ParsedRuleImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{RuleAction ruleAction,
|
||||
String? content,
|
||||
String? ruleTarget,
|
||||
String? ruleProvider,
|
||||
String? subRule,
|
||||
bool noResolve,
|
||||
bool src});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$ParsedRuleImplCopyWithImpl<$Res>
|
||||
extends _$ParsedRuleCopyWithImpl<$Res, _$ParsedRuleImpl>
|
||||
implements _$$ParsedRuleImplCopyWith<$Res> {
|
||||
__$$ParsedRuleImplCopyWithImpl(
|
||||
_$ParsedRuleImpl _value, $Res Function(_$ParsedRuleImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of ParsedRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? ruleAction = null,
|
||||
Object? content = freezed,
|
||||
Object? ruleTarget = freezed,
|
||||
Object? ruleProvider = freezed,
|
||||
Object? subRule = freezed,
|
||||
Object? noResolve = null,
|
||||
Object? src = null,
|
||||
}) {
|
||||
return _then(_$ParsedRuleImpl(
|
||||
ruleAction: null == ruleAction
|
||||
? _value.ruleAction
|
||||
: ruleAction // ignore: cast_nullable_to_non_nullable
|
||||
as RuleAction,
|
||||
content: freezed == content
|
||||
? _value.content
|
||||
: content // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
ruleTarget: freezed == ruleTarget
|
||||
? _value.ruleTarget
|
||||
: ruleTarget // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
ruleProvider: freezed == ruleProvider
|
||||
? _value.ruleProvider
|
||||
: ruleProvider // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
subRule: freezed == subRule
|
||||
? _value.subRule
|
||||
: subRule // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
noResolve: null == noResolve
|
||||
? _value.noResolve
|
||||
: noResolve // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
src: null == src
|
||||
? _value.src
|
||||
: src // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$ParsedRuleImpl implements _ParsedRule {
|
||||
const _$ParsedRuleImpl(
|
||||
{required this.ruleAction,
|
||||
this.content,
|
||||
this.ruleTarget,
|
||||
this.ruleProvider,
|
||||
this.subRule,
|
||||
this.noResolve = false,
|
||||
this.src = false});
|
||||
|
||||
@override
|
||||
final RuleAction ruleAction;
|
||||
@override
|
||||
final String? content;
|
||||
@override
|
||||
final String? ruleTarget;
|
||||
@override
|
||||
final String? ruleProvider;
|
||||
@override
|
||||
final String? subRule;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool noResolve;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool src;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ParsedRule(ruleAction: $ruleAction, content: $content, ruleTarget: $ruleTarget, ruleProvider: $ruleProvider, subRule: $subRule, noResolve: $noResolve, src: $src)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$ParsedRuleImpl &&
|
||||
(identical(other.ruleAction, ruleAction) ||
|
||||
other.ruleAction == ruleAction) &&
|
||||
(identical(other.content, content) || other.content == content) &&
|
||||
(identical(other.ruleTarget, ruleTarget) ||
|
||||
other.ruleTarget == ruleTarget) &&
|
||||
(identical(other.ruleProvider, ruleProvider) ||
|
||||
other.ruleProvider == ruleProvider) &&
|
||||
(identical(other.subRule, subRule) || other.subRule == subRule) &&
|
||||
(identical(other.noResolve, noResolve) ||
|
||||
other.noResolve == noResolve) &&
|
||||
(identical(other.src, src) || other.src == src));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, ruleAction, content, ruleTarget,
|
||||
ruleProvider, subRule, noResolve, src);
|
||||
|
||||
/// Create a copy of ParsedRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$ParsedRuleImplCopyWith<_$ParsedRuleImpl> get copyWith =>
|
||||
__$$ParsedRuleImplCopyWithImpl<_$ParsedRuleImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _ParsedRule implements ParsedRule {
|
||||
const factory _ParsedRule(
|
||||
{required final RuleAction ruleAction,
|
||||
final String? content,
|
||||
final String? ruleTarget,
|
||||
final String? ruleProvider,
|
||||
final String? subRule,
|
||||
final bool noResolve,
|
||||
final bool src}) = _$ParsedRuleImpl;
|
||||
|
||||
@override
|
||||
RuleAction get ruleAction;
|
||||
@override
|
||||
String? get content;
|
||||
@override
|
||||
String? get ruleTarget;
|
||||
@override
|
||||
String? get ruleProvider;
|
||||
@override
|
||||
String? get subRule;
|
||||
@override
|
||||
bool get noResolve;
|
||||
@override
|
||||
bool get src;
|
||||
|
||||
/// Create a copy of ParsedRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$ParsedRuleImplCopyWith<_$ParsedRuleImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Rule _$RuleFromJson(Map<String, dynamic> json) {
|
||||
return _Rule.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$Rule {
|
||||
String get id => throw _privateConstructorUsedError;
|
||||
String get value => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this Rule to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of Rule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$RuleCopyWith<Rule> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $RuleCopyWith<$Res> {
|
||||
factory $RuleCopyWith(Rule value, $Res Function(Rule) then) =
|
||||
_$RuleCopyWithImpl<$Res, Rule>;
|
||||
@useResult
|
||||
$Res call({String id, String value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$RuleCopyWithImpl<$Res, $Val extends Rule>
|
||||
implements $RuleCopyWith<$Res> {
|
||||
_$RuleCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of Rule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? value = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
value: null == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$RuleImplCopyWith<$Res> implements $RuleCopyWith<$Res> {
|
||||
factory _$$RuleImplCopyWith(
|
||||
_$RuleImpl value, $Res Function(_$RuleImpl) then) =
|
||||
__$$RuleImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String id, String value});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$RuleImplCopyWithImpl<$Res>
|
||||
extends _$RuleCopyWithImpl<$Res, _$RuleImpl>
|
||||
implements _$$RuleImplCopyWith<$Res> {
|
||||
__$$RuleImplCopyWithImpl(_$RuleImpl _value, $Res Function(_$RuleImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of Rule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? id = null,
|
||||
Object? value = null,
|
||||
}) {
|
||||
return _then(_$RuleImpl(
|
||||
id: null == id
|
||||
? _value.id
|
||||
: id // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
value: null == value
|
||||
? _value.value
|
||||
: value // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$RuleImpl implements _Rule {
|
||||
const _$RuleImpl({required this.id, required this.value});
|
||||
|
||||
factory _$RuleImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$RuleImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String id;
|
||||
@override
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Rule(id: $id, value: $value)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$RuleImpl &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.value, value) || other.value == value));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, id, value);
|
||||
|
||||
/// Create a copy of Rule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$RuleImplCopyWith<_$RuleImpl> get copyWith =>
|
||||
__$$RuleImplCopyWithImpl<_$RuleImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$RuleImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _Rule implements Rule {
|
||||
const factory _Rule({required final String id, required final String value}) =
|
||||
_$RuleImpl;
|
||||
|
||||
factory _Rule.fromJson(Map<String, dynamic> json) = _$RuleImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get id;
|
||||
@override
|
||||
String get value;
|
||||
|
||||
/// Create a copy of Rule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$RuleImplCopyWith<_$RuleImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
SubRule _$SubRuleFromJson(Map<String, dynamic> json) {
|
||||
return _SubRule.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SubRule {
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this SubRule to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of SubRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$SubRuleCopyWith<SubRule> get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $SubRuleCopyWith<$Res> {
|
||||
factory $SubRuleCopyWith(SubRule value, $Res Function(SubRule) then) =
|
||||
_$SubRuleCopyWithImpl<$Res, SubRule>;
|
||||
@useResult
|
||||
$Res call({String name});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$SubRuleCopyWithImpl<$Res, $Val extends SubRule>
|
||||
implements $SubRuleCopyWith<$Res> {
|
||||
_$SubRuleCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of SubRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SubRuleImplCopyWith<$Res> implements $SubRuleCopyWith<$Res> {
|
||||
factory _$$SubRuleImplCopyWith(
|
||||
_$SubRuleImpl value, $Res Function(_$SubRuleImpl) then) =
|
||||
__$$SubRuleImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({String name});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SubRuleImplCopyWithImpl<$Res>
|
||||
extends _$SubRuleCopyWithImpl<$Res, _$SubRuleImpl>
|
||||
implements _$$SubRuleImplCopyWith<$Res> {
|
||||
__$$SubRuleImplCopyWithImpl(
|
||||
_$SubRuleImpl _value, $Res Function(_$SubRuleImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of SubRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? name = null,
|
||||
}) {
|
||||
return _then(_$SubRuleImpl(
|
||||
name: null == name
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$SubRuleImpl implements _SubRule {
|
||||
const _$SubRuleImpl({required this.name});
|
||||
|
||||
factory _$SubRuleImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$SubRuleImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SubRule(name: $name)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SubRuleImpl &&
|
||||
(identical(other.name, name) || other.name == name));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, name);
|
||||
|
||||
/// Create a copy of SubRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SubRuleImplCopyWith<_$SubRuleImpl> get copyWith =>
|
||||
__$$SubRuleImplCopyWithImpl<_$SubRuleImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$SubRuleImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _SubRule implements SubRule {
|
||||
const factory _SubRule({required final String name}) = _$SubRuleImpl;
|
||||
|
||||
factory _SubRule.fromJson(Map<String, dynamic> json) = _$SubRuleImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get name;
|
||||
|
||||
/// Create a copy of SubRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$SubRuleImplCopyWith<_$SubRuleImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) {
|
||||
return _ClashConfigSnippet.fromJson(json);
|
||||
}
|
||||
@@ -1842,7 +2557,12 @@ ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) {
|
||||
mixin _$ClashConfigSnippet {
|
||||
@JsonKey(name: "proxy-groups")
|
||||
List<ProxyGroup> get proxyGroups => throw _privateConstructorUsedError;
|
||||
List<String> get rule => throw _privateConstructorUsedError;
|
||||
@JsonKey(fromJson: _genRule)
|
||||
List<Rule> get rule => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
|
||||
List<RuleProvider> get ruleProvider => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
|
||||
List<SubRule> get subRules => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ClashConfigSnippet to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -1862,7 +2582,11 @@ abstract class $ClashConfigSnippetCopyWith<$Res> {
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
|
||||
List<String> rule});
|
||||
@JsonKey(fromJson: _genRule) List<Rule> rule,
|
||||
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
|
||||
List<RuleProvider> ruleProvider,
|
||||
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
|
||||
List<SubRule> subRules});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1882,6 +2606,8 @@ class _$ClashConfigSnippetCopyWithImpl<$Res, $Val extends ClashConfigSnippet>
|
||||
$Res call({
|
||||
Object? proxyGroups = null,
|
||||
Object? rule = null,
|
||||
Object? ruleProvider = null,
|
||||
Object? subRules = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
proxyGroups: null == proxyGroups
|
||||
@@ -1891,7 +2617,15 @@ class _$ClashConfigSnippetCopyWithImpl<$Res, $Val extends ClashConfigSnippet>
|
||||
rule: null == rule
|
||||
? _value.rule
|
||||
: rule // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
as List<Rule>,
|
||||
ruleProvider: null == ruleProvider
|
||||
? _value.ruleProvider
|
||||
: ruleProvider // ignore: cast_nullable_to_non_nullable
|
||||
as List<RuleProvider>,
|
||||
subRules: null == subRules
|
||||
? _value.subRules
|
||||
: subRules // ignore: cast_nullable_to_non_nullable
|
||||
as List<SubRule>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@@ -1906,7 +2640,11 @@ abstract class _$$ClashConfigSnippetImplCopyWith<$Res>
|
||||
@useResult
|
||||
$Res call(
|
||||
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
|
||||
List<String> rule});
|
||||
@JsonKey(fromJson: _genRule) List<Rule> rule,
|
||||
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
|
||||
List<RuleProvider> ruleProvider,
|
||||
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
|
||||
List<SubRule> subRules});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -1924,6 +2662,8 @@ class __$$ClashConfigSnippetImplCopyWithImpl<$Res>
|
||||
$Res call({
|
||||
Object? proxyGroups = null,
|
||||
Object? rule = null,
|
||||
Object? ruleProvider = null,
|
||||
Object? subRules = null,
|
||||
}) {
|
||||
return _then(_$ClashConfigSnippetImpl(
|
||||
proxyGroups: null == proxyGroups
|
||||
@@ -1933,7 +2673,15 @@ class __$$ClashConfigSnippetImplCopyWithImpl<$Res>
|
||||
rule: null == rule
|
||||
? _value._rule
|
||||
: rule // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
as List<Rule>,
|
||||
ruleProvider: null == ruleProvider
|
||||
? _value._ruleProvider
|
||||
: ruleProvider // ignore: cast_nullable_to_non_nullable
|
||||
as List<RuleProvider>,
|
||||
subRules: null == subRules
|
||||
? _value._subRules
|
||||
: subRules // ignore: cast_nullable_to_non_nullable
|
||||
as List<SubRule>,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1944,9 +2692,15 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
|
||||
const _$ClashConfigSnippetImpl(
|
||||
{@JsonKey(name: "proxy-groups")
|
||||
final List<ProxyGroup> proxyGroups = const [],
|
||||
final List<String> rule = const []})
|
||||
@JsonKey(fromJson: _genRule) final List<Rule> rule = const [],
|
||||
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
|
||||
final List<RuleProvider> ruleProvider = const [],
|
||||
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
|
||||
final List<SubRule> subRules = const []})
|
||||
: _proxyGroups = proxyGroups,
|
||||
_rule = rule;
|
||||
_rule = rule,
|
||||
_ruleProvider = ruleProvider,
|
||||
_subRules = subRules;
|
||||
|
||||
factory _$ClashConfigSnippetImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ClashConfigSnippetImplFromJson(json);
|
||||
@@ -1960,18 +2714,36 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
|
||||
return EqualUnmodifiableListView(_proxyGroups);
|
||||
}
|
||||
|
||||
final List<String> _rule;
|
||||
final List<Rule> _rule;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<String> get rule {
|
||||
@JsonKey(fromJson: _genRule)
|
||||
List<Rule> get rule {
|
||||
if (_rule is EqualUnmodifiableListView) return _rule;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_rule);
|
||||
}
|
||||
|
||||
final List<RuleProvider> _ruleProvider;
|
||||
@override
|
||||
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
|
||||
List<RuleProvider> get ruleProvider {
|
||||
if (_ruleProvider is EqualUnmodifiableListView) return _ruleProvider;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_ruleProvider);
|
||||
}
|
||||
|
||||
final List<SubRule> _subRules;
|
||||
@override
|
||||
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
|
||||
List<SubRule> get subRules {
|
||||
if (_subRules is EqualUnmodifiableListView) return _subRules;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_subRules);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ClashConfigSnippet(proxyGroups: $proxyGroups, rule: $rule)';
|
||||
return 'ClashConfigSnippet(proxyGroups: $proxyGroups, rule: $rule, ruleProvider: $ruleProvider, subRules: $subRules)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1981,7 +2753,10 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
|
||||
other is _$ClashConfigSnippetImpl &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._proxyGroups, _proxyGroups) &&
|
||||
const DeepCollectionEquality().equals(other._rule, _rule));
|
||||
const DeepCollectionEquality().equals(other._rule, _rule) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._ruleProvider, _ruleProvider) &&
|
||||
const DeepCollectionEquality().equals(other._subRules, _subRules));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -1989,7 +2764,9 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(_proxyGroups),
|
||||
const DeepCollectionEquality().hash(_rule));
|
||||
const DeepCollectionEquality().hash(_rule),
|
||||
const DeepCollectionEquality().hash(_ruleProvider),
|
||||
const DeepCollectionEquality().hash(_subRules));
|
||||
|
||||
/// Create a copy of ClashConfigSnippet
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -2011,7 +2788,11 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
|
||||
abstract class _ClashConfigSnippet implements ClashConfigSnippet {
|
||||
const factory _ClashConfigSnippet(
|
||||
{@JsonKey(name: "proxy-groups") final List<ProxyGroup> proxyGroups,
|
||||
final List<String> rule}) = _$ClashConfigSnippetImpl;
|
||||
@JsonKey(fromJson: _genRule) final List<Rule> rule,
|
||||
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
|
||||
final List<RuleProvider> ruleProvider,
|
||||
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
|
||||
final List<SubRule> subRules}) = _$ClashConfigSnippetImpl;
|
||||
|
||||
factory _ClashConfigSnippet.fromJson(Map<String, dynamic> json) =
|
||||
_$ClashConfigSnippetImpl.fromJson;
|
||||
@@ -2020,7 +2801,14 @@ abstract class _ClashConfigSnippet implements ClashConfigSnippet {
|
||||
@JsonKey(name: "proxy-groups")
|
||||
List<ProxyGroup> get proxyGroups;
|
||||
@override
|
||||
List<String> get rule;
|
||||
@JsonKey(fromJson: _genRule)
|
||||
List<Rule> get rule;
|
||||
@override
|
||||
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
|
||||
List<RuleProvider> get ruleProvider;
|
||||
@override
|
||||
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
|
||||
List<SubRule> get subRules;
|
||||
|
||||
/// Create a copy of ClashConfigSnippet
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
@@ -21,7 +21,7 @@ _$ProxyGroupImpl _$$ProxyGroupImplFromJson(Map<String, dynamic> json) =>
|
||||
filter: json['filter'] as String?,
|
||||
excludeFilter: json['expected-filter'] as String?,
|
||||
excludeType: json['exclude-type'] as String?,
|
||||
expectedStatus: (json['expected-status'] as num?)?.toInt(),
|
||||
expectedStatus: json['expected-status'],
|
||||
hidden: json['hidden'] as bool?,
|
||||
icon: json['icon'] as String?,
|
||||
);
|
||||
@@ -53,6 +53,16 @@ const _$GroupTypeEnumMap = {
|
||||
GroupType.Relay: 'Relay',
|
||||
};
|
||||
|
||||
_$RuleProviderImpl _$$RuleProviderImplFromJson(Map<String, dynamic> json) =>
|
||||
_$RuleProviderImpl(
|
||||
name: json['name'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$RuleProviderImplToJson(_$RuleProviderImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
};
|
||||
|
||||
_$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
device: json['device'] as String? ?? appName,
|
||||
@@ -206,6 +216,27 @@ Map<String, dynamic> _$$GeoXUrlImplToJson(_$GeoXUrlImpl instance) =>
|
||||
'geosite': instance.geosite,
|
||||
};
|
||||
|
||||
_$RuleImpl _$$RuleImplFromJson(Map<String, dynamic> json) => _$RuleImpl(
|
||||
id: json['id'] as String,
|
||||
value: json['value'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$RuleImplToJson(_$RuleImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'value': instance.value,
|
||||
};
|
||||
|
||||
_$SubRuleImpl _$$SubRuleImplFromJson(Map<String, dynamic> json) =>
|
||||
_$SubRuleImpl(
|
||||
name: json['name'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SubRuleImplToJson(_$SubRuleImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
};
|
||||
|
||||
_$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ClashConfigSnippetImpl(
|
||||
@@ -213,9 +244,13 @@ _$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
|
||||
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
rule:
|
||||
(json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const [],
|
||||
rule: json['rule'] == null ? const [] : _genRule(json['rule'] as List?),
|
||||
ruleProvider: json['rule-providers'] == null
|
||||
? const []
|
||||
: _genRuleProviders(json['rule-providers'] as Map<String, dynamic>),
|
||||
subRules: json['sub-rules'] == null
|
||||
? const []
|
||||
: _genSubRules(json['sub-rules'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
|
||||
@@ -223,6 +258,8 @@ Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
|
||||
<String, dynamic>{
|
||||
'proxy-groups': instance.proxyGroups,
|
||||
'rule': instance.rule,
|
||||
'rule-providers': instance.ruleProvider,
|
||||
'sub-rules': instance.subRules,
|
||||
};
|
||||
|
||||
_$ClashConfigImpl _$$ClashConfigImplFromJson(Map<String, dynamic> json) =>
|
||||
|
||||
@@ -912,7 +912,7 @@ class __$$WindowPropsImplCopyWithImpl<$Res>
|
||||
@JsonSerializable()
|
||||
class _$WindowPropsImpl implements _WindowProps {
|
||||
const _$WindowPropsImpl(
|
||||
{this.width = 900, this.height = 600, this.top, this.left});
|
||||
{this.width = 750, this.height = 600, this.top, this.left});
|
||||
|
||||
factory _$WindowPropsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$WindowPropsImplFromJson(json);
|
||||
|
||||
@@ -102,7 +102,7 @@ const _$AccessSortTypeEnumMap = {
|
||||
|
||||
_$WindowPropsImpl _$$WindowPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$WindowPropsImpl(
|
||||
width: (json['width'] as num?)?.toDouble() ?? 900,
|
||||
width: (json['width'] as num?)?.toDouble() ?? 750,
|
||||
height: (json['height'] as num?)?.toDouble() ?? 600,
|
||||
top: (json['top'] as num?)?.toDouble(),
|
||||
left: (json['left'] as num?)?.toDouble(),
|
||||
|
||||
@@ -667,6 +667,8 @@ mixin _$ConfigExtendedParams {
|
||||
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "override-dns")
|
||||
bool get overrideDns => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "override-rule")
|
||||
bool get overrideRule => throw _privateConstructorUsedError;
|
||||
@JsonKey(name: "test-url")
|
||||
String get testUrl => throw _privateConstructorUsedError;
|
||||
|
||||
@@ -690,6 +692,7 @@ abstract class $ConfigExtendedParamsCopyWith<$Res> {
|
||||
{@JsonKey(name: "is-patch") bool isPatch,
|
||||
@JsonKey(name: "selected-map") Map<String, String> selectedMap,
|
||||
@JsonKey(name: "override-dns") bool overrideDns,
|
||||
@JsonKey(name: "override-rule") bool overrideRule,
|
||||
@JsonKey(name: "test-url") String testUrl});
|
||||
}
|
||||
|
||||
@@ -712,6 +715,7 @@ class _$ConfigExtendedParamsCopyWithImpl<$Res,
|
||||
Object? isPatch = null,
|
||||
Object? selectedMap = null,
|
||||
Object? overrideDns = null,
|
||||
Object? overrideRule = null,
|
||||
Object? testUrl = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@@ -727,6 +731,10 @@ class _$ConfigExtendedParamsCopyWithImpl<$Res,
|
||||
? _value.overrideDns
|
||||
: overrideDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
overrideRule: null == overrideRule
|
||||
? _value.overrideRule
|
||||
: overrideRule // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
testUrl: null == testUrl
|
||||
? _value.testUrl
|
||||
: testUrl // ignore: cast_nullable_to_non_nullable
|
||||
@@ -747,6 +755,7 @@ abstract class _$$ConfigExtendedParamsImplCopyWith<$Res>
|
||||
{@JsonKey(name: "is-patch") bool isPatch,
|
||||
@JsonKey(name: "selected-map") Map<String, String> selectedMap,
|
||||
@JsonKey(name: "override-dns") bool overrideDns,
|
||||
@JsonKey(name: "override-rule") bool overrideRule,
|
||||
@JsonKey(name: "test-url") String testUrl});
|
||||
}
|
||||
|
||||
@@ -766,6 +775,7 @@ class __$$ConfigExtendedParamsImplCopyWithImpl<$Res>
|
||||
Object? isPatch = null,
|
||||
Object? selectedMap = null,
|
||||
Object? overrideDns = null,
|
||||
Object? overrideRule = null,
|
||||
Object? testUrl = null,
|
||||
}) {
|
||||
return _then(_$ConfigExtendedParamsImpl(
|
||||
@@ -781,6 +791,10 @@ class __$$ConfigExtendedParamsImplCopyWithImpl<$Res>
|
||||
? _value.overrideDns
|
||||
: overrideDns // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
overrideRule: null == overrideRule
|
||||
? _value.overrideRule
|
||||
: overrideRule // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
testUrl: null == testUrl
|
||||
? _value.testUrl
|
||||
: testUrl // ignore: cast_nullable_to_non_nullable
|
||||
@@ -797,6 +811,7 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
|
||||
@JsonKey(name: "selected-map")
|
||||
required final Map<String, String> selectedMap,
|
||||
@JsonKey(name: "override-dns") required this.overrideDns,
|
||||
@JsonKey(name: "override-rule") required this.overrideRule,
|
||||
@JsonKey(name: "test-url") required this.testUrl})
|
||||
: _selectedMap = selectedMap;
|
||||
|
||||
@@ -819,12 +834,15 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
|
||||
@JsonKey(name: "override-dns")
|
||||
final bool overrideDns;
|
||||
@override
|
||||
@JsonKey(name: "override-rule")
|
||||
final bool overrideRule;
|
||||
@override
|
||||
@JsonKey(name: "test-url")
|
||||
final String testUrl;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ConfigExtendedParams(isPatch: $isPatch, selectedMap: $selectedMap, overrideDns: $overrideDns, testUrl: $testUrl)';
|
||||
return 'ConfigExtendedParams(isPatch: $isPatch, selectedMap: $selectedMap, overrideDns: $overrideDns, overrideRule: $overrideRule, testUrl: $testUrl)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -837,13 +855,20 @@ class _$ConfigExtendedParamsImpl implements _ConfigExtendedParams {
|
||||
.equals(other._selectedMap, _selectedMap) &&
|
||||
(identical(other.overrideDns, overrideDns) ||
|
||||
other.overrideDns == overrideDns) &&
|
||||
(identical(other.overrideRule, overrideRule) ||
|
||||
other.overrideRule == overrideRule) &&
|
||||
(identical(other.testUrl, testUrl) || other.testUrl == testUrl));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, isPatch,
|
||||
const DeepCollectionEquality().hash(_selectedMap), overrideDns, testUrl);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
isPatch,
|
||||
const DeepCollectionEquality().hash(_selectedMap),
|
||||
overrideDns,
|
||||
overrideRule,
|
||||
testUrl);
|
||||
|
||||
/// Create a copy of ConfigExtendedParams
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@@ -869,6 +894,7 @@ abstract class _ConfigExtendedParams implements ConfigExtendedParams {
|
||||
@JsonKey(name: "selected-map")
|
||||
required final Map<String, String> selectedMap,
|
||||
@JsonKey(name: "override-dns") required final bool overrideDns,
|
||||
@JsonKey(name: "override-rule") required final bool overrideRule,
|
||||
@JsonKey(name: "test-url") required final String testUrl}) =
|
||||
_$ConfigExtendedParamsImpl;
|
||||
|
||||
@@ -885,6 +911,9 @@ abstract class _ConfigExtendedParams implements ConfigExtendedParams {
|
||||
@JsonKey(name: "override-dns")
|
||||
bool get overrideDns;
|
||||
@override
|
||||
@JsonKey(name: "override-rule")
|
||||
bool get overrideRule;
|
||||
@override
|
||||
@JsonKey(name: "test-url")
|
||||
String get testUrl;
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ _$ConfigExtendedParamsImpl _$$ConfigExtendedParamsImplFromJson(
|
||||
isPatch: json['is-patch'] as bool,
|
||||
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
|
||||
overrideDns: json['override-dns'] as bool,
|
||||
overrideRule: json['override-rule'] as bool,
|
||||
testUrl: json['test-url'] as String,
|
||||
);
|
||||
|
||||
@@ -78,6 +79,7 @@ Map<String, dynamic> _$$ConfigExtendedParamsImplToJson(
|
||||
'is-patch': instance.isPatch,
|
||||
'selected-map': instance.selectedMap,
|
||||
'override-dns': instance.overrideDns,
|
||||
'override-rule': instance.overrideRule,
|
||||
'test-url': instance.testUrl,
|
||||
};
|
||||
|
||||
|
||||
@@ -238,6 +238,7 @@ mixin _$Profile {
|
||||
bool get autoUpdate => throw _privateConstructorUsedError;
|
||||
Map<String, String> get selectedMap => throw _privateConstructorUsedError;
|
||||
Set<String> get unfoldSet => throw _privateConstructorUsedError;
|
||||
OverrideData get overrideData => throw _privateConstructorUsedError;
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
bool get isUpdating => throw _privateConstructorUsedError;
|
||||
|
||||
@@ -266,9 +267,11 @@ abstract class $ProfileCopyWith<$Res> {
|
||||
bool autoUpdate,
|
||||
Map<String, String> selectedMap,
|
||||
Set<String> unfoldSet,
|
||||
OverrideData overrideData,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false) bool isUpdating});
|
||||
|
||||
$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;
|
||||
$OverrideDataCopyWith<$Res> get overrideData;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -296,6 +299,7 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
|
||||
Object? autoUpdate = null,
|
||||
Object? selectedMap = null,
|
||||
Object? unfoldSet = null,
|
||||
Object? overrideData = null,
|
||||
Object? isUpdating = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@@ -339,6 +343,10 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
|
||||
? _value.unfoldSet
|
||||
: unfoldSet // ignore: cast_nullable_to_non_nullable
|
||||
as Set<String>,
|
||||
overrideData: null == overrideData
|
||||
? _value.overrideData
|
||||
: overrideData // ignore: cast_nullable_to_non_nullable
|
||||
as OverrideData,
|
||||
isUpdating: null == isUpdating
|
||||
? _value.isUpdating
|
||||
: isUpdating // ignore: cast_nullable_to_non_nullable
|
||||
@@ -359,6 +367,16 @@ class _$ProfileCopyWithImpl<$Res, $Val extends Profile>
|
||||
return _then(_value.copyWith(subscriptionInfo: value) as $Val);
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of Profile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$OverrideDataCopyWith<$Res> get overrideData {
|
||||
return $OverrideDataCopyWith<$Res>(_value.overrideData, (value) {
|
||||
return _then(_value.copyWith(overrideData: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -379,10 +397,13 @@ abstract class _$$ProfileImplCopyWith<$Res> implements $ProfileCopyWith<$Res> {
|
||||
bool autoUpdate,
|
||||
Map<String, String> selectedMap,
|
||||
Set<String> unfoldSet,
|
||||
OverrideData overrideData,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false) bool isUpdating});
|
||||
|
||||
@override
|
||||
$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;
|
||||
@override
|
||||
$OverrideDataCopyWith<$Res> get overrideData;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@@ -408,6 +429,7 @@ class __$$ProfileImplCopyWithImpl<$Res>
|
||||
Object? autoUpdate = null,
|
||||
Object? selectedMap = null,
|
||||
Object? unfoldSet = null,
|
||||
Object? overrideData = null,
|
||||
Object? isUpdating = null,
|
||||
}) {
|
||||
return _then(_$ProfileImpl(
|
||||
@@ -451,6 +473,10 @@ class __$$ProfileImplCopyWithImpl<$Res>
|
||||
? _value._unfoldSet
|
||||
: unfoldSet // ignore: cast_nullable_to_non_nullable
|
||||
as Set<String>,
|
||||
overrideData: null == overrideData
|
||||
? _value.overrideData
|
||||
: overrideData // ignore: cast_nullable_to_non_nullable
|
||||
as OverrideData,
|
||||
isUpdating: null == isUpdating
|
||||
? _value.isUpdating
|
||||
: isUpdating // ignore: cast_nullable_to_non_nullable
|
||||
@@ -473,6 +499,7 @@ class _$ProfileImpl implements _Profile {
|
||||
this.autoUpdate = true,
|
||||
final Map<String, String> selectedMap = const {},
|
||||
final Set<String> unfoldSet = const {},
|
||||
this.overrideData = const OverrideData(),
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
this.isUpdating = false})
|
||||
: _selectedMap = selectedMap,
|
||||
@@ -517,13 +544,16 @@ class _$ProfileImpl implements _Profile {
|
||||
return EqualUnmodifiableSetView(_unfoldSet);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final OverrideData overrideData;
|
||||
@override
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
final bool isUpdating;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Profile(id: $id, label: $label, currentGroupName: $currentGroupName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, subscriptionInfo: $subscriptionInfo, autoUpdate: $autoUpdate, selectedMap: $selectedMap, unfoldSet: $unfoldSet, isUpdating: $isUpdating)';
|
||||
return 'Profile(id: $id, label: $label, currentGroupName: $currentGroupName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, subscriptionInfo: $subscriptionInfo, autoUpdate: $autoUpdate, selectedMap: $selectedMap, unfoldSet: $unfoldSet, overrideData: $overrideData, isUpdating: $isUpdating)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -548,6 +578,8 @@ class _$ProfileImpl implements _Profile {
|
||||
.equals(other._selectedMap, _selectedMap) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._unfoldSet, _unfoldSet) &&
|
||||
(identical(other.overrideData, overrideData) ||
|
||||
other.overrideData == overrideData) &&
|
||||
(identical(other.isUpdating, isUpdating) ||
|
||||
other.isUpdating == isUpdating));
|
||||
}
|
||||
@@ -566,6 +598,7 @@ class _$ProfileImpl implements _Profile {
|
||||
autoUpdate,
|
||||
const DeepCollectionEquality().hash(_selectedMap),
|
||||
const DeepCollectionEquality().hash(_unfoldSet),
|
||||
overrideData,
|
||||
isUpdating);
|
||||
|
||||
/// Create a copy of Profile
|
||||
@@ -596,6 +629,7 @@ abstract class _Profile implements Profile {
|
||||
final bool autoUpdate,
|
||||
final Map<String, String> selectedMap,
|
||||
final Set<String> unfoldSet,
|
||||
final OverrideData overrideData,
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
final bool isUpdating}) = _$ProfileImpl;
|
||||
|
||||
@@ -622,6 +656,8 @@ abstract class _Profile implements Profile {
|
||||
@override
|
||||
Set<String> get unfoldSet;
|
||||
@override
|
||||
OverrideData get overrideData;
|
||||
@override
|
||||
@JsonKey(includeToJson: false, includeFromJson: false)
|
||||
bool get isUpdating;
|
||||
|
||||
@@ -632,3 +668,398 @@ abstract class _Profile implements Profile {
|
||||
_$$ProfileImplCopyWith<_$ProfileImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
OverrideData _$OverrideDataFromJson(Map<String, dynamic> json) {
|
||||
return _OverrideData.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$OverrideData {
|
||||
bool get enable => throw _privateConstructorUsedError;
|
||||
OverrideRule get rule => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this OverrideData to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of OverrideData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$OverrideDataCopyWith<OverrideData> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $OverrideDataCopyWith<$Res> {
|
||||
factory $OverrideDataCopyWith(
|
||||
OverrideData value, $Res Function(OverrideData) then) =
|
||||
_$OverrideDataCopyWithImpl<$Res, OverrideData>;
|
||||
@useResult
|
||||
$Res call({bool enable, OverrideRule rule});
|
||||
|
||||
$OverrideRuleCopyWith<$Res> get rule;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$OverrideDataCopyWithImpl<$Res, $Val extends OverrideData>
|
||||
implements $OverrideDataCopyWith<$Res> {
|
||||
_$OverrideDataCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of OverrideData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? enable = null,
|
||||
Object? rule = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
enable: null == enable
|
||||
? _value.enable
|
||||
: enable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
rule: null == rule
|
||||
? _value.rule
|
||||
: rule // ignore: cast_nullable_to_non_nullable
|
||||
as OverrideRule,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
/// Create a copy of OverrideData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$OverrideRuleCopyWith<$Res> get rule {
|
||||
return $OverrideRuleCopyWith<$Res>(_value.rule, (value) {
|
||||
return _then(_value.copyWith(rule: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$OverrideDataImplCopyWith<$Res>
|
||||
implements $OverrideDataCopyWith<$Res> {
|
||||
factory _$$OverrideDataImplCopyWith(
|
||||
_$OverrideDataImpl value, $Res Function(_$OverrideDataImpl) then) =
|
||||
__$$OverrideDataImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool enable, OverrideRule rule});
|
||||
|
||||
@override
|
||||
$OverrideRuleCopyWith<$Res> get rule;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$OverrideDataImplCopyWithImpl<$Res>
|
||||
extends _$OverrideDataCopyWithImpl<$Res, _$OverrideDataImpl>
|
||||
implements _$$OverrideDataImplCopyWith<$Res> {
|
||||
__$$OverrideDataImplCopyWithImpl(
|
||||
_$OverrideDataImpl _value, $Res Function(_$OverrideDataImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of OverrideData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? enable = null,
|
||||
Object? rule = null,
|
||||
}) {
|
||||
return _then(_$OverrideDataImpl(
|
||||
enable: null == enable
|
||||
? _value.enable
|
||||
: enable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
rule: null == rule
|
||||
? _value.rule
|
||||
: rule // ignore: cast_nullable_to_non_nullable
|
||||
as OverrideRule,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$OverrideDataImpl implements _OverrideData {
|
||||
const _$OverrideDataImpl(
|
||||
{this.enable = false, this.rule = const OverrideRule()});
|
||||
|
||||
factory _$OverrideDataImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$OverrideDataImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool enable;
|
||||
@override
|
||||
@JsonKey()
|
||||
final OverrideRule rule;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'OverrideData(enable: $enable, rule: $rule)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$OverrideDataImpl &&
|
||||
(identical(other.enable, enable) || other.enable == enable) &&
|
||||
(identical(other.rule, rule) || other.rule == rule));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, enable, rule);
|
||||
|
||||
/// Create a copy of OverrideData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$OverrideDataImplCopyWith<_$OverrideDataImpl> get copyWith =>
|
||||
__$$OverrideDataImplCopyWithImpl<_$OverrideDataImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$OverrideDataImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _OverrideData implements OverrideData {
|
||||
const factory _OverrideData({final bool enable, final OverrideRule rule}) =
|
||||
_$OverrideDataImpl;
|
||||
|
||||
factory _OverrideData.fromJson(Map<String, dynamic> json) =
|
||||
_$OverrideDataImpl.fromJson;
|
||||
|
||||
@override
|
||||
bool get enable;
|
||||
@override
|
||||
OverrideRule get rule;
|
||||
|
||||
/// Create a copy of OverrideData
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$OverrideDataImplCopyWith<_$OverrideDataImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
OverrideRule _$OverrideRuleFromJson(Map<String, dynamic> json) {
|
||||
return _OverrideRule.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$OverrideRule {
|
||||
OverrideRuleType get type => throw _privateConstructorUsedError;
|
||||
List<Rule> get overrideRules => throw _privateConstructorUsedError;
|
||||
List<Rule> get addedRules => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this OverrideRule to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of OverrideRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$OverrideRuleCopyWith<OverrideRule> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $OverrideRuleCopyWith<$Res> {
|
||||
factory $OverrideRuleCopyWith(
|
||||
OverrideRule value, $Res Function(OverrideRule) then) =
|
||||
_$OverrideRuleCopyWithImpl<$Res, OverrideRule>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{OverrideRuleType type, List<Rule> overrideRules, List<Rule> addedRules});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$OverrideRuleCopyWithImpl<$Res, $Val extends OverrideRule>
|
||||
implements $OverrideRuleCopyWith<$Res> {
|
||||
_$OverrideRuleCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of OverrideRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? type = null,
|
||||
Object? overrideRules = null,
|
||||
Object? addedRules = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
type: null == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as OverrideRuleType,
|
||||
overrideRules: null == overrideRules
|
||||
? _value.overrideRules
|
||||
: overrideRules // ignore: cast_nullable_to_non_nullable
|
||||
as List<Rule>,
|
||||
addedRules: null == addedRules
|
||||
? _value.addedRules
|
||||
: addedRules // ignore: cast_nullable_to_non_nullable
|
||||
as List<Rule>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$OverrideRuleImplCopyWith<$Res>
|
||||
implements $OverrideRuleCopyWith<$Res> {
|
||||
factory _$$OverrideRuleImplCopyWith(
|
||||
_$OverrideRuleImpl value, $Res Function(_$OverrideRuleImpl) then) =
|
||||
__$$OverrideRuleImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{OverrideRuleType type, List<Rule> overrideRules, List<Rule> addedRules});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$OverrideRuleImplCopyWithImpl<$Res>
|
||||
extends _$OverrideRuleCopyWithImpl<$Res, _$OverrideRuleImpl>
|
||||
implements _$$OverrideRuleImplCopyWith<$Res> {
|
||||
__$$OverrideRuleImplCopyWithImpl(
|
||||
_$OverrideRuleImpl _value, $Res Function(_$OverrideRuleImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of OverrideRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? type = null,
|
||||
Object? overrideRules = null,
|
||||
Object? addedRules = null,
|
||||
}) {
|
||||
return _then(_$OverrideRuleImpl(
|
||||
type: null == type
|
||||
? _value.type
|
||||
: type // ignore: cast_nullable_to_non_nullable
|
||||
as OverrideRuleType,
|
||||
overrideRules: null == overrideRules
|
||||
? _value._overrideRules
|
||||
: overrideRules // ignore: cast_nullable_to_non_nullable
|
||||
as List<Rule>,
|
||||
addedRules: null == addedRules
|
||||
? _value._addedRules
|
||||
: addedRules // ignore: cast_nullable_to_non_nullable
|
||||
as List<Rule>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$OverrideRuleImpl implements _OverrideRule {
|
||||
const _$OverrideRuleImpl(
|
||||
{this.type = OverrideRuleType.added,
|
||||
final List<Rule> overrideRules = const [],
|
||||
final List<Rule> addedRules = const []})
|
||||
: _overrideRules = overrideRules,
|
||||
_addedRules = addedRules;
|
||||
|
||||
factory _$OverrideRuleImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$OverrideRuleImplFromJson(json);
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final OverrideRuleType type;
|
||||
final List<Rule> _overrideRules;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Rule> get overrideRules {
|
||||
if (_overrideRules is EqualUnmodifiableListView) return _overrideRules;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_overrideRules);
|
||||
}
|
||||
|
||||
final List<Rule> _addedRules;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Rule> get addedRules {
|
||||
if (_addedRules is EqualUnmodifiableListView) return _addedRules;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_addedRules);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'OverrideRule(type: $type, overrideRules: $overrideRules, addedRules: $addedRules)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$OverrideRuleImpl &&
|
||||
(identical(other.type, type) || other.type == type) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._overrideRules, _overrideRules) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._addedRules, _addedRules));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
type,
|
||||
const DeepCollectionEquality().hash(_overrideRules),
|
||||
const DeepCollectionEquality().hash(_addedRules));
|
||||
|
||||
/// Create a copy of OverrideRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$OverrideRuleImplCopyWith<_$OverrideRuleImpl> get copyWith =>
|
||||
__$$OverrideRuleImplCopyWithImpl<_$OverrideRuleImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$OverrideRuleImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _OverrideRule implements OverrideRule {
|
||||
const factory _OverrideRule(
|
||||
{final OverrideRuleType type,
|
||||
final List<Rule> overrideRules,
|
||||
final List<Rule> addedRules}) = _$OverrideRuleImpl;
|
||||
|
||||
factory _OverrideRule.fromJson(Map<String, dynamic> json) =
|
||||
_$OverrideRuleImpl.fromJson;
|
||||
|
||||
@override
|
||||
OverrideRuleType get type;
|
||||
@override
|
||||
List<Rule> get overrideRules;
|
||||
@override
|
||||
List<Rule> get addedRules;
|
||||
|
||||
/// Create a copy of OverrideRule
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$OverrideRuleImplCopyWith<_$OverrideRuleImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user