Add android separates the core process
Support core status check and force restart Optimize proxies page and access page Update flutter and pub dependencies Update go version Optimize more details
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/l10n/l10n.dart';
|
||||
import 'package:fl_clash/manager/hotkey_manager.dart';
|
||||
import 'package:fl_clash/manager/manager.dart';
|
||||
@@ -17,9 +17,7 @@ import 'controller.dart';
|
||||
import 'pages/pages.dart';
|
||||
|
||||
class Application extends ConsumerStatefulWidget {
|
||||
const Application({
|
||||
super.key,
|
||||
});
|
||||
const Application({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<Application> createState() => ApplicationState();
|
||||
@@ -48,7 +46,6 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_autoUpdateGroupTask();
|
||||
_autoUpdateProfilesTask();
|
||||
globalState.appController = AppController(context, ref);
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
@@ -62,15 +59,6 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
});
|
||||
}
|
||||
|
||||
void _autoUpdateGroupTask() {
|
||||
_autoUpdateGroupTaskTimer = Timer(const Duration(milliseconds: 20000), () {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
globalState.appController.updateGroupsDebounce();
|
||||
_autoUpdateGroupTask();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _autoUpdateProfilesTask() {
|
||||
_autoUpdateProfilesTaskTimer = Timer(const Duration(minutes: 20), () async {
|
||||
await globalState.appController.autoUpdateProfiles();
|
||||
@@ -78,32 +66,24 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildPlatformState(Widget child) {
|
||||
Widget _buildPlatformState({required Widget child}) {
|
||||
if (system.isDesktop) {
|
||||
return WindowManager(
|
||||
child: TrayManager(
|
||||
child: HotKeyManager(
|
||||
child: ProxyManager(
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: HotKeyManager(child: ProxyManager(child: child)),
|
||||
),
|
||||
);
|
||||
}
|
||||
return AndroidManager(
|
||||
child: TileManager(
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
return AndroidManager(child: TileManager(child: child));
|
||||
}
|
||||
|
||||
Widget _buildState(Widget child) {
|
||||
Widget _buildState({required Widget child}) {
|
||||
return AppStateManager(
|
||||
child: ClashManager(
|
||||
child: CoreManager(
|
||||
child: ConnectivityManager(
|
||||
onConnectivityChanged: (results) async {
|
||||
if (!results.contains(ConnectivityResult.vpn)) {
|
||||
clashCore.closeConnections();
|
||||
coreController.closeConnections();
|
||||
}
|
||||
globalState.appController.updateLocalIp();
|
||||
globalState.appController.addCheckIpNumDebounce();
|
||||
@@ -114,81 +94,68 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPlatformApp(Widget child) {
|
||||
Widget _buildPlatformApp({required Widget child}) {
|
||||
if (system.isDesktop) {
|
||||
return WindowHeaderContainer(
|
||||
child: child,
|
||||
);
|
||||
return WindowHeaderContainer(child: child);
|
||||
}
|
||||
return VpnManager(
|
||||
child: child,
|
||||
);
|
||||
return VpnManager(child: child);
|
||||
}
|
||||
|
||||
Widget _buildApp(Widget child) {
|
||||
return MessageManager(
|
||||
child: ThemeManager(
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
Widget _buildApp({required Widget child}) {
|
||||
return MessageManager(child: ThemeManager(child: child));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
return _buildPlatformState(
|
||||
_buildState(
|
||||
Consumer(
|
||||
builder: (_, ref, child) {
|
||||
final locale =
|
||||
ref.watch(appSettingProvider.select((state) => state.locale));
|
||||
final themeProps = ref.watch(themeSettingProvider);
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
navigatorKey: globalState.navigatorKey,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate
|
||||
],
|
||||
builder: (_, child) {
|
||||
return AppEnvManager(
|
||||
child: _buildApp(
|
||||
AppSidebarContainer(
|
||||
child: _buildPlatformApp(
|
||||
child!,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
scrollBehavior: BaseScrollBehavior(),
|
||||
title: appName,
|
||||
locale: utils.getLocaleForString(locale),
|
||||
supportedLocales: AppLocalizations.delegate.supportedLocales,
|
||||
themeMode: themeProps.themeMode,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: _pageTransitionsTheme,
|
||||
colorScheme: _getAppColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primaryColor: themeProps.primaryColor,
|
||||
return Consumer(
|
||||
builder: (_, ref, child) {
|
||||
final locale = ref.watch(
|
||||
appSettingProvider.select((state) => state.locale),
|
||||
);
|
||||
final themeProps = ref.watch(themeSettingProvider);
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
navigatorKey: globalState.navigatorKey,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
builder: (_, child) {
|
||||
return AppEnvManager(
|
||||
child: _buildApp(
|
||||
child: _buildPlatformState(
|
||||
child: _buildState(child: _buildPlatformApp(child: child!)),
|
||||
),
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: _pageTransitionsTheme,
|
||||
colorScheme: _getAppColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: themeProps.primaryColor,
|
||||
).toPureBlack(themeProps.pureBlack),
|
||||
),
|
||||
home: child!,
|
||||
);
|
||||
},
|
||||
child: const HomePage(),
|
||||
),
|
||||
),
|
||||
scrollBehavior: BaseScrollBehavior(),
|
||||
title: appName,
|
||||
locale: utils.getLocaleForString(locale),
|
||||
supportedLocales: AppLocalizations.delegate.supportedLocales,
|
||||
themeMode: themeProps.themeMode,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: _pageTransitionsTheme,
|
||||
colorScheme: _getAppColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primaryColor: themeProps.primaryColor,
|
||||
),
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
useMaterial3: true,
|
||||
pageTransitionsTheme: _pageTransitionsTheme,
|
||||
colorScheme: _getAppColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: themeProps.primaryColor,
|
||||
).toPureBlack(themeProps.pureBlack),
|
||||
),
|
||||
home: child!,
|
||||
);
|
||||
},
|
||||
child: const HomePage(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -197,7 +164,7 @@ class ApplicationState extends ConsumerState<Application> {
|
||||
linkManager.destroy();
|
||||
_autoUpdateGroupTaskTimer?.cancel();
|
||||
_autoUpdateProfilesTaskTimer?.cancel();
|
||||
await clashCore.destroy();
|
||||
await coreController.destroy();
|
||||
await globalState.appController.savePreferences();
|
||||
await globalState.appController.handleExit();
|
||||
super.dispose();
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/clash/interface.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/state.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
class ClashCore {
|
||||
static ClashCore? _instance;
|
||||
late ClashHandlerInterface clashInterface;
|
||||
|
||||
ClashCore._internal() {
|
||||
if (system.isAndroid) {
|
||||
clashInterface = clashLib!;
|
||||
} else {
|
||||
clashInterface = clashService!;
|
||||
}
|
||||
}
|
||||
|
||||
factory ClashCore() {
|
||||
_instance ??= ClashCore._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<bool> preload() {
|
||||
return clashInterface.preload();
|
||||
}
|
||||
|
||||
static Future<void> initGeo() async {
|
||||
final homePath = await appPath.homeDirPath;
|
||||
final homeDir = Directory(homePath);
|
||||
final isExists = await homeDir.exists();
|
||||
if (!isExists) {
|
||||
await homeDir.create(recursive: true);
|
||||
}
|
||||
const geoFileNameList = [
|
||||
mmdbFileName,
|
||||
geoIpFileName,
|
||||
geoSiteFileName,
|
||||
asnFileName,
|
||||
];
|
||||
try {
|
||||
for (final geoFileName in geoFileNameList) {
|
||||
final geoFile = File(
|
||||
join(homePath, geoFileName),
|
||||
);
|
||||
final isExists = await geoFile.exists();
|
||||
if (isExists) {
|
||||
continue;
|
||||
}
|
||||
final data = await rootBundle.load('assets/data/$geoFileName');
|
||||
List<int> bytes = data.buffer.asUint8List();
|
||||
await geoFile.writeAsBytes(bytes, flush: true);
|
||||
}
|
||||
} catch (e) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> init() async {
|
||||
await initGeo();
|
||||
if (globalState.config.appSetting.openLogs) {
|
||||
clashCore.startLog();
|
||||
} else {
|
||||
clashCore.stopLog();
|
||||
}
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
return await clashInterface.init(
|
||||
InitParams(
|
||||
homeDir: homeDirPath,
|
||||
version: globalState.appState.version,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> setState(CoreState state) async {
|
||||
return await clashInterface.setState(state);
|
||||
}
|
||||
|
||||
Future<void> shutdown() async {
|
||||
await clashInterface.shutdown();
|
||||
}
|
||||
|
||||
FutureOr<bool> get isInit => clashInterface.isInit;
|
||||
|
||||
FutureOr<String> validateConfig(String data) {
|
||||
return clashInterface.validateConfig(data);
|
||||
}
|
||||
|
||||
Future<String> updateConfig(UpdateParams updateParams) async {
|
||||
return await clashInterface.updateConfig(updateParams);
|
||||
}
|
||||
|
||||
Future<String> setupConfig(SetupParams setupParams) async {
|
||||
return await clashInterface.setupConfig(setupParams);
|
||||
}
|
||||
|
||||
Future<List<Group>> getProxiesGroups() async {
|
||||
final proxies = await clashInterface.getProxies();
|
||||
if (proxies.isEmpty) return [];
|
||||
final groupNames = [
|
||||
UsedProxy.GLOBAL.name,
|
||||
...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) {
|
||||
final proxy = proxies[e] ?? {};
|
||||
return GroupTypeExtension.valueList.contains(proxy['type']);
|
||||
})
|
||||
];
|
||||
final groupsRaw = groupNames.map((groupName) {
|
||||
final group = proxies[groupName];
|
||||
group['all'] = ((group['all'] ?? []) as List)
|
||||
.map(
|
||||
(name) => proxies[name],
|
||||
)
|
||||
.where((proxy) => proxy != null)
|
||||
.toList();
|
||||
return group;
|
||||
}).toList();
|
||||
return groupsRaw
|
||||
.map(
|
||||
(e) => Group.fromJson(e),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) async {
|
||||
return await clashInterface.changeProxy(changeProxyParams);
|
||||
}
|
||||
|
||||
Future<List<TrackerInfo>> getConnections() async {
|
||||
final res = await clashInterface.getConnections();
|
||||
final connectionsData = json.decode(res) as Map;
|
||||
final connectionsRaw = connectionsData['connections'] as List? ?? [];
|
||||
return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
void closeConnection(String id) {
|
||||
clashInterface.closeConnection(id);
|
||||
}
|
||||
|
||||
void closeConnections() {
|
||||
clashInterface.closeConnections();
|
||||
}
|
||||
|
||||
void resetConnections() {
|
||||
clashInterface.resetConnections();
|
||||
}
|
||||
|
||||
Future<List<ExternalProvider>> getExternalProviders() async {
|
||||
final externalProvidersRawString =
|
||||
await clashInterface.getExternalProviders();
|
||||
if (externalProvidersRawString.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
return Isolate.run<List<ExternalProvider>>(
|
||||
() {
|
||||
final externalProviders =
|
||||
(json.decode(externalProvidersRawString) as List<dynamic>)
|
||||
.map(
|
||||
(item) => ExternalProvider.fromJson(item),
|
||||
)
|
||||
.toList();
|
||||
return externalProviders;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<ExternalProvider?> getExternalProvider(
|
||||
String externalProviderName) async {
|
||||
final externalProvidersRawString =
|
||||
await clashInterface.getExternalProvider(externalProviderName);
|
||||
if (externalProvidersRawString.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
if (externalProvidersRawString.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return ExternalProvider.fromJson(json.decode(externalProvidersRawString));
|
||||
}
|
||||
|
||||
Future<String> updateGeoData(UpdateGeoDataParams params) {
|
||||
return clashInterface.updateGeoData(params);
|
||||
}
|
||||
|
||||
Future<String> sideLoadExternalProvider({
|
||||
required String providerName,
|
||||
required String data,
|
||||
}) {
|
||||
return clashInterface.sideLoadExternalProvider(
|
||||
providerName: providerName, data: data);
|
||||
}
|
||||
|
||||
Future<String> updateExternalProvider({
|
||||
required String providerName,
|
||||
}) async {
|
||||
return clashInterface.updateExternalProvider(providerName);
|
||||
}
|
||||
|
||||
Future<void> startListener() async {
|
||||
await clashInterface.startListener();
|
||||
}
|
||||
|
||||
Future<void> stopListener() async {
|
||||
await clashInterface.stopListener();
|
||||
}
|
||||
|
||||
Future<Delay> getDelay(String url, String proxyName) async {
|
||||
final data = await clashInterface.asyncTestDelay(url, proxyName);
|
||||
return Delay.fromJson(json.decode(data));
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getConfig(String id) async {
|
||||
final profilePath = await appPath.getProfilePath(id);
|
||||
final res = await clashInterface.getConfig(profilePath);
|
||||
if (res.isSuccess) {
|
||||
return res.data as Map<String, dynamic>;
|
||||
} else {
|
||||
throw res.message;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Traffic> getTraffic() async {
|
||||
final trafficString = await clashInterface.getTraffic();
|
||||
if (trafficString.isEmpty) {
|
||||
return Traffic();
|
||||
}
|
||||
return Traffic.fromMap(json.decode(trafficString));
|
||||
}
|
||||
|
||||
Future<IpInfo?> getCountryCode(String ip) async {
|
||||
final countryCode = await clashInterface.getCountryCode(ip);
|
||||
if (countryCode.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return IpInfo(
|
||||
ip: ip,
|
||||
countryCode: countryCode,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Traffic> getTotalTraffic() async {
|
||||
final totalTrafficString = await clashInterface.getTotalTraffic();
|
||||
if (totalTrafficString.isEmpty) {
|
||||
return Traffic();
|
||||
}
|
||||
return Traffic.fromMap(json.decode(totalTrafficString));
|
||||
}
|
||||
|
||||
Future<int> getMemory() async {
|
||||
final value = await clashInterface.getMemory();
|
||||
if (value.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
return int.parse(value);
|
||||
}
|
||||
|
||||
void resetTraffic() {
|
||||
clashInterface.resetTraffic();
|
||||
}
|
||||
|
||||
void startLog() {
|
||||
clashInterface.startLog();
|
||||
}
|
||||
|
||||
void stopLog() {
|
||||
clashInterface.stopLog();
|
||||
}
|
||||
|
||||
Future<void> requestGc() async {
|
||||
await clashInterface.forceGc();
|
||||
}
|
||||
|
||||
Future<void> destroy() async {
|
||||
await clashInterface.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
final clashCore = ClashCore();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,421 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/clash/message.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
|
||||
mixin ClashInterface {
|
||||
Future<bool> init(InitParams params);
|
||||
|
||||
Future<bool> preload();
|
||||
|
||||
Future<bool> shutdown();
|
||||
|
||||
Future<bool> get isInit;
|
||||
|
||||
Future<bool> forceGc();
|
||||
|
||||
FutureOr<String> validateConfig(String data);
|
||||
|
||||
FutureOr<Result> getConfig(String path);
|
||||
|
||||
Future<String> asyncTestDelay(String url, String proxyName);
|
||||
|
||||
FutureOr<String> updateConfig(UpdateParams updateParams);
|
||||
|
||||
FutureOr<String> setupConfig(SetupParams setupParams);
|
||||
|
||||
FutureOr<Map> getProxies();
|
||||
|
||||
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams);
|
||||
|
||||
Future<bool> startListener();
|
||||
|
||||
Future<bool> stopListener();
|
||||
|
||||
FutureOr<String> getExternalProviders();
|
||||
|
||||
FutureOr<String>? getExternalProvider(String externalProviderName);
|
||||
|
||||
Future<String> updateGeoData(UpdateGeoDataParams params);
|
||||
|
||||
Future<String> sideLoadExternalProvider({
|
||||
required String providerName,
|
||||
required String data,
|
||||
});
|
||||
|
||||
Future<String> updateExternalProvider(String providerName);
|
||||
|
||||
FutureOr<String> getTraffic();
|
||||
|
||||
FutureOr<String> getTotalTraffic();
|
||||
|
||||
FutureOr<String> getCountryCode(String ip);
|
||||
|
||||
FutureOr<String> getMemory();
|
||||
|
||||
FutureOr<void> resetTraffic();
|
||||
|
||||
FutureOr<void> startLog();
|
||||
|
||||
FutureOr<void> stopLog();
|
||||
|
||||
Future<bool> crash();
|
||||
|
||||
FutureOr<String> getConnections();
|
||||
|
||||
FutureOr<bool> closeConnection(String id);
|
||||
|
||||
FutureOr<bool> closeConnections();
|
||||
|
||||
FutureOr<bool> resetConnections();
|
||||
|
||||
Future<bool> setState(CoreState state);
|
||||
}
|
||||
|
||||
mixin AndroidClashInterface {
|
||||
Future<bool> updateDns(String value);
|
||||
|
||||
Future<AndroidVpnOptions?> getAndroidVpnOptions();
|
||||
|
||||
Future<String> getCurrentProfileName();
|
||||
|
||||
Future<DateTime?> getRunTime();
|
||||
}
|
||||
|
||||
abstract class ClashHandlerInterface with ClashInterface {
|
||||
Map<String, Completer> callbackCompleterMap = {};
|
||||
|
||||
Future<void> handleResult(ActionResult result) async {
|
||||
final completer = callbackCompleterMap[result.id];
|
||||
try {
|
||||
switch (result.method) {
|
||||
case ActionMethod.message:
|
||||
clashMessage.controller.add(result.data);
|
||||
completer?.complete(true);
|
||||
return;
|
||||
case ActionMethod.getConfig:
|
||||
completer?.complete(result.toResult);
|
||||
return;
|
||||
default:
|
||||
completer?.complete(result.data);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
commonPrint.log('${result.id} error $e');
|
||||
}
|
||||
}
|
||||
|
||||
void sendMessage(String message);
|
||||
|
||||
FutureOr<void> reStart();
|
||||
|
||||
FutureOr<bool> destroy();
|
||||
|
||||
Future<T> invoke<T>({
|
||||
required ActionMethod method,
|
||||
dynamic data,
|
||||
Duration? timeout,
|
||||
FutureOr<T> Function()? onTimeout,
|
||||
T? defaultValue,
|
||||
}) async {
|
||||
final id = '${method.name}#${utils.id}';
|
||||
|
||||
callbackCompleterMap[id] = Completer<T>();
|
||||
|
||||
dynamic mDefaultValue = defaultValue;
|
||||
if (mDefaultValue == null) {
|
||||
if (T == String) {
|
||||
mDefaultValue = '';
|
||||
} else if (T == bool) {
|
||||
mDefaultValue = false;
|
||||
} else if (T == Map) {
|
||||
mDefaultValue = {};
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(
|
||||
json.encode(
|
||||
Action(
|
||||
id: id,
|
||||
method: method,
|
||||
data: data,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return (callbackCompleterMap[id] as Completer<T>).safeFuture(
|
||||
timeout: timeout,
|
||||
onLast: () {
|
||||
callbackCompleterMap.remove(id);
|
||||
},
|
||||
onTimeout: onTimeout ??
|
||||
() {
|
||||
return mDefaultValue;
|
||||
},
|
||||
functionName: id,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> init(InitParams params) {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.initClash,
|
||||
data: json.encode(params),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> setState(CoreState state) {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.setState,
|
||||
data: json.encode(state),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
shutdown() async {
|
||||
return await invoke<bool>(
|
||||
method: ActionMethod.shutdown,
|
||||
timeout: Duration(seconds: 1),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> get isInit {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.getIsInit,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> forceGc() {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.forceGc,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> validateConfig(String data) {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.validateConfig,
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> updateConfig(UpdateParams updateParams) async {
|
||||
return await invoke<String>(
|
||||
method: ActionMethod.updateConfig,
|
||||
data: json.encode(updateParams),
|
||||
timeout: Duration(minutes: 2),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result> getConfig(String path) async {
|
||||
final res = await invoke<Result>(
|
||||
method: ActionMethod.getConfig,
|
||||
data: path,
|
||||
timeout: Duration(minutes: 2),
|
||||
defaultValue: Result.success({}),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> setupConfig(SetupParams setupParams) async {
|
||||
final data = await Isolate.run(() => json.encode(setupParams));
|
||||
return await invoke<String>(
|
||||
method: ActionMethod.setupConfig,
|
||||
data: data,
|
||||
timeout: Duration(minutes: 2),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> crash() {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.crash,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map> getProxies() {
|
||||
return invoke<Map>(
|
||||
method: ActionMethod.getProxies,
|
||||
timeout: Duration(seconds: 5),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.changeProxy,
|
||||
data: json.encode(changeProxyParams),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> getExternalProviders() {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.getExternalProviders,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> getExternalProvider(String externalProviderName) {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.getExternalProvider,
|
||||
data: externalProviderName,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> updateGeoData(UpdateGeoDataParams params) {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.updateGeoData,
|
||||
data: json.encode(params),
|
||||
timeout: Duration(minutes: 1));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> sideLoadExternalProvider({
|
||||
required String providerName,
|
||||
required String data,
|
||||
}) {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.sideLoadExternalProvider,
|
||||
data: json.encode({
|
||||
'providerName': providerName,
|
||||
'data': data,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> updateExternalProvider(String providerName) {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.updateExternalProvider,
|
||||
data: providerName,
|
||||
timeout: Duration(minutes: 1),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> getConnections() {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.getConnections,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> closeConnections() {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.closeConnections,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> resetConnections() {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.resetConnections,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> closeConnection(String id) {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.closeConnection,
|
||||
data: id,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> getTotalTraffic() {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.getTotalTraffic,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> getTraffic() {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.getTraffic,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
resetTraffic() {
|
||||
invoke(method: ActionMethod.resetTraffic);
|
||||
}
|
||||
|
||||
@override
|
||||
startLog() {
|
||||
invoke(method: ActionMethod.startLog);
|
||||
}
|
||||
|
||||
@override
|
||||
stopLog() {
|
||||
invoke<bool>(
|
||||
method: ActionMethod.stopLog,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> startListener() {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.startListener,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
stopListener() {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.stopListener,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> asyncTestDelay(String url, String proxyName) {
|
||||
final delayParams = {
|
||||
'proxy-name': proxyName,
|
||||
'timeout': httpTimeoutDuration.inMilliseconds,
|
||||
'test-url': url,
|
||||
};
|
||||
return invoke<String>(
|
||||
method: ActionMethod.asyncTestDelay,
|
||||
data: json.encode(delayParams),
|
||||
timeout: Duration(
|
||||
milliseconds: 6000,
|
||||
),
|
||||
onTimeout: () {
|
||||
return json.encode(
|
||||
Delay(
|
||||
name: proxyName,
|
||||
value: -1,
|
||||
url: url,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> getCountryCode(String ip) {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.getCountryCode,
|
||||
data: ip,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<String> getMemory() {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.getMemory,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:isolate';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:ffi/ffi.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/plugins/service.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
|
||||
import 'generated/clash_ffi.dart';
|
||||
import 'interface.dart';
|
||||
|
||||
class ClashLib extends ClashHandlerInterface with AndroidClashInterface {
|
||||
static ClashLib? _instance;
|
||||
Completer<bool> _canSendCompleter = Completer();
|
||||
SendPort? sendPort;
|
||||
final receiverPort = ReceivePort();
|
||||
|
||||
ClashLib._internal() {
|
||||
_initService();
|
||||
}
|
||||
|
||||
@override
|
||||
preload() {
|
||||
return _canSendCompleter.future;
|
||||
}
|
||||
|
||||
Future<void> _initService() async {
|
||||
await service?.destroy();
|
||||
_registerMainPort(receiverPort.sendPort);
|
||||
receiverPort.listen((message) {
|
||||
if (message is SendPort) {
|
||||
if (_canSendCompleter.isCompleted) {
|
||||
sendPort = null;
|
||||
_canSendCompleter = Completer();
|
||||
}
|
||||
sendPort = message;
|
||||
_canSendCompleter.complete(true);
|
||||
} else {
|
||||
handleResult(
|
||||
ActionResult.fromJson(json.decode(
|
||||
message,
|
||||
)),
|
||||
);
|
||||
}
|
||||
});
|
||||
await service?.init();
|
||||
}
|
||||
|
||||
void _registerMainPort(SendPort sendPort) {
|
||||
IsolateNameServer.removePortNameMapping(mainIsolate);
|
||||
IsolateNameServer.registerPortWithName(sendPort, mainIsolate);
|
||||
}
|
||||
|
||||
factory ClashLib() {
|
||||
_instance ??= ClashLib._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
destroy() async {
|
||||
await service?.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
reStart() {
|
||||
_initService();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> shutdown() async {
|
||||
await super.shutdown();
|
||||
destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
sendMessage(String message) async {
|
||||
await _canSendCompleter.future;
|
||||
sendPort?.send(message);
|
||||
}
|
||||
|
||||
// @override
|
||||
// Future<bool> stopTun() {
|
||||
// return invoke<bool>(
|
||||
// method: ActionMethod.stopTun,
|
||||
// );
|
||||
// }
|
||||
|
||||
@override
|
||||
Future<AndroidVpnOptions?> getAndroidVpnOptions() async {
|
||||
final res = await invoke<String>(
|
||||
method: ActionMethod.getAndroidVpnOptions,
|
||||
);
|
||||
if (res.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return AndroidVpnOptions.fromJson(json.decode(res));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> updateDns(String value) {
|
||||
return invoke<bool>(
|
||||
method: ActionMethod.updateDns,
|
||||
data: value,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DateTime?> getRunTime() async {
|
||||
final runTimeString = await invoke<String>(
|
||||
method: ActionMethod.getRunTime,
|
||||
);
|
||||
if (runTimeString.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getCurrentProfileName() {
|
||||
return invoke<String>(
|
||||
method: ActionMethod.getCurrentProfileName,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ClashLibHandler {
|
||||
static ClashLibHandler? _instance;
|
||||
|
||||
late final ClashFFI clashFFI;
|
||||
|
||||
late final DynamicLibrary lib;
|
||||
|
||||
ClashLibHandler._internal() {
|
||||
lib = DynamicLibrary.open('libclash.so');
|
||||
clashFFI = ClashFFI(lib);
|
||||
clashFFI.initNativeApiBridge(
|
||||
NativeApi.initializeApiDLData,
|
||||
);
|
||||
}
|
||||
|
||||
factory ClashLibHandler() {
|
||||
_instance ??= ClashLibHandler._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<String> invokeAction(String actionParams) {
|
||||
final completer = Completer<String>();
|
||||
final receiver = ReceivePort();
|
||||
receiver.listen((message) {
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete(message);
|
||||
receiver.close();
|
||||
}
|
||||
});
|
||||
final actionParamsChar = actionParams.toNativeUtf8().cast<Char>();
|
||||
clashFFI.invokeAction(
|
||||
actionParamsChar,
|
||||
receiver.sendPort.nativePort,
|
||||
);
|
||||
malloc.free(actionParamsChar);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
void attachMessagePort(int messagePort) {
|
||||
clashFFI.attachMessagePort(
|
||||
messagePort,
|
||||
);
|
||||
}
|
||||
|
||||
void updateDns(String dns) {
|
||||
final dnsChar = dns.toNativeUtf8().cast<Char>();
|
||||
clashFFI.updateDns(dnsChar);
|
||||
malloc.free(dnsChar);
|
||||
}
|
||||
|
||||
void setState(CoreState state) {
|
||||
final stateChar = json.encode(state).toNativeUtf8().cast<Char>();
|
||||
clashFFI.setState(stateChar);
|
||||
malloc.free(stateChar);
|
||||
}
|
||||
|
||||
String getCurrentProfileName() {
|
||||
final currentProfileRaw = clashFFI.getCurrentProfileName();
|
||||
final currentProfile = currentProfileRaw.cast<Utf8>().toDartString();
|
||||
clashFFI.freeCString(currentProfileRaw);
|
||||
return currentProfile;
|
||||
}
|
||||
|
||||
AndroidVpnOptions getAndroidVpnOptions() {
|
||||
final vpnOptionsRaw = clashFFI.getAndroidVpnOptions();
|
||||
final vpnOptions = json.decode(vpnOptionsRaw.cast<Utf8>().toDartString());
|
||||
clashFFI.freeCString(vpnOptionsRaw);
|
||||
return AndroidVpnOptions.fromJson(vpnOptions);
|
||||
}
|
||||
|
||||
Traffic getTraffic() {
|
||||
final trafficRaw = clashFFI.getTraffic();
|
||||
final trafficString = trafficRaw.cast<Utf8>().toDartString();
|
||||
clashFFI.freeCString(trafficRaw);
|
||||
if (trafficString.isEmpty) {
|
||||
return Traffic();
|
||||
}
|
||||
return Traffic.fromMap(json.decode(trafficString));
|
||||
}
|
||||
|
||||
Traffic getTotalTraffic(bool value) {
|
||||
final trafficRaw = clashFFI.getTotalTraffic();
|
||||
final trafficString = trafficRaw.cast<Utf8>().toDartString();
|
||||
clashFFI.freeCString(trafficRaw);
|
||||
if (trafficString.isEmpty) {
|
||||
return Traffic();
|
||||
}
|
||||
return Traffic.fromMap(json.decode(trafficString));
|
||||
}
|
||||
|
||||
Future<bool> startListener() async {
|
||||
clashFFI.startListener();
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<bool> stopListener() async {
|
||||
clashFFI.stopListener();
|
||||
return true;
|
||||
}
|
||||
|
||||
DateTime? getRunTime() {
|
||||
final runTimeRaw = clashFFI.getRunTime();
|
||||
final runTimeString = runTimeRaw.cast<Utf8>().toDartString();
|
||||
if (runTimeString.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getConfig(String id) async {
|
||||
final path = await appPath.getProfilePath(id);
|
||||
final pathChar = path.toNativeUtf8().cast<Char>();
|
||||
final configRaw = clashFFI.getConfig(pathChar);
|
||||
final configString = configRaw.cast<Utf8>().toDartString();
|
||||
if (configString.isEmpty) {
|
||||
return {};
|
||||
}
|
||||
final config = json.decode(configString);
|
||||
malloc.free(pathChar);
|
||||
clashFFI.freeCString(configRaw);
|
||||
return config;
|
||||
}
|
||||
|
||||
Future<String> quickStart(
|
||||
InitParams initParams,
|
||||
SetupParams setupParams,
|
||||
CoreState state,
|
||||
) {
|
||||
final completer = Completer<String>();
|
||||
final receiver = ReceivePort();
|
||||
receiver.listen((message) {
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete(message);
|
||||
receiver.close();
|
||||
}
|
||||
});
|
||||
final params = json.encode(setupParams);
|
||||
final initValue = json.encode(initParams);
|
||||
final stateParams = json.encode(state);
|
||||
final initParamsChar = initValue.toNativeUtf8().cast<Char>();
|
||||
final paramsChar = params.toNativeUtf8().cast<Char>();
|
||||
final stateParamsChar = stateParams.toNativeUtf8().cast<Char>();
|
||||
clashFFI.quickStart(
|
||||
initParamsChar,
|
||||
paramsChar,
|
||||
stateParamsChar,
|
||||
receiver.sendPort.nativePort,
|
||||
);
|
||||
malloc.free(initParamsChar);
|
||||
malloc.free(paramsChar);
|
||||
malloc.free(stateParamsChar);
|
||||
return completer.future;
|
||||
}
|
||||
}
|
||||
|
||||
ClashLib? get clashLib =>
|
||||
system.isAndroid && !globalState.isService ? ClashLib() : null;
|
||||
|
||||
ClashLibHandler? get clashLibHandler =>
|
||||
system.isAndroid && globalState.isService ? ClashLibHandler() : null;
|
||||
@@ -1,55 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class ClashMessage {
|
||||
final controller = StreamController<Map<String, Object?>>();
|
||||
|
||||
ClashMessage._() {
|
||||
controller.stream.listen(
|
||||
(message) {
|
||||
if (message.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final m = AppMessage.fromJson(message);
|
||||
for (final AppMessageListener listener in _listeners) {
|
||||
switch (m.type) {
|
||||
case AppMessageType.log:
|
||||
listener.onLog(Log.fromJson(m.data));
|
||||
break;
|
||||
case AppMessageType.delay:
|
||||
listener.onDelay(Delay.fromJson(m.data));
|
||||
break;
|
||||
case AppMessageType.request:
|
||||
listener.onRequest(TrackerInfo.fromJson(m.data));
|
||||
break;
|
||||
case AppMessageType.loaded:
|
||||
listener.onLoaded(m.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static final ClashMessage instance = ClashMessage._();
|
||||
|
||||
final ObserverList<AppMessageListener> _listeners =
|
||||
ObserverList<AppMessageListener>();
|
||||
|
||||
bool get hasListeners {
|
||||
return _listeners.isNotEmpty;
|
||||
}
|
||||
|
||||
void addListener(AppMessageListener listener) {
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
void removeListener(AppMessageListener listener) {
|
||||
_listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
final clashMessage = ClashMessage.instance;
|
||||
@@ -1,160 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/clash/interface.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/models/core.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
|
||||
class ClashService extends ClashHandlerInterface {
|
||||
static ClashService? _instance;
|
||||
|
||||
Completer<ServerSocket> serverCompleter = Completer();
|
||||
|
||||
Completer<Socket> socketCompleter = Completer();
|
||||
|
||||
bool isStarting = false;
|
||||
|
||||
Process? process;
|
||||
|
||||
factory ClashService() {
|
||||
_instance ??= ClashService._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
ClashService._internal() {
|
||||
_initServer();
|
||||
reStart();
|
||||
}
|
||||
|
||||
Future<void> _initServer() async {
|
||||
runZonedGuarded(() async {
|
||||
final address = !system.isWindows
|
||||
? InternetAddress(
|
||||
unixSocketPath,
|
||||
type: InternetAddressType.unix,
|
||||
)
|
||||
: InternetAddress(
|
||||
localhost,
|
||||
type: InternetAddressType.IPv4,
|
||||
);
|
||||
await _deleteSocketFile();
|
||||
final server = await ServerSocket.bind(
|
||||
address,
|
||||
0,
|
||||
shared: true,
|
||||
);
|
||||
serverCompleter.complete(server);
|
||||
await for (final socket in server) {
|
||||
await _destroySocket();
|
||||
socketCompleter.complete(socket);
|
||||
socket
|
||||
.transform(uint8ListToListIntConverter)
|
||||
.transform(utf8.decoder)
|
||||
.transform(LineSplitter())
|
||||
.listen(
|
||||
(data) {
|
||||
handleResult(
|
||||
ActionResult.fromJson(
|
||||
json.decode(data.trim()),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}, (error, stack) {
|
||||
commonPrint.log(error.toString());
|
||||
if (error is SocketException) {
|
||||
globalState.showNotifier(error.toString());
|
||||
// globalState.appController.restartCore();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
reStart() async {
|
||||
if (isStarting == true) {
|
||||
return;
|
||||
}
|
||||
isStarting = true;
|
||||
socketCompleter = Completer();
|
||||
if (process != null) {
|
||||
await shutdown();
|
||||
}
|
||||
final serverSocket = await serverCompleter.future;
|
||||
final arg = system.isWindows
|
||||
? '${serverSocket.port}'
|
||||
: serverSocket.address.address;
|
||||
if (system.isWindows && await system.checkIsAdmin()) {
|
||||
final isSuccess = await request.startCoreByHelper(arg);
|
||||
if (isSuccess) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
process = await Process.start(
|
||||
appPath.corePath,
|
||||
[
|
||||
arg,
|
||||
],
|
||||
);
|
||||
process?.stdout.listen((_) {});
|
||||
process?.stderr.listen((e) {
|
||||
final error = utf8.decode(e);
|
||||
if (error.isNotEmpty) {
|
||||
commonPrint.log(error);
|
||||
}
|
||||
});
|
||||
isStarting = false;
|
||||
}
|
||||
|
||||
@override
|
||||
destroy() async {
|
||||
final server = await serverCompleter.future;
|
||||
await server.close();
|
||||
await _deleteSocketFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
sendMessage(String message) async {
|
||||
final socket = await socketCompleter.future;
|
||||
socket.writeln(message);
|
||||
}
|
||||
|
||||
Future<void> _deleteSocketFile() async {
|
||||
if (!system.isWindows) {
|
||||
final file = File(unixSocketPath);
|
||||
if (await file.exists()) {
|
||||
await file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _destroySocket() async {
|
||||
if (socketCompleter.isCompleted) {
|
||||
final lastSocket = await socketCompleter.future;
|
||||
await lastSocket.close();
|
||||
socketCompleter = Completer();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
shutdown() async {
|
||||
if (system.isWindows) {
|
||||
await request.stopCoreByHelper();
|
||||
}
|
||||
await _destroySocket();
|
||||
process?.kill();
|
||||
process = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> preload() async {
|
||||
await serverCompleter.future;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final clashService = system.isDesktop ? ClashService() : null;
|
||||
@@ -1,14 +0,0 @@
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
|
||||
import 'system.dart';
|
||||
|
||||
class Android {
|
||||
Future<void> init() async {
|
||||
app?.onExit = () async {
|
||||
await globalState.appController.savePreferences();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
final android = system.isAndroid ? Android() : null;
|
||||
@@ -14,16 +14,15 @@ extension ArchiveExt on Archive {
|
||||
final data = entity.readAsBytesSync();
|
||||
final archiveFile = ArchiveFile(relativePath, data.length, data);
|
||||
addFile(archiveFile);
|
||||
} else if (entity is Directory) {
|
||||
addDirectoryToArchive(entity.path, parentPath);
|
||||
}
|
||||
// else if (entity is Directory) {
|
||||
// addDirectoryToArchive(entity.path, parentPath);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void add<T>(String name, T raw) {
|
||||
void addTextFile<T>(String name, T raw) {
|
||||
final data = json.encode(raw);
|
||||
addFile(
|
||||
ArchiveFile(name, data.length, data),
|
||||
);
|
||||
addFile(ArchiveFile.string(name, data));
|
||||
}
|
||||
}
|
||||
|
||||
185
lib/common/cache.dart
Normal file
185
lib/common/cache.dart
Normal file
@@ -0,0 +1,185 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
|
||||
class LocalImageCacheManager extends CacheManager {
|
||||
static const key = 'ImageCaches';
|
||||
|
||||
static final LocalImageCacheManager _instance = LocalImageCacheManager._();
|
||||
|
||||
factory LocalImageCacheManager() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
LocalImageCacheManager._()
|
||||
: super(Config(key, fileService: _LocalImageCacheFileService()));
|
||||
}
|
||||
|
||||
class _LocalImageCacheFileService extends FileService {
|
||||
_LocalImageCacheFileService();
|
||||
|
||||
@override
|
||||
Future<FileServiceResponse> get(
|
||||
String url, {
|
||||
Map<String, String>? headers,
|
||||
}) async {
|
||||
final response = await request.dio.get<ResponseBody>(
|
||||
url,
|
||||
options: Options(headers: headers, responseType: ResponseType.stream),
|
||||
);
|
||||
return _LocalImageResponse(response);
|
||||
}
|
||||
}
|
||||
|
||||
class _LocalImageResponse implements FileServiceResponse {
|
||||
_LocalImageResponse(this._response);
|
||||
|
||||
final DateTime _receivedTime = DateTime.now();
|
||||
|
||||
final Response<ResponseBody> _response;
|
||||
|
||||
String? _header(String name) {
|
||||
return _response.headers.value(name);
|
||||
}
|
||||
|
||||
@override
|
||||
int get statusCode => _response.statusCode ?? 0;
|
||||
|
||||
@override
|
||||
Stream<List<int>> get content =>
|
||||
_response.data!.stream.transform(uint8ListToListIntConverter);
|
||||
|
||||
@override
|
||||
int? get contentLength => _response.data?.contentLength;
|
||||
|
||||
@override
|
||||
DateTime get validTill {
|
||||
var ageDuration = const Duration(days: 7);
|
||||
final controlHeader = _header(HttpHeaders.cacheControlHeader);
|
||||
if (controlHeader != null) {
|
||||
final controlSettings = controlHeader.split(',');
|
||||
for (final setting in controlSettings) {
|
||||
final sanitizedSetting = setting.trim().toLowerCase();
|
||||
if (sanitizedSetting == 'no-cache') {
|
||||
ageDuration = Duration.zero;
|
||||
}
|
||||
if (sanitizedSetting.startsWith('max-age=')) {
|
||||
final validSeconds =
|
||||
int.tryParse(sanitizedSetting.split('=')[1]) ?? 0;
|
||||
if (validSeconds > 0) {
|
||||
ageDuration = Duration(seconds: validSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ageDuration > const Duration(days: 7)) {
|
||||
return _receivedTime.add(ageDuration);
|
||||
}
|
||||
return _receivedTime.add(const Duration(days: 7));
|
||||
}
|
||||
|
||||
@override
|
||||
String? get eTag => _header(HttpHeaders.etagHeader);
|
||||
|
||||
@override
|
||||
String get fileExtension {
|
||||
var fileExtension = '';
|
||||
final contentTypeHeader = _header(HttpHeaders.contentTypeHeader);
|
||||
if (contentTypeHeader != null) {
|
||||
final contentType = ContentType.parse(contentTypeHeader);
|
||||
fileExtension = contentType.fileExtension;
|
||||
}
|
||||
return fileExtension;
|
||||
}
|
||||
}
|
||||
|
||||
extension ContentTypeConverter on ContentType {
|
||||
String get fileExtension => mimeTypes[mimeType] ?? '.$subType';
|
||||
}
|
||||
|
||||
const mimeTypes = {
|
||||
'application/vnd.android.package-archive': '.apk',
|
||||
'application/epub+zip': '.epub',
|
||||
'application/gzip': '.gz',
|
||||
'application/java-archive': '.jar',
|
||||
'application/json': '.json',
|
||||
'application/ld+json': '.jsonld',
|
||||
'application/msword': '.doc',
|
||||
'application/octet-stream': '.bin',
|
||||
'application/ogg': '.ogx',
|
||||
'application/pdf': '.pdf',
|
||||
'application/php': '.php',
|
||||
'application/rtf': '.rtf',
|
||||
'application/vnd.amazon.ebook': '.azw',
|
||||
'application/vnd.apple.installer+xml': '.mpkg',
|
||||
'application/vnd.mozilla.xul+xml': '.xul',
|
||||
'application/vnd.ms-excel': '.xls',
|
||||
'application/vnd.ms-fontobject': '.eot',
|
||||
'application/vnd.ms-powerpoint': '.ppt',
|
||||
'application/vnd.oasis.opendocument.presentation': '.odp',
|
||||
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
|
||||
'application/vnd.oasis.opendocument.text': '.odt',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation':
|
||||
'.pptx',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
|
||||
'.docx',
|
||||
'application/vnd.rar': '.rar',
|
||||
'application/vnd.visio': '.vsd',
|
||||
'application/x-7z-compressed': '.7z',
|
||||
'application/x-abiword': '.abw',
|
||||
'application/x-bzip': '.bz',
|
||||
'application/x-bzip2': '.bz2',
|
||||
'application/x-csh': '.csh',
|
||||
'application/x-freearc': '.arc',
|
||||
'application/x-sh': '.sh',
|
||||
'application/x-shockwave-flash': '.swf',
|
||||
'application/x-tar': '.tar',
|
||||
'application/xhtml+xml': '.xhtml',
|
||||
'application/xml': '.xml',
|
||||
'application/zip': '.zip',
|
||||
'audio/3gpp': '.3gp',
|
||||
'audio/3gpp2': '.3g2',
|
||||
'audio/aac': '.aac',
|
||||
'audio/x-aac': '.aac',
|
||||
'audio/midi': '.midi',
|
||||
'audio/x-midi': '.midi',
|
||||
'audio/x-m4a': '.m4a',
|
||||
'audio/m4a': '.m4a',
|
||||
'audio/mpeg': '.mp3',
|
||||
'audio/ogg': '.oga',
|
||||
'audio/opus': '.opus',
|
||||
'audio/wav': '.wav',
|
||||
'audio/x-wav': '.wav',
|
||||
'audio/webm': '.weba',
|
||||
'font/otf': '.otf',
|
||||
'font/ttf': '.ttf',
|
||||
'font/woff': '.woff',
|
||||
'font/woff2': '.woff2',
|
||||
'image/bmp': '.bmp',
|
||||
'image/gif': '.gif',
|
||||
'image/jpeg': '.jpg',
|
||||
'image/png': '.png',
|
||||
'image/svg+xml': '.svg',
|
||||
'image/tiff': '.tiff',
|
||||
'image/vnd.microsoft.icon': '.ico',
|
||||
'image/webp': '.webp',
|
||||
'text/calendar': '.ics',
|
||||
'text/css': '.css',
|
||||
'text/csv': '.csv',
|
||||
'text/html': '.html',
|
||||
'text/javascript': '.js',
|
||||
'text/plain': '.txt',
|
||||
'text/xml': '.xml',
|
||||
'video/3gpp': '.3gp',
|
||||
'video/3gpp2': '.3g2',
|
||||
'video/mp2t': '.ts',
|
||||
'video/mpeg': '.mpeg',
|
||||
'video/ogg': '.ogv',
|
||||
'video/webm': '.webm',
|
||||
'video/x-msvideo': '.avi',
|
||||
'video/quicktime': '.mov',
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
export 'android.dart';
|
||||
export 'app_localizations.dart';
|
||||
export 'cache.dart';
|
||||
export 'color.dart';
|
||||
export 'compute.dart';
|
||||
export 'constant.dart';
|
||||
export 'context.dart';
|
||||
export 'converter.dart';
|
||||
|
||||
110
lib/common/compute.dart
Normal file
110
lib/common/compute.dart
Normal file
@@ -0,0 +1,110 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
|
||||
import 'string.dart';
|
||||
|
||||
List<Group> computeSort({
|
||||
required List<Group> groups,
|
||||
required ProxiesSortType sortType,
|
||||
required DelayMap delayMap,
|
||||
required SelectedMap selectedMap,
|
||||
required String defaultTestUrl,
|
||||
}) {
|
||||
return groups.map((group) {
|
||||
final proxies = group.all;
|
||||
final newProxies = switch (sortType) {
|
||||
ProxiesSortType.none => proxies,
|
||||
ProxiesSortType.delay => _sortOfDelay(
|
||||
groups: groups,
|
||||
proxies: proxies,
|
||||
delayMap: delayMap,
|
||||
selectedMap: selectedMap,
|
||||
testUrl: group.testUrl.getSafeValue(defaultTestUrl),
|
||||
),
|
||||
ProxiesSortType.name => _sortOfName(proxies),
|
||||
};
|
||||
return group.copyWith(all: newProxies);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
DelayState computeProxyDelayState({
|
||||
required String proxyName,
|
||||
required String testUrl,
|
||||
required List<Group> groups,
|
||||
required SelectedMap selectedMap,
|
||||
required DelayMap delayMap,
|
||||
}) {
|
||||
final state = computeRealSelectedProxyState(
|
||||
proxyName,
|
||||
groups: groups,
|
||||
selectedMap: selectedMap,
|
||||
);
|
||||
final currentDelayMap = delayMap[state.testUrl.getSafeValue(testUrl)] ?? {};
|
||||
final delay = currentDelayMap[state.proxyName];
|
||||
return DelayState(delay: delay ?? 0, group: state.group);
|
||||
}
|
||||
|
||||
SelectedProxyState computeRealSelectedProxyState(
|
||||
String proxyName, {
|
||||
required List<Group> groups,
|
||||
required SelectedMap selectedMap,
|
||||
}) {
|
||||
return _getRealSelectedProxyState(
|
||||
SelectedProxyState(proxyName: proxyName),
|
||||
groups: groups,
|
||||
selectedMap: selectedMap,
|
||||
);
|
||||
}
|
||||
|
||||
SelectedProxyState _getRealSelectedProxyState(
|
||||
SelectedProxyState state, {
|
||||
required List<Group> groups,
|
||||
required SelectedMap selectedMap,
|
||||
}) {
|
||||
if (state.proxyName.isEmpty) return state;
|
||||
final index = groups.indexWhere((element) => element.name == state.proxyName);
|
||||
final newState = state.copyWith(group: true);
|
||||
if (index == -1) return newState;
|
||||
final group = groups[index];
|
||||
final currentSelectedName = group.getCurrentSelectedName(
|
||||
selectedMap[newState.proxyName] ?? '',
|
||||
);
|
||||
if (currentSelectedName.isEmpty) {
|
||||
return newState;
|
||||
}
|
||||
return _getRealSelectedProxyState(
|
||||
newState.copyWith(proxyName: currentSelectedName, testUrl: group.testUrl),
|
||||
groups: groups,
|
||||
selectedMap: selectedMap,
|
||||
);
|
||||
}
|
||||
|
||||
List<Proxy> _sortOfDelay({
|
||||
required List<Group> groups,
|
||||
required List<Proxy> proxies,
|
||||
required DelayMap delayMap,
|
||||
required SelectedMap selectedMap,
|
||||
required String testUrl,
|
||||
}) {
|
||||
return List.from(proxies)..sort((a, b) {
|
||||
final aDelayState = computeProxyDelayState(
|
||||
proxyName: a.name,
|
||||
testUrl: testUrl,
|
||||
groups: groups,
|
||||
selectedMap: selectedMap,
|
||||
delayMap: delayMap,
|
||||
);
|
||||
final bDelayState = computeProxyDelayState(
|
||||
proxyName: b.name,
|
||||
testUrl: testUrl,
|
||||
groups: groups,
|
||||
selectedMap: selectedMap,
|
||||
delayMap: delayMap,
|
||||
);
|
||||
return aDelayState.compareTo(bDelayState);
|
||||
});
|
||||
}
|
||||
|
||||
List<Proxy> _sortOfName(List<Proxy> proxies) {
|
||||
return List.of(proxies)..sort((a, b) => a.name.compareTo(b.name));
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
@@ -30,14 +32,14 @@ const animateDuration = Duration(milliseconds: 100);
|
||||
const midDuration = Duration(milliseconds: 200);
|
||||
const commonDuration = Duration(milliseconds: 300);
|
||||
const defaultUpdateDuration = Duration(days: 1);
|
||||
const mmdbFileName = 'geoip.metadb';
|
||||
const asnFileName = 'ASN.mmdb';
|
||||
const geoIpFileName = 'GeoIP.dat';
|
||||
const geoSiteFileName = 'GeoSite.dat';
|
||||
const MMDB = 'GEOIP.metadb';
|
||||
const ASN = 'ASN.mmdb';
|
||||
const GEOIP = 'GEOIP.dat';
|
||||
const GEOSITE = 'GEOSITE.dat';
|
||||
final double kHeaderHeight = system.isDesktop
|
||||
? !system.isMacOS
|
||||
? 40
|
||||
: 28
|
||||
? 40
|
||||
: 28
|
||||
: 0;
|
||||
const profilesDirectoryName = 'profiles';
|
||||
const localhost = '127.0.0.1';
|
||||
@@ -84,7 +86,7 @@ const profilesStoreKey = PageStorageKey<String>('profiles');
|
||||
const defaultPrimaryColor = 0XFFD8C0C3;
|
||||
|
||||
double getWidgetHeight(num lines) {
|
||||
return max(lines * 84 + (lines - 1) * 16, 0).ap;
|
||||
return max(lines * 80 + (lines - 1) * 16, 0).ap;
|
||||
}
|
||||
|
||||
const maxLength = 1000;
|
||||
|
||||
@@ -7,28 +7,17 @@ extension BuildContextExtension on BuildContext {
|
||||
return findAncestorStateOfType<CommonScaffoldState>();
|
||||
}
|
||||
|
||||
Future<void>? showNotifier(String text) {
|
||||
void showNotifier(String text) {
|
||||
return findAncestorStateOfType<MessageManagerState>()?.message(text);
|
||||
}
|
||||
|
||||
void showSnackBar(
|
||||
String message, {
|
||||
SnackBarAction? action,
|
||||
}) {
|
||||
void showSnackBar(String message, {SnackBarAction? action}) {
|
||||
final width = viewWidth;
|
||||
EdgeInsets margin;
|
||||
if (width < 600) {
|
||||
margin = const EdgeInsets.only(
|
||||
bottom: 16,
|
||||
right: 16,
|
||||
left: 16,
|
||||
);
|
||||
margin = const EdgeInsets.only(bottom: 16, right: 16, left: 16);
|
||||
} else {
|
||||
margin = EdgeInsets.only(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: width - 316,
|
||||
);
|
||||
margin = EdgeInsets.only(bottom: 16, left: 16, right: width - 316);
|
||||
}
|
||||
ScaffoldMessenger.of(this).showSnackBar(
|
||||
SnackBar(
|
||||
@@ -76,8 +65,11 @@ extension BuildContextExtension on BuildContext {
|
||||
class BackHandleInherited extends InheritedWidget {
|
||||
final Function handleBack;
|
||||
|
||||
const BackHandleInherited(
|
||||
{super.key, required this.handleBack, required super.child});
|
||||
const BackHandleInherited({
|
||||
super.key,
|
||||
required this.handleBack,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
static BackHandleInherited? of(BuildContext context) =>
|
||||
context.dependOnInheritedWidgetOfExactType<BackHandleInherited>();
|
||||
|
||||
@@ -9,23 +9,17 @@ class Debouncer {
|
||||
FunctionTag tag,
|
||||
Function func, {
|
||||
List<dynamic>? args,
|
||||
Duration duration = const Duration(milliseconds: 600),
|
||||
Duration? duration,
|
||||
}) {
|
||||
final timer = _operations[tag];
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
}
|
||||
_operations[tag] = Timer(
|
||||
duration,
|
||||
() {
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
Function.apply(
|
||||
func,
|
||||
args,
|
||||
);
|
||||
},
|
||||
);
|
||||
_operations[tag] = Timer(duration ?? const Duration(milliseconds: 600), () {
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
Function.apply(func, args);
|
||||
});
|
||||
}
|
||||
|
||||
void cancel(dynamic tag) {
|
||||
@@ -42,22 +36,25 @@ class Throttler {
|
||||
Function func, {
|
||||
List<dynamic>? args,
|
||||
Duration duration = const Duration(milliseconds: 600),
|
||||
bool fire = false,
|
||||
}) {
|
||||
final timer = _operations[tag];
|
||||
if (timer != null) {
|
||||
return true;
|
||||
}
|
||||
_operations[tag] = Timer(
|
||||
duration,
|
||||
() {
|
||||
if (fire) {
|
||||
Function.apply(func, args);
|
||||
_operations[tag] = Timer(duration, () {
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
Function.apply(
|
||||
func,
|
||||
args,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
_operations[tag] = Timer(duration, () {
|
||||
Function.apply(func, args);
|
||||
_operations[tag]?.cancel();
|
||||
_operations.remove(tag);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,42 +3,37 @@ import 'dart:ui';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
|
||||
extension CompleterExt<T> on Completer<T> {
|
||||
Future<T> safeFuture({
|
||||
extension FutureExt<T> on Future<T> {
|
||||
Future<T> withTimeout({
|
||||
Duration? timeout,
|
||||
String? tag,
|
||||
VoidCallback? onLast,
|
||||
FutureOr<T> Function()? onTimeout,
|
||||
required String functionName,
|
||||
}) {
|
||||
final realTimeout = timeout ?? const Duration(seconds: 30);
|
||||
Timer(realTimeout + commonDuration, () {
|
||||
final realTimout = timeout ?? const Duration(minutes: 3);
|
||||
Timer(realTimout + commonDuration, () {
|
||||
if (onLast != null) {
|
||||
onLast();
|
||||
}
|
||||
});
|
||||
return future.withTimeout(
|
||||
timeout: realTimeout,
|
||||
functionName: functionName,
|
||||
onTimeout: onTimeout,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension FutureExt<T> on Future<T> {
|
||||
Future<T> withTimeout({
|
||||
required Duration timeout,
|
||||
required String functionName,
|
||||
FutureOr<T> Function()? onTimeout,
|
||||
}) {
|
||||
return this.timeout(
|
||||
timeout,
|
||||
realTimout,
|
||||
onTimeout: () async {
|
||||
if (onTimeout != null) {
|
||||
return onTimeout();
|
||||
} else {
|
||||
throw TimeoutException('$functionName timeout');
|
||||
throw TimeoutException('${tag ?? runtimeType} timeout');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension CompleterExt<T> on Completer<T> {
|
||||
void safeCompleter(T value) {
|
||||
if (isCompleted) {
|
||||
return;
|
||||
}
|
||||
complete(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class FlClashHttpOverrides extends HttpOverrides {
|
||||
@override
|
||||
HttpClient createHttpClient(SecurityContext? context) {
|
||||
final client = super.createHttpClient(context);
|
||||
client.badCertificateCallback = (_, __, ___) => true;
|
||||
client.badCertificateCallback = (_, _, _) => true;
|
||||
client.findProxy = handleFindProxy;
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
mixin AutoDisposeNotifierMixin<T> on AutoDisposeNotifier<T> {
|
||||
mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
|
||||
set value(T value) {
|
||||
state = value;
|
||||
if (ref.mounted) {
|
||||
state = value;
|
||||
} else {
|
||||
onUpdate(value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -17,28 +22,25 @@ mixin AutoDisposeNotifierMixin<T> on AutoDisposeNotifier<T> {
|
||||
void onUpdate(T value) {}
|
||||
}
|
||||
|
||||
// mixin PageMixin<T extends StatefulWidget> on State<T> {
|
||||
// initPageState() {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// final commonScaffoldState = context.commonScaffoldState;
|
||||
// commonScaffoldState?.actions = actions;
|
||||
// commonScaffoldState?.floatingActionButton = floatingActionButton;
|
||||
// commonScaffoldState?.onKeywordsUpdate = onKeywordsUpdate;
|
||||
// commonScaffoldState?.updateSearchState(
|
||||
// (_) => onSearch != null
|
||||
// ? AppBarSearchState(
|
||||
// onSearch: onSearch!,
|
||||
// )
|
||||
// : null,
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// List<Widget> get actions => [];
|
||||
//
|
||||
// Widget? get floatingActionButton => null;
|
||||
//
|
||||
// Function(String)? get onSearch => null;
|
||||
//
|
||||
// Function(List<String>)? get onKeywordsUpdate => null;
|
||||
// }
|
||||
mixin AnyNotifierMixin<T> on AnyNotifier<T, T> {
|
||||
T get value;
|
||||
|
||||
set value(T value) {
|
||||
if (ref.mounted) {
|
||||
state = value;
|
||||
} else {
|
||||
onUpdate(value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(previous, next) {
|
||||
final res = super.updateShouldNotify(previous, next);
|
||||
if (res) {
|
||||
onUpdate(next);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void onUpdate(T value) {}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
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/views/views.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class Navigation {
|
||||
static Navigation? _instance;
|
||||
@@ -17,25 +15,14 @@ class Navigation {
|
||||
keep: false,
|
||||
icon: Icon(Icons.space_dashboard),
|
||||
label: PageLabel.dashboard,
|
||||
builder: (_) => const DashboardView(
|
||||
key: GlobalObjectKey(PageLabel.dashboard),
|
||||
),
|
||||
builder: (_) =>
|
||||
const DashboardView(key: GlobalObjectKey(PageLabel.dashboard)),
|
||||
),
|
||||
NavigationItem(
|
||||
icon: const Icon(Icons.article),
|
||||
label: PageLabel.proxies,
|
||||
builder: (_) => ProviderScope(
|
||||
overrides: [
|
||||
queryProvider.overrideWith(
|
||||
() => Query(),
|
||||
),
|
||||
],
|
||||
child: const ProxiesView(
|
||||
key: GlobalObjectKey(
|
||||
PageLabel.proxies,
|
||||
),
|
||||
),
|
||||
),
|
||||
builder: (_) =>
|
||||
const ProxiesView(key: GlobalObjectKey(PageLabel.proxies)),
|
||||
modes: hasProxies
|
||||
? [NavigationItemMode.mobile, NavigationItemMode.desktop]
|
||||
: [],
|
||||
@@ -43,31 +30,22 @@ class Navigation {
|
||||
NavigationItem(
|
||||
icon: Icon(Icons.folder),
|
||||
label: PageLabel.profiles,
|
||||
builder: (_) => const ProfilesView(
|
||||
key: GlobalObjectKey(
|
||||
PageLabel.profiles,
|
||||
),
|
||||
),
|
||||
builder: (_) =>
|
||||
const ProfilesView(key: GlobalObjectKey(PageLabel.profiles)),
|
||||
),
|
||||
NavigationItem(
|
||||
icon: Icon(Icons.view_timeline),
|
||||
label: PageLabel.requests,
|
||||
builder: (_) => const RequestsView(
|
||||
key: GlobalObjectKey(
|
||||
PageLabel.requests,
|
||||
),
|
||||
),
|
||||
builder: (_) =>
|
||||
const RequestsView(key: GlobalObjectKey(PageLabel.requests)),
|
||||
description: 'requestsDesc',
|
||||
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
|
||||
),
|
||||
NavigationItem(
|
||||
icon: Icon(Icons.ballot),
|
||||
label: PageLabel.connections,
|
||||
builder: (_) => const ConnectionsView(
|
||||
key: GlobalObjectKey(
|
||||
PageLabel.connections,
|
||||
),
|
||||
),
|
||||
builder: (_) =>
|
||||
const ConnectionsView(key: GlobalObjectKey(PageLabel.connections)),
|
||||
description: 'connectionsDesc',
|
||||
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
|
||||
),
|
||||
@@ -75,21 +53,14 @@ class Navigation {
|
||||
icon: Icon(Icons.storage),
|
||||
label: PageLabel.resources,
|
||||
description: 'resourcesDesc',
|
||||
builder: (_) => const ResourcesView(
|
||||
key: GlobalObjectKey(
|
||||
PageLabel.resources,
|
||||
),
|
||||
),
|
||||
builder: (_) =>
|
||||
const ResourcesView(key: GlobalObjectKey(PageLabel.resources)),
|
||||
modes: [NavigationItemMode.more],
|
||||
),
|
||||
NavigationItem(
|
||||
icon: const Icon(Icons.adb),
|
||||
label: PageLabel.logs,
|
||||
builder: (_) => const LogsView(
|
||||
key: GlobalObjectKey(
|
||||
PageLabel.logs,
|
||||
),
|
||||
),
|
||||
builder: (_) => const LogsView(key: GlobalObjectKey(PageLabel.logs)),
|
||||
description: 'logsDesc',
|
||||
modes: openLogs
|
||||
? [NavigationItemMode.desktop, NavigationItemMode.more]
|
||||
@@ -98,11 +69,7 @@ class Navigation {
|
||||
NavigationItem(
|
||||
icon: Icon(Icons.construction),
|
||||
label: PageLabel.tools,
|
||||
builder: (_) => const ToolsView(
|
||||
key: GlobalObjectKey(
|
||||
PageLabel.tools,
|
||||
),
|
||||
),
|
||||
builder: (_) => const ToolsView(key: GlobalObjectKey(PageLabel.tools)),
|
||||
modes: [NavigationItemMode.desktop, NavigationItemMode.mobile],
|
||||
),
|
||||
];
|
||||
|
||||
@@ -7,17 +7,13 @@ import 'package:flutter/material.dart';
|
||||
class BaseNavigator {
|
||||
static Future<T?> push<T>(BuildContext context, Widget child) async {
|
||||
if (globalState.appState.viewMode != ViewMode.mobile) {
|
||||
return await Navigator.of(context).push<T>(
|
||||
CommonDesktopRoute(
|
||||
builder: (context) => child,
|
||||
),
|
||||
);
|
||||
return await Navigator.of(
|
||||
context,
|
||||
).push<T>(CommonDesktopRoute(builder: (context) => child));
|
||||
}
|
||||
return await Navigator.of(context).push<T>(
|
||||
CommonRoute(
|
||||
builder: (context) => child,
|
||||
),
|
||||
);
|
||||
return await Navigator.of(
|
||||
context,
|
||||
).push<T>(CommonRoute(builder: (context) => child));
|
||||
}
|
||||
|
||||
// static Future<T?> modal<T>(BuildContext context, Widget child) async {
|
||||
@@ -39,9 +35,7 @@ class BaseNavigator {
|
||||
class CommonDesktopRoute<T> extends PageRoute<T> {
|
||||
final Widget Function(BuildContext context) builder;
|
||||
|
||||
CommonDesktopRoute({
|
||||
required this.builder,
|
||||
});
|
||||
CommonDesktopRoute({required this.builder});
|
||||
|
||||
@override
|
||||
Color? get barrierColor => null;
|
||||
@@ -59,10 +53,7 @@ class CommonDesktopRoute<T> extends PageRoute<T> {
|
||||
return Semantics(
|
||||
scopesRoute: true,
|
||||
explicitChildNodes: true,
|
||||
child: FadeTransition(
|
||||
opacity: animation,
|
||||
child: result,
|
||||
),
|
||||
child: FadeTransition(opacity: animation, child: result),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -77,9 +68,7 @@ class CommonDesktopRoute<T> extends PageRoute<T> {
|
||||
}
|
||||
|
||||
class CommonRoute<T> extends MaterialPageRoute<T> {
|
||||
CommonRoute({
|
||||
required super.builder,
|
||||
});
|
||||
CommonRoute({required super.builder});
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => const Duration(milliseconds: 500);
|
||||
@@ -139,16 +128,21 @@ class CommonPageTransition extends StatefulWidget {
|
||||
final bool linearTransition;
|
||||
|
||||
static Widget? delegatedTransition(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
bool allowSnapshotting,
|
||||
Widget? child) {
|
||||
final Animation<Offset> delegatedPositionAnimation = CurvedAnimation(
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
bool allowSnapshotting,
|
||||
Widget? child,
|
||||
) {
|
||||
final CurvedAnimation animation = CurvedAnimation(
|
||||
parent: secondaryAnimation,
|
||||
curve: Curves.linearToEaseOut,
|
||||
reverseCurve: Curves.easeInToLinear,
|
||||
).drive(_kMiddleLeftTween);
|
||||
);
|
||||
final Animation<Offset> delegatedPositionAnimation = animation.drive(
|
||||
_kMiddleLeftTween,
|
||||
);
|
||||
animation.dispose();
|
||||
|
||||
assert(debugCheckHasDirectionality(context));
|
||||
final TextDirection textDirection = Directionality.of(context);
|
||||
@@ -222,23 +216,23 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
|
||||
);
|
||||
}
|
||||
_primaryPositionAnimation =
|
||||
(_primaryPositionCurve ?? widget.primaryRouteAnimation)
|
||||
.drive(_kRightMiddleTween);
|
||||
(_primaryPositionCurve ?? widget.primaryRouteAnimation).drive(
|
||||
_kRightMiddleTween,
|
||||
);
|
||||
_secondaryPositionAnimation =
|
||||
(_secondaryPositionCurve ?? widget.secondaryRouteAnimation)
|
||||
.drive(_kMiddleLeftTween);
|
||||
(_secondaryPositionCurve ?? widget.secondaryRouteAnimation).drive(
|
||||
_kMiddleLeftTween,
|
||||
);
|
||||
_primaryShadowAnimation =
|
||||
(_primaryShadowCurve ?? widget.primaryRouteAnimation).drive(
|
||||
DecorationTween(
|
||||
begin: const _CommonEdgeShadowDecoration(),
|
||||
end: _CommonEdgeShadowDecoration(
|
||||
<Color>[
|
||||
widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02),
|
||||
Colors.transparent,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
DecorationTween(
|
||||
begin: const _CommonEdgeShadowDecoration(),
|
||||
end: _CommonEdgeShadowDecoration(<Color>[
|
||||
widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02),
|
||||
Colors.transparent,
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -273,10 +267,8 @@ class _CommonEdgeShadowDecoration extends Decoration {
|
||||
}
|
||||
|
||||
class _CommonEdgeShadowPainter extends BoxPainter {
|
||||
_CommonEdgeShadowPainter(
|
||||
this._decoration,
|
||||
super.onChanged,
|
||||
) : assert(_decoration._colors == null || _decoration._colors!.length > 1);
|
||||
_CommonEdgeShadowPainter(this._decoration, super.onChanged)
|
||||
: assert(_decoration._colors == null || _decoration._colors.length > 1);
|
||||
|
||||
final _CommonEdgeShadowDecoration _decoration;
|
||||
|
||||
@@ -304,11 +296,16 @@ class _CommonEdgeShadowPainter extends BoxPainter {
|
||||
bandColorIndex += 1;
|
||||
}
|
||||
final Paint paint = Paint()
|
||||
..color = Color.lerp(colors[bandColorIndex], colors[bandColorIndex + 1],
|
||||
(dx % bandWidth) / bandWidth)!;
|
||||
..color = Color.lerp(
|
||||
colors[bandColorIndex],
|
||||
colors[bandColorIndex + 1],
|
||||
(dx % bandWidth) / bandWidth,
|
||||
)!;
|
||||
final double x = start + shadowDirection * dx;
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(x - 1.0, offset.dy, 1.0, shadowHeight), paint);
|
||||
Rect.fromLTWH(x - 1.0, offset.dy, 1.0, shadowHeight),
|
||||
paint,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -17,6 +19,20 @@ extension NumExt on num {
|
||||
double get ap {
|
||||
return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);
|
||||
}
|
||||
|
||||
TrafficShow get traffic {
|
||||
final units = TrafficUnit.values;
|
||||
var size = toDouble();
|
||||
var unitIndex = 0;
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
return TrafficShow(
|
||||
value: size.fixed(decimals: 1),
|
||||
unit: units[unitIndex].name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension DoubleExt on double {
|
||||
|
||||
@@ -10,6 +10,7 @@ class AppPath {
|
||||
Completer<Directory> dataDir = Completer();
|
||||
Completer<Directory> downloadDir = Completer();
|
||||
Completer<Directory> tempDir = Completer();
|
||||
Completer<Directory> cacheDir = Completer();
|
||||
late String appDirPath;
|
||||
|
||||
AppPath._internal() {
|
||||
@@ -23,6 +24,9 @@ class AppPath {
|
||||
getDownloadsDirectory().then((value) {
|
||||
downloadDir.complete(value);
|
||||
});
|
||||
getApplicationCacheDirectory().then((value) {
|
||||
cacheDir.complete(value);
|
||||
});
|
||||
}
|
||||
|
||||
factory AppPath() {
|
||||
@@ -58,8 +62,13 @@ class AppPath {
|
||||
}
|
||||
|
||||
Future<String> get lockFilePath async {
|
||||
final directory = await dataDir.future;
|
||||
return join(directory.path, 'FlClash.lock');
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
return join(homeDirPath, 'FlClash.lock');
|
||||
}
|
||||
|
||||
Future<String> get configFilePath async {
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
return join(homeDirPath, 'config.json');
|
||||
}
|
||||
|
||||
Future<String> get sharedPreferencesPath async {
|
||||
@@ -77,13 +86,19 @@ class AppPath {
|
||||
return join(directory, '$id.yaml');
|
||||
}
|
||||
|
||||
Future<String> getIconsCacheDir() async {
|
||||
final directory = await cacheDir.future;
|
||||
return join(directory.path, 'icons');
|
||||
}
|
||||
|
||||
Future<String> getProvidersRootPath() async {
|
||||
final directory = await profilesPath;
|
||||
return join(directory, 'providers');
|
||||
}
|
||||
|
||||
Future<String> getProvidersDirPath(String id) async {
|
||||
final directory = await profilesPath;
|
||||
return join(
|
||||
directory,
|
||||
'providers',
|
||||
id,
|
||||
);
|
||||
return join(directory, 'providers', id);
|
||||
}
|
||||
|
||||
Future<String> getProvidersFilePath(
|
||||
@@ -92,13 +107,7 @@ class AppPath {
|
||||
String url,
|
||||
) async {
|
||||
final directory = await profilesPath;
|
||||
return join(
|
||||
directory,
|
||||
'providers',
|
||||
id,
|
||||
type,
|
||||
url.toMd5(),
|
||||
);
|
||||
return join(directory, 'providers', id, type, url.toMd5());
|
||||
}
|
||||
|
||||
Future<String> get tempPath async {
|
||||
|
||||
@@ -35,9 +35,10 @@ class Picker {
|
||||
return null;
|
||||
}
|
||||
final controller = MobileScannerController();
|
||||
final capture = await controller.analyzeImage(xFile.path, formats: [
|
||||
BarcodeFormat.qrCode,
|
||||
]);
|
||||
final capture = await controller.analyzeImage(
|
||||
xFile.path,
|
||||
formats: [BarcodeFormat.qrCode],
|
||||
);
|
||||
final result = capture?.barcodes.first.rawValue;
|
||||
if (result == null || !result.isUrl) {
|
||||
throw appLocalizations.pleaseUploadValidQrcode;
|
||||
|
||||
@@ -16,7 +16,7 @@ class Preferences {
|
||||
Preferences._internal() {
|
||||
SharedPreferences.getInstance()
|
||||
.then((value) => sharedPreferencesCompleter.complete(value))
|
||||
.onError((_, __) => sharedPreferencesCompleter.complete(null));
|
||||
.onError((_, _) => sharedPreferencesCompleter.complete(null));
|
||||
}
|
||||
|
||||
factory Preferences() {
|
||||
@@ -42,10 +42,7 @@ class Preferences {
|
||||
|
||||
Future<bool> saveConfig(Config config) async {
|
||||
final preferences = await sharedPreferencesCompleter.future;
|
||||
return await preferences?.setString(
|
||||
configKey,
|
||||
json.encode(config),
|
||||
) ??
|
||||
return await preferences?.setString(configKey, json.encode(config)) ??
|
||||
false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -12,14 +13,14 @@ class CommonPrint {
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
void log(String? text) {
|
||||
void log(String? text, {LogLevel logLevel = LogLevel.info}) {
|
||||
final payload = '[APP] $text';
|
||||
debugPrint(payload);
|
||||
if (!globalState.isInit) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.addLog(
|
||||
Log.app(payload),
|
||||
Log.app(payload).copyWith(logLevel: logLevel),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,35 +11,29 @@ import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class Request {
|
||||
late final Dio _dio;
|
||||
late final Dio dio;
|
||||
late final Dio _clashDio;
|
||||
String? userAgent;
|
||||
|
||||
Request() {
|
||||
_dio = Dio(
|
||||
BaseOptions(
|
||||
headers: {
|
||||
'User-Agent': browserUa,
|
||||
},
|
||||
),
|
||||
);
|
||||
dio = Dio(BaseOptions(headers: {'User-Agent': browserUa}));
|
||||
_clashDio = Dio();
|
||||
_clashDio.httpClientAdapter = IOHttpClientAdapter(createHttpClient: () {
|
||||
final client = HttpClient();
|
||||
client.findProxy = (Uri uri) {
|
||||
client.userAgent = globalState.ua;
|
||||
return FlClashHttpOverrides.handleFindProxy(uri);
|
||||
};
|
||||
return client;
|
||||
});
|
||||
_clashDio.httpClientAdapter = IOHttpClientAdapter(
|
||||
createHttpClient: () {
|
||||
final client = HttpClient();
|
||||
client.findProxy = (Uri uri) {
|
||||
client.userAgent = globalState.ua;
|
||||
return FlClashHttpOverrides.handleFindProxy(uri);
|
||||
};
|
||||
return client;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<Response> getFileResponseForUrl(String url) async {
|
||||
final response = await _clashDio.get(
|
||||
url,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
options: Options(responseType: ResponseType.bytes),
|
||||
);
|
||||
return response;
|
||||
}
|
||||
@@ -47,20 +41,16 @@ class Request {
|
||||
Future<Response> getTextResponseForUrl(String url) async {
|
||||
final response = await _clashDio.get(
|
||||
url,
|
||||
options: Options(
|
||||
responseType: ResponseType.plain,
|
||||
),
|
||||
options: Options(responseType: ResponseType.plain),
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<MemoryImage?> getImage(String url) async {
|
||||
if (url.isEmpty) return null;
|
||||
final response = await _dio.get<Uint8List>(
|
||||
final response = await dio.get<Uint8List>(
|
||||
url,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
),
|
||||
options: Options(responseType: ResponseType.bytes),
|
||||
);
|
||||
final data = response.data;
|
||||
if (data == null) return null;
|
||||
@@ -68,11 +58,9 @@ class Request {
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> checkForUpdate() async {
|
||||
final response = await _dio.get(
|
||||
final response = await dio.get(
|
||||
'https://api.github.com/repos/$repository/releases/latest',
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
options: Options(responseType: ResponseType.json),
|
||||
);
|
||||
if (response.statusCode != 200) return null;
|
||||
final data = response.data as Map<String, dynamic>;
|
||||
@@ -85,10 +73,13 @@ class Request {
|
||||
}
|
||||
|
||||
final Map<String, IpInfo Function(Map<String, dynamic>)> _ipInfoSources = {
|
||||
'https://ipwho.is/': IpInfo.fromIpwhoIsJson,
|
||||
'https://api.ip.sb/geoip/': IpInfo.fromIpSbJson,
|
||||
'https://ipapi.co/json/': IpInfo.fromIpApiCoJson,
|
||||
'https://ipinfo.io/json/': IpInfo.fromIpInfoIoJson,
|
||||
'https://ipwho.is': IpInfo.fromIpWhoIsJson,
|
||||
'https://api.myip.com': IpInfo.fromMyIpJson,
|
||||
'https://ipapi.co/json': IpInfo.fromIpApiCoJson,
|
||||
'https://ident.me/json': IpInfo.fromIdentMeJson,
|
||||
'http://ip-api.com/json': IpInfo.fromIpAPIJson,
|
||||
'https://api.ip.sb/geoip': IpInfo.fromIpSbJson,
|
||||
'https://ipinfo.io/json': IpInfo.fromIpInfoIoJson,
|
||||
};
|
||||
|
||||
Future<Result<IpInfo?>> checkIp({CancelToken? cancelToken}) async {
|
||||
@@ -101,27 +92,29 @@ class Request {
|
||||
}
|
||||
}
|
||||
|
||||
final future = Dio().get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
future.then((res) {
|
||||
if (res.statusCode == HttpStatus.ok && res.data != null) {
|
||||
completer.complete(Result.success(source.value(res.data!)));
|
||||
} else {
|
||||
failureCount++;
|
||||
handleFailRes();
|
||||
}
|
||||
}).catchError((e) {
|
||||
failureCount++;
|
||||
if (e is DioException && e.type == DioExceptionType.cancel) {
|
||||
completer.complete(Result.error('cancelled'));
|
||||
}
|
||||
handleFailRes();
|
||||
});
|
||||
final future = dio
|
||||
.get<Map<String, dynamic>>(
|
||||
source.key,
|
||||
cancelToken: cancelToken,
|
||||
options: Options(responseType: ResponseType.json),
|
||||
)
|
||||
.timeout(const Duration(seconds: 10));
|
||||
future
|
||||
.then((res) {
|
||||
if (res.statusCode == HttpStatus.ok && res.data != null) {
|
||||
completer.complete(Result.success(source.value(res.data!)));
|
||||
return;
|
||||
}
|
||||
failureCount++;
|
||||
handleFailRes();
|
||||
})
|
||||
.catchError((e) {
|
||||
failureCount++;
|
||||
if (e is DioException && e.type == DioExceptionType.cancel) {
|
||||
completer.complete(Result.error('cancelled'));
|
||||
}
|
||||
handleFailRes();
|
||||
});
|
||||
return completer.future;
|
||||
});
|
||||
final res = await Future.any(futures);
|
||||
@@ -131,18 +124,12 @@ class Request {
|
||||
|
||||
Future<bool> pingHelper() async {
|
||||
try {
|
||||
final response = await _dio
|
||||
final response = await dio
|
||||
.get(
|
||||
'http://$localhost:$helperPort/ping',
|
||||
options: Options(
|
||||
responseType: ResponseType.plain,
|
||||
),
|
||||
options: Options(responseType: ResponseType.plain),
|
||||
)
|
||||
.timeout(
|
||||
const Duration(
|
||||
milliseconds: 2000,
|
||||
),
|
||||
);
|
||||
.timeout(const Duration(milliseconds: 2000));
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
return false;
|
||||
}
|
||||
@@ -154,22 +141,13 @@ class Request {
|
||||
|
||||
Future<bool> startCoreByHelper(String arg) async {
|
||||
try {
|
||||
final response = await _dio
|
||||
final response = await dio
|
||||
.post(
|
||||
'http://$localhost:$helperPort/start',
|
||||
data: json.encode({
|
||||
'path': appPath.corePath,
|
||||
'arg': arg,
|
||||
}),
|
||||
options: Options(
|
||||
responseType: ResponseType.plain,
|
||||
),
|
||||
data: json.encode({'path': appPath.corePath, 'arg': arg}),
|
||||
options: Options(responseType: ResponseType.plain),
|
||||
)
|
||||
.timeout(
|
||||
const Duration(
|
||||
milliseconds: 2000,
|
||||
),
|
||||
);
|
||||
.timeout(const Duration(milliseconds: 2000));
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
return false;
|
||||
}
|
||||
@@ -182,18 +160,12 @@ class Request {
|
||||
|
||||
Future<bool> stopCoreByHelper() async {
|
||||
try {
|
||||
final response = await _dio
|
||||
final response = await dio
|
||||
.post(
|
||||
'http://$localhost:$helperPort/stop',
|
||||
options: Options(
|
||||
responseType: ResponseType.plain,
|
||||
),
|
||||
options: Options(responseType: ResponseType.plain),
|
||||
)
|
||||
.timeout(
|
||||
const Duration(
|
||||
milliseconds: 2000,
|
||||
),
|
||||
);
|
||||
.timeout(const Duration(milliseconds: 2000));
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -8,13 +8,40 @@ import 'package:flutter/material.dart';
|
||||
class BaseScrollBehavior extends MaterialScrollBehavior {
|
||||
@override
|
||||
Set<PointerDeviceKind> get dragDevices => {
|
||||
PointerDeviceKind.touch,
|
||||
PointerDeviceKind.stylus,
|
||||
PointerDeviceKind.invertedStylus,
|
||||
PointerDeviceKind.trackpad,
|
||||
if (system.isDesktop) PointerDeviceKind.mouse,
|
||||
PointerDeviceKind.unknown,
|
||||
};
|
||||
PointerDeviceKind.touch,
|
||||
PointerDeviceKind.stylus,
|
||||
PointerDeviceKind.invertedStylus,
|
||||
PointerDeviceKind.trackpad,
|
||||
if (system.isDesktop) PointerDeviceKind.mouse,
|
||||
PointerDeviceKind.unknown,
|
||||
};
|
||||
|
||||
@override
|
||||
Widget buildScrollbar(
|
||||
BuildContext context,
|
||||
Widget child,
|
||||
ScrollableDetails details,
|
||||
) {
|
||||
switch (axisDirectionToAxis(details.direction)) {
|
||||
case Axis.horizontal:
|
||||
return child;
|
||||
case Axis.vertical:
|
||||
switch (getPlatform(context)) {
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.macOS:
|
||||
case TargetPlatform.windows:
|
||||
assert(details.controller != null);
|
||||
return CommonScrollBar(
|
||||
controller: details.controller,
|
||||
child: child,
|
||||
);
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.iOS:
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HiddenBarScrollBehavior extends BaseScrollBehavior {
|
||||
@@ -35,10 +62,7 @@ class ShowBarScrollBehavior extends BaseScrollBehavior {
|
||||
Widget child,
|
||||
ScrollableDetails details,
|
||||
) {
|
||||
return CommonScrollBar(
|
||||
controller: details.controller,
|
||||
child: child,
|
||||
);
|
||||
return CommonScrollBar(controller: details.controller, child: child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +76,9 @@ class NextClampingScrollPhysics extends ClampingScrollPhysics {
|
||||
|
||||
@override
|
||||
Simulation? createBallisticSimulation(
|
||||
ScrollMetrics position, double velocity) {
|
||||
ScrollMetrics position,
|
||||
double velocity,
|
||||
) {
|
||||
final Tolerance tolerance = toleranceFor(position);
|
||||
if (position.outOfRange) {
|
||||
double? end;
|
||||
|
||||
@@ -11,16 +11,15 @@ extension StringExtension on String {
|
||||
}
|
||||
|
||||
dynamic get splitByMultipleSeparators {
|
||||
final parts =
|
||||
split(RegExp(r'[, ;]+')).where((part) => part.isNotEmpty).toList();
|
||||
final parts = split(
|
||||
RegExp(r'[, ;]+'),
|
||||
).where((part) => part.isNotEmpty).toList();
|
||||
|
||||
return parts.length > 1 ? parts : this;
|
||||
}
|
||||
|
||||
int compareToLower(String other) {
|
||||
return toLowerCase().compareTo(
|
||||
other.toLowerCase(),
|
||||
);
|
||||
return toLowerCase().compareTo(other.toLowerCase());
|
||||
}
|
||||
|
||||
List<int> get encodeUtf16LeWithBom {
|
||||
@@ -66,9 +65,9 @@ extension StringExtension on String {
|
||||
return md5.convert(bytes).toString();
|
||||
}
|
||||
|
||||
// bool containsToLower(String target) {
|
||||
// return toLowerCase().contains(target);
|
||||
// }
|
||||
// bool containsToLower(String target) {
|
||||
// return toLowerCase().contains(target);
|
||||
// }
|
||||
}
|
||||
|
||||
extension StringExtensionSafe on String? {
|
||||
|
||||
@@ -37,7 +37,7 @@ class System {
|
||||
'macos' => (deviceInfo as MacOsDeviceInfo).majorVersion,
|
||||
'android' => (deviceInfo as AndroidDeviceInfo).version.sdkInt,
|
||||
'windows' => (deviceInfo as WindowsDeviceInfo).majorVersion,
|
||||
String() => 0
|
||||
String() => 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class System {
|
||||
);
|
||||
final arguments = [
|
||||
'-c',
|
||||
'echo "$password" | sudo -S chown root:root "$corePath" && echo "$password" | sudo -S chmod +sx "$corePath"'
|
||||
'echo "$password" | sudo -S chown root:root "$corePath" && echo "$password" | sudo -S chmod +sx "$corePath"',
|
||||
];
|
||||
final result = await Process.run(shell, arguments);
|
||||
if (result.exitCode != 0) {
|
||||
@@ -148,21 +148,25 @@ class Windows {
|
||||
final argumentsPtr = arguments.toNativeUtf16();
|
||||
final operationPtr = 'runas'.toNativeUtf16();
|
||||
|
||||
final shellExecute = _shell32.lookupFunction<
|
||||
Int32 Function(
|
||||
final shellExecute = _shell32
|
||||
.lookupFunction<
|
||||
Int32 Function(
|
||||
Pointer<Utf16> hwnd,
|
||||
Pointer<Utf16> lpOperation,
|
||||
Pointer<Utf16> lpFile,
|
||||
Pointer<Utf16> lpParameters,
|
||||
Pointer<Utf16> lpDirectory,
|
||||
Int32 nShowCmd),
|
||||
int Function(
|
||||
Int32 nShowCmd,
|
||||
),
|
||||
int Function(
|
||||
Pointer<Utf16> hwnd,
|
||||
Pointer<Utf16> lpOperation,
|
||||
Pointer<Utf16> lpFile,
|
||||
Pointer<Utf16> lpParameters,
|
||||
Pointer<Utf16> lpDirectory,
|
||||
int nShowCmd)>('ShellExecuteW');
|
||||
int nShowCmd,
|
||||
)
|
||||
>('ShellExecuteW');
|
||||
|
||||
final result = shellExecute(
|
||||
nullptr,
|
||||
@@ -177,7 +181,10 @@ class Windows {
|
||||
calloc.free(argumentsPtr);
|
||||
calloc.free(operationPtr);
|
||||
|
||||
commonPrint.log('windows runas: $command $arguments resultCode:$result');
|
||||
commonPrint.log(
|
||||
'windows runas: $command $arguments resultCode:$result',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
|
||||
if (result < 42) {
|
||||
return false;
|
||||
@@ -248,15 +255,14 @@ class Windows {
|
||||
|
||||
final res = runas('cmd.exe', command);
|
||||
|
||||
await Future.delayed(
|
||||
Duration(milliseconds: 300),
|
||||
);
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<bool> registerTask(String appName) async {
|
||||
final taskXml = '''
|
||||
final taskXml =
|
||||
'''
|
||||
<?xml version="1.0" encoding="UTF-16"?>
|
||||
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
||||
<Principals>
|
||||
@@ -295,8 +301,9 @@ class Windows {
|
||||
</Task>''';
|
||||
final taskPath = join(await appPath.tempPath, 'task.xml');
|
||||
await File(taskPath).create(recursive: true);
|
||||
await File(taskPath)
|
||||
.writeAsBytes(taskXml.encodeUtf16LeWithBom, flush: true);
|
||||
await File(
|
||||
taskPath,
|
||||
).writeAsBytes(taskXml.encodeUtf16LeWithBom, flush: true);
|
||||
final commandLine = [
|
||||
'/Create',
|
||||
'/TN',
|
||||
@@ -305,10 +312,7 @@ class Windows {
|
||||
'%s',
|
||||
'/F',
|
||||
].join(' ');
|
||||
return runas(
|
||||
'schtasks',
|
||||
commandLine.replaceFirst('%s', taskPath),
|
||||
);
|
||||
return runas('schtasks', commandLine.replaceFirst('%s', taskPath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,23 +341,25 @@ class MacOS {
|
||||
return null;
|
||||
}
|
||||
final device = lineSplits[1];
|
||||
final serviceResult = await Process.run(
|
||||
'networksetup',
|
||||
['-listnetworkserviceorder'],
|
||||
);
|
||||
final serviceResult = await Process.run('networksetup', [
|
||||
'-listnetworkserviceorder',
|
||||
]);
|
||||
final serviceResultOutput = serviceResult.stdout.toString();
|
||||
final currentService = serviceResultOutput.split('\n\n').firstWhere(
|
||||
(s) => s.contains('Device: $device'),
|
||||
orElse: () => '',
|
||||
);
|
||||
final currentService = serviceResultOutput
|
||||
.split('\n\n')
|
||||
.firstWhere((s) => s.contains('Device: $device'), orElse: () => '');
|
||||
if (currentService.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final currentServiceNameLine = currentService.split('\n').firstWhere(
|
||||
(line) => RegExp(r'^\(\d+\).*').hasMatch(line),
|
||||
orElse: () => '');
|
||||
final currentServiceNameLineSplits =
|
||||
currentServiceNameLine.trim().split(' ');
|
||||
final currentServiceNameLine = currentService
|
||||
.split('\n')
|
||||
.firstWhere(
|
||||
(line) => RegExp(r'^\(\d+\).*').hasMatch(line),
|
||||
orElse: () => '',
|
||||
);
|
||||
final currentServiceNameLineSplits = currentServiceNameLine.trim().split(
|
||||
' ',
|
||||
);
|
||||
if (currentServiceNameLineSplits.length < 2) {
|
||||
return null;
|
||||
}
|
||||
@@ -365,10 +371,10 @@ class MacOS {
|
||||
if (deviceServiceName == null) {
|
||||
return null;
|
||||
}
|
||||
final result = await Process.run(
|
||||
'networksetup',
|
||||
['-getdnsservers', deviceServiceName],
|
||||
);
|
||||
final result = await Process.run('networksetup', [
|
||||
'-getdnsservers',
|
||||
deviceServiceName,
|
||||
]);
|
||||
final output = result.stdout.toString().trim();
|
||||
if (output.startsWith("There aren't any DNS Servers set on")) {
|
||||
originDns = [];
|
||||
@@ -400,15 +406,12 @@ class MacOS {
|
||||
if (nextDns == null) {
|
||||
return;
|
||||
}
|
||||
await Process.run(
|
||||
'networksetup',
|
||||
[
|
||||
'-setdnsservers',
|
||||
serviceName,
|
||||
if (nextDns.isNotEmpty) ...nextDns,
|
||||
if (nextDns.isEmpty) 'Empty',
|
||||
],
|
||||
);
|
||||
await Process.run('networksetup', [
|
||||
'-setdnsservers',
|
||||
serviceName,
|
||||
if (nextDns.isNotEmpty) ...nextDns,
|
||||
if (nextDns.isEmpty) 'Empty',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ class Tray {
|
||||
required Brightness? brightness,
|
||||
bool force = false,
|
||||
}) async {
|
||||
if (system.isAndroid) {
|
||||
return;
|
||||
}
|
||||
if (Platform.isLinux || force) {
|
||||
await trayManager.destroy();
|
||||
}
|
||||
@@ -197,4 +194,4 @@ class Tray {
|
||||
}
|
||||
}
|
||||
|
||||
final tray = Tray();
|
||||
final tray = system.isDesktop ? Tray() : null;
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:lpinyin/lpinyin.dart';
|
||||
|
||||
class Utils {
|
||||
Color? getDelayColor(int? delay) {
|
||||
@@ -21,16 +20,15 @@ class Utils {
|
||||
String get id {
|
||||
final timestamp = DateTime.now().microsecondsSinceEpoch;
|
||||
final random = Random();
|
||||
final randomStr =
|
||||
String.fromCharCodes(List.generate(8, (_) => random.nextInt(26) + 97));
|
||||
final randomStr = String.fromCharCodes(
|
||||
List.generate(8, (_) => random.nextInt(26) + 97),
|
||||
);
|
||||
return '$timestamp$randomStr';
|
||||
}
|
||||
|
||||
String getDateStringLast2(int value) {
|
||||
var valueRaw = '0$value';
|
||||
return valueRaw.substring(
|
||||
valueRaw.length - 2,
|
||||
);
|
||||
return valueRaw.substring(valueRaw.length - 2);
|
||||
}
|
||||
|
||||
String generateRandomString({int minLength = 10, int maxLength = 100}) {
|
||||
@@ -43,8 +41,9 @@ class Utils {
|
||||
String result = '';
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (random.nextBool()) {
|
||||
result +=
|
||||
String.fromCharCode(0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1));
|
||||
result += String.fromCharCode(
|
||||
0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1),
|
||||
);
|
||||
} else {
|
||||
result += latinChars[random.nextInt(latinChars.length)];
|
||||
}
|
||||
@@ -60,8 +59,9 @@ class Utils {
|
||||
bytes[6] = (bytes[6] & 0x0F) | 0x40;
|
||||
bytes[8] = (bytes[8] & 0x3F) | 0x80;
|
||||
|
||||
final hex =
|
||||
bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
|
||||
final hex = bytes
|
||||
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
|
||||
.join();
|
||||
|
||||
return '${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20, 32)}';
|
||||
}
|
||||
@@ -102,9 +102,10 @@ class Utils {
|
||||
}
|
||||
if (localSplit.length == 3) {
|
||||
return Locale.fromSubtags(
|
||||
languageCode: localSplit[0],
|
||||
scriptCode: localSplit[1],
|
||||
countryCode: localSplit[2]);
|
||||
languageCode: localSplit[0],
|
||||
scriptCode: localSplit[1],
|
||||
countryCode: localSplit[2],
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -141,9 +142,7 @@ class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
String getTrayIconPath({
|
||||
required Brightness brightness,
|
||||
}) {
|
||||
String getTrayIconPath({required Brightness brightness}) {
|
||||
if (system.isMacOS) {
|
||||
return 'assets/images/icon_white.png';
|
||||
}
|
||||
@@ -178,26 +177,30 @@ class Utils {
|
||||
return build1.compareTo(build2);
|
||||
}
|
||||
|
||||
String getPinyin(String value) {
|
||||
return value.isNotEmpty
|
||||
? PinyinHelper.getFirstWordPinyin(value.substring(0, 1))
|
||||
: '';
|
||||
}
|
||||
// String getPinyin(String value) {
|
||||
// return value.isNotEmpty
|
||||
// ? PinyinHelper.getFirstWordPinyin(value.substring(0, 1))
|
||||
// : '';
|
||||
// }
|
||||
|
||||
String? getFileNameForDisposition(String? disposition) {
|
||||
if (disposition == null) return null;
|
||||
final parseValue = HeaderValue.parse(disposition);
|
||||
final parameters = parseValue.parameters;
|
||||
final fileNamePointKey = parameters.keys
|
||||
.firstWhere((key) => key == 'filename*', orElse: () => '');
|
||||
final fileNamePointKey = parameters.keys.firstWhere(
|
||||
(key) => key == 'filename*',
|
||||
orElse: () => '',
|
||||
);
|
||||
if (fileNamePointKey.isNotEmpty) {
|
||||
final res = parameters[fileNamePointKey]?.split("''") ?? [];
|
||||
if (res.length >= 2) {
|
||||
return Uri.decodeComponent(res[1]);
|
||||
}
|
||||
}
|
||||
final fileNameKey = parameters.keys
|
||||
.firstWhere((key) => key == 'filename', orElse: () => '');
|
||||
final fileNameKey = parameters.keys.firstWhere(
|
||||
(key) => key == 'filename',
|
||||
orElse: () => '',
|
||||
);
|
||||
if (fileNameKey.isEmpty) return null;
|
||||
return parameters[fileNameKey];
|
||||
}
|
||||
@@ -224,7 +227,7 @@ class Utils {
|
||||
}
|
||||
|
||||
int getProxiesColumns(double viewWidth, ProxiesLayout proxiesLayout) {
|
||||
final columns = max((viewWidth / 300).ceil(), 2);
|
||||
final columns = max((viewWidth / 250).ceil(), 2);
|
||||
return switch (proxiesLayout) {
|
||||
ProxiesLayout.tight => columns + 1,
|
||||
ProxiesLayout.standard => columns,
|
||||
@@ -233,22 +236,10 @@ class Utils {
|
||||
}
|
||||
|
||||
int getProfilesColumns(double viewWidth) {
|
||||
return max((viewWidth / 320).floor(), 1);
|
||||
return max((viewWidth / 280).floor(), 1);
|
||||
}
|
||||
|
||||
final _indexPrimary = [
|
||||
50,
|
||||
100,
|
||||
200,
|
||||
300,
|
||||
400,
|
||||
500,
|
||||
600,
|
||||
700,
|
||||
800,
|
||||
850,
|
||||
900,
|
||||
];
|
||||
final _indexPrimary = [50, 100, 200, 300, 400, 500, 600, 700, 800, 850, 900];
|
||||
|
||||
MaterialColor _createPrimarySwatch(Color color) {
|
||||
final Map<int, Color> swatch = <int, Color>{};
|
||||
@@ -302,16 +293,15 @@ class Utils {
|
||||
}
|
||||
|
||||
Future<String?> getLocalIpAddress() async {
|
||||
List<NetworkInterface> interfaces = await NetworkInterface.list(
|
||||
includeLoopback: false,
|
||||
)
|
||||
..sort((a, b) {
|
||||
if (a.isWifi && !b.isWifi) return -1;
|
||||
if (!a.isWifi && b.isWifi) return 1;
|
||||
if (a.includesIPv4 && !b.includesIPv4) return -1;
|
||||
if (!a.includesIPv4 && b.includesIPv4) return 1;
|
||||
return 0;
|
||||
});
|
||||
List<NetworkInterface> interfaces =
|
||||
await NetworkInterface.list(includeLoopback: false)
|
||||
..sort((a, b) {
|
||||
if (a.isWifi && !b.isWifi) return -1;
|
||||
if (!a.isWifi && b.isWifi) return 1;
|
||||
if (a.includesIPv4 && !b.includesIPv4) return -1;
|
||||
if (!a.includesIPv4 && b.includesIPv4) return 1;
|
||||
return 0;
|
||||
});
|
||||
for (final interface in interfaces) {
|
||||
final addresses = interface.addresses;
|
||||
if (addresses.isEmpty) {
|
||||
@@ -329,59 +319,9 @@ class Utils {
|
||||
|
||||
SingleActivator controlSingleActivator(LogicalKeyboardKey trigger) {
|
||||
final control = system.isMacOS ? false : true;
|
||||
return SingleActivator(
|
||||
trigger,
|
||||
control: control,
|
||||
meta: !control,
|
||||
);
|
||||
return SingleActivator(trigger, control: control, meta: !control);
|
||||
}
|
||||
|
||||
// dynamic convertYamlNode(dynamic node) {
|
||||
// if (node is YamlMap) {
|
||||
// final map = <String, dynamic>{};
|
||||
// YamlNode? mergeKeyNode;
|
||||
// for (final entry in node.nodes.entries) {
|
||||
// if (entry.key is YamlScalar &&
|
||||
// (entry.key as YamlScalar).value == '<<') {
|
||||
// mergeKeyNode = entry.value;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (mergeKeyNode != null) {
|
||||
// final mergeValue = mergeKeyNode.value;
|
||||
// if (mergeValue is YamlMap) {
|
||||
// map.addAll(convertYamlNode(mergeValue) as Map<String, dynamic>);
|
||||
// } else if (mergeValue is YamlList) {
|
||||
// for (final node in mergeValue.nodes) {
|
||||
// if (node.value is YamlMap) {
|
||||
// map.addAll(convertYamlNode(node.value) as Map<String, dynamic>);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// node.nodes.forEach((key, value) {
|
||||
// String stringKey;
|
||||
// if (key is YamlScalar) {
|
||||
// stringKey = key.value.toString();
|
||||
// } else {
|
||||
// stringKey = key.toString();
|
||||
// }
|
||||
// map[stringKey] = convertYamlNode(value.value);
|
||||
// });
|
||||
// return map;
|
||||
// } else if (node is YamlList) {
|
||||
// final list = <dynamic>[];
|
||||
// for (final item in node.nodes) {
|
||||
// list.add(convertYamlNode(item.value));
|
||||
// }
|
||||
// return list;
|
||||
// } else if (node is YamlScalar) {
|
||||
// return node.value;
|
||||
// }
|
||||
// return node;
|
||||
// }
|
||||
|
||||
FutureOr<T> handleWatch<T>(Function function) async {
|
||||
if (kDebugMode) {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:io';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.dart' as acrylic;
|
||||
import 'package:screen_retriever/screen_retriever.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
@@ -19,9 +18,6 @@ class Window {
|
||||
protocol.register('clashmeta');
|
||||
protocol.register('flclash');
|
||||
}
|
||||
if ((version > 10 && system.isMacOS)) {
|
||||
await acrylic.Window.initialize();
|
||||
}
|
||||
await windowManager.ensureInitialized();
|
||||
WindowOptions windowOptions = WindowOptions(
|
||||
size: Size(props.width, props.height),
|
||||
@@ -39,25 +35,18 @@ class Window {
|
||||
await windowManager.setAlignment(Alignment.center);
|
||||
} else {
|
||||
final displays = await screenRetriever.getAllDisplays();
|
||||
final isPositionValid = displays.any(
|
||||
(display) {
|
||||
final displayBounds = Rect.fromLTWH(
|
||||
display.visiblePosition!.dx,
|
||||
display.visiblePosition!.dy,
|
||||
display.size.width,
|
||||
display.size.height,
|
||||
);
|
||||
return displayBounds.contains(Offset(left, top)) ||
|
||||
displayBounds.contains(Offset(right, bottom));
|
||||
},
|
||||
);
|
||||
if (isPositionValid) {
|
||||
await windowManager.setPosition(
|
||||
Offset(
|
||||
left,
|
||||
top,
|
||||
),
|
||||
final isPositionValid = displays.any((display) {
|
||||
final displayBounds = Rect.fromLTWH(
|
||||
display.visiblePosition!.dx,
|
||||
display.visiblePosition!.dy,
|
||||
display.size.width,
|
||||
display.size.height,
|
||||
);
|
||||
return displayBounds.contains(Offset(left, top)) ||
|
||||
displayBounds.contains(Offset(right, bottom));
|
||||
});
|
||||
if (isPositionValid) {
|
||||
await windowManager.setPosition(Offset(left, top));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,13 +55,6 @@ class Window {
|
||||
});
|
||||
}
|
||||
|
||||
void updateMacOSBrightness(Brightness brightness) {
|
||||
if (!system.isMacOS) {
|
||||
return;
|
||||
}
|
||||
acrylic.Window.overrideMacOSBrightness(dark: brightness == Brightness.dark);
|
||||
}
|
||||
|
||||
Future<void> show() async {
|
||||
render?.resume();
|
||||
await windowManager.show();
|
||||
|
||||
@@ -4,8 +4,8 @@ import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/archive.dart';
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
@@ -20,7 +20,6 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'common/common.dart';
|
||||
import 'models/models.dart';
|
||||
import 'views/profiles/override_profile.dart';
|
||||
|
||||
class AppController {
|
||||
int? lastProfileModified;
|
||||
@@ -42,8 +41,8 @@ class AppController {
|
||||
});
|
||||
}
|
||||
|
||||
void updateGroupsDebounce() {
|
||||
debouncer.call(FunctionTag.updateGroups, updateGroups);
|
||||
void updateGroupsDebounce([Duration? duration]) {
|
||||
debouncer.call(FunctionTag.updateGroups, updateGroups, duration: duration);
|
||||
}
|
||||
|
||||
void addCheckIpNumDebounce() {
|
||||
@@ -52,9 +51,7 @@ class AppController {
|
||||
});
|
||||
}
|
||||
|
||||
void applyProfileDebounce({
|
||||
bool silence = false,
|
||||
}) {
|
||||
void applyProfileDebounce({bool silence = false}) {
|
||||
debouncer.call(FunctionTag.applyProfile, (silence) {
|
||||
applyProfile(silence: silence);
|
||||
}, args: [silence]);
|
||||
@@ -65,33 +62,32 @@ class AppController {
|
||||
}
|
||||
|
||||
void changeProxyDebounce(String groupName, String proxyName) {
|
||||
debouncer.call(FunctionTag.changeProxy,
|
||||
(String groupName, String proxyName) async {
|
||||
await changeProxy(
|
||||
groupName: groupName,
|
||||
proxyName: proxyName,
|
||||
);
|
||||
debouncer.call(FunctionTag.changeProxy, (
|
||||
String groupName,
|
||||
String proxyName,
|
||||
) async {
|
||||
await changeProxy(groupName: groupName, proxyName: proxyName);
|
||||
await updateGroups();
|
||||
}, args: [groupName, proxyName]);
|
||||
}
|
||||
|
||||
Future<void> restartCore() async {
|
||||
commonPrint.log('restart core');
|
||||
await clashService?.reStart();
|
||||
globalState.isUserDisconnected = true;
|
||||
await coreController.shutdown();
|
||||
await _connectCore();
|
||||
await _initCore();
|
||||
if (_ref.read(runTimeProvider.notifier).isStart) {
|
||||
_ref.read(initProvider.notifier).value = true;
|
||||
if (_ref.read(isStartProvider)) {
|
||||
await globalState.handleStart();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateStatus(bool isStart) async {
|
||||
if (isStart) {
|
||||
await globalState.handleStart([
|
||||
updateRunTime,
|
||||
updateTraffic,
|
||||
]);
|
||||
final currentLastModified =
|
||||
await _ref.read(currentProfileProvider)?.profileLastModified;
|
||||
await globalState.handleStart([updateRunTime, updateTraffic]);
|
||||
final currentLastModified = await _ref
|
||||
.read(currentProfileProvider)
|
||||
?.profileLastModified;
|
||||
if (currentLastModified == null || lastProfileModified == null) {
|
||||
addCheckIpNumDebounce();
|
||||
return;
|
||||
@@ -103,7 +99,7 @@ class AppController {
|
||||
applyProfileDebounce();
|
||||
} else {
|
||||
await globalState.handleStop();
|
||||
clashCore.resetTraffic();
|
||||
coreController.resetTraffic();
|
||||
_ref.read(trafficsProvider.notifier).clear();
|
||||
_ref.read(totalTrafficProvider.notifier).value = Traffic();
|
||||
_ref.read(runTimeProvider.notifier).value = null;
|
||||
@@ -123,10 +119,13 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> updateTraffic() async {
|
||||
final traffic = await clashCore.getTraffic();
|
||||
final onlyStatisticsProxy = _ref.read(
|
||||
appSettingProvider.select((state) => state.onlyStatisticsProxy),
|
||||
);
|
||||
final traffic = await coreController.getTraffic(onlyStatisticsProxy);
|
||||
_ref.read(trafficsProvider.notifier).addTraffic(traffic);
|
||||
_ref.read(totalTrafficProvider.notifier).value =
|
||||
await clashCore.getTotalTraffic();
|
||||
_ref.read(totalTrafficProvider.notifier).value = await coreController
|
||||
.getTotalTraffic(onlyStatisticsProxy);
|
||||
}
|
||||
|
||||
Future<void> addProfile(Profile profile) async {
|
||||
@@ -152,8 +151,8 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> updateProviders() async {
|
||||
_ref.read(providersProvider.notifier).value =
|
||||
await clashCore.getExternalProviders();
|
||||
_ref.read(providersProvider.notifier).value = await coreController
|
||||
.getExternalProviders();
|
||||
}
|
||||
|
||||
Future<void> updateLocalIp() async {
|
||||
@@ -193,8 +192,9 @@ class AppController {
|
||||
|
||||
void updateOrAddHotKeyAction(HotKeyAction hotKeyAction) {
|
||||
final hotKeyActions = _ref.read(hotKeyActionsProvider);
|
||||
final index =
|
||||
hotKeyActions.indexWhere((item) => item.action == hotKeyAction.action);
|
||||
final index = hotKeyActions.indexWhere(
|
||||
(item) => item.action == hotKeyAction.action,
|
||||
);
|
||||
if (index == -1) {
|
||||
_ref.read(hotKeyActionsProvider.notifier).value = List.from(hotKeyActions)
|
||||
..add(hotKeyAction);
|
||||
@@ -225,16 +225,12 @@ class AppController {
|
||||
}
|
||||
|
||||
String? getCurrentGroupName() {
|
||||
final currentGroupName = _ref.read(currentProfileProvider.select(
|
||||
(state) => state?.currentGroupName,
|
||||
));
|
||||
final currentGroupName = _ref.read(
|
||||
currentProfileProvider.select((state) => state?.currentGroupName),
|
||||
);
|
||||
return currentGroupName;
|
||||
}
|
||||
|
||||
ProxyCardState getProxyCardState(String proxyName) {
|
||||
return _ref.read(getProxyCardStateProvider(proxyName));
|
||||
}
|
||||
|
||||
String? getSelectedProxyName(String groupName) {
|
||||
return _ref.read(getSelectedProxyNameProvider(groupName));
|
||||
}
|
||||
@@ -244,18 +240,13 @@ class AppController {
|
||||
if (profile == null || profile.currentGroupName == groupName) {
|
||||
return;
|
||||
}
|
||||
setProfile(
|
||||
profile.copyWith(currentGroupName: groupName),
|
||||
);
|
||||
setProfile(profile.copyWith(currentGroupName: groupName));
|
||||
}
|
||||
|
||||
Future<void> updateClashConfig() async {
|
||||
await safeRun(
|
||||
() async {
|
||||
await _updateClashConfig();
|
||||
},
|
||||
needLoading: true,
|
||||
);
|
||||
await safeRun(() async {
|
||||
await _updateClashConfig();
|
||||
}, needLoading: true);
|
||||
}
|
||||
|
||||
Future<void> _updateClashConfig() async {
|
||||
@@ -265,16 +256,14 @@ class AppController {
|
||||
return;
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
final message = await clashCore.updateConfig(
|
||||
updateParams.copyWith.tun(
|
||||
enable: realTunEnable,
|
||||
),
|
||||
final message = await coreController.updateConfig(
|
||||
updateParams.copyWith.tun(enable: realTunEnable),
|
||||
);
|
||||
if (message.isNotEmpty) throw message;
|
||||
}
|
||||
|
||||
Future<Result<bool>> _requestAdmin(bool enableTun) async {
|
||||
if(system.isWindows && kDebugMode){
|
||||
if (system.isWindows && kDebugMode) {
|
||||
return Result.success(false);
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
@@ -296,12 +285,9 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> setupClashConfig() async {
|
||||
await safeRun(
|
||||
() async {
|
||||
await _setupClashConfig();
|
||||
},
|
||||
needLoading: true,
|
||||
);
|
||||
await safeRun(() async {
|
||||
await _setupClashConfig();
|
||||
}, needLoading: true);
|
||||
}
|
||||
|
||||
Future<void> _setupClashConfig() async {
|
||||
@@ -313,14 +299,9 @@ class AppController {
|
||||
}
|
||||
final realTunEnable = _ref.read(realTunEnableProvider);
|
||||
final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable);
|
||||
final params = await globalState.getSetupParams(
|
||||
pathConfig: realPatchConfig,
|
||||
);
|
||||
final message = await clashCore.setupConfig(params);
|
||||
final message = await coreController.setupConfig(realPatchConfig);
|
||||
lastProfileModified = await _ref.read(
|
||||
currentProfileProvider.select(
|
||||
(state) => state?.profileLastModified,
|
||||
),
|
||||
currentProfileProvider.select((state) => state?.profileLastModified),
|
||||
);
|
||||
if (message.isNotEmpty) {
|
||||
throw message;
|
||||
@@ -328,7 +309,7 @@ class AppController {
|
||||
}
|
||||
|
||||
Future _applyProfile() async {
|
||||
await clashCore.requestGc();
|
||||
await coreController.requestGc();
|
||||
await setupClashConfig();
|
||||
await updateGroups();
|
||||
await updateProviders();
|
||||
@@ -338,12 +319,9 @@ class AppController {
|
||||
if (silence) {
|
||||
await _applyProfile();
|
||||
} else {
|
||||
await safeRun(
|
||||
() async {
|
||||
await _applyProfile();
|
||||
},
|
||||
needLoading: true,
|
||||
);
|
||||
await safeRun(() async {
|
||||
await _applyProfile();
|
||||
}, needLoading: true);
|
||||
}
|
||||
addCheckIpNumDebounce();
|
||||
}
|
||||
@@ -367,9 +345,7 @@ class AppController {
|
||||
for (final profile in _ref.read(profilesProvider)) {
|
||||
if (!profile.autoUpdate) continue;
|
||||
final isNotNeedUpdate = profile.lastUpdateDate
|
||||
?.add(
|
||||
profile.autoUpdateDuration,
|
||||
)
|
||||
?.add(profile.autoUpdateDuration)
|
||||
.isBeforeNow;
|
||||
if (isNotNeedUpdate == false || profile.type == ProfileType.file) {
|
||||
continue;
|
||||
@@ -377,7 +353,7 @@ class AppController {
|
||||
try {
|
||||
await updateProfile(profile);
|
||||
} catch (e) {
|
||||
commonPrint.log(e.toString());
|
||||
commonPrint.log(e.toString(), logLevel: LogLevel.warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,7 +362,22 @@ class AppController {
|
||||
try {
|
||||
_ref.read(groupsProvider.notifier).value = await retry(
|
||||
task: () async {
|
||||
return await clashCore.getProxiesGroups();
|
||||
final sortType = _ref.read(
|
||||
proxiesStyleSettingProvider.select((state) => state.sortType),
|
||||
);
|
||||
final delayMap = _ref.read(delayDataSourceProvider);
|
||||
final testUrl = _ref.read(
|
||||
appSettingProvider.select((state) => state.testUrl),
|
||||
);
|
||||
final selectedMap = _ref.read(
|
||||
currentProfileProvider.select((state) => state?.selectedMap ?? {}),
|
||||
);
|
||||
return await coreController.getProxiesGroups(
|
||||
selectedMap: selectedMap,
|
||||
sortType: sortType,
|
||||
delayMap: delayMap,
|
||||
defaultTestUrl: testUrl,
|
||||
);
|
||||
},
|
||||
retryIf: (res) => res.isEmpty,
|
||||
);
|
||||
@@ -413,14 +404,11 @@ class AppController {
|
||||
required String groupName,
|
||||
required String proxyName,
|
||||
}) async {
|
||||
await clashCore.changeProxy(
|
||||
ChangeProxyParams(
|
||||
groupName: groupName,
|
||||
proxyName: proxyName,
|
||||
),
|
||||
await coreController.changeProxy(
|
||||
ChangeProxyParams(groupName: groupName, proxyName: proxyName),
|
||||
);
|
||||
if (_ref.read(appSettingProvider).closeConnections) {
|
||||
clashCore.closeConnections();
|
||||
coreController.closeConnections();
|
||||
}
|
||||
addCheckIpNumDebounce();
|
||||
}
|
||||
@@ -455,8 +443,8 @@ class AppController {
|
||||
await savePreferences();
|
||||
await macOS?.updateDns(true);
|
||||
await proxy?.stopProxy();
|
||||
await clashCore.shutdown();
|
||||
await clashService?.destroy();
|
||||
await coreController.shutdown();
|
||||
await coreController.destroy();
|
||||
} finally {
|
||||
system.exit();
|
||||
}
|
||||
@@ -465,9 +453,7 @@ class AppController {
|
||||
Future handleClear() async {
|
||||
await preferences.clearPreferences();
|
||||
commonPrint.log('clear preferences');
|
||||
globalState.config = Config(
|
||||
themeProps: defaultThemeProps,
|
||||
);
|
||||
globalState.config = Config(themeProps: defaultThemeProps);
|
||||
}
|
||||
|
||||
Future<void> autoCheckUpdate() async {
|
||||
@@ -494,15 +480,9 @@ class AppController {
|
||||
text: '$tagName \n',
|
||||
style: textTheme.headlineSmall,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '\n',
|
||||
style: textTheme.bodyMedium,
|
||||
),
|
||||
TextSpan(text: '\n', style: textTheme.bodyMedium),
|
||||
for (final submit in submits)
|
||||
TextSpan(
|
||||
text: '- $submit \n',
|
||||
style: textTheme.bodyMedium,
|
||||
),
|
||||
TextSpan(text: '- $submit \n', style: textTheme.bodyMedium),
|
||||
],
|
||||
),
|
||||
confirmText: appLocalizations.goDownload,
|
||||
@@ -510,15 +490,11 @@ class AppController {
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
launchUrl(
|
||||
Uri.parse('https://github.com/$repository/releases/latest'),
|
||||
);
|
||||
launchUrl(Uri.parse('https://github.com/$repository/releases/latest'));
|
||||
} else if (handleError) {
|
||||
globalState.showMessage(
|
||||
title: appLocalizations.checkUpdate,
|
||||
message: TextSpan(
|
||||
text: appLocalizations.checkUpdateError,
|
||||
),
|
||||
message: TextSpan(text: appLocalizations.checkUpdateError),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -542,30 +518,24 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> _initCore() async {
|
||||
final isInit = await clashCore.isInit;
|
||||
final isInit = await coreController.isInit;
|
||||
if (!isInit) {
|
||||
await clashCore.init();
|
||||
await clashCore.setState(
|
||||
globalState.getCoreState(),
|
||||
);
|
||||
await coreController.init(globalState.appState.version);
|
||||
}
|
||||
await applyProfile();
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
FlutterError.onError = (details) {
|
||||
if (kDebugMode) {
|
||||
commonPrint.log(details.stack.toString());
|
||||
}
|
||||
commonPrint.log(
|
||||
'exception: ${details.exception} stack: ${details.stack}',
|
||||
logLevel: LogLevel.warning,
|
||||
);
|
||||
};
|
||||
updateTray(true);
|
||||
await _initCore();
|
||||
await _initStatus();
|
||||
autoLaunch?.updateStatus(
|
||||
_ref.read(appSettingProvider).autoLaunch,
|
||||
);
|
||||
autoUpdateProfiles();
|
||||
autoCheckUpdate();
|
||||
autoLaunch?.updateStatus(_ref.read(appSettingProvider).autoLaunch);
|
||||
if (!_ref.read(appSettingProvider).silentLaunch) {
|
||||
window?.show();
|
||||
} else {
|
||||
@@ -573,9 +543,31 @@ class AppController {
|
||||
}
|
||||
await _handlePreference();
|
||||
await _handlerDisclaimer();
|
||||
await _showCrashlyticsTip();
|
||||
await _connectCore();
|
||||
await _initCore();
|
||||
await _initStatus();
|
||||
_ref.read(initProvider.notifier).value = true;
|
||||
}
|
||||
|
||||
Future<void> _connectCore() async {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connecting;
|
||||
final result = await Future.wait([
|
||||
coreController.preload(),
|
||||
if (!globalState.isService) Future.delayed(Duration(milliseconds: 300)),
|
||||
]);
|
||||
final String message = result[0];
|
||||
await Future.delayed(commonDuration);
|
||||
if (message.isNotEmpty) {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
if (context.mounted) {
|
||||
context.showNotifier(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.connected;
|
||||
}
|
||||
|
||||
Future<void> _initStatus() async {
|
||||
if (system.isAndroid) {
|
||||
await globalState.updateStartTime();
|
||||
@@ -603,34 +595,32 @@ class AppController {
|
||||
}
|
||||
|
||||
void initLink() {
|
||||
linkManager.initAppLinksListen(
|
||||
(url) async {
|
||||
final res = await globalState.showMessage(
|
||||
title: '${appLocalizations.add}${appLocalizations.profile}',
|
||||
message: TextSpan(
|
||||
children: [
|
||||
TextSpan(text: appLocalizations.doYouWantToPass),
|
||||
TextSpan(
|
||||
text: ' $url ',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
linkManager.initAppLinksListen((url) async {
|
||||
final res = await globalState.showMessage(
|
||||
title: '${appLocalizations.add}${appLocalizations.profile}',
|
||||
message: TextSpan(
|
||||
children: [
|
||||
TextSpan(text: appLocalizations.doYouWantToPass),
|
||||
TextSpan(
|
||||
text: ' $url ',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
TextSpan(
|
||||
text:
|
||||
'${appLocalizations.create}${appLocalizations.profile}'),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
TextSpan(
|
||||
text: '${appLocalizations.create}${appLocalizations.profile}',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
addProfileFormURL(url);
|
||||
},
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
addProfileFormURL(url);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> showDisclaimer() async {
|
||||
@@ -647,30 +637,47 @@ class AppController {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
_ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(disclaimerAccepted: true),
|
||||
);
|
||||
Navigator.of(context).pop<bool>(true);
|
||||
},
|
||||
child: Text(appLocalizations.agree),
|
||||
)
|
||||
),
|
||||
],
|
||||
child: SelectableText(
|
||||
appLocalizations.disclaimerDesc,
|
||||
),
|
||||
child: Text(appLocalizations.disclaimerDesc),
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
Future<void> _showCrashlyticsTip() async {
|
||||
if (!system.isAndroid) {
|
||||
return;
|
||||
}
|
||||
if (_ref.read(appSettingProvider.select((state) => state.crashlyticsTip))) {
|
||||
return;
|
||||
}
|
||||
await globalState.showMessage(
|
||||
title: appLocalizations.dataCollectionTip,
|
||||
cancelable: false,
|
||||
message: TextSpan(text: appLocalizations.dataCollectionContent),
|
||||
);
|
||||
_ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(crashlyticsTip: true));
|
||||
}
|
||||
|
||||
Future<void> _handlerDisclaimer() async {
|
||||
if (_ref.read(appSettingProvider).disclaimerAccepted) {
|
||||
if (_ref.read(
|
||||
appSettingProvider.select((state) => state.disclaimerAccepted),
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
final isDisclaimerAccepted = await showDisclaimer();
|
||||
if (!isDisclaimerAccepted) {
|
||||
await handleExit();
|
||||
}
|
||||
_ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(disclaimerAccepted: true));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -682,9 +689,7 @@ class AppController {
|
||||
|
||||
final profile = await safeRun(
|
||||
() async {
|
||||
return await Profile.normal(
|
||||
url: url,
|
||||
).update();
|
||||
return await Profile.normal(url: url).update();
|
||||
},
|
||||
needLoading: true,
|
||||
title: '${appLocalizations.add}${appLocalizations.profile}',
|
||||
@@ -718,9 +723,7 @@ class AppController {
|
||||
}
|
||||
|
||||
Future<void> addProfileFormQrCode() async {
|
||||
final url = await safeRun(
|
||||
picker.pickerConfigQRCode,
|
||||
);
|
||||
final url = await safeRun(picker.pickerConfigQRCode);
|
||||
if (url == null) return;
|
||||
addProfileFormURL(url);
|
||||
}
|
||||
@@ -735,63 +738,7 @@ class AppController {
|
||||
_ref.read(providersProvider.notifier).setProvider(provider);
|
||||
}
|
||||
|
||||
List<Proxy> _sortOfName(List<Proxy> proxies) {
|
||||
return List.of(proxies)
|
||||
..sort(
|
||||
(a, b) => utils.sortByChar(
|
||||
utils.getPinyin(a.name),
|
||||
utils.getPinyin(b.name),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Proxy> _sortOfDelay({
|
||||
required List<Proxy> proxies,
|
||||
String? testUrl,
|
||||
}) {
|
||||
return List.of(proxies)
|
||||
..sort(
|
||||
(a, b) {
|
||||
final aDelay = _ref.read(getDelayProvider(
|
||||
proxyName: a.name,
|
||||
testUrl: testUrl,
|
||||
));
|
||||
final bDelay = _ref.read(
|
||||
getDelayProvider(
|
||||
proxyName: b.name,
|
||||
testUrl: testUrl,
|
||||
),
|
||||
);
|
||||
if (aDelay == null && bDelay == null) {
|
||||
return 0;
|
||||
}
|
||||
if (aDelay == null || aDelay == -1) {
|
||||
return 1;
|
||||
}
|
||||
if (bDelay == null || bDelay == -1) {
|
||||
return -1;
|
||||
}
|
||||
return aDelay.compareTo(bDelay);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<Proxy> getSortProxies({
|
||||
required List<Proxy> proxies,
|
||||
required ProxiesSortType sortType,
|
||||
String? testUrl,
|
||||
}) {
|
||||
return switch (sortType) {
|
||||
ProxiesSortType.none => proxies,
|
||||
ProxiesSortType.delay => _sortOfDelay(
|
||||
proxies: proxies,
|
||||
testUrl: testUrl,
|
||||
),
|
||||
ProxiesSortType.name => _sortOfName(proxies),
|
||||
};
|
||||
}
|
||||
|
||||
Future<Null> clearEffect(String profileId) async {
|
||||
Future<void> clearEffect(String profileId) async {
|
||||
final profilePath = await appPath.getProfilePath(profileId);
|
||||
final providersDirPath = await appPath.getProvidersDirPath(profileId);
|
||||
return await Isolate.run(() async {
|
||||
@@ -800,28 +747,28 @@ class AppController {
|
||||
if (isExists) {
|
||||
profileFile.delete(recursive: true);
|
||||
}
|
||||
final providersFileDir = File(providersDirPath);
|
||||
final providersFileIsExists = await providersFileDir.exists();
|
||||
if (providersFileIsExists) {
|
||||
providersFileDir.delete(recursive: true);
|
||||
}
|
||||
await coreController.deleteFile(providersDirPath);
|
||||
});
|
||||
}
|
||||
|
||||
void updateTun() {
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.tun(enable: !state.tun.enable),
|
||||
);
|
||||
_ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith.tun(enable: !state.tun.enable));
|
||||
}
|
||||
|
||||
void updateSystemProxy() {
|
||||
_ref.read(networkSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
systemProxy: !state.systemProxy,
|
||||
),
|
||||
_ref
|
||||
.read(networkSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(systemProxy: !state.systemProxy),
|
||||
);
|
||||
}
|
||||
|
||||
void handleCoreDisconnected() {
|
||||
_ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
}
|
||||
|
||||
Future<List<Package>> getPackages() async {
|
||||
if (_ref.read(isMobileViewProvider)) {
|
||||
await Future.delayed(commonDuration);
|
||||
@@ -834,21 +781,18 @@ class AppController {
|
||||
}
|
||||
|
||||
void updateStart() {
|
||||
updateStatus(!_ref.read(runTimeProvider.notifier).isStart);
|
||||
updateStatus(!_ref.read(isStartProvider));
|
||||
}
|
||||
|
||||
void updateCurrentSelectedMap(String groupName, String proxyName) {
|
||||
final currentProfile = _ref.read(currentProfileProvider);
|
||||
if (currentProfile != null &&
|
||||
currentProfile.selectedMap[groupName] != proxyName) {
|
||||
final SelectedMap selectedMap = Map.from(
|
||||
currentProfile.selectedMap,
|
||||
)..[groupName] = proxyName;
|
||||
_ref.read(profilesProvider.notifier).setProfile(
|
||||
currentProfile.copyWith(
|
||||
selectedMap: selectedMap,
|
||||
),
|
||||
);
|
||||
final SelectedMap selectedMap = Map.from(currentProfile.selectedMap)
|
||||
..[groupName] = proxyName;
|
||||
_ref
|
||||
.read(profilesProvider.notifier)
|
||||
.setProfile(currentProfile.copyWith(selectedMap: selectedMap));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,17 +801,15 @@ class AppController {
|
||||
if (currentProfile == null) {
|
||||
return;
|
||||
}
|
||||
_ref.read(profilesProvider.notifier).setProfile(
|
||||
currentProfile.copyWith(
|
||||
unfoldSet: value,
|
||||
),
|
||||
);
|
||||
_ref
|
||||
.read(profilesProvider.notifier)
|
||||
.setProfile(currentProfile.copyWith(unfoldSet: value));
|
||||
}
|
||||
|
||||
void changeMode(Mode mode) {
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(mode: mode),
|
||||
);
|
||||
_ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(mode: mode));
|
||||
if (mode == Mode.global) {
|
||||
updateCurrentGroupName(GroupName.GLOBAL.name);
|
||||
}
|
||||
@@ -875,11 +817,9 @@ class AppController {
|
||||
}
|
||||
|
||||
void updateAutoLaunch() {
|
||||
_ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
autoLaunch: !state.autoLaunch,
|
||||
),
|
||||
);
|
||||
_ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(autoLaunch: !state.autoLaunch));
|
||||
}
|
||||
|
||||
Future<void> updateVisible() async {
|
||||
@@ -892,64 +832,23 @@ class AppController {
|
||||
}
|
||||
|
||||
void updateMode() {
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) {
|
||||
final index = Mode.values.indexWhere((item) => item == state.mode);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1;
|
||||
return state.copyWith(
|
||||
mode: Mode.values[nextIndex],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> 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;
|
||||
},
|
||||
);
|
||||
_ref.read(patchClashConfigProvider.notifier).updateState((state) {
|
||||
final index = Mode.values.indexWhere((item) => item == state.mode);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1;
|
||||
return state.copyWith(mode: Mode.values[nextIndex]);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> exportLogs() async {
|
||||
final logsRaw = _ref.read(logsProvider).list.map(
|
||||
(item) => item.toString(),
|
||||
);
|
||||
final logsRaw = _ref.read(logsProvider).list.map((item) => item.toString());
|
||||
final data = await Isolate.run<List<int>>(() async {
|
||||
final logsRawString = logsRaw.join('\n');
|
||||
return utf8.encode(logsRawString);
|
||||
});
|
||||
return await picker.saveFile(
|
||||
utils.logFile,
|
||||
Uint8List.fromList(data),
|
||||
) !=
|
||||
return await picker.saveFile(utils.logFile, Uint8List.fromList(data)) !=
|
||||
null;
|
||||
}
|
||||
|
||||
@@ -959,17 +858,15 @@ class AppController {
|
||||
final configJson = globalState.config.toJson();
|
||||
return Isolate.run<List<int>>(() async {
|
||||
final archive = Archive();
|
||||
archive.add('config.json', configJson);
|
||||
archive.addTextFile('config.json', configJson);
|
||||
archive.addDirectoryToArchive(profilesPath, homeDirPath);
|
||||
final zipEncoder = ZipEncoder();
|
||||
return zipEncoder.encode(archive) ?? [];
|
||||
return zipEncoder.encode(archive);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateTray([bool focus = false]) async {
|
||||
tray.update(
|
||||
trayState: _ref.read(trayStateProvider),
|
||||
);
|
||||
tray?.update(trayState: _ref.read(trayStateProvider));
|
||||
}
|
||||
|
||||
Future<void> recoveryData(
|
||||
@@ -981,18 +878,19 @@ class AppController {
|
||||
return zipDecoder.decodeBytes(data);
|
||||
});
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
final configs =
|
||||
archive.files.where((item) => item.name.endsWith('.json')).toList();
|
||||
final profiles =
|
||||
archive.files.where((item) => !item.name.endsWith('.json'));
|
||||
final configIndex =
|
||||
configs.indexWhere((config) => config.name == 'config.json');
|
||||
final configs = archive.files
|
||||
.where((item) => item.name.endsWith('.json'))
|
||||
.toList();
|
||||
final profiles = archive.files.where(
|
||||
(item) => !item.name.endsWith('.json'),
|
||||
);
|
||||
final configIndex = configs.indexWhere(
|
||||
(config) => config.name == 'config.json',
|
||||
);
|
||||
if (configIndex == -1) throw 'invalid backup file';
|
||||
final configFile = configs[configIndex];
|
||||
var tempConfig = Config.compatibleFromJson(
|
||||
json.decode(
|
||||
utf8.decode(configFile.content),
|
||||
),
|
||||
json.decode(utf8.decode(configFile.content)),
|
||||
);
|
||||
for (final profile in profiles) {
|
||||
final filePath = join(homeDirPath, profile.name);
|
||||
@@ -1000,38 +898,30 @@ class AppController {
|
||||
await file.create(recursive: true);
|
||||
await file.writeAsBytes(profile.content);
|
||||
}
|
||||
final clashConfigIndex =
|
||||
configs.indexWhere((config) => config.name == 'clashConfig.json');
|
||||
final clashConfigIndex = configs.indexWhere(
|
||||
(config) => config.name == 'clashConfig.json',
|
||||
);
|
||||
if (clashConfigIndex != -1) {
|
||||
final clashConfigFile = configs[clashConfigIndex];
|
||||
tempConfig = tempConfig.copyWith(
|
||||
patchClashConfig: ClashConfig.fromJson(
|
||||
json.decode(
|
||||
utf8.decode(
|
||||
clashConfigFile.content,
|
||||
),
|
||||
),
|
||||
json.decode(utf8.decode(clashConfigFile.content)),
|
||||
),
|
||||
);
|
||||
}
|
||||
_recovery(
|
||||
tempConfig,
|
||||
recoveryOption,
|
||||
);
|
||||
_recovery(tempConfig, recoveryOption);
|
||||
}
|
||||
|
||||
void _recovery(Config config, RecoveryOption recoveryOption) {
|
||||
final recoveryStrategy = _ref.read(appSettingProvider.select(
|
||||
(state) => state.recoveryStrategy,
|
||||
));
|
||||
final recoveryStrategy = _ref.read(
|
||||
appSettingProvider.select((state) => state.recoveryStrategy),
|
||||
);
|
||||
final profiles = config.profiles;
|
||||
if (recoveryStrategy == RecoveryStrategy.override) {
|
||||
_ref.read(profilesProvider.notifier).value = profiles;
|
||||
} else {
|
||||
for (final profile in profiles) {
|
||||
_ref.read(profilesProvider.notifier).setProfile(
|
||||
profile,
|
||||
);
|
||||
_ref.read(profilesProvider.notifier).setProfile(profile);
|
||||
}
|
||||
}
|
||||
final onlyProfiles = recoveryOption == RecoveryOption.onlyProfiles;
|
||||
@@ -1072,15 +962,13 @@ class AppController {
|
||||
final res = await futureFunction();
|
||||
return res;
|
||||
} catch (e) {
|
||||
commonPrint.log('$e');
|
||||
commonPrint.log('$futureFunction ===> $e', logLevel: LogLevel.warning);
|
||||
if (realSilence) {
|
||||
globalState.showNotifier(e.toString());
|
||||
} else {
|
||||
globalState.showMessage(
|
||||
title: title ?? appLocalizations.tip,
|
||||
message: TextSpan(
|
||||
text: e.toString(),
|
||||
),
|
||||
message: TextSpan(text: e.toString()),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
276
lib/core/controller.dart
Normal file
276
lib/core/controller.dart
Normal file
@@ -0,0 +1,276 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/core/interface.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
class CoreController {
|
||||
static CoreController? _instance;
|
||||
late CoreHandlerInterface _interface;
|
||||
|
||||
CoreController._internal() {
|
||||
if (system.isAndroid) {
|
||||
_interface = coreLib!;
|
||||
} else {
|
||||
_interface = coreService!;
|
||||
}
|
||||
}
|
||||
|
||||
factory CoreController() {
|
||||
_instance ??= CoreController._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<String> preload() {
|
||||
return _interface.preload();
|
||||
}
|
||||
|
||||
static Future<void> initGeo() async {
|
||||
final homePath = await appPath.homeDirPath;
|
||||
final homeDir = Directory(homePath);
|
||||
final isExists = await homeDir.exists();
|
||||
if (!isExists) {
|
||||
await homeDir.create(recursive: true);
|
||||
}
|
||||
const geoFileNameList = [MMDB, GEOIP, GEOSITE, ASN];
|
||||
try {
|
||||
for (final geoFileName in geoFileNameList) {
|
||||
final geoFile = File(join(homePath, geoFileName));
|
||||
final isExists = await geoFile.exists();
|
||||
if (isExists) {
|
||||
continue;
|
||||
}
|
||||
final data = await rootBundle.load('assets/data/$geoFileName');
|
||||
List<int> bytes = data.buffer.asUint8List();
|
||||
await geoFile.writeAsBytes(bytes, flush: true);
|
||||
}
|
||||
} catch (e) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> init(int version) async {
|
||||
await initGeo();
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
return await _interface.init(
|
||||
InitParams(homeDir: homeDirPath, version: version),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> shutdown() async {
|
||||
await _interface.shutdown();
|
||||
}
|
||||
|
||||
FutureOr<bool> get isInit => _interface.isInit;
|
||||
|
||||
FutureOr<String> validateConfig(String data) {
|
||||
return _interface.validateConfig(data);
|
||||
}
|
||||
|
||||
Future<String> updateConfig(UpdateParams updateParams) async {
|
||||
return await _interface.updateConfig(updateParams);
|
||||
}
|
||||
|
||||
Future<String> setupConfig(ClashConfig clashConfig) async {
|
||||
await globalState.genConfigFile(clashConfig);
|
||||
final params = await globalState.getSetupParams();
|
||||
return await _interface.setupConfig(params);
|
||||
}
|
||||
|
||||
Future<List<Group>> getProxiesGroups({
|
||||
required ProxiesSortType sortType,
|
||||
required DelayMap delayMap,
|
||||
required SelectedMap selectedMap,
|
||||
required String defaultTestUrl,
|
||||
}) async {
|
||||
final proxies = await _interface.getProxies();
|
||||
return Isolate.run<List<Group>>(() {
|
||||
if (proxies.isEmpty) return [];
|
||||
final groupNames = [
|
||||
UsedProxy.GLOBAL.name,
|
||||
...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) {
|
||||
final proxy = proxies[e] ?? {};
|
||||
return GroupTypeExtension.valueList.contains(proxy['type']);
|
||||
}),
|
||||
];
|
||||
final groupsRaw = groupNames.map((groupName) {
|
||||
final group = proxies[groupName];
|
||||
group['all'] = ((group['all'] ?? []) as List)
|
||||
.map((name) => proxies[name])
|
||||
.where((proxy) => proxy != null)
|
||||
.toList();
|
||||
return group;
|
||||
}).toList();
|
||||
final groups = groupsRaw.map((e) => Group.fromJson(e)).toList();
|
||||
return computeSort(
|
||||
groups: groups,
|
||||
sortType: sortType,
|
||||
delayMap: delayMap,
|
||||
selectedMap: selectedMap,
|
||||
defaultTestUrl: defaultTestUrl,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) async {
|
||||
return await _interface.changeProxy(changeProxyParams);
|
||||
}
|
||||
|
||||
Future<List<TrackerInfo>> getConnections() async {
|
||||
final res = await _interface.getConnections();
|
||||
final connectionsData = json.decode(res) as Map;
|
||||
final connectionsRaw = connectionsData['connections'] as List? ?? [];
|
||||
return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
void closeConnection(String id) {
|
||||
_interface.closeConnection(id);
|
||||
}
|
||||
|
||||
void closeConnections() {
|
||||
_interface.closeConnections();
|
||||
}
|
||||
|
||||
void resetConnections() {
|
||||
_interface.resetConnections();
|
||||
}
|
||||
|
||||
Future<List<ExternalProvider>> getExternalProviders() async {
|
||||
final externalProvidersRawString = await _interface.getExternalProviders();
|
||||
if (externalProvidersRawString.isEmpty) {
|
||||
return [];
|
||||
}
|
||||
return Isolate.run<List<ExternalProvider>>(() {
|
||||
final externalProviders =
|
||||
(json.decode(externalProvidersRawString) as List<dynamic>)
|
||||
.map((item) => ExternalProvider.fromJson(item))
|
||||
.toList();
|
||||
return externalProviders;
|
||||
});
|
||||
}
|
||||
|
||||
Future<ExternalProvider?> getExternalProvider(
|
||||
String externalProviderName,
|
||||
) async {
|
||||
final externalProvidersRawString = await _interface.getExternalProvider(
|
||||
externalProviderName,
|
||||
);
|
||||
if (externalProvidersRawString.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return ExternalProvider.fromJson(json.decode(externalProvidersRawString));
|
||||
}
|
||||
|
||||
Future<String> updateGeoData(UpdateGeoDataParams params) {
|
||||
return _interface.updateGeoData(params);
|
||||
}
|
||||
|
||||
Future<String> sideLoadExternalProvider({
|
||||
required String providerName,
|
||||
required String data,
|
||||
}) {
|
||||
return _interface.sideLoadExternalProvider(
|
||||
providerName: providerName,
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> updateExternalProvider({required String providerName}) async {
|
||||
return _interface.updateExternalProvider(providerName);
|
||||
}
|
||||
|
||||
Future<bool> startListener() async {
|
||||
return await _interface.startListener();
|
||||
}
|
||||
|
||||
Future<bool> stopListener() async {
|
||||
return await _interface.stopListener();
|
||||
}
|
||||
|
||||
Future<Delay> getDelay(String url, String proxyName) async {
|
||||
final data = await _interface.asyncTestDelay(url, proxyName);
|
||||
return Delay.fromJson(json.decode(data));
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getConfig(String id) async {
|
||||
final profilePath = await appPath.getProfilePath(id);
|
||||
final res = await _interface.getConfig(profilePath);
|
||||
if (res.isSuccess) {
|
||||
return res.data;
|
||||
} else {
|
||||
throw res.message;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Traffic> getTraffic(bool onlyStatisticsProxy) async {
|
||||
final trafficString = await _interface.getTraffic(onlyStatisticsProxy);
|
||||
if (trafficString.isEmpty) {
|
||||
return Traffic();
|
||||
}
|
||||
return Traffic.fromJson(json.decode(trafficString));
|
||||
}
|
||||
|
||||
Future<IpInfo?> getCountryCode(String ip) async {
|
||||
final countryCode = await _interface.getCountryCode(ip);
|
||||
if (countryCode.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return IpInfo(ip: ip, countryCode: countryCode);
|
||||
}
|
||||
|
||||
Future<Traffic> getTotalTraffic(bool onlyStatisticsProxy) async {
|
||||
final totalTrafficString = await _interface.getTotalTraffic(
|
||||
onlyStatisticsProxy,
|
||||
);
|
||||
if (totalTrafficString.isEmpty) {
|
||||
return Traffic();
|
||||
}
|
||||
return Traffic.fromJson(json.decode(totalTrafficString));
|
||||
}
|
||||
|
||||
Future<int> getMemory() async {
|
||||
final value = await _interface.getMemory();
|
||||
if (value.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
return int.parse(value);
|
||||
}
|
||||
|
||||
void resetTraffic() {
|
||||
_interface.resetTraffic();
|
||||
}
|
||||
|
||||
void startLog() {
|
||||
_interface.startLog();
|
||||
}
|
||||
|
||||
void stopLog() {
|
||||
_interface.stopLog();
|
||||
}
|
||||
|
||||
Future<void> requestGc() async {
|
||||
await _interface.forceGc();
|
||||
}
|
||||
|
||||
Future<void> destroy() async {
|
||||
await _interface.destroy();
|
||||
}
|
||||
|
||||
Future<void> crash() async {
|
||||
await _interface.crash();
|
||||
}
|
||||
|
||||
Future<String> deleteFile(String path) async {
|
||||
return await _interface.deleteFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
final coreController = CoreController();
|
||||
@@ -1,4 +1,5 @@
|
||||
export 'controller.dart';
|
||||
export 'core.dart';
|
||||
export 'event.dart';
|
||||
export 'lib.dart';
|
||||
export 'message.dart';
|
||||
export 'service.dart';
|
||||
68
lib/core/event.dart
Normal file
68
lib/core/event.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract mixin class CoreEventListener {
|
||||
void onLog(Log log) {}
|
||||
|
||||
void onDelay(Delay delay) {}
|
||||
|
||||
void onRequest(TrackerInfo connection) {}
|
||||
|
||||
void onLoaded(String providerName) {}
|
||||
|
||||
void onCrash(String message) {}
|
||||
}
|
||||
|
||||
class CoreEventManager {
|
||||
final _controller = StreamController<CoreEvent>();
|
||||
|
||||
CoreEventManager._() {
|
||||
_controller.stream.listen((event) {
|
||||
for (final CoreEventListener listener in _listeners) {
|
||||
switch (event.type) {
|
||||
case CoreEventType.log:
|
||||
listener.onLog(Log.fromJson(event.data));
|
||||
break;
|
||||
case CoreEventType.delay:
|
||||
listener.onDelay(Delay.fromJson(event.data));
|
||||
break;
|
||||
case CoreEventType.request:
|
||||
listener.onRequest(TrackerInfo.fromJson(event.data));
|
||||
break;
|
||||
case CoreEventType.loaded:
|
||||
listener.onLoaded(event.data);
|
||||
break;
|
||||
case CoreEventType.crash:
|
||||
listener.onCrash(event.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static final CoreEventManager instance = CoreEventManager._();
|
||||
|
||||
final ObserverList<CoreEventListener> _listeners =
|
||||
ObserverList<CoreEventListener>();
|
||||
|
||||
bool get hasListeners {
|
||||
return _listeners.isNotEmpty;
|
||||
}
|
||||
|
||||
void sendEvent(CoreEvent event) {
|
||||
_controller.add(event);
|
||||
}
|
||||
|
||||
void addListener(CoreEventListener listener) {
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
void removeListener(CoreEventListener listener) {
|
||||
_listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
final coreEventManager = CoreEventManager.instance;
|
||||
327
lib/core/interface.dart
Normal file
327
lib/core/interface.dart
Normal file
@@ -0,0 +1,327 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
|
||||
mixin CoreInterface {
|
||||
Future<bool> init(InitParams params);
|
||||
|
||||
Future<String> preload();
|
||||
|
||||
Future<bool> shutdown();
|
||||
|
||||
Future<bool> get isInit;
|
||||
|
||||
Future<bool> forceGc();
|
||||
|
||||
Future<String> validateConfig(String data);
|
||||
|
||||
Future<Result> getConfig(String path);
|
||||
|
||||
Future<String> asyncTestDelay(String url, String proxyName);
|
||||
|
||||
Future<String> updateConfig(UpdateParams updateParams);
|
||||
|
||||
Future<String> setupConfig(SetupParams setupParams);
|
||||
|
||||
Future<Map> getProxies();
|
||||
|
||||
Future<String> changeProxy(ChangeProxyParams changeProxyParams);
|
||||
|
||||
Future<bool> startListener();
|
||||
|
||||
Future<bool> stopListener();
|
||||
|
||||
Future<String> getExternalProviders();
|
||||
|
||||
Future<String>? getExternalProvider(String externalProviderName);
|
||||
|
||||
Future<String> updateGeoData(UpdateGeoDataParams params);
|
||||
|
||||
Future<String> sideLoadExternalProvider({
|
||||
required String providerName,
|
||||
required String data,
|
||||
});
|
||||
|
||||
Future<String> updateExternalProvider(String providerName);
|
||||
|
||||
FutureOr<String> getTraffic(bool onlyStatisticsProxy);
|
||||
|
||||
FutureOr<String> getTotalTraffic(bool onlyStatisticsProxy);
|
||||
|
||||
FutureOr<String> getCountryCode(String ip);
|
||||
|
||||
FutureOr<String> getMemory();
|
||||
|
||||
FutureOr<void> resetTraffic();
|
||||
|
||||
FutureOr<void> startLog();
|
||||
|
||||
FutureOr<void> stopLog();
|
||||
|
||||
Future<bool> crash();
|
||||
|
||||
FutureOr<String> getConnections();
|
||||
|
||||
FutureOr<bool> closeConnection(String id);
|
||||
|
||||
FutureOr<String> deleteFile(String path);
|
||||
|
||||
FutureOr<bool> closeConnections();
|
||||
|
||||
FutureOr<bool> resetConnections();
|
||||
}
|
||||
|
||||
abstract class CoreHandlerInterface with CoreInterface {
|
||||
Future get connected;
|
||||
|
||||
FutureOr<bool> destroy();
|
||||
|
||||
Future<T?> _invoke<T>({
|
||||
required ActionMethod method,
|
||||
dynamic data,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
await connected;
|
||||
return invoke(method: method, data: data, timeout: timeout);
|
||||
}
|
||||
|
||||
Future<T?> invoke<T>({
|
||||
required ActionMethod method,
|
||||
dynamic data,
|
||||
Duration? timeout,
|
||||
});
|
||||
|
||||
Future<T> parasResult<T>(ActionResult result) async {
|
||||
return switch (result.method) {
|
||||
ActionMethod.getConfig => result.toResult as T,
|
||||
_ => result.data as T,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> init(InitParams params) async {
|
||||
return await _invoke<bool>(
|
||||
method: ActionMethod.initClash,
|
||||
data: json.encode(params),
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> shutdown();
|
||||
|
||||
@override
|
||||
Future<bool> get isInit async {
|
||||
return await _invoke<bool>(method: ActionMethod.getIsInit) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> forceGc() async {
|
||||
return await _invoke<bool>(method: ActionMethod.forceGc) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> validateConfig(String data) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.validateConfig,
|
||||
data: data,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> updateConfig(UpdateParams updateParams) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.updateConfig,
|
||||
data: json.encode(updateParams),
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result> getConfig(String path) async {
|
||||
return await _invoke<Result>(method: ActionMethod.getConfig, data: path) ??
|
||||
Result<Map<String, dynamic>>.success({});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> setupConfig(SetupParams setupParams) async {
|
||||
final data = await Isolate.run(() => json.encode(setupParams));
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.setupConfig,
|
||||
data: data,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> crash() async {
|
||||
return await _invoke<bool>(method: ActionMethod.crash) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map> getProxies() async {
|
||||
final map = await _invoke<Map>(method: ActionMethod.getProxies);
|
||||
return map ?? {};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> changeProxy(ChangeProxyParams changeProxyParams) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.changeProxy,
|
||||
data: json.encode(changeProxyParams),
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getExternalProviders() async {
|
||||
return await _invoke<String>(method: ActionMethod.getExternalProviders) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getExternalProvider(String externalProviderName) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.getExternalProvider,
|
||||
data: externalProviderName,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> updateGeoData(UpdateGeoDataParams params) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.updateGeoData,
|
||||
data: json.encode(params),
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> sideLoadExternalProvider({
|
||||
required String providerName,
|
||||
required String data,
|
||||
}) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.sideLoadExternalProvider,
|
||||
data: json.encode({'providerName': providerName, 'data': data}),
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> updateExternalProvider(String providerName) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.updateExternalProvider,
|
||||
data: providerName,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getConnections() async {
|
||||
return await _invoke<String>(method: ActionMethod.getConnections) ?? '';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> closeConnections() async {
|
||||
return await _invoke<bool>(method: ActionMethod.closeConnections) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> resetConnections() async {
|
||||
return await _invoke<bool>(method: ActionMethod.resetConnections) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> closeConnection(String id) async {
|
||||
return await _invoke<bool>(
|
||||
method: ActionMethod.closeConnection,
|
||||
data: id,
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getTotalTraffic(bool onlyStatisticsProxy) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.getTotalTraffic,
|
||||
data: onlyStatisticsProxy,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getTraffic(bool onlyStatisticsProxy) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.getTraffic,
|
||||
data: onlyStatisticsProxy,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> deleteFile(String path) async {
|
||||
return await _invoke<String>(method: ActionMethod.deleteFile, data: path) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
resetTraffic() {
|
||||
_invoke(method: ActionMethod.resetTraffic);
|
||||
}
|
||||
|
||||
@override
|
||||
startLog() {
|
||||
_invoke(method: ActionMethod.startLog);
|
||||
}
|
||||
|
||||
@override
|
||||
stopLog() {
|
||||
_invoke<bool>(method: ActionMethod.stopLog);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> startListener() async {
|
||||
return await _invoke<bool>(method: ActionMethod.startListener) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> stopListener() async {
|
||||
return await _invoke<bool>(method: ActionMethod.stopListener) ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> asyncTestDelay(String url, String proxyName) async {
|
||||
final delayParams = {
|
||||
'proxy-name': proxyName,
|
||||
'timeout': httpTimeoutDuration.inMilliseconds,
|
||||
'test-url': url,
|
||||
};
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.asyncTestDelay,
|
||||
data: json.encode(delayParams),
|
||||
timeout: Duration(seconds: 6),
|
||||
) ??
|
||||
json.encode(Delay(name: proxyName, value: -1, url: url));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getCountryCode(String ip) async {
|
||||
return await _invoke<String>(
|
||||
method: ActionMethod.getCountryCode,
|
||||
data: ip,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getMemory() async {
|
||||
return await _invoke<String>(method: ActionMethod.getMemory) ?? '';
|
||||
}
|
||||
}
|
||||
68
lib/core/lib.dart
Normal file
68
lib/core/lib.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/core.dart';
|
||||
import 'package:fl_clash/plugins/service.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
|
||||
import 'interface.dart';
|
||||
|
||||
class CoreLib extends CoreHandlerInterface {
|
||||
static CoreLib? _instance;
|
||||
|
||||
Completer<bool> _connectedCompleter = Completer();
|
||||
|
||||
CoreLib._internal();
|
||||
|
||||
@override
|
||||
Future<String> preload() async {
|
||||
final res = await service?.init();
|
||||
if (res?.isEmpty != true) {
|
||||
return res ?? '';
|
||||
}
|
||||
_connectedCompleter.complete(true);
|
||||
final syncRes = await service?.syncAndroidState(
|
||||
globalState.getAndroidState(),
|
||||
);
|
||||
return syncRes ?? '';
|
||||
}
|
||||
|
||||
factory CoreLib() {
|
||||
_instance ??= CoreLib._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
@override
|
||||
destroy() async {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> shutdown() async {
|
||||
await service?.shutdown();
|
||||
_connectedCompleter = Completer();
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<T?> invoke<T>({
|
||||
required ActionMethod method,
|
||||
dynamic data,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
final id = '${method.name}#${utils.id}';
|
||||
final result = await service
|
||||
?.invokeAction(Action(id: id, method: method, data: data))
|
||||
.withTimeout(onTimeout: () => null);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return parasResult<T>(result);
|
||||
}
|
||||
|
||||
@override
|
||||
Future get connected => _connectedCompleter.future;
|
||||
}
|
||||
|
||||
CoreLib? get coreLib => system.isAndroid ? CoreLib() : null;
|
||||
188
lib/core/service.dart
Normal file
188
lib/core/service.dart
Normal file
@@ -0,0 +1,188 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/core.dart';
|
||||
|
||||
import 'interface.dart';
|
||||
|
||||
class CoreService extends CoreHandlerInterface {
|
||||
static CoreService? _instance;
|
||||
|
||||
final Completer<ServerSocket> _serverCompleter = Completer();
|
||||
|
||||
Completer<Socket> _socketCompleter = Completer();
|
||||
|
||||
final Map<String, Completer> _callbackCompleterMap = {};
|
||||
|
||||
Process? _process;
|
||||
|
||||
factory CoreService() {
|
||||
_instance ??= CoreService._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
CoreService._internal() {
|
||||
_initServer();
|
||||
}
|
||||
|
||||
Future<void> handleResult(ActionResult result) async {
|
||||
final completer = _callbackCompleterMap[result.id];
|
||||
final data = await parasResult(result);
|
||||
if (result.id?.isEmpty == true) {
|
||||
coreEventManager.sendEvent(CoreEvent.fromJson(result.data));
|
||||
}
|
||||
completer?.complete(data);
|
||||
}
|
||||
|
||||
void _initServer() {
|
||||
runZonedGuarded(
|
||||
() async {
|
||||
final address = !system.isWindows
|
||||
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
|
||||
: InternetAddress(localhost, type: InternetAddressType.IPv4);
|
||||
await _deleteSocketFile();
|
||||
final server = await ServerSocket.bind(address, 0, shared: true);
|
||||
_serverCompleter.complete(server);
|
||||
await for (final socket in server) {
|
||||
await _attachSocket(socket);
|
||||
}
|
||||
},
|
||||
(error, stack) async {
|
||||
commonPrint.log('Service error: $error', logLevel: LogLevel.warning);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _attachSocket(Socket socket) async {
|
||||
await _destroySocket();
|
||||
_socketCompleter.complete(socket);
|
||||
socket
|
||||
.transform(uint8ListToListIntConverter)
|
||||
.transform(utf8.decoder)
|
||||
.transform(LineSplitter())
|
||||
.listen((data) {
|
||||
handleResult(ActionResult.fromJson(json.decode(data.trim())));
|
||||
})
|
||||
.onDone(() {
|
||||
_handleInvokeCrashEvent();
|
||||
});
|
||||
}
|
||||
|
||||
void _handleInvokeCrashEvent() {
|
||||
coreEventManager.sendEvent(
|
||||
CoreEvent(type: CoreEventType.crash, data: 'socket done'),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> start() async {
|
||||
if (_process != null) {
|
||||
await shutdown();
|
||||
}
|
||||
final serverSocket = await _serverCompleter.future;
|
||||
final arg = system.isWindows
|
||||
? '${serverSocket.port}'
|
||||
: serverSocket.address.address;
|
||||
if (system.isWindows && await system.checkIsAdmin()) {
|
||||
final isSuccess = await request.startCoreByHelper(arg);
|
||||
if (isSuccess) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_process = await Process.start(appPath.corePath, [arg]);
|
||||
_process?.stdout.listen((_) {});
|
||||
_process?.stderr.listen((e) {
|
||||
final error = utf8.decode(e);
|
||||
if (error.isNotEmpty) {
|
||||
commonPrint.log(error, logLevel: LogLevel.warning);
|
||||
}
|
||||
});
|
||||
await _socketCompleter.future;
|
||||
}
|
||||
|
||||
@override
|
||||
destroy() async {
|
||||
final server = await _serverCompleter.future;
|
||||
await server.close();
|
||||
await _deleteSocketFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> sendMessage(String message) async {
|
||||
final socket = await _socketCompleter.future;
|
||||
socket.writeln(message);
|
||||
}
|
||||
|
||||
Future<void> _deleteSocketFile() async {
|
||||
if (!system.isWindows) {
|
||||
final file = File(unixSocketPath);
|
||||
if (await file.exists()) {
|
||||
await file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _destroySocket() async {
|
||||
if (_socketCompleter.isCompleted) {
|
||||
final socket = await _socketCompleter.future;
|
||||
_socketCompleter = Completer();
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
shutdown() async {
|
||||
await _destroySocket();
|
||||
_clearCompleter();
|
||||
if (system.isWindows) {
|
||||
await request.stopCoreByHelper();
|
||||
}
|
||||
_process?.kill();
|
||||
_process = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
void _clearCompleter() {
|
||||
for (final completer in _callbackCompleterMap.values) {
|
||||
completer.safeCompleter(null);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> preload() async {
|
||||
await _serverCompleter.future;
|
||||
await start();
|
||||
return '';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<T?> invoke<T>({
|
||||
required ActionMethod method,
|
||||
dynamic data,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
final id = '${method.name}#${utils.id}';
|
||||
_callbackCompleterMap[id] = Completer<T?>();
|
||||
sendMessage(json.encode(Action(id: id, method: method, data: data)));
|
||||
return (_callbackCompleterMap[id] as Completer<T?>).future.withTimeout(
|
||||
timeout: timeout,
|
||||
onLast: () {
|
||||
final completer = _callbackCompleterMap[id];
|
||||
completer?.safeCompleter(null);
|
||||
_callbackCompleterMap.remove(id);
|
||||
},
|
||||
tag: id,
|
||||
onTimeout: () => null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future get connected {
|
||||
return _socketCompleter.future;
|
||||
}
|
||||
}
|
||||
|
||||
final coreService = system.isDesktop ? CoreService() : null;
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_clash/common/color.dart';
|
||||
import 'package:fl_clash/common/system.dart';
|
||||
import 'package:fl_clash/views/dashboard/widgets/widgets.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
@@ -58,11 +59,8 @@ enum GroupType {
|
||||
enum GroupName { GLOBAL, Proxy, Auto, Fallback }
|
||||
|
||||
extension GroupTypeExtension on GroupType {
|
||||
static List<String> get valueList => GroupType.values
|
||||
.map(
|
||||
(e) => e.toString().split('.').last,
|
||||
)
|
||||
.toList();
|
||||
static List<String> get valueList =>
|
||||
GroupType.values.map((e) => e.toString().split('.').last).toList();
|
||||
|
||||
bool get isComputedSelected {
|
||||
return [GroupType.URLTest, GroupType.Fallback].contains(this);
|
||||
@@ -80,11 +78,8 @@ extension GroupTypeExtension on GroupType {
|
||||
enum UsedProxy { GLOBAL, DIRECT, REJECT }
|
||||
|
||||
extension UsedProxyExtension on UsedProxy {
|
||||
static List<String> get valueList => UsedProxy.values
|
||||
.map(
|
||||
(e) => e.toString().split('.').last,
|
||||
)
|
||||
.toList();
|
||||
static List<String> get valueList =>
|
||||
UsedProxy.values.map((e) => e.toString().split('.').last).toList();
|
||||
|
||||
String get value => UsedProxyExtension.valueList[index];
|
||||
}
|
||||
@@ -93,13 +88,7 @@ enum Mode { rule, global, direct }
|
||||
|
||||
enum ViewMode { mobile, laptop, desktop }
|
||||
|
||||
enum LogLevel {
|
||||
debug,
|
||||
info,
|
||||
warning,
|
||||
error,
|
||||
silent,
|
||||
}
|
||||
enum LogLevel { debug, info, warning, error, silent }
|
||||
|
||||
extension LogLevelExt on LogLevel {
|
||||
Color? get color {
|
||||
@@ -107,7 +96,7 @@ extension LogLevelExt on LogLevel {
|
||||
LogLevel.silent => Colors.grey.shade700,
|
||||
LogLevel.debug => Colors.grey.shade400,
|
||||
LogLevel.info => null,
|
||||
LogLevel.warning => Colors.yellowAccent,
|
||||
LogLevel.warning => Colors.orangeAccent.darken(),
|
||||
LogLevel.error => Colors.redAccent,
|
||||
};
|
||||
}
|
||||
@@ -138,24 +127,13 @@ enum ResultType {
|
||||
error,
|
||||
}
|
||||
|
||||
enum AppMessageType {
|
||||
log,
|
||||
delay,
|
||||
request,
|
||||
loaded,
|
||||
}
|
||||
enum CoreEventType { log, delay, request, loaded, crash }
|
||||
|
||||
enum InvokeMessageType {
|
||||
protect,
|
||||
process,
|
||||
}
|
||||
enum InvokeMessageType { protect, process }
|
||||
|
||||
enum FindProcessMode { always, off }
|
||||
|
||||
enum RecoveryOption {
|
||||
all,
|
||||
onlyProfiles,
|
||||
}
|
||||
enum RecoveryOption { all, onlyProfiles }
|
||||
|
||||
enum ChipType { action, delete }
|
||||
|
||||
@@ -177,7 +155,7 @@ enum DnsMode {
|
||||
fakeIp,
|
||||
@JsonValue('redir-host')
|
||||
redirHost,
|
||||
hosts
|
||||
hosts,
|
||||
}
|
||||
|
||||
enum ExternalControllerStatus {
|
||||
@@ -192,28 +170,12 @@ enum ExternalControllerStatus {
|
||||
}
|
||||
|
||||
enum KeyboardModifier {
|
||||
alt([
|
||||
PhysicalKeyboardKey.altLeft,
|
||||
PhysicalKeyboardKey.altRight,
|
||||
]),
|
||||
capsLock([
|
||||
PhysicalKeyboardKey.capsLock,
|
||||
]),
|
||||
control([
|
||||
PhysicalKeyboardKey.controlLeft,
|
||||
PhysicalKeyboardKey.controlRight,
|
||||
]),
|
||||
fn([
|
||||
PhysicalKeyboardKey.fn,
|
||||
]),
|
||||
meta([
|
||||
PhysicalKeyboardKey.metaLeft,
|
||||
PhysicalKeyboardKey.metaRight,
|
||||
]),
|
||||
shift([
|
||||
PhysicalKeyboardKey.shiftLeft,
|
||||
PhysicalKeyboardKey.shiftRight,
|
||||
]);
|
||||
alt([PhysicalKeyboardKey.altLeft, PhysicalKeyboardKey.altRight]),
|
||||
capsLock([PhysicalKeyboardKey.capsLock]),
|
||||
control([PhysicalKeyboardKey.controlLeft, PhysicalKeyboardKey.controlRight]),
|
||||
fn([PhysicalKeyboardKey.fn]),
|
||||
meta([PhysicalKeyboardKey.metaLeft, PhysicalKeyboardKey.metaRight]),
|
||||
shift([PhysicalKeyboardKey.shiftLeft, PhysicalKeyboardKey.shiftRight]);
|
||||
|
||||
final List<PhysicalKeyboardKey> physicalKeys;
|
||||
|
||||
@@ -233,19 +195,9 @@ extension KeyboardModifierExt on KeyboardModifier {
|
||||
}
|
||||
}
|
||||
|
||||
enum HotAction {
|
||||
start,
|
||||
view,
|
||||
mode,
|
||||
proxy,
|
||||
tun,
|
||||
}
|
||||
enum HotAction { start, view, mode, proxy, tun }
|
||||
|
||||
enum ProxiesIconStyle {
|
||||
standard,
|
||||
none,
|
||||
icon,
|
||||
}
|
||||
enum ProxiesIconStyle { standard, none, icon }
|
||||
|
||||
enum FontFamily {
|
||||
twEmoji('Twemoji'),
|
||||
@@ -257,10 +209,7 @@ enum FontFamily {
|
||||
const FontFamily(this.value);
|
||||
}
|
||||
|
||||
enum RouteMode {
|
||||
bypassPrivate,
|
||||
config,
|
||||
}
|
||||
enum RouteMode { bypassPrivate, config }
|
||||
|
||||
enum ActionMethod {
|
||||
message,
|
||||
@@ -294,6 +243,7 @@ enum ActionMethod {
|
||||
getMemory,
|
||||
crash,
|
||||
setupConfig,
|
||||
deleteFile,
|
||||
|
||||
///Android,
|
||||
setState,
|
||||
@@ -307,11 +257,7 @@ enum ActionMethod {
|
||||
|
||||
enum AuthorizeCode { none, success, error }
|
||||
|
||||
enum WindowsHelperServiceStatus {
|
||||
none,
|
||||
presence,
|
||||
running,
|
||||
}
|
||||
enum WindowsHelperServiceStatus { none, presence, running }
|
||||
|
||||
enum FunctionTag {
|
||||
updateClashConfig,
|
||||
@@ -337,79 +283,30 @@ enum FunctionTag {
|
||||
}
|
||||
|
||||
enum DashboardWidget {
|
||||
networkSpeed(
|
||||
GridItem(
|
||||
crossAxisCellCount: 8,
|
||||
child: NetworkSpeed(),
|
||||
),
|
||||
),
|
||||
outboundModeV2(
|
||||
GridItem(
|
||||
crossAxisCellCount: 8,
|
||||
child: OutboundModeV2(),
|
||||
),
|
||||
),
|
||||
outboundMode(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: OutboundMode(),
|
||||
),
|
||||
),
|
||||
trafficUsage(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: TrafficUsage(),
|
||||
),
|
||||
),
|
||||
networkDetection(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: NetworkDetection(),
|
||||
),
|
||||
),
|
||||
networkSpeed(GridItem(crossAxisCellCount: 8, child: NetworkSpeed())),
|
||||
outboundModeV2(GridItem(crossAxisCellCount: 8, child: OutboundModeV2())),
|
||||
outboundMode(GridItem(crossAxisCellCount: 4, child: OutboundMode())),
|
||||
trafficUsage(GridItem(crossAxisCellCount: 4, child: TrafficUsage())),
|
||||
networkDetection(GridItem(crossAxisCellCount: 4, child: NetworkDetection())),
|
||||
tunButton(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: TUNButton(),
|
||||
),
|
||||
GridItem(crossAxisCellCount: 4, child: TUNButton()),
|
||||
platforms: desktopPlatforms,
|
||||
),
|
||||
vpnButton(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: VpnButton(),
|
||||
),
|
||||
platforms: [
|
||||
SupportPlatform.Android,
|
||||
],
|
||||
GridItem(crossAxisCellCount: 4, child: VpnButton()),
|
||||
platforms: [SupportPlatform.Android],
|
||||
),
|
||||
systemProxyButton(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: SystemProxyButton(),
|
||||
),
|
||||
GridItem(crossAxisCellCount: 4, child: SystemProxyButton()),
|
||||
platforms: desktopPlatforms,
|
||||
),
|
||||
intranetIp(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: IntranetIP(),
|
||||
),
|
||||
),
|
||||
memoryInfo(
|
||||
GridItem(
|
||||
crossAxisCellCount: 4,
|
||||
child: MemoryInfo(),
|
||||
),
|
||||
);
|
||||
intranetIp(GridItem(crossAxisCellCount: 4, child: IntranetIP())),
|
||||
memoryInfo(GridItem(crossAxisCellCount: 4, child: MemoryInfo()));
|
||||
|
||||
final GridItem widget;
|
||||
final List<SupportPlatform> platforms;
|
||||
|
||||
const DashboardWidget(
|
||||
this.widget, {
|
||||
this.platforms = SupportPlatform.values,
|
||||
});
|
||||
const DashboardWidget(this.widget, {this.platforms = SupportPlatform.values});
|
||||
|
||||
static DashboardWidget getDashboardWidget(GridItem gridItem) {
|
||||
final dashboardWidgets = DashboardWidget.values;
|
||||
@@ -420,10 +317,7 @@ enum DashboardWidget {
|
||||
}
|
||||
}
|
||||
|
||||
enum GeodataLoader {
|
||||
standard,
|
||||
memconservative,
|
||||
}
|
||||
enum GeodataLoader { standard, memconservative }
|
||||
|
||||
enum PageLabel {
|
||||
dashboard,
|
||||
@@ -478,51 +372,30 @@ enum RuleAction {
|
||||
|
||||
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);
|
||||
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 OverrideRuleType { override, added }
|
||||
|
||||
enum RuleTarget {
|
||||
DIRECT,
|
||||
REJECT,
|
||||
}
|
||||
enum RuleTarget { DIRECT, REJECT }
|
||||
|
||||
enum RecoveryStrategy {
|
||||
compatible,
|
||||
override,
|
||||
}
|
||||
enum RecoveryStrategy { compatible, override }
|
||||
|
||||
enum CacheTag {
|
||||
logs,
|
||||
rules,
|
||||
requests,
|
||||
proxiesList,
|
||||
}
|
||||
enum CacheTag { logs, rules, requests, proxiesList }
|
||||
|
||||
enum Language {
|
||||
yaml,
|
||||
javaScript,
|
||||
}
|
||||
enum Language { yaml, javaScript }
|
||||
|
||||
enum ImportOption {
|
||||
file,
|
||||
url,
|
||||
}
|
||||
enum ImportOption { file, url }
|
||||
|
||||
enum ScrollPositionCacheKeys {
|
||||
tools,
|
||||
profiles,
|
||||
proxiesList,
|
||||
proxiesTabList,
|
||||
}
|
||||
enum ScrollPositionCacheKey { tools, profiles, proxiesList, proxiesTabList }
|
||||
|
||||
enum QueryTag { proxies }
|
||||
|
||||
enum CoreStatus { connecting, connected, disconnected }
|
||||
|
||||
@@ -177,6 +177,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Opening it will lose part of its application ability and gain the support of full amount of Clash.",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("Confirm"),
|
||||
"connected": MessageLookupByLibrary.simpleMessage("Connected"),
|
||||
"connecting": MessageLookupByLibrary.simpleMessage("Connecting..."),
|
||||
"connection": MessageLookupByLibrary.simpleMessage("Connection"),
|
||||
"connections": MessageLookupByLibrary.simpleMessage("Connections"),
|
||||
"connectionsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -194,13 +196,24 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"copySuccess": MessageLookupByLibrary.simpleMessage("Copy success"),
|
||||
"core": MessageLookupByLibrary.simpleMessage("Core"),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
|
||||
"coreStatus": MessageLookupByLibrary.simpleMessage("Core status"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("Country"),
|
||||
"crashTest": MessageLookupByLibrary.simpleMessage("Crash test"),
|
||||
"crashlytics": MessageLookupByLibrary.simpleMessage("Crash Analysis"),
|
||||
"crashlyticsTip": MessageLookupByLibrary.simpleMessage(
|
||||
"When enabled, automatically uploads crash logs without sensitive information when the app crashes",
|
||||
),
|
||||
"create": MessageLookupByLibrary.simpleMessage("Create"),
|
||||
"creationTime": MessageLookupByLibrary.simpleMessage("Creation time"),
|
||||
"cut": MessageLookupByLibrary.simpleMessage("Cut"),
|
||||
"dark": MessageLookupByLibrary.simpleMessage("Dark"),
|
||||
"dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"),
|
||||
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
|
||||
"This app uses Firebase Crashlytics to collect crash information to improve app stability.\nThe collected data includes device information and crash details, but does not contain personal sensitive data.\nYou can disable this feature in settings.",
|
||||
),
|
||||
"dataCollectionTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Data Collection Notice",
|
||||
),
|
||||
"days": MessageLookupByLibrary.simpleMessage("Days"),
|
||||
"defaultNameserver": MessageLookupByLibrary.simpleMessage(
|
||||
"Default nameserver",
|
||||
@@ -238,6 +251,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"This software is only used for non-commercial purposes such as learning exchanges and scientific research. It is strictly prohibited to use this software for commercial purposes. Any commercial activity, if any, has nothing to do with this software.",
|
||||
),
|
||||
"disconnected": MessageLookupByLibrary.simpleMessage("Disconnected"),
|
||||
"discoverNewVersion": MessageLookupByLibrary.simpleMessage(
|
||||
"Discover the new version",
|
||||
),
|
||||
@@ -247,6 +261,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"dnsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Update DNS related settings",
|
||||
),
|
||||
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS hijacking"),
|
||||
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS mode"),
|
||||
"doYouWantToPass": MessageLookupByLibrary.simpleMessage(
|
||||
"Do you want to pass",
|
||||
@@ -301,6 +316,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"There is a certain performance loss after opening",
|
||||
),
|
||||
"fontFamily": MessageLookupByLibrary.simpleMessage("FontFamily"),
|
||||
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure you want to force restart the core?",
|
||||
),
|
||||
"fourColumns": MessageLookupByLibrary.simpleMessage("Four columns"),
|
||||
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("FruitSalad"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("General"),
|
||||
@@ -513,6 +531,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Please press the keyboard.",
|
||||
),
|
||||
"preview": MessageLookupByLibrary.simpleMessage("Preview"),
|
||||
"process": MessageLookupByLibrary.simpleMessage("Process"),
|
||||
"profile": MessageLookupByLibrary.simpleMessage("Profile"),
|
||||
"profileAutoUpdateIntervalInvalidValidationDesc":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
@@ -539,7 +558,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"profiles": MessageLookupByLibrary.simpleMessage("Profiles"),
|
||||
"profilesSort": MessageLookupByLibrary.simpleMessage("Profiles sort"),
|
||||
"progress": MessageLookupByLibrary.simpleMessage("Progress"),
|
||||
"project": MessageLookupByLibrary.simpleMessage("Project"),
|
||||
"providers": MessageLookupByLibrary.simpleMessage("Providers"),
|
||||
"proxies": MessageLookupByLibrary.simpleMessage("Proxies"),
|
||||
@@ -606,6 +624,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS connection following rules, need to configure proxy-server-nameserver",
|
||||
),
|
||||
"restartCoreTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure you want to restart the core?",
|
||||
),
|
||||
"routeAddress": MessageLookupByLibrary.simpleMessage("Route address"),
|
||||
"routeAddressDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Config listen route address",
|
||||
|
||||
@@ -133,6 +133,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"有効化すると一部機能を失いますが、Clashの完全サポートを獲得",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("確認"),
|
||||
"connected": MessageLookupByLibrary.simpleMessage("接続済み"),
|
||||
"connecting": MessageLookupByLibrary.simpleMessage("接続中..."),
|
||||
"connection": MessageLookupByLibrary.simpleMessage("接続"),
|
||||
"connections": MessageLookupByLibrary.simpleMessage("接続"),
|
||||
"connectionsDesc": MessageLookupByLibrary.simpleMessage("現在の接続データを表示"),
|
||||
@@ -146,13 +148,22 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"copySuccess": MessageLookupByLibrary.simpleMessage("コピー成功"),
|
||||
"core": MessageLookupByLibrary.simpleMessage("コア"),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("コア情報"),
|
||||
"coreStatus": MessageLookupByLibrary.simpleMessage("コアステータス"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("国"),
|
||||
"crashTest": MessageLookupByLibrary.simpleMessage("クラッシュテスト"),
|
||||
"crashlytics": MessageLookupByLibrary.simpleMessage("クラッシュ分析"),
|
||||
"crashlyticsTip": MessageLookupByLibrary.simpleMessage(
|
||||
"有効にすると、アプリがクラッシュした際に機密情報を含まないクラッシュログを自動的にアップロードします",
|
||||
),
|
||||
"create": MessageLookupByLibrary.simpleMessage("作成"),
|
||||
"creationTime": MessageLookupByLibrary.simpleMessage("作成時間"),
|
||||
"cut": MessageLookupByLibrary.simpleMessage("切り取り"),
|
||||
"dark": MessageLookupByLibrary.simpleMessage("ダーク"),
|
||||
"dashboard": MessageLookupByLibrary.simpleMessage("ダッシュボード"),
|
||||
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
|
||||
"本アプリはFirebase Crashlyticsを使用してクラッシュ情報を収集し、アプリの安定性を向上させます。\n収集されるデータにはデバイス情報とクラッシュ詳細が含まれますが、個人の機密データは含まれません。\n設定でこの機能を無効にすることができます。",
|
||||
),
|
||||
"dataCollectionTip": MessageLookupByLibrary.simpleMessage("データ収集説明"),
|
||||
"days": MessageLookupByLibrary.simpleMessage("日"),
|
||||
"defaultNameserver": MessageLookupByLibrary.simpleMessage("デフォルトネームサーバー"),
|
||||
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -182,9 +193,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"本ソフトウェアは学習交流や科学研究などの非営利目的でのみ使用されます。商用利用は厳禁です。いかなる商用活動も本ソフトウェアとは無関係です。",
|
||||
),
|
||||
"disconnected": MessageLookupByLibrary.simpleMessage("切断済み"),
|
||||
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("新バージョンを発見"),
|
||||
"discovery": MessageLookupByLibrary.simpleMessage("新しいバージョンを発見"),
|
||||
"dnsDesc": MessageLookupByLibrary.simpleMessage("DNS関連設定の更新"),
|
||||
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNSハイジャッキング"),
|
||||
"dnsMode": MessageLookupByLibrary.simpleMessage("DNSモード"),
|
||||
"doYouWantToPass": MessageLookupByLibrary.simpleMessage("通過させますか?"),
|
||||
"domain": MessageLookupByLibrary.simpleMessage("ドメイン"),
|
||||
@@ -229,6 +242,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"有効化するとパフォーマンスが若干低下します",
|
||||
),
|
||||
"fontFamily": MessageLookupByLibrary.simpleMessage("フォントファミリー"),
|
||||
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
|
||||
"コアを強制再起動してもよろしいですか?",
|
||||
),
|
||||
"fourColumns": MessageLookupByLibrary.simpleMessage("4列"),
|
||||
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("フルーツサラダ"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("一般"),
|
||||
@@ -387,6 +403,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"preferH3Desc": MessageLookupByLibrary.simpleMessage("DOHのHTTP/3を優先使用"),
|
||||
"pressKeyboard": MessageLookupByLibrary.simpleMessage("キーボードを押してください"),
|
||||
"preview": MessageLookupByLibrary.simpleMessage("プレビュー"),
|
||||
"process": MessageLookupByLibrary.simpleMessage("プロセス"),
|
||||
"profile": MessageLookupByLibrary.simpleMessage("プロファイル"),
|
||||
"profileAutoUpdateIntervalInvalidValidationDesc":
|
||||
MessageLookupByLibrary.simpleMessage("有効な間隔形式を入力してください"),
|
||||
@@ -409,7 +426,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"profiles": MessageLookupByLibrary.simpleMessage("プロファイル一覧"),
|
||||
"profilesSort": MessageLookupByLibrary.simpleMessage("プロファイルの並び替え"),
|
||||
"progress": MessageLookupByLibrary.simpleMessage("進捗"),
|
||||
"project": MessageLookupByLibrary.simpleMessage("プロジェクト"),
|
||||
"providers": MessageLookupByLibrary.simpleMessage("プロバイダー"),
|
||||
"proxies": MessageLookupByLibrary.simpleMessage("プロキシ"),
|
||||
@@ -460,6 +476,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS接続がルールに従う(proxy-server-nameserverの設定が必要)",
|
||||
),
|
||||
"restartCoreTip": MessageLookupByLibrary.simpleMessage("コアを再起動してもよろしいですか?"),
|
||||
"routeAddress": MessageLookupByLibrary.simpleMessage("ルートアドレス"),
|
||||
"routeAddressDesc": MessageLookupByLibrary.simpleMessage("ルートアドレスを設定"),
|
||||
"routeMode": MessageLookupByLibrary.simpleMessage("ルートモード"),
|
||||
|
||||
@@ -182,6 +182,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Включение приведет к потере части функциональности приложения, но обеспечит полную поддержку Clash.",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("Подтвердить"),
|
||||
"connected": MessageLookupByLibrary.simpleMessage("Подключено"),
|
||||
"connecting": MessageLookupByLibrary.simpleMessage("Подключение..."),
|
||||
"connection": MessageLookupByLibrary.simpleMessage("Соединение"),
|
||||
"connections": MessageLookupByLibrary.simpleMessage("Соединения"),
|
||||
"connectionsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -199,13 +201,24 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"copySuccess": MessageLookupByLibrary.simpleMessage("Копирование успешно"),
|
||||
"core": MessageLookupByLibrary.simpleMessage("Ядро"),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("Информация о ядре"),
|
||||
"coreStatus": MessageLookupByLibrary.simpleMessage("Основной статус"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("Страна"),
|
||||
"crashTest": MessageLookupByLibrary.simpleMessage("Тест на сбои"),
|
||||
"crashlytics": MessageLookupByLibrary.simpleMessage("Анализ сбоев"),
|
||||
"crashlyticsTip": MessageLookupByLibrary.simpleMessage(
|
||||
"При включении автоматически загружает журналы сбоев без конфиденциальной информации, когда приложение выходит из строя",
|
||||
),
|
||||
"create": MessageLookupByLibrary.simpleMessage("Создать"),
|
||||
"creationTime": MessageLookupByLibrary.simpleMessage("Время создания"),
|
||||
"cut": MessageLookupByLibrary.simpleMessage("Вырезать"),
|
||||
"dark": MessageLookupByLibrary.simpleMessage("Темный"),
|
||||
"dashboard": MessageLookupByLibrary.simpleMessage("Панель управления"),
|
||||
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
|
||||
"Это приложение использует Firebase Crashlytics для сбора информации о сбоях nhằm улучшения стабильности приложения.\nСобираемые данные включают информацию об устройстве и подробности о сбоях, но не содержат персональных конфиденциальных данных.\nВы можете отключить эту функцию в настройках.",
|
||||
),
|
||||
"dataCollectionTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Уведомление о сборе данных",
|
||||
),
|
||||
"days": MessageLookupByLibrary.simpleMessage("Дней"),
|
||||
"defaultNameserver": MessageLookupByLibrary.simpleMessage(
|
||||
"Сервер имен по умолчанию",
|
||||
@@ -245,6 +258,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Это программное обеспечение используется только в некоммерческих целях, таких как учебные обмены и научные исследования. Запрещено использовать это программное обеспечение в коммерческих целях. Любая коммерческая деятельность, если таковая имеется, не имеет отношения к этому программному обеспечению.",
|
||||
),
|
||||
"disconnected": MessageLookupByLibrary.simpleMessage("Отключено"),
|
||||
"discoverNewVersion": MessageLookupByLibrary.simpleMessage(
|
||||
"Обнаружена новая версия",
|
||||
),
|
||||
@@ -254,6 +268,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"dnsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Обновление настроек, связанных с DNS",
|
||||
),
|
||||
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS-перехват"),
|
||||
"dnsMode": MessageLookupByLibrary.simpleMessage("Режим DNS"),
|
||||
"doYouWantToPass": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы хотите пропустить",
|
||||
@@ -316,6 +331,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"При включении возможны небольшие потери производительности",
|
||||
),
|
||||
"fontFamily": MessageLookupByLibrary.simpleMessage("Семейство шрифтов"),
|
||||
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы уверены, что хотите принудительно перезапустить ядро?",
|
||||
),
|
||||
"fourColumns": MessageLookupByLibrary.simpleMessage("Четыре столбца"),
|
||||
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("Фруктовый микс"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("Общие"),
|
||||
@@ -540,6 +558,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Пожалуйста, нажмите клавишу.",
|
||||
),
|
||||
"preview": MessageLookupByLibrary.simpleMessage("Предпросмотр"),
|
||||
"process": MessageLookupByLibrary.simpleMessage("процесс"),
|
||||
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
|
||||
"profileAutoUpdateIntervalInvalidValidationDesc":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
@@ -566,7 +585,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"profiles": MessageLookupByLibrary.simpleMessage("Профили"),
|
||||
"profilesSort": MessageLookupByLibrary.simpleMessage("Сортировка профилей"),
|
||||
"progress": MessageLookupByLibrary.simpleMessage("Прогресс"),
|
||||
"project": MessageLookupByLibrary.simpleMessage("Проект"),
|
||||
"providers": MessageLookupByLibrary.simpleMessage("Провайдеры"),
|
||||
"proxies": MessageLookupByLibrary.simpleMessage("Прокси"),
|
||||
@@ -641,6 +659,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS-соединение следует правилам, необходимо настроить proxy-server-nameserver",
|
||||
),
|
||||
"restartCoreTip": MessageLookupByLibrary.simpleMessage(
|
||||
"Вы уверены, что хотите перезапустить ядро?",
|
||||
),
|
||||
"routeAddress": MessageLookupByLibrary.simpleMessage("Адрес маршрутизации"),
|
||||
"routeAddressDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"Настройка адреса прослушивания маршрутизации",
|
||||
|
||||
@@ -123,6 +123,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"开启将失去部分应用能力,获得全量的Clash的支持",
|
||||
),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("确定"),
|
||||
"connected": MessageLookupByLibrary.simpleMessage("已连接"),
|
||||
"connecting": MessageLookupByLibrary.simpleMessage("连接中..."),
|
||||
"connection": MessageLookupByLibrary.simpleMessage("连接"),
|
||||
"connections": MessageLookupByLibrary.simpleMessage("连接"),
|
||||
"connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"),
|
||||
@@ -136,13 +138,22 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"copySuccess": MessageLookupByLibrary.simpleMessage("复制成功"),
|
||||
"core": MessageLookupByLibrary.simpleMessage("内核"),
|
||||
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
|
||||
"coreStatus": MessageLookupByLibrary.simpleMessage("核心状态"),
|
||||
"country": MessageLookupByLibrary.simpleMessage("区域"),
|
||||
"crashTest": MessageLookupByLibrary.simpleMessage("崩溃测试"),
|
||||
"crashlytics": MessageLookupByLibrary.simpleMessage("崩溃分析"),
|
||||
"crashlyticsTip": MessageLookupByLibrary.simpleMessage(
|
||||
"开启后,应用崩溃时自动上传不包含敏感信息的崩溃日志",
|
||||
),
|
||||
"create": MessageLookupByLibrary.simpleMessage("创建"),
|
||||
"creationTime": MessageLookupByLibrary.simpleMessage("创建时间"),
|
||||
"cut": MessageLookupByLibrary.simpleMessage("剪切"),
|
||||
"dark": MessageLookupByLibrary.simpleMessage("深色"),
|
||||
"dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"),
|
||||
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
|
||||
"本应用使用 Firebase Crashlytics 收集崩溃信息以改进应用稳定性。\n收集的数据包括设备信息和崩溃详情,不包含个人敏感数据。\n您可以在设置中关闭此功能。",
|
||||
),
|
||||
"dataCollectionTip": MessageLookupByLibrary.simpleMessage("数据收集说明"),
|
||||
"days": MessageLookupByLibrary.simpleMessage("天"),
|
||||
"defaultNameserver": MessageLookupByLibrary.simpleMessage("默认域名服务器"),
|
||||
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析DNS服务器"),
|
||||
@@ -168,9 +179,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"本软件仅供学习交流、科研等非商业性质的用途,严禁将本软件用于商业目的。如有任何商业行为,均与本软件无关。",
|
||||
),
|
||||
"disconnected": MessageLookupByLibrary.simpleMessage("已断开"),
|
||||
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"),
|
||||
"discovery": MessageLookupByLibrary.simpleMessage("发现新版本"),
|
||||
"dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"),
|
||||
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS劫持"),
|
||||
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"),
|
||||
"doYouWantToPass": MessageLookupByLibrary.simpleMessage("是否要通过"),
|
||||
"domain": MessageLookupByLibrary.simpleMessage("域名"),
|
||||
@@ -209,6 +222,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"),
|
||||
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后会有一定性能损耗"),
|
||||
"fontFamily": MessageLookupByLibrary.simpleMessage("字体"),
|
||||
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要强制重启核心吗?"),
|
||||
"fourColumns": MessageLookupByLibrary.simpleMessage("四列"),
|
||||
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("果缤纷"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("常规"),
|
||||
@@ -341,6 +355,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"),
|
||||
"pressKeyboard": MessageLookupByLibrary.simpleMessage("请按下按键"),
|
||||
"preview": MessageLookupByLibrary.simpleMessage("预览"),
|
||||
"process": MessageLookupByLibrary.simpleMessage("进程"),
|
||||
"profile": MessageLookupByLibrary.simpleMessage("配置"),
|
||||
"profileAutoUpdateIntervalInvalidValidationDesc":
|
||||
MessageLookupByLibrary.simpleMessage("请输入有效间隔时间格式"),
|
||||
@@ -361,7 +376,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
),
|
||||
"profiles": MessageLookupByLibrary.simpleMessage("配置"),
|
||||
"profilesSort": MessageLookupByLibrary.simpleMessage("配置排序"),
|
||||
"progress": MessageLookupByLibrary.simpleMessage("进度"),
|
||||
"project": MessageLookupByLibrary.simpleMessage("项目"),
|
||||
"providers": MessageLookupByLibrary.simpleMessage("提供者"),
|
||||
"proxies": MessageLookupByLibrary.simpleMessage("代理"),
|
||||
@@ -404,6 +418,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"respectRulesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"DNS连接跟随rules,需配置proxy-server-nameserver",
|
||||
),
|
||||
"restartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要重启核心吗?"),
|
||||
"routeAddress": MessageLookupByLibrary.simpleMessage("路由地址"),
|
||||
"routeAddressDesc": MessageLookupByLibrary.simpleMessage("配置监听路由地址"),
|
||||
"routeMode": MessageLookupByLibrary.simpleMessage("路由模式"),
|
||||
|
||||
@@ -28,10 +28,9 @@ class AppLocalizations {
|
||||
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
||||
|
||||
static Future<AppLocalizations> load(Locale locale) {
|
||||
final name =
|
||||
(locale.countryCode?.isEmpty ?? false)
|
||||
? locale.languageCode
|
||||
: locale.toString();
|
||||
final name = (locale.countryCode?.isEmpty ?? false)
|
||||
? locale.languageCode
|
||||
: locale.toString();
|
||||
final localeName = Intl.canonicalizedLocale(name);
|
||||
return initializeMessages(localeName).then((_) {
|
||||
Intl.defaultLocale = localeName;
|
||||
@@ -3160,9 +3159,9 @@ class AppLocalizations {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Progress`
|
||||
String get progress {
|
||||
return Intl.message('Progress', name: 'progress', desc: '', args: []);
|
||||
/// `Process`
|
||||
String get process {
|
||||
return Intl.message('Process', name: 'process', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Host`
|
||||
@@ -3259,6 +3258,106 @@ class AppLocalizations {
|
||||
String get request {
|
||||
return Intl.message('Request', name: 'request', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Connected`
|
||||
String get connected {
|
||||
return Intl.message('Connected', name: 'connected', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Disconnected`
|
||||
String get disconnected {
|
||||
return Intl.message(
|
||||
'Disconnected',
|
||||
name: 'disconnected',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Connecting...`
|
||||
String get connecting {
|
||||
return Intl.message(
|
||||
'Connecting...',
|
||||
name: 'connecting',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Are you sure you want to restart the core?`
|
||||
String get restartCoreTip {
|
||||
return Intl.message(
|
||||
'Are you sure you want to restart the core?',
|
||||
name: 'restartCoreTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Are you sure you want to force restart the core?`
|
||||
String get forceRestartCoreTip {
|
||||
return Intl.message(
|
||||
'Are you sure you want to force restart the core?',
|
||||
name: 'forceRestartCoreTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `DNS hijacking`
|
||||
String get dnsHijacking {
|
||||
return Intl.message(
|
||||
'DNS hijacking',
|
||||
name: 'dnsHijacking',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Core status`
|
||||
String get coreStatus {
|
||||
return Intl.message('Core status', name: 'coreStatus', desc: '', args: []);
|
||||
}
|
||||
|
||||
/// `Data Collection Notice`
|
||||
String get dataCollectionTip {
|
||||
return Intl.message(
|
||||
'Data Collection Notice',
|
||||
name: 'dataCollectionTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `This app uses Firebase Crashlytics to collect crash information to improve app stability.\nThe collected data includes device information and crash details, but does not contain personal sensitive data.\nYou can disable this feature in settings.`
|
||||
String get dataCollectionContent {
|
||||
return Intl.message(
|
||||
'This app uses Firebase Crashlytics to collect crash information to improve app stability.\nThe collected data includes device information and crash details, but does not contain personal sensitive data.\nYou can disable this feature in settings.',
|
||||
name: 'dataCollectionContent',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Crash Analysis`
|
||||
String get crashlytics {
|
||||
return Intl.message(
|
||||
'Crash Analysis',
|
||||
name: 'crashlytics',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `When enabled, automatically uploads crash logs without sensitive information when the app crashes`
|
||||
String get crashlyticsTip {
|
||||
return Intl.message(
|
||||
'When enabled, automatically uploads crash logs without sensitive information when the app crashes',
|
||||
name: 'crashlyticsTip',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
119
lib/main.dart
119
lib/main.dart
@@ -1,128 +1,47 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/plugins/tile.dart';
|
||||
import 'package:fl_clash/plugins/vpn.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'application.dart';
|
||||
import 'clash/core.dart';
|
||||
import 'clash/lib.dart';
|
||||
import 'common/common.dart';
|
||||
import 'models/models.dart';
|
||||
import 'core/controller.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
globalState.isService = false;
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final version = await system.version;
|
||||
await clashCore.preload();
|
||||
await globalState.initApp(version);
|
||||
await android?.init();
|
||||
await window?.init(version);
|
||||
HttpOverrides.global = FlClashHttpOverrides();
|
||||
runApp(ProviderScope(
|
||||
child: const Application(),
|
||||
));
|
||||
runApp(ProviderScope(child: const Application()));
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _service(List<String> flags) async {
|
||||
globalState.isService = true;
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final quickStart = flags.contains('quick');
|
||||
final clashLibHandler = ClashLibHandler();
|
||||
globalState.isService = true;
|
||||
await globalState.init();
|
||||
|
||||
await coreController.preload();
|
||||
tile?.addListener(
|
||||
_TileListenerWithService(
|
||||
onStop: () async {
|
||||
await app?.tip(appLocalizations.stopVpn);
|
||||
clashLibHandler.stopListener();
|
||||
await vpn?.stop();
|
||||
exit(0);
|
||||
await globalState.handleStop();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
vpn?.handleGetStartForegroundParams = () {
|
||||
final traffic = clashLibHandler.getTraffic();
|
||||
return json.encode({
|
||||
'title': clashLibHandler.getCurrentProfileName(),
|
||||
'content': '$traffic'
|
||||
});
|
||||
};
|
||||
|
||||
vpn?.addListener(
|
||||
_VpnListenerWithService(
|
||||
onDnsChanged: (String dns) {
|
||||
clashLibHandler.updateDns(dns);
|
||||
},
|
||||
),
|
||||
);
|
||||
if (!quickStart) {
|
||||
_handleMainIpc(clashLibHandler);
|
||||
} else {
|
||||
commonPrint.log('quick start');
|
||||
await ClashCore.initGeo();
|
||||
Future(() async {
|
||||
app?.tip(appLocalizations.startVpn);
|
||||
final homeDirPath = await appPath.homeDirPath;
|
||||
final version = await system.version;
|
||||
await coreController.init(version);
|
||||
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
|
||||
enable: false,
|
||||
);
|
||||
Future(() async {
|
||||
final profileId = globalState.config.currentProfileId;
|
||||
if (profileId == null) {
|
||||
return;
|
||||
}
|
||||
final params = await globalState.getSetupParams(
|
||||
pathConfig: clashConfig,
|
||||
);
|
||||
final res = await clashLibHandler.quickStart(
|
||||
InitParams(
|
||||
homeDir: homeDirPath,
|
||||
version: version,
|
||||
),
|
||||
params,
|
||||
globalState.getCoreState(),
|
||||
);
|
||||
debugPrint(res);
|
||||
if (res.isNotEmpty) {
|
||||
await vpn?.stop();
|
||||
exit(0);
|
||||
}
|
||||
await vpn?.start(
|
||||
clashLibHandler.getAndroidVpnOptions(),
|
||||
);
|
||||
clashLibHandler.startListener();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _handleMainIpc(ClashLibHandler clashLibHandler) {
|
||||
final sendPort = IsolateNameServer.lookupPortByName(mainIsolate);
|
||||
if (sendPort == null) {
|
||||
return;
|
||||
}
|
||||
final serviceReceiverPort = ReceivePort();
|
||||
serviceReceiverPort.listen((message) async {
|
||||
final res = await clashLibHandler.invokeAction(message);
|
||||
sendPort.send(res);
|
||||
});
|
||||
sendPort.send(serviceReceiverPort.sendPort);
|
||||
final messageReceiverPort = ReceivePort();
|
||||
clashLibHandler.attachMessagePort(
|
||||
messageReceiverPort.sendPort.nativePort,
|
||||
);
|
||||
messageReceiverPort.listen((message) {
|
||||
sendPort.send(message);
|
||||
await globalState.handleStart();
|
||||
await coreController.setupConfig(clashConfig);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -130,27 +49,11 @@ void _handleMainIpc(ClashLibHandler clashLibHandler) {
|
||||
class _TileListenerWithService with TileListener {
|
||||
final Function() _onStop;
|
||||
|
||||
const _TileListenerWithService({
|
||||
required Function() onStop,
|
||||
}) : _onStop = onStop;
|
||||
const _TileListenerWithService({required Function() onStop})
|
||||
: _onStop = onStop;
|
||||
|
||||
@override
|
||||
void onStop() {
|
||||
_onStop();
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _VpnListenerWithService with VpnListener {
|
||||
final Function(String dns) _onDnsChanged;
|
||||
|
||||
const _VpnListenerWithService({
|
||||
required Function(String dns) onDnsChanged,
|
||||
}) : _onDnsChanged = onDnsChanged;
|
||||
|
||||
@override
|
||||
void onDnsChanged(String dns) {
|
||||
super.onDnsChanged(dns);
|
||||
_onDnsChanged(dns);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/core.dart';
|
||||
import 'package:fl_clash/plugins/app.dart';
|
||||
import 'package:fl_clash/plugins/service.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@@ -6,23 +10,49 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
class AndroidManager extends ConsumerStatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const AndroidManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const AndroidManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
ConsumerState<AndroidManager> createState() => _AndroidContainerState();
|
||||
}
|
||||
|
||||
class _AndroidContainerState extends ConsumerState<AndroidManager> {
|
||||
class _AndroidContainerState extends ConsumerState<AndroidManager>
|
||||
with ServiceListener {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
ref.listenManual(appSettingProvider.select((state) => state.hidden),
|
||||
(prev, next) {
|
||||
ref.listenManual(appSettingProvider.select((state) => state.hidden), (
|
||||
prev,
|
||||
next,
|
||||
) {
|
||||
app?.updateExcludeFromRecents(next);
|
||||
}, fireImmediately: true);
|
||||
ref.listenManual(androidStateProvider, (prev, next) {
|
||||
if (prev != next) {
|
||||
service?.syncAndroidState(next);
|
||||
}
|
||||
});
|
||||
service?.addListener(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
service?.removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void onServiceEvent(CoreEvent event) {
|
||||
coreEventManager.sendEvent(event);
|
||||
super.onServiceEvent(event);
|
||||
}
|
||||
|
||||
@override
|
||||
void onServiceCrash(String message) {
|
||||
coreEventManager.sendEvent(
|
||||
CoreEvent(type: CoreEventType.crash, data: message),
|
||||
);
|
||||
super.onServiceCrash(message);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -7,17 +7,13 @@ import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_acrylic/widgets/transparent_macos_sidebar.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class AppStateManager extends ConsumerStatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const AppStateManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const AppStateManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
ConsumerState<AppStateManager> createState() => _AppStateManagerState();
|
||||
@@ -36,46 +32,34 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
|
||||
}
|
||||
});
|
||||
});
|
||||
ref.listenManual(
|
||||
checkIpProvider,
|
||||
(prev, next) {
|
||||
if (prev != next && next.b) {
|
||||
detectionState.startCheck();
|
||||
}
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
ref.listenManual(checkIpProvider, (prev, next) {
|
||||
if (prev != next && next.b) {
|
||||
detectionState.startCheck();
|
||||
}
|
||||
}, fireImmediately: true);
|
||||
ref.listenManual(configStateProvider, (prev, next) {
|
||||
if (prev != next) {
|
||||
globalState.appController.savePreferencesDebounce();
|
||||
}
|
||||
});
|
||||
ref.listenManual(needUpdateGroupsProvider, (prev, next) {
|
||||
if (prev != next) {
|
||||
globalState.appController.updateGroupsDebounce();
|
||||
}
|
||||
});
|
||||
if (window == null) {
|
||||
return;
|
||||
}
|
||||
ref.listenManual(
|
||||
autoSetSystemDnsStateProvider,
|
||||
(prev, next) async {
|
||||
if (prev == next) {
|
||||
return;
|
||||
}
|
||||
if (next.a == true && next.b == true) {
|
||||
macOS?.updateDns(false);
|
||||
} else {
|
||||
macOS?.updateDns(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
ref.listenManual(
|
||||
currentBrightnessProvider,
|
||||
(prev, next) {
|
||||
if (prev == next) {
|
||||
return;
|
||||
}
|
||||
window?.updateMacOSBrightness(next);
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
ref.listenManual(autoSetSystemDnsStateProvider, (prev, next) async {
|
||||
if (prev == next) {
|
||||
return;
|
||||
}
|
||||
if (next.a == true && next.b == true) {
|
||||
macOS?.updateDns(false);
|
||||
} else {
|
||||
macOS?.updateDns(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -119,10 +103,7 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
|
||||
class AppEnvManager extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const AppEnvManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const AppEnvManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -149,14 +130,11 @@ class AppEnvManager extends StatelessWidget {
|
||||
class AppSidebarContainer extends ConsumerWidget {
|
||||
final Widget child;
|
||||
|
||||
const AppSidebarContainer({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const AppSidebarContainer({super.key, required this.child});
|
||||
|
||||
Widget _buildLoading() {
|
||||
return Consumer(
|
||||
builder: (_, ref, __) {
|
||||
builder: (_, ref, _) {
|
||||
final loading = ref.watch(loadingProvider);
|
||||
final isMobileView = ref.watch(isMobileViewProvider);
|
||||
return loading && !isMobileView
|
||||
@@ -173,18 +151,25 @@ class AppSidebarContainer extends ConsumerWidget {
|
||||
required BuildContext context,
|
||||
required Widget child,
|
||||
}) {
|
||||
if (!system.isMacOS) {
|
||||
return Material(
|
||||
color: context.colorScheme.surfaceContainer,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return TransparentMacOSSidebar(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
return Material(color: context.colorScheme.surfaceContainer, child: child);
|
||||
// if (!system.isMacOS) {
|
||||
// return Material(
|
||||
// color: context.colorScheme.surfaceContainer,
|
||||
// child: child,
|
||||
// );
|
||||
// }
|
||||
// return child;
|
||||
// return TransparentMacOSSidebar(
|
||||
// child: Material(color: Colors.transparent, child: child),
|
||||
// );
|
||||
}
|
||||
|
||||
void _updateSideBarWidth(WidgetRef ref, double contentWidth) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.read(sideWidthProvider.notifier).value =
|
||||
ref.read(viewSizeProvider.select((state) => state.width)) -
|
||||
contentWidth;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -199,92 +184,102 @@ class AppSidebarContainer extends ConsumerWidget {
|
||||
final showLabel = ref.watch(appSettingProvider).showLabel;
|
||||
return Row(
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.topRight,
|
||||
children: [
|
||||
_buildBackground(
|
||||
context: context,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
if (!system.isMacOS) ...[
|
||||
AppIcon(),
|
||||
SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: ScrollConfiguration(
|
||||
behavior: HiddenBarScrollBehavior(),
|
||||
child: SingleChildScrollView(
|
||||
child: IntrinsicHeight(
|
||||
child: NavigationRail(
|
||||
backgroundColor: Colors.transparent,
|
||||
selectedLabelTextStyle:
|
||||
context.textTheme.labelLarge!.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
unselectedLabelTextStyle:
|
||||
context.textTheme.labelLarge!.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
destinations: navigationItems
|
||||
.map(
|
||||
(e) => NavigationRailDestination(
|
||||
icon: e.icon,
|
||||
label: Text(
|
||||
Intl.message(e.label.name),
|
||||
_buildBackground(
|
||||
context: context,
|
||||
child: SafeArea(
|
||||
child: Stack(
|
||||
alignment: Alignment.topRight,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (system.isMacOS) SizedBox(height: 22),
|
||||
SizedBox(height: 10),
|
||||
if (!system.isMacOS) ...[
|
||||
ClipRect(child: AppIcon()),
|
||||
SizedBox(height: 12),
|
||||
],
|
||||
Expanded(
|
||||
child: ScrollConfiguration(
|
||||
behavior: HiddenBarScrollBehavior(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: NavigationRail(
|
||||
scrollable: true,
|
||||
minExtendedWidth: 200,
|
||||
backgroundColor: Colors.transparent,
|
||||
selectedLabelTextStyle: context
|
||||
.textTheme
|
||||
.labelLarge!
|
||||
.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (index) {
|
||||
globalState.appController
|
||||
.toPage(navigationItems[index].label);
|
||||
},
|
||||
extended: false,
|
||||
selectedIndex: currentIndex,
|
||||
labelType: showLabel
|
||||
? NavigationRailLabelType.all
|
||||
: NavigationRailLabelType.none,
|
||||
),
|
||||
unselectedLabelTextStyle: context
|
||||
.textTheme
|
||||
.labelLarge!
|
||||
.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
destinations: navigationItems
|
||||
.map(
|
||||
(e) => NavigationRailDestination(
|
||||
icon: e.icon,
|
||||
label: Text(Intl.message(e.label.name)),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (index) {
|
||||
globalState.appController.toPage(
|
||||
navigationItems[index].label,
|
||||
);
|
||||
},
|
||||
extended: false,
|
||||
selectedIndex: currentIndex,
|
||||
labelType: showLabel
|
||||
? NavigationRailLabelType.all
|
||||
: NavigationRailLabelType.none,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
showLabel: !state.showLabel,
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.menu,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
const SizedBox(height: 16),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) =>
|
||||
state.copyWith(showLabel: !state.showLabel),
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.menu,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
_buildLoading(),
|
||||
],
|
||||
),
|
||||
_buildLoading(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ClipRect(
|
||||
child: child,
|
||||
child: LayoutBuilder(
|
||||
builder: (_, constraints) {
|
||||
_updateSideBarWidth(ref, constraints.maxWidth);
|
||||
return child;
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/providers/app.dart';
|
||||
@@ -9,20 +9,17 @@ import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
class ClashManager extends ConsumerStatefulWidget {
|
||||
class CoreManager extends ConsumerStatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const ClashManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const CoreManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
ConsumerState<ClashManager> createState() => _ClashContainerState();
|
||||
ConsumerState<CoreManager> createState() => _CoreContainerState();
|
||||
}
|
||||
|
||||
class _ClashContainerState extends ConsumerState<ClashManager>
|
||||
with AppMessageListener {
|
||||
class _CoreContainerState extends ConsumerState<CoreManager>
|
||||
with CoreEventListener {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
@@ -31,38 +28,32 @@ class _ClashContainerState extends ConsumerState<ClashManager>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
clashMessage.addListener(this);
|
||||
coreEventManager.addListener(this);
|
||||
ref.listenManual(needSetupProvider, (prev, next) {
|
||||
if (prev != next) {
|
||||
globalState.appController.handleChangeProfile();
|
||||
}
|
||||
});
|
||||
ref.listenManual(coreStateProvider, (prev, next) async {
|
||||
if (prev != next) {
|
||||
await clashCore.setState(next);
|
||||
}
|
||||
});
|
||||
ref.listenManual(updateParamsProvider, (prev, next) {
|
||||
if (prev != next) {
|
||||
globalState.appController.updateClashConfigDebounce();
|
||||
}
|
||||
});
|
||||
|
||||
ref.listenManual(
|
||||
appSettingProvider.select((state) => state.openLogs),
|
||||
(prev, next) {
|
||||
if (next) {
|
||||
clashCore.startLog();
|
||||
} else {
|
||||
clashCore.stopLog();
|
||||
}
|
||||
},
|
||||
);
|
||||
ref.listenManual(appSettingProvider.select((state) => state.openLogs), (
|
||||
prev,
|
||||
next,
|
||||
) {
|
||||
if (next) {
|
||||
coreController.startLog();
|
||||
} else {
|
||||
coreController.stopLog();
|
||||
}
|
||||
}, fireImmediately: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
clashMessage.removeListener(this);
|
||||
coreEventManager.removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -71,13 +62,9 @@ class _ClashContainerState extends ConsumerState<ClashManager>
|
||||
super.onDelay(delay);
|
||||
final appController = globalState.appController;
|
||||
appController.setDelay(delay);
|
||||
debouncer.call(
|
||||
FunctionTag.updateDelay,
|
||||
() async {
|
||||
appController.updateGroupsDebounce();
|
||||
},
|
||||
duration: const Duration(milliseconds: 5000),
|
||||
);
|
||||
debouncer.call(FunctionTag.updateDelay, () async {
|
||||
appController.updateGroupsDebounce();
|
||||
}, duration: const Duration(milliseconds: 5000));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -97,12 +84,24 @@ class _ClashContainerState extends ConsumerState<ClashManager>
|
||||
|
||||
@override
|
||||
Future<void> onLoaded(String providerName) async {
|
||||
ref.read(providersProvider.notifier).setProvider(
|
||||
await clashCore.getExternalProvider(
|
||||
providerName,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(providersProvider.notifier)
|
||||
.setProvider(await coreController.getExternalProvider(providerName));
|
||||
globalState.appController.updateGroupsDebounce();
|
||||
super.onLoaded(providerName);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onCrash(String message) async {
|
||||
if (!globalState.isUserDisconnected) {
|
||||
context.showNotifier(message);
|
||||
}
|
||||
globalState.isUserDisconnected = false;
|
||||
if (ref.read(coreStatusProvider) != CoreStatus.connected) {
|
||||
return;
|
||||
}
|
||||
ref.read(coreStatusProvider.notifier).value = CoreStatus.disconnected;
|
||||
await coreController.shutdown();
|
||||
super.onCrash(message);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
export 'android_manager.dart';
|
||||
export 'app_manager.dart';
|
||||
export 'clash_manager.dart';
|
||||
export 'connectivity_manager.dart';
|
||||
export 'core_manager.dart';
|
||||
export 'message_manager.dart';
|
||||
export 'proxy_manager.dart';
|
||||
export 'theme_manager.dart';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
@@ -9,10 +10,7 @@ import 'package:flutter/material.dart';
|
||||
class MessageManager extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const MessageManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const MessageManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
State<MessageManager> createState() => MessageManagerState();
|
||||
@@ -20,8 +18,9 @@ class MessageManager extends StatefulWidget {
|
||||
|
||||
class MessageManagerState extends State<MessageManager> {
|
||||
final _messagesNotifier = ValueNotifier<List<CommonMessage>>([]);
|
||||
final List<CommonMessage> _bufferMessages = [];
|
||||
bool _pushing = false;
|
||||
final _bufferMessages = Queue<CommonMessage>();
|
||||
final _activeTimers = <String, Timer>{};
|
||||
bool _isDisplayingMessage = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -31,43 +30,48 @@ class MessageManagerState extends State<MessageManager> {
|
||||
@override
|
||||
void dispose() {
|
||||
_messagesNotifier.dispose();
|
||||
for (final timer in _activeTimers.values) {
|
||||
timer.cancel();
|
||||
}
|
||||
_activeTimers.clear();
|
||||
_bufferMessages.clear();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> message(String text) async {
|
||||
final commonMessage = CommonMessage(
|
||||
id: utils.uuidV4,
|
||||
text: text,
|
||||
);
|
||||
commonPrint.log(text);
|
||||
void message(String text) {
|
||||
final commonMessage = CommonMessage(id: utils.uuidV4, text: text);
|
||||
_bufferMessages.add(commonMessage);
|
||||
await _showMessage();
|
||||
commonPrint.log('message: $text');
|
||||
_processQueue();
|
||||
}
|
||||
|
||||
Future<void> _showMessage() async {
|
||||
if (_pushing == true) {
|
||||
void _cancelMessage(String id) {
|
||||
_bufferMessages.removeWhere((msg) => msg.id == id);
|
||||
if (_activeTimers.containsKey(id)) {
|
||||
_removeMessage(id);
|
||||
}
|
||||
}
|
||||
|
||||
void _processQueue() {
|
||||
if (_isDisplayingMessage || _bufferMessages.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_pushing = true;
|
||||
while (_bufferMessages.isNotEmpty) {
|
||||
final commonMessage = _bufferMessages.removeAt(0);
|
||||
_messagesNotifier.value = List.from(_messagesNotifier.value)
|
||||
..add(
|
||||
commonMessage,
|
||||
);
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
Future.delayed(commonMessage.duration, () {
|
||||
_handleRemove(commonMessage);
|
||||
});
|
||||
if (_bufferMessages.isEmpty) {
|
||||
_pushing = false;
|
||||
}
|
||||
}
|
||||
_isDisplayingMessage = true;
|
||||
final message = _bufferMessages.removeFirst();
|
||||
_messagesNotifier.value = List.from(_messagesNotifier.value)..add(message);
|
||||
final timer = Timer(message.duration, () {
|
||||
_removeMessage(message.id);
|
||||
});
|
||||
_activeTimers[message.id] = timer;
|
||||
}
|
||||
|
||||
Future<void> _handleRemove(CommonMessage commonMessage) async {
|
||||
_messagesNotifier.value = List<CommonMessage>.from(_messagesNotifier.value)
|
||||
..remove(commonMessage);
|
||||
void _removeMessage(String id) {
|
||||
_activeTimers.remove(id)?.cancel();
|
||||
final currentMessages = List<CommonMessage>.from(_messagesNotifier.value);
|
||||
currentMessages.removeWhere((msg) => msg.id == id);
|
||||
_messagesNotifier.value = currentMessages;
|
||||
_isDisplayingMessage = false;
|
||||
_processQueue();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -77,43 +81,67 @@ class MessageManagerState extends State<MessageManager> {
|
||||
widget.child,
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _messagesNotifier,
|
||||
builder: (_, messages, __) {
|
||||
return FadeThroughBox(
|
||||
builder: (_, messages, _) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: kToolbarHeight + 8,
|
||||
top: kToolbarHeight + 12,
|
||||
left: 12,
|
||||
right: 12,
|
||||
),
|
||||
alignment: Alignment.topRight,
|
||||
child: messages.isEmpty
|
||||
? SizedBox()
|
||||
: LayoutBuilder(
|
||||
key: Key(messages.last.id),
|
||||
builder: (_, constraints) {
|
||||
return Card(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(12.0),
|
||||
child: FadeThroughBox(
|
||||
alignment: Alignment.topRight,
|
||||
child: messages.isEmpty
|
||||
? SizedBox()
|
||||
: LayoutBuilder(
|
||||
key: Key(messages.last.id),
|
||||
builder: (_, constraints) {
|
||||
return Dismissible(
|
||||
key: ValueKey(messages.last.id),
|
||||
onDismissed: (_) {
|
||||
_cancelMessage(messages.last.id);
|
||||
},
|
||||
child: Card(
|
||||
shape: const RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(14),
|
||||
),
|
||||
),
|
||||
elevation: 10,
|
||||
color: context.colorScheme.surfaceContainerHigh,
|
||||
child: Container(
|
||||
width: min(constraints.maxWidth, 500),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
messages.last.text,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 16),
|
||||
IconButton(
|
||||
padding: EdgeInsets.all(2),
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () {
|
||||
_cancelMessage(messages.last.id);
|
||||
},
|
||||
icon: Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
elevation: 10,
|
||||
color: context.colorScheme.surfaceContainerHigh,
|
||||
child: Container(
|
||||
width: min(
|
||||
constraints.maxWidth,
|
||||
500,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Text(
|
||||
messages.last.text,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -13,10 +13,7 @@ import '../providers/state.dart';
|
||||
class ThemeManager extends ConsumerWidget {
|
||||
final Widget child;
|
||||
|
||||
const ThemeManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const ThemeManager({super.key, required this.child});
|
||||
|
||||
Widget _buildSystemUi(Widget child) {
|
||||
if (!system.isAndroid) {
|
||||
@@ -85,23 +82,28 @@ class ThemeManager extends ConsumerWidget {
|
||||
final height = MediaQuery.of(context).size.height;
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaler: TextScaler.linear(
|
||||
textScaleFactor,
|
||||
),
|
||||
textScaler: TextScaler.linear(textScaleFactor),
|
||||
padding: padding.copyWith(
|
||||
top: padding.top > height * 0.3 ? 20.0 : padding.top,
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
globalState.appController.updateViewSize(
|
||||
Size(
|
||||
container.maxWidth,
|
||||
container.maxHeight,
|
||||
),
|
||||
);
|
||||
return _buildSystemUi(child);
|
||||
},
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
floatingActionButtonTheme: Theme.of(context).floatingActionButtonTheme
|
||||
.copyWith(
|
||||
shape: const RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16.0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: LayoutBuilder(
|
||||
builder: (_, container) {
|
||||
globalState.appController.updateViewSize(
|
||||
Size(container.maxWidth, container.maxHeight),
|
||||
);
|
||||
return _buildSystemUi(child);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:fl_clash/models/app.dart';
|
||||
import 'package:fl_clash/plugins/tile.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -5,10 +6,7 @@ import 'package:flutter/material.dart';
|
||||
class TileManager extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const TileManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const TileManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
State<TileManager> createState() => _TileContainerState();
|
||||
@@ -22,12 +20,18 @@ class _TileContainerState extends State<TileManager> with TileListener {
|
||||
|
||||
@override
|
||||
void onStart() {
|
||||
if (globalState.appState.isStart) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.updateStatus(true);
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onStop() async {
|
||||
if (!globalState.appState.isStart) {
|
||||
return;
|
||||
}
|
||||
globalState.appController.updateStatus(false);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class _TrayContainerState extends ConsumerState<TrayManager> with TrayListener {
|
||||
|
||||
@override
|
||||
void onTrayIconRightMouseDown() {
|
||||
// ignore: deprecated_member_use
|
||||
trayManager.popUpContextMenu(bringAppToFront: true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/providers/app.dart';
|
||||
import 'package:fl_clash/providers/state.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -9,10 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
class VpnManager extends ConsumerStatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const VpnManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const VpnManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
ConsumerState<VpnManager> createState() => _VpnContainerState();
|
||||
@@ -28,15 +24,15 @@ class _VpnContainerState extends ConsumerState<VpnManager> {
|
||||
}
|
||||
|
||||
void showTip() {
|
||||
debouncer.call(
|
||||
throttler.call(
|
||||
FunctionTag.vpnTip,
|
||||
() {
|
||||
if (ref.read(runTimeProvider.notifier).isStart) {
|
||||
globalState.showNotifier(
|
||||
appLocalizations.vpnTip,
|
||||
);
|
||||
if (ref.read(isStartProvider)) {
|
||||
globalState.showNotifier(appLocalizations.vpnTip);
|
||||
}
|
||||
},
|
||||
duration: const Duration(seconds: 6),
|
||||
fire: true,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,7 @@ import 'package:window_manager/window_manager.dart';
|
||||
class WindowManager extends ConsumerStatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const WindowManager({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const WindowManager({super.key, required this.child});
|
||||
|
||||
@override
|
||||
ConsumerState<WindowManager> createState() => _WindowContainerState();
|
||||
@@ -31,19 +28,16 @@ class _WindowContainerState extends ConsumerState<WindowManager>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
ref.listenManual(
|
||||
appSettingProvider.select((state) => state.autoLaunch),
|
||||
(prev, next) {
|
||||
if (prev != next) {
|
||||
debouncer.call(
|
||||
FunctionTag.autoLaunch,
|
||||
() {
|
||||
autoLaunch?.updateStatus(next);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
ref.listenManual(appSettingProvider.select((state) => state.autoLaunch), (
|
||||
prev,
|
||||
next,
|
||||
) {
|
||||
if (prev != next) {
|
||||
debouncer.call(FunctionTag.autoLaunch, () {
|
||||
autoLaunch?.updateStatus(next);
|
||||
});
|
||||
}
|
||||
});
|
||||
windowExtManager.addListener(this);
|
||||
windowManager.addListener(this);
|
||||
}
|
||||
@@ -71,11 +65,10 @@ class _WindowContainerState extends ConsumerState<WindowManager>
|
||||
Future<void> onWindowMoved() async {
|
||||
super.onWindowMoved();
|
||||
final offset = await windowManager.getPosition();
|
||||
ref.read(windowSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
top: offset.dy,
|
||||
left: offset.dx,
|
||||
),
|
||||
ref
|
||||
.read(windowSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(top: offset.dy, left: offset.dx),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,11 +76,10 @@ class _WindowContainerState extends ConsumerState<WindowManager>
|
||||
Future<void> onWindowResized() async {
|
||||
super.onWindowResized();
|
||||
final size = await windowManager.getSize();
|
||||
ref.read(windowSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
),
|
||||
ref
|
||||
.read(windowSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(width: size.width, height: size.height),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,10 +109,7 @@ class _WindowContainerState extends ConsumerState<WindowManager>
|
||||
class WindowHeaderContainer extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const WindowHeaderContainer({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const WindowHeaderContainer({super.key, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -135,13 +124,8 @@ class WindowHeaderContainer extends StatelessWidget {
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: kHeaderHeight,
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: child!,
|
||||
),
|
||||
SizedBox(height: kHeaderHeight),
|
||||
Expanded(flex: 1, child: child!),
|
||||
],
|
||||
),
|
||||
const WindowHeader(),
|
||||
@@ -210,14 +194,10 @@ class _WindowHeaderState extends State<WindowHeader> {
|
||||
},
|
||||
icon: ValueListenableBuilder(
|
||||
valueListenable: isPinNotifier,
|
||||
builder: (_, value, ___) {
|
||||
builder: (_, value, _) {
|
||||
return value
|
||||
? const Icon(
|
||||
Icons.push_pin,
|
||||
)
|
||||
: const Icon(
|
||||
Icons.push_pin_outlined,
|
||||
);
|
||||
? const Icon(Icons.push_pin)
|
||||
: const Icon(Icons.push_pin_outlined);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -233,15 +213,10 @@ class _WindowHeaderState extends State<WindowHeader> {
|
||||
},
|
||||
icon: ValueListenableBuilder(
|
||||
valueListenable: isMaximizedNotifier,
|
||||
builder: (_, value, ___) {
|
||||
builder: (_, value, _) {
|
||||
return value
|
||||
? const Icon(
|
||||
Icons.filter_none,
|
||||
size: 20,
|
||||
)
|
||||
: const Icon(
|
||||
Icons.crop_square,
|
||||
);
|
||||
? const Icon(Icons.filter_none, size: 20)
|
||||
: const Icon(Icons.crop_square);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -280,15 +255,10 @@ class _WindowHeaderState extends State<WindowHeader> {
|
||||
),
|
||||
),
|
||||
if (system.isMacOS)
|
||||
const Text(
|
||||
appName,
|
||||
)
|
||||
const Text(appName)
|
||||
else ...[
|
||||
Positioned(
|
||||
right: 0,
|
||||
child: _buildActions(),
|
||||
),
|
||||
]
|
||||
Positioned(right: 0, child: _buildActions()),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -301,19 +271,17 @@ class AppIcon extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: EdgeInsets.all(6),
|
||||
child: SizedBox(
|
||||
width: 28,
|
||||
height: 28,
|
||||
child: CircleAvatar(
|
||||
foregroundImage: AssetImage('assets/images/icon.png'),
|
||||
backgroundColor: Colors.transparent,
|
||||
decoration: ShapeDecoration(
|
||||
color: context.colorScheme.surfaceContainerHighest,
|
||||
shape: RoundedSuperellipseBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Transform.translate(
|
||||
offset: Offset(0, -1),
|
||||
child: Image.asset('assets/images/icon.png', width: 34, height: 34),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/selector.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -11,7 +12,7 @@ part 'generated/app.freezed.dart';
|
||||
typedef DelayMap = Map<String, Map<String, int?>>;
|
||||
|
||||
@freezed
|
||||
class AppState with _$AppState {
|
||||
abstract class AppState with _$AppState {
|
||||
const factory AppState({
|
||||
@Default(false) bool isInit,
|
||||
@Default(false) bool backBlock,
|
||||
@@ -19,6 +20,7 @@ class AppState with _$AppState {
|
||||
@Default([]) List<Package> packages,
|
||||
@Default(0) int sortNum,
|
||||
required Size viewSize,
|
||||
@Default(0) double sideWidth,
|
||||
@Default({}) DelayMap delayMap,
|
||||
@Default([]) List<Group> groups,
|
||||
@Default(0) int checkIpNum,
|
||||
@@ -34,6 +36,9 @@ class AppState with _$AppState {
|
||||
@Default(false) bool realTunEnable,
|
||||
@Default(false) bool loading,
|
||||
required SystemUiOverlayStyle systemUiOverlayStyle,
|
||||
ProfileOverrideModel? profileOverrideModel,
|
||||
@Default({}) Map<QueryTag, String> queryMap,
|
||||
@Default(CoreStatus.connecting) CoreStatus coreStatus,
|
||||
}) = _AppState;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@@ -100,17 +98,14 @@ const defaultBypassPrivateRouteAddress = [
|
||||
'f000::/5',
|
||||
'f800::/6',
|
||||
'fe00::/9',
|
||||
'fec0::/10'
|
||||
'fec0::/10',
|
||||
];
|
||||
|
||||
@freezed
|
||||
class ProxyGroup with _$ProxyGroup {
|
||||
abstract class ProxyGroup with _$ProxyGroup {
|
||||
const factory ProxyGroup({
|
||||
required String name,
|
||||
@JsonKey(
|
||||
fromJson: GroupType.parseProfileType,
|
||||
)
|
||||
required GroupType type,
|
||||
@JsonKey(fromJson: GroupType.parseProfileType) required GroupType type,
|
||||
List<String>? proxies,
|
||||
List<String>? use,
|
||||
int? interval,
|
||||
@@ -131,17 +126,15 @@ class ProxyGroup with _$ProxyGroup {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class RuleProvider with _$RuleProvider {
|
||||
const factory RuleProvider({
|
||||
required String name,
|
||||
}) = _RuleProvider;
|
||||
abstract class RuleProvider with _$RuleProvider {
|
||||
const factory RuleProvider({required String name}) = _RuleProvider;
|
||||
|
||||
factory RuleProvider.fromJson(Map<String, Object?> json) =>
|
||||
_$RuleProviderFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Sniffer with _$Sniffer {
|
||||
abstract class Sniffer with _$Sniffer {
|
||||
const factory Sniffer({
|
||||
@Default(false) bool enable,
|
||||
@Default(true) @JsonKey(name: 'override-destination') bool overrideDest,
|
||||
@@ -165,7 +158,7 @@ List<String> _formJsonPorts(List? ports) {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SnifferConfig with _$SnifferConfig {
|
||||
abstract class SnifferConfig with _$SnifferConfig {
|
||||
const factory SnifferConfig({
|
||||
@Default([]) @JsonKey(fromJson: _formJsonPorts) List<String> ports,
|
||||
@JsonKey(name: 'override-destination') bool? overrideDest,
|
||||
@@ -176,7 +169,7 @@ class SnifferConfig with _$SnifferConfig {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Tun with _$Tun {
|
||||
abstract class Tun with _$Tun {
|
||||
const factory Tun({
|
||||
@Default(false) bool enable,
|
||||
@Default(appName) String device,
|
||||
@@ -206,30 +199,23 @@ extension TunExt on Tun {
|
||||
? defaultBypassPrivateRouteAddress
|
||||
: routeAddress;
|
||||
return switch (system.isDesktop) {
|
||||
true => copyWith(
|
||||
autoRoute: true,
|
||||
routeAddress: [],
|
||||
),
|
||||
true => copyWith(autoRoute: true, routeAddress: []),
|
||||
false => copyWith(
|
||||
autoRoute: mRouteAddress.isEmpty ? true : false,
|
||||
routeAddress: mRouteAddress,
|
||||
),
|
||||
autoRoute: mRouteAddress.isEmpty ? true : false,
|
||||
routeAddress: mRouteAddress,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FallbackFilter with _$FallbackFilter {
|
||||
abstract class FallbackFilter with _$FallbackFilter {
|
||||
const factory FallbackFilter({
|
||||
@Default(true) bool geoip,
|
||||
@Default('CN') @JsonKey(name: 'geoip-code') String geoipCode,
|
||||
@Default(['gfw']) List<String> geosite,
|
||||
@Default(['240.0.0.0/4']) List<String> ipcidr,
|
||||
@Default([
|
||||
'+.google.com',
|
||||
'+.facebook.com',
|
||||
'+.youtube.com',
|
||||
])
|
||||
@Default(['+.google.com', '+.facebook.com', '+.youtube.com'])
|
||||
List<String> domain,
|
||||
}) = _FallbackFilter;
|
||||
|
||||
@@ -238,7 +224,7 @@ class FallbackFilter with _$FallbackFilter {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Dns with _$Dns {
|
||||
abstract class Dns with _$Dns {
|
||||
const factory Dns({
|
||||
@Default(true) bool enable,
|
||||
@Default('0.0.0.0:1053') String listen,
|
||||
@@ -256,32 +242,20 @@ class Dns with _$Dns {
|
||||
@Default('198.18.0.1/16')
|
||||
@JsonKey(name: 'fake-ip-range')
|
||||
String fakeIpRange,
|
||||
@Default([
|
||||
'*.lan',
|
||||
'localhost.ptlogin2.qq.com',
|
||||
])
|
||||
@Default(['*.lan', 'localhost.ptlogin2.qq.com'])
|
||||
@JsonKey(name: 'fake-ip-filter')
|
||||
List<String> fakeIpFilter,
|
||||
@Default({
|
||||
'www.baidu.com': '114.114.114.114',
|
||||
'+.internal.crop.com': '10.0.0.1',
|
||||
'geosite:cn': 'https://doh.pub/dns-query'
|
||||
'geosite:cn': 'https://doh.pub/dns-query',
|
||||
})
|
||||
@JsonKey(name: 'nameserver-policy')
|
||||
Map<String, String> nameserverPolicy,
|
||||
@Default([
|
||||
'https://doh.pub/dns-query',
|
||||
'https://dns.alidns.com/dns-query',
|
||||
])
|
||||
@Default(['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'])
|
||||
List<String> nameserver,
|
||||
@Default([
|
||||
'tls://8.8.4.4',
|
||||
'tls://1.1.1.1',
|
||||
])
|
||||
List<String> fallback,
|
||||
@Default([
|
||||
'https://doh.pub/dns-query',
|
||||
])
|
||||
@Default(['tls://8.8.4.4', 'tls://1.1.1.1']) List<String> fallback,
|
||||
@Default(['https://doh.pub/dns-query'])
|
||||
@JsonKey(name: 'proxy-server-nameserver')
|
||||
List<String> proxyServerNameserver,
|
||||
@Default(FallbackFilter())
|
||||
@@ -301,7 +275,7 @@ class Dns with _$Dns {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GeoXUrl with _$GeoXUrl {
|
||||
abstract class GeoXUrl with _$GeoXUrl {
|
||||
const factory GeoXUrl({
|
||||
@Default(
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
|
||||
@@ -337,7 +311,7 @@ class GeoXUrl with _$GeoXUrl {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ParsedRule with _$ParsedRule {
|
||||
abstract class ParsedRule with _$ParsedRule {
|
||||
const factory ParsedRule({
|
||||
required RuleAction ruleAction,
|
||||
String? content,
|
||||
@@ -351,9 +325,7 @@ class ParsedRule with _$ParsedRule {
|
||||
factory ParsedRule.parseString(String value) {
|
||||
final splits = value.split(',');
|
||||
final shortSplits = splits
|
||||
.where(
|
||||
(item) => !item.contains('src') && !item.contains('no-resolve'),
|
||||
)
|
||||
.where((item) => !item.contains('src') && !item.contains('no-resolve'))
|
||||
.toList();
|
||||
final ruleAction = RuleAction.values.firstWhere(
|
||||
(item) => item.value == shortSplits.first,
|
||||
@@ -398,33 +370,25 @@ extension ParsedRuleExt on ParsedRule {
|
||||
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;
|
||||
abstract class Rule with _$Rule {
|
||||
const factory Rule({required String id, required String value}) = _Rule;
|
||||
|
||||
factory Rule.value(String value) {
|
||||
return Rule(
|
||||
value: value,
|
||||
id: utils.uuidV4,
|
||||
);
|
||||
return Rule(value: value, id: utils.uuidV4);
|
||||
}
|
||||
|
||||
factory Rule.fromJson(Map<String, Object?> json) => _$RuleFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SubRule with _$SubRule {
|
||||
const factory SubRule({
|
||||
required String name,
|
||||
}) = _SubRule;
|
||||
abstract class SubRule with _$SubRule {
|
||||
const factory SubRule({required String name}) = _SubRule;
|
||||
|
||||
factory SubRule.fromJson(Map<String, Object?> json) =>
|
||||
_$SubRuleFromJson(json);
|
||||
@@ -434,11 +398,7 @@ List<Rule> _genRule(List<dynamic>? rules) {
|
||||
if (rules == null) {
|
||||
return [];
|
||||
}
|
||||
return rules
|
||||
.map(
|
||||
(item) => Rule.value(item),
|
||||
)
|
||||
.toList();
|
||||
return rules.map((item) => Rule.value(item)).toList();
|
||||
}
|
||||
|
||||
List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
|
||||
@@ -446,17 +406,11 @@ List<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {
|
||||
}
|
||||
|
||||
List<SubRule> _genSubRules(Map<String, dynamic> json) {
|
||||
return json.entries
|
||||
.map(
|
||||
(entry) => SubRule(
|
||||
name: entry.key,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return json.entries.map((entry) => SubRule(name: entry.key)).toList();
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ClashConfigSnippet with _$ClashConfigSnippet {
|
||||
abstract class ClashConfigSnippet with _$ClashConfigSnippet {
|
||||
const factory ClashConfigSnippet({
|
||||
@Default([]) @JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,
|
||||
@JsonKey(fromJson: _genRule, name: 'rules') @Default([]) List<Rule> rule,
|
||||
@@ -473,7 +427,7 @@ class ClashConfigSnippet with _$ClashConfigSnippet {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ClashConfig with _$ClashConfig {
|
||||
abstract class ClashConfig with _$ClashConfig {
|
||||
const factory ClashConfig({
|
||||
@Default(defaultMixedPort) @JsonKey(name: 'mixed-port') int mixedPort,
|
||||
@Default(0) @JsonKey(name: 'socks-port') int socksPort,
|
||||
@@ -484,7 +438,7 @@ class ClashConfig with _$ClashConfig {
|
||||
@Default(false) @JsonKey(name: 'allow-lan') bool allowLan,
|
||||
@Default(LogLevel.error) @JsonKey(name: 'log-level') LogLevel logLevel,
|
||||
@Default(false) bool ipv6,
|
||||
@Default(FindProcessMode.off)
|
||||
@Default(FindProcessMode.always)
|
||||
@JsonKey(
|
||||
name: 'find-process-mode',
|
||||
unknownEnumValue: FindProcessMode.always,
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -11,7 +7,7 @@ part 'generated/common.freezed.dart';
|
||||
part 'generated/common.g.dart';
|
||||
|
||||
@freezed
|
||||
class NavigationItem with _$NavigationItem {
|
||||
abstract class NavigationItem with _$NavigationItem {
|
||||
const factory NavigationItem({
|
||||
required Icon icon,
|
||||
required PageLabel label,
|
||||
@@ -25,7 +21,7 @@ class NavigationItem with _$NavigationItem {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Package with _$Package {
|
||||
abstract class Package with _$Package {
|
||||
const factory Package({
|
||||
required String packageName,
|
||||
required String label,
|
||||
@@ -39,7 +35,7 @@ class Package with _$Package {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Metadata with _$Metadata {
|
||||
abstract class Metadata with _$Metadata {
|
||||
const factory Metadata({
|
||||
@Default(0) int uid,
|
||||
@Default('') String network,
|
||||
@@ -65,7 +61,7 @@ class Metadata with _$Metadata {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TrackerInfo with _$TrackerInfo {
|
||||
abstract class TrackerInfo with _$TrackerInfo {
|
||||
const factory TrackerInfo({
|
||||
required String id,
|
||||
@Default(0) int upload,
|
||||
@@ -99,9 +95,9 @@ extension TrackerInfoExt on TrackerInfo {
|
||||
final process = metadata.process;
|
||||
final uid = metadata.uid;
|
||||
if (uid != 0) {
|
||||
return '$process($uid)';
|
||||
return '$process($uid)'.trim();
|
||||
}
|
||||
return process;
|
||||
return process.trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +110,7 @@ String _logDateTime(dynamic _) {
|
||||
// }
|
||||
|
||||
@freezed
|
||||
class Log with _$Log {
|
||||
abstract class Log with _$Log {
|
||||
const factory Log({
|
||||
// @JsonKey(fromJson: _logId) required String id,
|
||||
@JsonKey(name: 'LogLevel') @Default(LogLevel.info) LogLevel logLevel,
|
||||
@@ -122,9 +118,7 @@ class Log with _$Log {
|
||||
@JsonKey(fromJson: _logDateTime) required String dateTime,
|
||||
}) = _Log;
|
||||
|
||||
factory Log.app(
|
||||
String payload,
|
||||
) {
|
||||
factory Log.app(String payload) {
|
||||
return Log(
|
||||
payload: payload,
|
||||
dateTime: _logDateTime(null),
|
||||
@@ -136,7 +130,7 @@ class Log with _$Log {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class LogsState with _$LogsState {
|
||||
abstract class LogsState with _$LogsState {
|
||||
const factory LogsState({
|
||||
@Default([]) List<Log> logs,
|
||||
@Default([]) List<String> keywords,
|
||||
@@ -148,19 +142,17 @@ class LogsState with _$LogsState {
|
||||
extension LogsStateExt on LogsState {
|
||||
List<Log> get list {
|
||||
final lowQuery = query.toLowerCase();
|
||||
return logs.where(
|
||||
(log) {
|
||||
final logLevelName = log.logLevel.name;
|
||||
return {logLevelName}.containsAll(keywords) &&
|
||||
((log.payload.toLowerCase().contains(lowQuery)) ||
|
||||
logLevelName.contains(lowQuery));
|
||||
},
|
||||
).toList();
|
||||
return logs.where((log) {
|
||||
final logLevelName = log.logLevel.name;
|
||||
return {logLevelName}.containsAll(keywords) &&
|
||||
((log.payload.toLowerCase().contains(lowQuery)) ||
|
||||
logLevelName.contains(lowQuery));
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TrackerInfosState with _$TrackerInfosState {
|
||||
abstract class TrackerInfosState with _$TrackerInfosState {
|
||||
const factory TrackerInfosState({
|
||||
@Default([]) List<TrackerInfo> trackerInfos,
|
||||
@Default([]) List<String> keywords,
|
||||
@@ -178,8 +170,8 @@ extension TrackerInfosStateExt on TrackerInfosState {
|
||||
final process = trackerInfo.metadata.process;
|
||||
final networkText = trackerInfo.metadata.network.toLowerCase();
|
||||
final hostText = trackerInfo.metadata.host.toLowerCase();
|
||||
final destinationIPText =
|
||||
trackerInfo.metadata.destinationIP.toLowerCase();
|
||||
final destinationIPText = trackerInfo.metadata.destinationIP
|
||||
.toLowerCase();
|
||||
final processText = trackerInfo.metadata.process.toLowerCase();
|
||||
final chainsText = chains.join('').toLowerCase();
|
||||
return {...chains, process}.containsAll(keywords) &&
|
||||
@@ -195,7 +187,7 @@ extension TrackerInfosStateExt on TrackerInfosState {
|
||||
const defaultDavFileName = 'backup.zip';
|
||||
|
||||
@freezed
|
||||
class DAV with _$DAV {
|
||||
abstract class DAV with _$DAV {
|
||||
const factory DAV({
|
||||
required String uri,
|
||||
required String user,
|
||||
@@ -207,20 +199,18 @@ class DAV with _$DAV {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class FileInfo with _$FileInfo {
|
||||
const factory FileInfo({
|
||||
required int size,
|
||||
required DateTime lastModified,
|
||||
}) = _FileInfo;
|
||||
abstract class FileInfo with _$FileInfo {
|
||||
const factory FileInfo({required int size, required DateTime lastModified}) =
|
||||
_FileInfo;
|
||||
}
|
||||
|
||||
extension FileInfoExt on FileInfo {
|
||||
String get desc =>
|
||||
'${TrafficValue(value: size).show} · ${lastModified.lastUpdateTimeDesc}';
|
||||
'${size.traffic.show} · ${lastModified.lastUpdateTimeDesc}';
|
||||
}
|
||||
|
||||
@freezed
|
||||
class VersionInfo with _$VersionInfo {
|
||||
abstract class VersionInfo with _$VersionInfo {
|
||||
const factory VersionInfo({
|
||||
@Default('') String clashName,
|
||||
@Default('') String version,
|
||||
@@ -230,60 +220,38 @@ class VersionInfo with _$VersionInfo {
|
||||
_$VersionInfoFromJson(json);
|
||||
}
|
||||
|
||||
class Traffic {
|
||||
int id;
|
||||
TrafficValue up;
|
||||
TrafficValue down;
|
||||
@freezed
|
||||
abstract class Traffic with _$Traffic {
|
||||
const factory Traffic({@Default(0) num up, @Default(0) num down}) = _Traffic;
|
||||
|
||||
Traffic({int? up, int? down})
|
||||
: id = DateTime.now().millisecondsSinceEpoch,
|
||||
up = TrafficValue(value: up),
|
||||
down = TrafficValue(value: down);
|
||||
|
||||
num get speed => up.value + down.value;
|
||||
|
||||
factory Traffic.fromMap(Map<String, dynamic> map) {
|
||||
return Traffic(
|
||||
up: map['up'],
|
||||
down: map['down'],
|
||||
);
|
||||
}
|
||||
|
||||
String toSpeedText() {
|
||||
return '↑ $up/s ↓ $down/s';
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$up↑ $down↓';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is Traffic &&
|
||||
runtimeType == other.runtimeType &&
|
||||
id == other.id &&
|
||||
up == other.up &&
|
||||
down == other.down;
|
||||
|
||||
@override
|
||||
int get hashCode => id.hashCode ^ up.hashCode ^ down.hashCode;
|
||||
factory Traffic.fromJson(Map<String, Object?> json) =>
|
||||
_$TrafficFromJson(json);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class TrafficValueShow {
|
||||
final double value;
|
||||
final TrafficUnit unit;
|
||||
extension TrafficExt on Traffic {
|
||||
String get speedText {
|
||||
return '↑ ${up.traffic.show}/s ↓ ${down.traffic.show}/s';
|
||||
}
|
||||
|
||||
const TrafficValueShow({
|
||||
required this.value,
|
||||
required this.unit,
|
||||
});
|
||||
String get desc {
|
||||
return '${up.traffic.show} ↑ ${down.traffic.show} ↓';
|
||||
}
|
||||
|
||||
num get speed => up + down;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Proxy with _$Proxy {
|
||||
abstract class TrafficShow with _$TrafficShow {
|
||||
const factory TrafficShow({required String value, required String unit}) =
|
||||
_TrafficShow;
|
||||
}
|
||||
|
||||
extension TrafficShowExt on TrafficShow {
|
||||
String get show => '$value$unit';
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class Proxy with _$Proxy {
|
||||
const factory Proxy({
|
||||
required String name,
|
||||
required String type,
|
||||
@@ -294,7 +262,7 @@ class Proxy with _$Proxy {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Group with _$Group {
|
||||
abstract class Group with _$Group {
|
||||
const factory Group({
|
||||
required GroupType type,
|
||||
@Default([]) List<Proxy> all,
|
||||
@@ -326,70 +294,8 @@ extension GroupExt on Group {
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class TrafficValue {
|
||||
final int _value;
|
||||
|
||||
const TrafficValue({int? value}) : _value = value ?? 0;
|
||||
|
||||
int get value => _value;
|
||||
|
||||
String get show => '$showValue $showUnit';
|
||||
|
||||
String get shortShow =>
|
||||
'${trafficValueShow.value.fixed(decimals: 1)} $showUnit';
|
||||
|
||||
String get showValue => trafficValueShow.value.fixed();
|
||||
|
||||
String get showUnit => trafficValueShow.unit.name;
|
||||
|
||||
TrafficValueShow get trafficValueShow {
|
||||
if (_value > pow(1024, 4)) {
|
||||
return TrafficValueShow(
|
||||
value: _value / pow(1024, 4),
|
||||
unit: TrafficUnit.TB,
|
||||
);
|
||||
}
|
||||
if (_value > pow(1024, 3)) {
|
||||
return TrafficValueShow(
|
||||
value: _value / pow(1024, 3),
|
||||
unit: TrafficUnit.GB,
|
||||
);
|
||||
}
|
||||
if (_value > pow(1024, 2)) {
|
||||
return TrafficValueShow(
|
||||
value: _value / pow(1024, 2), unit: TrafficUnit.MB);
|
||||
}
|
||||
if (_value > pow(1024, 1)) {
|
||||
return TrafficValueShow(
|
||||
value: _value / pow(1024, 1),
|
||||
unit: TrafficUnit.KB,
|
||||
);
|
||||
}
|
||||
return TrafficValueShow(
|
||||
value: _value.toDouble(),
|
||||
unit: TrafficUnit.B,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$showValue$showUnit';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is TrafficValue &&
|
||||
runtimeType == other.runtimeType &&
|
||||
_value == other._value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ColorSchemes with _$ColorSchemes {
|
||||
abstract class ColorSchemes with _$ColorSchemes {
|
||||
const factory ColorSchemes({
|
||||
ColorScheme? lightColorScheme,
|
||||
ColorScheme? darkColorScheme,
|
||||
@@ -426,79 +332,76 @@ extension ColorSchemesExt on ColorSchemes {
|
||||
}
|
||||
}
|
||||
|
||||
class IpInfo {
|
||||
final String ip;
|
||||
final String countryCode;
|
||||
|
||||
const IpInfo({
|
||||
required this.ip,
|
||||
required this.countryCode,
|
||||
});
|
||||
@freezed
|
||||
abstract class IpInfo with _$IpInfo {
|
||||
const factory IpInfo({required String ip, required String countryCode}) =
|
||||
_IpInfo;
|
||||
|
||||
static IpInfo fromIpInfoIoJson(Map<String, dynamic> json) {
|
||||
return switch (json) {
|
||||
{
|
||||
'ip': final String ip,
|
||||
'country': final String country,
|
||||
} =>
|
||||
IpInfo(
|
||||
ip: ip,
|
||||
countryCode: country,
|
||||
),
|
||||
{'ip': final String ip, 'country': final String country} => IpInfo(
|
||||
ip: ip,
|
||||
countryCode: country,
|
||||
),
|
||||
_ => throw const FormatException('invalid json'),
|
||||
};
|
||||
}
|
||||
|
||||
static IpInfo fromIpApiCoJson(Map<String, dynamic> json) {
|
||||
return switch (json) {
|
||||
{
|
||||
'ip': final String ip,
|
||||
'country_code': final String countryCode,
|
||||
} =>
|
||||
IpInfo(
|
||||
ip: ip,
|
||||
countryCode: countryCode,
|
||||
),
|
||||
{'ip': final String ip, 'country_code': final String countryCode} =>
|
||||
IpInfo(ip: ip, countryCode: countryCode),
|
||||
_ => throw const FormatException('invalid json'),
|
||||
};
|
||||
}
|
||||
|
||||
static IpInfo fromIpSbJson(Map<String, dynamic> json) {
|
||||
return switch (json) {
|
||||
{
|
||||
'ip': final String ip,
|
||||
'country_code': final String countryCode,
|
||||
} =>
|
||||
IpInfo(
|
||||
ip: ip,
|
||||
countryCode: countryCode,
|
||||
),
|
||||
{'ip': final String ip, 'country_code': final String countryCode} =>
|
||||
IpInfo(ip: ip, countryCode: countryCode),
|
||||
_ => throw const FormatException('invalid json'),
|
||||
};
|
||||
}
|
||||
|
||||
static IpInfo fromIpwhoIsJson(Map<String, dynamic> json) {
|
||||
static IpInfo fromIpWhoIsJson(Map<String, dynamic> json) {
|
||||
return switch (json) {
|
||||
{
|
||||
'ip': final String ip,
|
||||
'country_code': final String countryCode,
|
||||
} =>
|
||||
IpInfo(
|
||||
ip: ip,
|
||||
countryCode: countryCode,
|
||||
),
|
||||
{'ip': final String ip, 'country_code': final String countryCode} =>
|
||||
IpInfo(ip: ip, countryCode: countryCode),
|
||||
_ => throw const FormatException('invalid json'),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IpInfo{ip: $ip, countryCode: $countryCode}';
|
||||
static IpInfo fromMyIpJson(Map<String, dynamic> json) {
|
||||
return switch (json) {
|
||||
{'ip': final String ip, 'cc': final String countryCode} => IpInfo(
|
||||
ip: ip,
|
||||
countryCode: countryCode,
|
||||
),
|
||||
_ => throw const FormatException('invalid json'),
|
||||
};
|
||||
}
|
||||
|
||||
static IpInfo fromIpAPIJson(Map<String, dynamic> json) {
|
||||
return switch (json) {
|
||||
{'query': final String ip, 'countryCode': final String countryCode} =>
|
||||
IpInfo(ip: ip, countryCode: countryCode),
|
||||
_ => throw const FormatException('invalid json'),
|
||||
};
|
||||
}
|
||||
|
||||
static IpInfo fromIdentMeJson(Map<String, dynamic> json) {
|
||||
return switch (json) {
|
||||
{'ip': final String ip, 'cc': final String countryCode} => IpInfo(
|
||||
ip: ip,
|
||||
countryCode: countryCode,
|
||||
),
|
||||
_ => throw const FormatException('invalid json'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class HotKeyAction with _$HotKeyAction {
|
||||
abstract class HotKeyAction with _$HotKeyAction {
|
||||
const factory HotKeyAction({
|
||||
required HotAction action,
|
||||
int? key,
|
||||
@@ -512,7 +415,7 @@ class HotKeyAction with _$HotKeyAction {
|
||||
typedef Validator = String? Function(String? value);
|
||||
|
||||
@freezed
|
||||
class Field with _$Field {
|
||||
abstract class Field with _$Field {
|
||||
const factory Field({
|
||||
required String label,
|
||||
required String value,
|
||||
@@ -520,35 +423,33 @@ class Field with _$Field {
|
||||
}) = _Field;
|
||||
}
|
||||
|
||||
enum PopupMenuItemType {
|
||||
primary,
|
||||
danger,
|
||||
}
|
||||
enum PopupMenuItemType { primary, danger }
|
||||
|
||||
class PopupMenuItemData {
|
||||
const PopupMenuItemData({
|
||||
this.icon,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.danger = false,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final VoidCallback? onPressed;
|
||||
final IconData? icon;
|
||||
final bool danger;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TextPainterParams with _$TextPainterParams {
|
||||
const factory TextPainterParams({
|
||||
required String? text,
|
||||
required double? fontSize,
|
||||
required double textScaleFactor,
|
||||
@Default(double.infinity) double maxWidth,
|
||||
int? maxLines,
|
||||
}) = _TextPainterParams;
|
||||
abstract class AndroidState with _$AndroidState {
|
||||
const factory AndroidState({
|
||||
required String currentProfileName,
|
||||
required String stopText,
|
||||
required bool onlyStatisticsProxy,
|
||||
required bool crashlytics,
|
||||
}) = _AndroidState;
|
||||
|
||||
factory TextPainterParams.fromJson(Map<String, Object?> json) =>
|
||||
_$TextPainterParamsFromJson(json);
|
||||
factory AndroidState.fromJson(Map<String, Object?> json) =>
|
||||
_$AndroidStateFromJson(json);
|
||||
}
|
||||
|
||||
class CloseWindowIntent extends Intent {
|
||||
@@ -556,24 +457,18 @@ class CloseWindowIntent extends Intent {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Result<T> with _$Result<T> {
|
||||
abstract class Result<T> with _$Result<T> {
|
||||
const factory Result({
|
||||
required T? data,
|
||||
required ResultType type,
|
||||
required String message,
|
||||
}) = _Result;
|
||||
|
||||
factory Result.success(T data) => Result(
|
||||
data: data,
|
||||
type: ResultType.success,
|
||||
message: '',
|
||||
);
|
||||
factory Result.success(T data) =>
|
||||
Result(data: data, type: ResultType.success, message: '');
|
||||
|
||||
factory Result.error(String message) => Result(
|
||||
data: null,
|
||||
type: ResultType.error,
|
||||
message: message,
|
||||
);
|
||||
factory Result.error(String message) =>
|
||||
Result(data: null, type: ResultType.error, message: message);
|
||||
}
|
||||
|
||||
extension ResultExt on Result {
|
||||
@@ -583,23 +478,42 @@ extension ResultExt on Result {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Script with _$Script {
|
||||
abstract class Script with _$Script {
|
||||
const factory Script({
|
||||
required String id,
|
||||
required String label,
|
||||
required String content,
|
||||
}) = _Script;
|
||||
|
||||
factory Script.create({
|
||||
required String label,
|
||||
required String content,
|
||||
}) {
|
||||
return Script(
|
||||
id: utils.uuidV4,
|
||||
label: label,
|
||||
content: content,
|
||||
);
|
||||
factory Script.create({required String label, required String content}) {
|
||||
return Script(id: utils.uuidV4, label: label, content: content);
|
||||
}
|
||||
|
||||
factory Script.fromJson(Map<String, Object?> json) => _$ScriptFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class DelayState with _$DelayState {
|
||||
const factory DelayState({required int delay, required bool group}) =
|
||||
_DelayState;
|
||||
}
|
||||
|
||||
extension DelayStateExt on DelayState {
|
||||
int get priority {
|
||||
if (delay > 0) return 0;
|
||||
if (delay == 0) return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int compareTo(DelayState other) {
|
||||
if (priority != other.priority) {
|
||||
return priority.compareTo(other.priority);
|
||||
}
|
||||
if (delay != other.delay) {
|
||||
return delay.compareTo(other.delay);
|
||||
}
|
||||
if (group && !group) return -1;
|
||||
if (!group && group) return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -27,7 +25,7 @@ const defaultBypassDomain = [
|
||||
'172.2*',
|
||||
'172.30.*',
|
||||
'172.31.*',
|
||||
'192.168.*'
|
||||
'192.168.*',
|
||||
];
|
||||
|
||||
const defaultAppSettingProps = AppSettingProps();
|
||||
@@ -36,9 +34,7 @@ const defaultNetworkProps = NetworkProps();
|
||||
const defaultProxiesStyle = ProxiesStyle();
|
||||
const defaultWindowProps = WindowProps();
|
||||
const defaultAccessControl = AccessControl();
|
||||
final defaultThemeProps = ThemeProps(
|
||||
primaryColor: defaultPrimaryColor,
|
||||
);
|
||||
final defaultThemeProps = ThemeProps(primaryColor: defaultPrimaryColor);
|
||||
|
||||
const List<DashboardWidget> defaultDashboardWidgets = [
|
||||
DashboardWidget.networkSpeed,
|
||||
@@ -64,7 +60,7 @@ List<DashboardWidget> dashboardWidgetsSafeFormJson(
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AppSettingProps with _$AppSettingProps {
|
||||
abstract class AppSettingProps with _$AppSettingProps {
|
||||
const factory AppSettingProps({
|
||||
String? locale,
|
||||
@Default(defaultDashboardWidgets)
|
||||
@@ -81,6 +77,8 @@ class AppSettingProps with _$AppSettingProps {
|
||||
@Default(true) bool autoCheckUpdate,
|
||||
@Default(false) bool showLabel,
|
||||
@Default(false) bool disclaimerAccepted,
|
||||
@Default(false) bool crashlyticsTip,
|
||||
@Default(false) bool crashlytics,
|
||||
@Default(true) bool minimizeOnExit,
|
||||
@Default(false) bool hidden,
|
||||
@Default(false) bool developerMode,
|
||||
@@ -98,7 +96,7 @@ class AppSettingProps with _$AppSettingProps {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AccessControl with _$AccessControl {
|
||||
abstract class AccessControl with _$AccessControl {
|
||||
const factory AccessControl({
|
||||
@Default(false) bool enable,
|
||||
@Default(AccessControlMode.rejectSelected) AccessControlMode mode,
|
||||
@@ -115,13 +113,13 @@ class AccessControl with _$AccessControl {
|
||||
|
||||
extension AccessControlExt on AccessControl {
|
||||
List<String> get currentList => switch (mode) {
|
||||
AccessControlMode.acceptSelected => acceptList,
|
||||
AccessControlMode.rejectSelected => rejectList,
|
||||
};
|
||||
AccessControlMode.acceptSelected => acceptList,
|
||||
AccessControlMode.rejectSelected => rejectList,
|
||||
};
|
||||
}
|
||||
|
||||
@freezed
|
||||
class WindowProps with _$WindowProps {
|
||||
abstract class WindowProps with _$WindowProps {
|
||||
const factory WindowProps({
|
||||
@Default(750) double width,
|
||||
@Default(600) double height,
|
||||
@@ -134,12 +132,13 @@ class WindowProps with _$WindowProps {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class VpnProps with _$VpnProps {
|
||||
abstract class VpnProps with _$VpnProps {
|
||||
const factory VpnProps({
|
||||
@Default(true) bool enable,
|
||||
@Default(true) bool systemProxy,
|
||||
@Default(false) bool ipv6,
|
||||
@Default(true) bool allowBypass,
|
||||
@Default(false) bool dnsHijacking,
|
||||
@Default(defaultAccessControl) AccessControl accessControl,
|
||||
}) = _VpnProps;
|
||||
|
||||
@@ -148,7 +147,7 @@ class VpnProps with _$VpnProps {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NetworkProps with _$NetworkProps {
|
||||
abstract class NetworkProps with _$NetworkProps {
|
||||
const factory NetworkProps({
|
||||
@Default(true) bool systemProxy,
|
||||
@Default(defaultBypassDomain) List<String> bypassDomain,
|
||||
@@ -161,7 +160,7 @@ class NetworkProps with _$NetworkProps {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProxiesStyle with _$ProxiesStyle {
|
||||
abstract class ProxiesStyle with _$ProxiesStyle {
|
||||
const factory ProxiesStyle({
|
||||
@Default(ProxiesType.tab) ProxiesType type,
|
||||
@Default(ProxiesSortType.none) ProxiesSortType sortType,
|
||||
@@ -176,7 +175,7 @@ class ProxiesStyle with _$ProxiesStyle {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TextScale with _$TextScale {
|
||||
abstract class TextScale with _$TextScale {
|
||||
const factory TextScale({
|
||||
@Default(false) bool enable,
|
||||
@Default(1.0) double scale,
|
||||
@@ -187,7 +186,7 @@ class TextScale with _$TextScale {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ThemeProps with _$ThemeProps {
|
||||
abstract class ThemeProps with _$ThemeProps {
|
||||
const factory ThemeProps({
|
||||
int? primaryColor,
|
||||
@Default(defaultPrimaryColors) List<int> primaryColors,
|
||||
@@ -213,7 +212,7 @@ class ThemeProps with _$ThemeProps {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ScriptProps with _$ScriptProps {
|
||||
abstract class ScriptProps with _$ScriptProps {
|
||||
const factory ScriptProps({
|
||||
String? currentId,
|
||||
@Default([]) List<Script> scripts,
|
||||
@@ -242,7 +241,7 @@ extension ScriptPropsExt on ScriptProps {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Config with _$Config {
|
||||
abstract class Config with _$Config {
|
||||
const factory Config({
|
||||
@JsonKey(fromJson: AppSettingProps.safeFromJson)
|
||||
@Default(defaultAppSettingProps)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@@ -7,26 +5,9 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'generated/core.freezed.dart';
|
||||
part 'generated/core.g.dart';
|
||||
|
||||
abstract mixin class AppMessageListener {
|
||||
void onLog(Log log) {}
|
||||
|
||||
void onDelay(Delay delay) {}
|
||||
|
||||
void onRequest(TrackerInfo connection) {}
|
||||
|
||||
void onLoaded(String providerName) {}
|
||||
}
|
||||
|
||||
// abstract mixin class ServiceMessageListener {
|
||||
// onProtect(Fd fd) {}
|
||||
//
|
||||
// onProcess(ProcessData process) {}
|
||||
// }
|
||||
|
||||
@freezed
|
||||
class SetupParams with _$SetupParams {
|
||||
abstract class SetupParams with _$SetupParams {
|
||||
const factory SetupParams({
|
||||
@JsonKey(name: 'config') required Map<String, dynamic> config,
|
||||
@JsonKey(name: 'selected-map') required Map<String, String> selectedMap,
|
||||
@JsonKey(name: 'test-url') required String testUrl,
|
||||
}) = _SetupParams;
|
||||
@@ -35,17 +16,8 @@ class SetupParams with _$SetupParams {
|
||||
_$SetupParamsFromJson(json);
|
||||
}
|
||||
|
||||
// extension SetupParamsExt on SetupParams {
|
||||
// Map<String, dynamic> get json {
|
||||
// final json = Map<String, dynamic>.from(config);
|
||||
// json["selected-map"] = selectedMap;
|
||||
// json["test-url"] = testUrl;
|
||||
// return json;
|
||||
// }
|
||||
// }
|
||||
|
||||
@freezed
|
||||
class UpdateParams with _$UpdateParams {
|
||||
abstract class UpdateParams with _$UpdateParams {
|
||||
const factory UpdateParams({
|
||||
required Tun tun,
|
||||
@JsonKey(name: 'mixed-port') required int mixedPort,
|
||||
@@ -66,39 +38,26 @@ class UpdateParams with _$UpdateParams {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CoreState with _$CoreState {
|
||||
const factory CoreState({
|
||||
@JsonKey(name: 'vpn-props') required VpnProps vpnProps,
|
||||
@JsonKey(name: 'only-statistics-proxy') required bool onlyStatisticsProxy,
|
||||
@JsonKey(name: 'current-profile-name') required String currentProfileName,
|
||||
@JsonKey(name: 'bypass-domain') @Default([]) List<String> bypassDomain,
|
||||
}) = _CoreState;
|
||||
|
||||
factory CoreState.fromJson(Map<String, Object?> json) =>
|
||||
_$CoreStateFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AndroidVpnOptions with _$AndroidVpnOptions {
|
||||
const factory AndroidVpnOptions({
|
||||
abstract class VpnOptions with _$VpnOptions {
|
||||
const factory VpnOptions({
|
||||
required bool enable,
|
||||
required int port,
|
||||
required AccessControl? accessControl,
|
||||
required bool ipv6,
|
||||
required bool dnsHijacking,
|
||||
required AccessControl accessControl,
|
||||
required bool allowBypass,
|
||||
required bool systemProxy,
|
||||
required List<String> bypassDomain,
|
||||
required String ipv4Address,
|
||||
required String ipv6Address,
|
||||
required String stack,
|
||||
@Default([]) List<String> routeAddress,
|
||||
required String dnsServerAddress,
|
||||
}) = _AndroidVpnOptions;
|
||||
}) = _VpnOptions;
|
||||
|
||||
factory AndroidVpnOptions.fromJson(Map<String, Object?> json) =>
|
||||
_$AndroidVpnOptionsFromJson(json);
|
||||
factory VpnOptions.fromJson(Map<String, Object?> json) =>
|
||||
_$VpnOptionsFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class InitParams with _$InitParams {
|
||||
abstract class InitParams with _$InitParams {
|
||||
const factory InitParams({
|
||||
@JsonKey(name: 'home-dir') required String homeDir,
|
||||
required int version,
|
||||
@@ -109,7 +68,7 @@ class InitParams with _$InitParams {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ChangeProxyParams with _$ChangeProxyParams {
|
||||
abstract class ChangeProxyParams with _$ChangeProxyParams {
|
||||
const factory ChangeProxyParams({
|
||||
@JsonKey(name: 'group-name') required String groupName,
|
||||
@JsonKey(name: 'proxy-name') required String proxyName,
|
||||
@@ -120,7 +79,7 @@ class ChangeProxyParams with _$ChangeProxyParams {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class UpdateGeoDataParams with _$UpdateGeoDataParams {
|
||||
abstract class UpdateGeoDataParams with _$UpdateGeoDataParams {
|
||||
const factory UpdateGeoDataParams({
|
||||
@JsonKey(name: 'geo-type') required String geoType,
|
||||
@JsonKey(name: 'geo-name') required String geoName,
|
||||
@@ -131,71 +90,40 @@ class UpdateGeoDataParams with _$UpdateGeoDataParams {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AppMessage with _$AppMessage {
|
||||
const factory AppMessage({
|
||||
required AppMessageType type,
|
||||
dynamic data,
|
||||
}) = _AppMessage;
|
||||
abstract class CoreEvent with _$CoreEvent {
|
||||
const factory CoreEvent({required CoreEventType type, dynamic data}) =
|
||||
_CoreEvent;
|
||||
|
||||
factory AppMessage.fromJson(Map<String, Object?> json) =>
|
||||
_$AppMessageFromJson(json);
|
||||
factory CoreEvent.fromJson(Map<String, Object?> json) =>
|
||||
_$CoreEventFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class InvokeMessage with _$InvokeMessage {
|
||||
const factory InvokeMessage({
|
||||
required InvokeMessageType type,
|
||||
dynamic data,
|
||||
}) = _InvokeMessage;
|
||||
abstract class InvokeMessage with _$InvokeMessage {
|
||||
const factory InvokeMessage({required InvokeMessageType type, dynamic data}) =
|
||||
_InvokeMessage;
|
||||
|
||||
factory InvokeMessage.fromJson(Map<String, Object?> json) =>
|
||||
_$InvokeMessageFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Delay with _$Delay {
|
||||
const factory Delay({
|
||||
required String name,
|
||||
required String url,
|
||||
int? value,
|
||||
}) = _Delay;
|
||||
abstract class Delay with _$Delay {
|
||||
const factory Delay({required String name, required String url, int? value}) =
|
||||
_Delay;
|
||||
|
||||
factory Delay.fromJson(Map<String, Object?> json) => _$DelayFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Now with _$Now {
|
||||
const factory Now({
|
||||
required String name,
|
||||
required String value,
|
||||
}) = _Now;
|
||||
abstract class Now with _$Now {
|
||||
const factory Now({required String name, required String value}) = _Now;
|
||||
|
||||
factory Now.fromJson(Map<String, Object?> json) => _$NowFromJson(json);
|
||||
}
|
||||
|
||||
// @freezed
|
||||
// class ProcessData with _$ProcessData {
|
||||
// const factory ProcessData({
|
||||
// required String id,
|
||||
// required Metadata metadata,
|
||||
// }) = _ProcessData;
|
||||
//
|
||||
// factory ProcessData.fromJson(Map<String, Object?> json) =>
|
||||
// _$ProcessDataFromJson(json);
|
||||
// }
|
||||
//
|
||||
// @freezed
|
||||
// class Fd with _$Fd {
|
||||
// const factory Fd({
|
||||
// required String id,
|
||||
// required int value,
|
||||
// }) = _Fd;
|
||||
//
|
||||
// factory Fd.fromJson(Map<String, Object?> json) => _$FdFromJson(json);
|
||||
// }
|
||||
|
||||
@freezed
|
||||
class ProviderSubscriptionInfo with _$ProviderSubscriptionInfo {
|
||||
abstract class ProviderSubscriptionInfo with _$ProviderSubscriptionInfo {
|
||||
const factory ProviderSubscriptionInfo({
|
||||
@JsonKey(name: 'UPLOAD') @Default(0) int upload,
|
||||
@JsonKey(name: 'DOWNLOAD') @Default(0) int download,
|
||||
@@ -218,7 +146,7 @@ SubscriptionInfo? subscriptionInfoFormCore(Map<String, Object?>? json) {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ExternalProvider with _$ExternalProvider {
|
||||
abstract class ExternalProvider with _$ExternalProvider {
|
||||
const factory ExternalProvider({
|
||||
required String name,
|
||||
required String type,
|
||||
@@ -236,7 +164,7 @@ class ExternalProvider with _$ExternalProvider {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Action with _$Action {
|
||||
abstract class Action with _$Action {
|
||||
const factory Action({
|
||||
required ActionMethod method,
|
||||
required dynamic data,
|
||||
@@ -247,7 +175,7 @@ class Action with _$Action {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ActionResult with _$ActionResult {
|
||||
abstract class ActionResult with _$ActionResult {
|
||||
const factory ActionResult({
|
||||
required ActionMethod method,
|
||||
required dynamic data,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
@@ -9,614 +9,407 @@ part of '../app.dart';
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$AppState {
|
||||
bool get isInit => throw _privateConstructorUsedError;
|
||||
bool get backBlock => throw _privateConstructorUsedError;
|
||||
PageLabel get pageLabel => throw _privateConstructorUsedError;
|
||||
List<Package> get packages => throw _privateConstructorUsedError;
|
||||
int get sortNum => throw _privateConstructorUsedError;
|
||||
Size get viewSize => throw _privateConstructorUsedError;
|
||||
Map<String, Map<String, int?>> get delayMap =>
|
||||
throw _privateConstructorUsedError;
|
||||
List<Group> get groups => throw _privateConstructorUsedError;
|
||||
int get checkIpNum => throw _privateConstructorUsedError;
|
||||
Brightness get brightness => throw _privateConstructorUsedError;
|
||||
int? get runTime => throw _privateConstructorUsedError;
|
||||
List<ExternalProvider> get providers => throw _privateConstructorUsedError;
|
||||
String? get localIp => throw _privateConstructorUsedError;
|
||||
FixedList<TrackerInfo> get requests => throw _privateConstructorUsedError;
|
||||
int get version => throw _privateConstructorUsedError;
|
||||
FixedList<Log> get logs => throw _privateConstructorUsedError;
|
||||
FixedList<Traffic> get traffics => throw _privateConstructorUsedError;
|
||||
Traffic get totalTraffic => throw _privateConstructorUsedError;
|
||||
bool get realTunEnable => throw _privateConstructorUsedError;
|
||||
bool get loading => throw _privateConstructorUsedError;
|
||||
SystemUiOverlayStyle get systemUiOverlayStyle =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$AppStateCopyWith<AppState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
bool get isInit; bool get backBlock; PageLabel get pageLabel; List<Package> get packages; int get sortNum; Size get viewSize; double get sideWidth; DelayMap get delayMap; List<Group> get groups; int get checkIpNum; Brightness get brightness; int? get runTime; List<ExternalProvider> get providers; String? get localIp; FixedList<TrackerInfo> get requests; int get version; FixedList<Log> get logs; FixedList<Traffic> get traffics; Traffic get totalTraffic; bool get realTunEnable; bool get loading; SystemUiOverlayStyle get systemUiOverlayStyle; ProfileOverrideModel? get profileOverrideModel; Map<QueryTag, String> get queryMap; CoreStatus get coreStatus;
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$AppStateCopyWith<AppState> get copyWith => _$AppStateCopyWithImpl<AppState>(this as AppState, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppState&&(identical(other.isInit, isInit) || other.isInit == isInit)&&(identical(other.backBlock, backBlock) || other.backBlock == backBlock)&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&const DeepCollectionEquality().equals(other.packages, packages)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&(identical(other.sideWidth, sideWidth) || other.sideWidth == sideWidth)&&const DeepCollectionEquality().equals(other.delayMap, delayMap)&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other.providers, providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle)&&(identical(other.profileOverrideModel, profileOverrideModel) || other.profileOverrideModel == profileOverrideModel)&&const DeepCollectionEquality().equals(other.queryMap, queryMap)&&(identical(other.coreStatus, coreStatus) || other.coreStatus == coreStatus));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([runtimeType,isInit,backBlock,pageLabel,const DeepCollectionEquality().hash(packages),sortNum,viewSize,sideWidth,const DeepCollectionEquality().hash(delayMap),const DeepCollectionEquality().hash(groups),checkIpNum,brightness,runTime,const DeepCollectionEquality().hash(providers),localIp,requests,version,logs,traffics,totalTraffic,realTunEnable,loading,systemUiOverlayStyle,profileOverrideModel,const DeepCollectionEquality().hash(queryMap),coreStatus]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, sideWidth: $sideWidth, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle, profileOverrideModel: $profileOverrideModel, queryMap: $queryMap, coreStatus: $coreStatus)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $AppStateCopyWith<$Res> {
|
||||
factory $AppStateCopyWith(AppState value, $Res Function(AppState) then) =
|
||||
_$AppStateCopyWithImpl<$Res, AppState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool isInit,
|
||||
bool backBlock,
|
||||
PageLabel pageLabel,
|
||||
List<Package> packages,
|
||||
int sortNum,
|
||||
Size viewSize,
|
||||
Map<String, Map<String, int?>> delayMap,
|
||||
List<Group> groups,
|
||||
int checkIpNum,
|
||||
Brightness brightness,
|
||||
int? runTime,
|
||||
List<ExternalProvider> providers,
|
||||
String? localIp,
|
||||
FixedList<TrackerInfo> requests,
|
||||
int version,
|
||||
FixedList<Log> logs,
|
||||
FixedList<Traffic> traffics,
|
||||
Traffic totalTraffic,
|
||||
bool realTunEnable,
|
||||
bool loading,
|
||||
SystemUiOverlayStyle systemUiOverlayStyle});
|
||||
}
|
||||
abstract mixin class $AppStateCopyWith<$Res> {
|
||||
factory $AppStateCopyWith(AppState value, $Res Function(AppState) _then) = _$AppStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus
|
||||
});
|
||||
|
||||
|
||||
$TrafficCopyWith<$Res> get totalTraffic;$ProfileOverrideModelCopyWith<$Res>? get profileOverrideModel;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
|
||||
class _$AppStateCopyWithImpl<$Res>
|
||||
implements $AppStateCopyWith<$Res> {
|
||||
_$AppStateCopyWithImpl(this._value, this._then);
|
||||
_$AppStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
final AppState _self;
|
||||
final $Res Function(AppState) _then;
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? isInit = null,
|
||||
Object? backBlock = null,
|
||||
Object? pageLabel = null,
|
||||
Object? packages = null,
|
||||
Object? sortNum = null,
|
||||
Object? viewSize = null,
|
||||
Object? delayMap = null,
|
||||
Object? groups = null,
|
||||
Object? checkIpNum = null,
|
||||
Object? brightness = null,
|
||||
Object? runTime = freezed,
|
||||
Object? providers = null,
|
||||
Object? localIp = freezed,
|
||||
Object? requests = null,
|
||||
Object? version = null,
|
||||
Object? logs = null,
|
||||
Object? traffics = null,
|
||||
Object? totalTraffic = null,
|
||||
Object? realTunEnable = null,
|
||||
Object? loading = null,
|
||||
Object? systemUiOverlayStyle = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
isInit: null == isInit
|
||||
? _value.isInit
|
||||
: isInit // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
backBlock: null == backBlock
|
||||
? _value.backBlock
|
||||
: backBlock // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
pageLabel: null == pageLabel
|
||||
? _value.pageLabel
|
||||
: pageLabel // ignore: cast_nullable_to_non_nullable
|
||||
as PageLabel,
|
||||
packages: null == packages
|
||||
? _value.packages
|
||||
: packages // ignore: cast_nullable_to_non_nullable
|
||||
as List<Package>,
|
||||
sortNum: null == sortNum
|
||||
? _value.sortNum
|
||||
: sortNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
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
|
||||
as Map<String, Map<String, int?>>,
|
||||
groups: null == groups
|
||||
? _value.groups
|
||||
: groups // ignore: cast_nullable_to_non_nullable
|
||||
as List<Group>,
|
||||
checkIpNum: null == checkIpNum
|
||||
? _value.checkIpNum
|
||||
: checkIpNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
brightness: null == brightness
|
||||
? _value.brightness
|
||||
: brightness // ignore: cast_nullable_to_non_nullable
|
||||
as Brightness,
|
||||
runTime: freezed == runTime
|
||||
? _value.runTime
|
||||
: runTime // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
providers: null == providers
|
||||
? _value.providers
|
||||
: providers // ignore: cast_nullable_to_non_nullable
|
||||
as List<ExternalProvider>,
|
||||
localIp: freezed == localIp
|
||||
? _value.localIp
|
||||
: localIp // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
requests: null == requests
|
||||
? _value.requests
|
||||
: requests // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<TrackerInfo>,
|
||||
version: null == version
|
||||
? _value.version
|
||||
: version // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
logs: null == logs
|
||||
? _value.logs
|
||||
: logs // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<Log>,
|
||||
traffics: null == traffics
|
||||
? _value.traffics
|
||||
: traffics // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<Traffic>,
|
||||
totalTraffic: null == totalTraffic
|
||||
? _value.totalTraffic
|
||||
: totalTraffic // ignore: cast_nullable_to_non_nullable
|
||||
as Traffic,
|
||||
realTunEnable: null == realTunEnable
|
||||
? _value.realTunEnable
|
||||
: realTunEnable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
loading: null == loading
|
||||
? _value.loading
|
||||
: loading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
systemUiOverlayStyle: null == systemUiOverlayStyle
|
||||
? _value.systemUiOverlayStyle
|
||||
: systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable
|
||||
as SystemUiOverlayStyle,
|
||||
) as $Val);
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? sideWidth = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,Object? profileOverrideModel = freezed,Object? queryMap = null,Object? coreStatus = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
isInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable
|
||||
as bool,backBlock: null == backBlock ? _self.backBlock : backBlock // ignore: cast_nullable_to_non_nullable
|
||||
as bool,pageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable
|
||||
as PageLabel,packages: null == packages ? _self.packages : packages // ignore: cast_nullable_to_non_nullable
|
||||
as List<Package>,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,viewSize: null == viewSize ? _self.viewSize : viewSize // ignore: cast_nullable_to_non_nullable
|
||||
as Size,sideWidth: null == sideWidth ? _self.sideWidth : sideWidth // ignore: cast_nullable_to_non_nullable
|
||||
as double,delayMap: null == delayMap ? _self.delayMap : delayMap // ignore: cast_nullable_to_non_nullable
|
||||
as DelayMap,groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable
|
||||
as List<Group>,checkIpNum: null == checkIpNum ? _self.checkIpNum : checkIpNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,brightness: null == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable
|
||||
as Brightness,runTime: freezed == runTime ? _self.runTime : runTime // ignore: cast_nullable_to_non_nullable
|
||||
as int?,providers: null == providers ? _self.providers : providers // ignore: cast_nullable_to_non_nullable
|
||||
as List<ExternalProvider>,localIp: freezed == localIp ? _self.localIp : localIp // ignore: cast_nullable_to_non_nullable
|
||||
as String?,requests: null == requests ? _self.requests : requests // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<TrackerInfo>,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
|
||||
as int,logs: null == logs ? _self.logs : logs // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<Log>,traffics: null == traffics ? _self.traffics : traffics // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<Traffic>,totalTraffic: null == totalTraffic ? _self.totalTraffic : totalTraffic // ignore: cast_nullable_to_non_nullable
|
||||
as Traffic,realTunEnable: null == realTunEnable ? _self.realTunEnable : realTunEnable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,systemUiOverlayStyle: null == systemUiOverlayStyle ? _self.systemUiOverlayStyle : systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable
|
||||
as SystemUiOverlayStyle,profileOverrideModel: freezed == profileOverrideModel ? _self.profileOverrideModel : profileOverrideModel // ignore: cast_nullable_to_non_nullable
|
||||
as ProfileOverrideModel?,queryMap: null == queryMap ? _self.queryMap : queryMap // ignore: cast_nullable_to_non_nullable
|
||||
as Map<QueryTag, String>,coreStatus: null == coreStatus ? _self.coreStatus : coreStatus // ignore: cast_nullable_to_non_nullable
|
||||
as CoreStatus,
|
||||
));
|
||||
}
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$TrafficCopyWith<$Res> get totalTraffic {
|
||||
|
||||
return $TrafficCopyWith<$Res>(_self.totalTraffic, (value) {
|
||||
return _then(_self.copyWith(totalTraffic: value));
|
||||
});
|
||||
}/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ProfileOverrideModelCopyWith<$Res>? get profileOverrideModel {
|
||||
if (_self.profileOverrideModel == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $ProfileOverrideModelCopyWith<$Res>(_self.profileOverrideModel!, (value) {
|
||||
return _then(_self.copyWith(profileOverrideModel: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$AppStateImplCopyWith<$Res>
|
||||
implements $AppStateCopyWith<$Res> {
|
||||
factory _$$AppStateImplCopyWith(
|
||||
_$AppStateImpl value, $Res Function(_$AppStateImpl) then) =
|
||||
__$$AppStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{bool isInit,
|
||||
bool backBlock,
|
||||
PageLabel pageLabel,
|
||||
List<Package> packages,
|
||||
int sortNum,
|
||||
Size viewSize,
|
||||
Map<String, Map<String, int?>> delayMap,
|
||||
List<Group> groups,
|
||||
int checkIpNum,
|
||||
Brightness brightness,
|
||||
int? runTime,
|
||||
List<ExternalProvider> providers,
|
||||
String? localIp,
|
||||
FixedList<TrackerInfo> requests,
|
||||
int version,
|
||||
FixedList<Log> logs,
|
||||
FixedList<Traffic> traffics,
|
||||
Traffic totalTraffic,
|
||||
bool realTunEnable,
|
||||
bool loading,
|
||||
SystemUiOverlayStyle systemUiOverlayStyle});
|
||||
|
||||
/// Adds pattern-matching-related methods to [AppState].
|
||||
extension AppStatePatterns on AppState {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppState value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppState value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppState():
|
||||
return $default(_that);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppState value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _AppState() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppState() when $default != null:
|
||||
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppState():
|
||||
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppState() when $default != null:
|
||||
return $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.sideWidth,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle,_that.profileOverrideModel,_that.queryMap,_that.coreStatus);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$AppStateImplCopyWithImpl<$Res>
|
||||
extends _$AppStateCopyWithImpl<$Res, _$AppStateImpl>
|
||||
implements _$$AppStateImplCopyWith<$Res> {
|
||||
__$$AppStateImplCopyWithImpl(
|
||||
_$AppStateImpl _value, $Res Function(_$AppStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? isInit = null,
|
||||
Object? backBlock = null,
|
||||
Object? pageLabel = null,
|
||||
Object? packages = null,
|
||||
Object? sortNum = null,
|
||||
Object? viewSize = null,
|
||||
Object? delayMap = null,
|
||||
Object? groups = null,
|
||||
Object? checkIpNum = null,
|
||||
Object? brightness = null,
|
||||
Object? runTime = freezed,
|
||||
Object? providers = null,
|
||||
Object? localIp = freezed,
|
||||
Object? requests = null,
|
||||
Object? version = null,
|
||||
Object? logs = null,
|
||||
Object? traffics = null,
|
||||
Object? totalTraffic = null,
|
||||
Object? realTunEnable = null,
|
||||
Object? loading = null,
|
||||
Object? systemUiOverlayStyle = null,
|
||||
}) {
|
||||
return _then(_$AppStateImpl(
|
||||
isInit: null == isInit
|
||||
? _value.isInit
|
||||
: isInit // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
backBlock: null == backBlock
|
||||
? _value.backBlock
|
||||
: backBlock // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
pageLabel: null == pageLabel
|
||||
? _value.pageLabel
|
||||
: pageLabel // ignore: cast_nullable_to_non_nullable
|
||||
as PageLabel,
|
||||
packages: null == packages
|
||||
? _value._packages
|
||||
: packages // ignore: cast_nullable_to_non_nullable
|
||||
as List<Package>,
|
||||
sortNum: null == sortNum
|
||||
? _value.sortNum
|
||||
: sortNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
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
|
||||
as Map<String, Map<String, int?>>,
|
||||
groups: null == groups
|
||||
? _value._groups
|
||||
: groups // ignore: cast_nullable_to_non_nullable
|
||||
as List<Group>,
|
||||
checkIpNum: null == checkIpNum
|
||||
? _value.checkIpNum
|
||||
: checkIpNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
brightness: null == brightness
|
||||
? _value.brightness
|
||||
: brightness // ignore: cast_nullable_to_non_nullable
|
||||
as Brightness,
|
||||
runTime: freezed == runTime
|
||||
? _value.runTime
|
||||
: runTime // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
providers: null == providers
|
||||
? _value._providers
|
||||
: providers // ignore: cast_nullable_to_non_nullable
|
||||
as List<ExternalProvider>,
|
||||
localIp: freezed == localIp
|
||||
? _value.localIp
|
||||
: localIp // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
requests: null == requests
|
||||
? _value.requests
|
||||
: requests // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<TrackerInfo>,
|
||||
version: null == version
|
||||
? _value.version
|
||||
: version // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
logs: null == logs
|
||||
? _value.logs
|
||||
: logs // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<Log>,
|
||||
traffics: null == traffics
|
||||
? _value.traffics
|
||||
: traffics // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<Traffic>,
|
||||
totalTraffic: null == totalTraffic
|
||||
? _value.totalTraffic
|
||||
: totalTraffic // ignore: cast_nullable_to_non_nullable
|
||||
as Traffic,
|
||||
realTunEnable: null == realTunEnable
|
||||
? _value.realTunEnable
|
||||
: realTunEnable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
loading: null == loading
|
||||
? _value.loading
|
||||
: loading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
systemUiOverlayStyle: null == systemUiOverlayStyle
|
||||
? _value.systemUiOverlayStyle
|
||||
: systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable
|
||||
as SystemUiOverlayStyle,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$AppStateImpl implements _AppState {
|
||||
const _$AppStateImpl(
|
||||
{this.isInit = false,
|
||||
this.backBlock = false,
|
||||
this.pageLabel = PageLabel.dashboard,
|
||||
final List<Package> packages = const [],
|
||||
this.sortNum = 0,
|
||||
required this.viewSize,
|
||||
final Map<String, Map<String, int?>> delayMap = const {},
|
||||
final List<Group> groups = const [],
|
||||
this.checkIpNum = 0,
|
||||
required this.brightness,
|
||||
this.runTime,
|
||||
final List<ExternalProvider> providers = const [],
|
||||
this.localIp,
|
||||
required this.requests,
|
||||
required this.version,
|
||||
required this.logs,
|
||||
required this.traffics,
|
||||
required this.totalTraffic,
|
||||
this.realTunEnable = false,
|
||||
this.loading = false,
|
||||
required this.systemUiOverlayStyle})
|
||||
: _packages = packages,
|
||||
_delayMap = delayMap,
|
||||
_groups = groups,
|
||||
_providers = providers;
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool isInit;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool backBlock;
|
||||
@override
|
||||
@JsonKey()
|
||||
final PageLabel pageLabel;
|
||||
final List<Package> _packages;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Package> get packages {
|
||||
if (_packages is EqualUnmodifiableListView) return _packages;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_packages);
|
||||
}
|
||||
class _AppState implements AppState {
|
||||
const _AppState({this.isInit = false, this.backBlock = false, this.pageLabel = PageLabel.dashboard, final List<Package> packages = const [], this.sortNum = 0, required this.viewSize, this.sideWidth = 0, final DelayMap delayMap = const {}, final List<Group> groups = const [], this.checkIpNum = 0, required this.brightness, this.runTime, final List<ExternalProvider> providers = const [], this.localIp, required this.requests, required this.version, required this.logs, required this.traffics, required this.totalTraffic, this.realTunEnable = false, this.loading = false, required this.systemUiOverlayStyle, this.profileOverrideModel, final Map<QueryTag, String> queryMap = const {}, this.coreStatus = CoreStatus.connecting}): _packages = packages,_delayMap = delayMap,_groups = groups,_providers = providers,_queryMap = queryMap;
|
||||
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final int sortNum;
|
||||
@override
|
||||
final Size viewSize;
|
||||
final Map<String, Map<String, int?>> _delayMap;
|
||||
@override
|
||||
@JsonKey()
|
||||
Map<String, Map<String, int?>> get delayMap {
|
||||
if (_delayMap is EqualUnmodifiableMapView) return _delayMap;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_delayMap);
|
||||
}
|
||||
|
||||
final List<Group> _groups;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<Group> get groups {
|
||||
if (_groups is EqualUnmodifiableListView) return _groups;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_groups);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final int checkIpNum;
|
||||
@override
|
||||
final Brightness brightness;
|
||||
@override
|
||||
final int? runTime;
|
||||
final List<ExternalProvider> _providers;
|
||||
@override
|
||||
@JsonKey()
|
||||
List<ExternalProvider> get providers {
|
||||
if (_providers is EqualUnmodifiableListView) return _providers;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_providers);
|
||||
}
|
||||
|
||||
@override
|
||||
final String? localIp;
|
||||
@override
|
||||
final FixedList<TrackerInfo> requests;
|
||||
@override
|
||||
final int version;
|
||||
@override
|
||||
final FixedList<Log> logs;
|
||||
@override
|
||||
final FixedList<Traffic> traffics;
|
||||
@override
|
||||
final Traffic totalTraffic;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool realTunEnable;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool loading;
|
||||
@override
|
||||
final SystemUiOverlayStyle systemUiOverlayStyle;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, 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, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$AppStateImpl &&
|
||||
(identical(other.isInit, isInit) || other.isInit == isInit) &&
|
||||
(identical(other.backBlock, backBlock) ||
|
||||
other.backBlock == backBlock) &&
|
||||
(identical(other.pageLabel, pageLabel) ||
|
||||
other.pageLabel == pageLabel) &&
|
||||
const DeepCollectionEquality().equals(other._packages, _packages) &&
|
||||
(identical(other.sortNum, sortNum) || other.sortNum == sortNum) &&
|
||||
(identical(other.viewSize, viewSize) ||
|
||||
other.viewSize == viewSize) &&
|
||||
const DeepCollectionEquality().equals(other._delayMap, _delayMap) &&
|
||||
const DeepCollectionEquality().equals(other._groups, _groups) &&
|
||||
(identical(other.checkIpNum, checkIpNum) ||
|
||||
other.checkIpNum == checkIpNum) &&
|
||||
(identical(other.brightness, brightness) ||
|
||||
other.brightness == brightness) &&
|
||||
(identical(other.runTime, runTime) || other.runTime == runTime) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._providers, _providers) &&
|
||||
(identical(other.localIp, localIp) || other.localIp == localIp) &&
|
||||
(identical(other.requests, requests) ||
|
||||
other.requests == requests) &&
|
||||
(identical(other.version, version) || other.version == version) &&
|
||||
(identical(other.logs, logs) || other.logs == logs) &&
|
||||
(identical(other.traffics, traffics) ||
|
||||
other.traffics == traffics) &&
|
||||
(identical(other.totalTraffic, totalTraffic) ||
|
||||
other.totalTraffic == totalTraffic) &&
|
||||
(identical(other.realTunEnable, realTunEnable) ||
|
||||
other.realTunEnable == realTunEnable) &&
|
||||
(identical(other.loading, loading) || other.loading == loading) &&
|
||||
(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) ||
|
||||
other.systemUiOverlayStyle == systemUiOverlayStyle));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([
|
||||
runtimeType,
|
||||
isInit,
|
||||
backBlock,
|
||||
pageLabel,
|
||||
const DeepCollectionEquality().hash(_packages),
|
||||
sortNum,
|
||||
viewSize,
|
||||
const DeepCollectionEquality().hash(_delayMap),
|
||||
const DeepCollectionEquality().hash(_groups),
|
||||
checkIpNum,
|
||||
brightness,
|
||||
runTime,
|
||||
const DeepCollectionEquality().hash(_providers),
|
||||
localIp,
|
||||
requests,
|
||||
version,
|
||||
logs,
|
||||
traffics,
|
||||
totalTraffic,
|
||||
realTunEnable,
|
||||
loading,
|
||||
systemUiOverlayStyle
|
||||
]);
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$AppStateImplCopyWith<_$AppStateImpl> get copyWith =>
|
||||
__$$AppStateImplCopyWithImpl<_$AppStateImpl>(this, _$identity);
|
||||
@override@JsonKey() final bool isInit;
|
||||
@override@JsonKey() final bool backBlock;
|
||||
@override@JsonKey() final PageLabel pageLabel;
|
||||
final List<Package> _packages;
|
||||
@override@JsonKey() List<Package> get packages {
|
||||
if (_packages is EqualUnmodifiableListView) return _packages;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_packages);
|
||||
}
|
||||
|
||||
abstract class _AppState implements AppState {
|
||||
const factory _AppState(
|
||||
{final bool isInit,
|
||||
final bool backBlock,
|
||||
final PageLabel pageLabel,
|
||||
final List<Package> packages,
|
||||
final int sortNum,
|
||||
required final Size viewSize,
|
||||
final Map<String, Map<String, int?>> delayMap,
|
||||
final List<Group> groups,
|
||||
final int checkIpNum,
|
||||
required final Brightness brightness,
|
||||
final int? runTime,
|
||||
final List<ExternalProvider> providers,
|
||||
final String? localIp,
|
||||
required final FixedList<TrackerInfo> requests,
|
||||
required final int version,
|
||||
required final FixedList<Log> logs,
|
||||
required final FixedList<Traffic> traffics,
|
||||
required final Traffic totalTraffic,
|
||||
final bool realTunEnable,
|
||||
final bool loading,
|
||||
required final SystemUiOverlayStyle systemUiOverlayStyle}) =
|
||||
_$AppStateImpl;
|
||||
|
||||
@override
|
||||
bool get isInit;
|
||||
@override
|
||||
bool get backBlock;
|
||||
@override
|
||||
PageLabel get pageLabel;
|
||||
@override
|
||||
List<Package> get packages;
|
||||
@override
|
||||
int get sortNum;
|
||||
@override
|
||||
Size get viewSize;
|
||||
@override
|
||||
Map<String, Map<String, int?>> get delayMap;
|
||||
@override
|
||||
List<Group> get groups;
|
||||
@override
|
||||
int get checkIpNum;
|
||||
@override
|
||||
Brightness get brightness;
|
||||
@override
|
||||
int? get runTime;
|
||||
@override
|
||||
List<ExternalProvider> get providers;
|
||||
@override
|
||||
String? get localIp;
|
||||
@override
|
||||
FixedList<TrackerInfo> get requests;
|
||||
@override
|
||||
int get version;
|
||||
@override
|
||||
FixedList<Log> get logs;
|
||||
@override
|
||||
FixedList<Traffic> get traffics;
|
||||
@override
|
||||
Traffic get totalTraffic;
|
||||
@override
|
||||
bool get realTunEnable;
|
||||
@override
|
||||
bool get loading;
|
||||
@override
|
||||
SystemUiOverlayStyle get systemUiOverlayStyle;
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$AppStateImplCopyWith<_$AppStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
@override@JsonKey() final int sortNum;
|
||||
@override final Size viewSize;
|
||||
@override@JsonKey() final double sideWidth;
|
||||
final DelayMap _delayMap;
|
||||
@override@JsonKey() DelayMap get delayMap {
|
||||
if (_delayMap is EqualUnmodifiableMapView) return _delayMap;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_delayMap);
|
||||
}
|
||||
|
||||
final List<Group> _groups;
|
||||
@override@JsonKey() List<Group> get groups {
|
||||
if (_groups is EqualUnmodifiableListView) return _groups;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_groups);
|
||||
}
|
||||
|
||||
@override@JsonKey() final int checkIpNum;
|
||||
@override final Brightness brightness;
|
||||
@override final int? runTime;
|
||||
final List<ExternalProvider> _providers;
|
||||
@override@JsonKey() List<ExternalProvider> get providers {
|
||||
if (_providers is EqualUnmodifiableListView) return _providers;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_providers);
|
||||
}
|
||||
|
||||
@override final String? localIp;
|
||||
@override final FixedList<TrackerInfo> requests;
|
||||
@override final int version;
|
||||
@override final FixedList<Log> logs;
|
||||
@override final FixedList<Traffic> traffics;
|
||||
@override final Traffic totalTraffic;
|
||||
@override@JsonKey() final bool realTunEnable;
|
||||
@override@JsonKey() final bool loading;
|
||||
@override final SystemUiOverlayStyle systemUiOverlayStyle;
|
||||
@override final ProfileOverrideModel? profileOverrideModel;
|
||||
final Map<QueryTag, String> _queryMap;
|
||||
@override@JsonKey() Map<QueryTag, String> get queryMap {
|
||||
if (_queryMap is EqualUnmodifiableMapView) return _queryMap;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_queryMap);
|
||||
}
|
||||
|
||||
@override@JsonKey() final CoreStatus coreStatus;
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$AppStateCopyWith<_AppState> get copyWith => __$AppStateCopyWithImpl<_AppState>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppState&&(identical(other.isInit, isInit) || other.isInit == isInit)&&(identical(other.backBlock, backBlock) || other.backBlock == backBlock)&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&const DeepCollectionEquality().equals(other._packages, _packages)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&(identical(other.sideWidth, sideWidth) || other.sideWidth == sideWidth)&&const DeepCollectionEquality().equals(other._delayMap, _delayMap)&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other._providers, _providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle)&&(identical(other.profileOverrideModel, profileOverrideModel) || other.profileOverrideModel == profileOverrideModel)&&const DeepCollectionEquality().equals(other._queryMap, _queryMap)&&(identical(other.coreStatus, coreStatus) || other.coreStatus == coreStatus));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hashAll([runtimeType,isInit,backBlock,pageLabel,const DeepCollectionEquality().hash(_packages),sortNum,viewSize,sideWidth,const DeepCollectionEquality().hash(_delayMap),const DeepCollectionEquality().hash(_groups),checkIpNum,brightness,runTime,const DeepCollectionEquality().hash(_providers),localIp,requests,version,logs,traffics,totalTraffic,realTunEnable,loading,systemUiOverlayStyle,profileOverrideModel,const DeepCollectionEquality().hash(_queryMap),coreStatus]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, sideWidth: $sideWidth, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle, profileOverrideModel: $profileOverrideModel, queryMap: $queryMap, coreStatus: $coreStatus)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res> {
|
||||
factory _$AppStateCopyWith(_AppState value, $Res Function(_AppState) _then) = __$AppStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, double sideWidth, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle, ProfileOverrideModel? profileOverrideModel, Map<QueryTag, String> queryMap, CoreStatus coreStatus
|
||||
});
|
||||
|
||||
|
||||
@override $TrafficCopyWith<$Res> get totalTraffic;@override $ProfileOverrideModelCopyWith<$Res>? get profileOverrideModel;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$AppStateCopyWithImpl<$Res>
|
||||
implements _$AppStateCopyWith<$Res> {
|
||||
__$AppStateCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _AppState _self;
|
||||
final $Res Function(_AppState) _then;
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? sideWidth = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,Object? profileOverrideModel = freezed,Object? queryMap = null,Object? coreStatus = null,}) {
|
||||
return _then(_AppState(
|
||||
isInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable
|
||||
as bool,backBlock: null == backBlock ? _self.backBlock : backBlock // ignore: cast_nullable_to_non_nullable
|
||||
as bool,pageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable
|
||||
as PageLabel,packages: null == packages ? _self._packages : packages // ignore: cast_nullable_to_non_nullable
|
||||
as List<Package>,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,viewSize: null == viewSize ? _self.viewSize : viewSize // ignore: cast_nullable_to_non_nullable
|
||||
as Size,sideWidth: null == sideWidth ? _self.sideWidth : sideWidth // ignore: cast_nullable_to_non_nullable
|
||||
as double,delayMap: null == delayMap ? _self._delayMap : delayMap // ignore: cast_nullable_to_non_nullable
|
||||
as DelayMap,groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable
|
||||
as List<Group>,checkIpNum: null == checkIpNum ? _self.checkIpNum : checkIpNum // ignore: cast_nullable_to_non_nullable
|
||||
as int,brightness: null == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable
|
||||
as Brightness,runTime: freezed == runTime ? _self.runTime : runTime // ignore: cast_nullable_to_non_nullable
|
||||
as int?,providers: null == providers ? _self._providers : providers // ignore: cast_nullable_to_non_nullable
|
||||
as List<ExternalProvider>,localIp: freezed == localIp ? _self.localIp : localIp // ignore: cast_nullable_to_non_nullable
|
||||
as String?,requests: null == requests ? _self.requests : requests // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<TrackerInfo>,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable
|
||||
as int,logs: null == logs ? _self.logs : logs // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<Log>,traffics: null == traffics ? _self.traffics : traffics // ignore: cast_nullable_to_non_nullable
|
||||
as FixedList<Traffic>,totalTraffic: null == totalTraffic ? _self.totalTraffic : totalTraffic // ignore: cast_nullable_to_non_nullable
|
||||
as Traffic,realTunEnable: null == realTunEnable ? _self.realTunEnable : realTunEnable // ignore: cast_nullable_to_non_nullable
|
||||
as bool,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable
|
||||
as bool,systemUiOverlayStyle: null == systemUiOverlayStyle ? _self.systemUiOverlayStyle : systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable
|
||||
as SystemUiOverlayStyle,profileOverrideModel: freezed == profileOverrideModel ? _self.profileOverrideModel : profileOverrideModel // ignore: cast_nullable_to_non_nullable
|
||||
as ProfileOverrideModel?,queryMap: null == queryMap ? _self._queryMap : queryMap // ignore: cast_nullable_to_non_nullable
|
||||
as Map<QueryTag, String>,coreStatus: null == coreStatus ? _self.coreStatus : coreStatus // ignore: cast_nullable_to_non_nullable
|
||||
as CoreStatus,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$TrafficCopyWith<$Res> get totalTraffic {
|
||||
|
||||
return $TrafficCopyWith<$Res>(_self.totalTraffic, (value) {
|
||||
return _then(_self.copyWith(totalTraffic: value));
|
||||
});
|
||||
}/// Create a copy of AppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$ProfileOverrideModelCopyWith<$Res>? get profileOverrideModel {
|
||||
if (_self.profileOverrideModel == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $ProfileOverrideModelCopyWith<$Res>(_self.profileOverrideModel!, (value) {
|
||||
return _then(_self.copyWith(profileOverrideModel: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,27 +6,27 @@ part of '../clash_config.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$ProxyGroupImpl _$$ProxyGroupImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ProxyGroupImpl(
|
||||
name: json['name'] as String,
|
||||
type: GroupType.parseProfileType(json['type'] as String),
|
||||
proxies:
|
||||
(json['proxies'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||
use: (json['use'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||
interval: (json['interval'] as num?)?.toInt(),
|
||||
lazy: json['lazy'] as bool?,
|
||||
url: json['url'] as String?,
|
||||
timeout: (json['timeout'] as num?)?.toInt(),
|
||||
maxFailedTimes: (json['max-failed-times'] as num?)?.toInt(),
|
||||
filter: json['filter'] as String?,
|
||||
excludeFilter: json['expected-filter'] as String?,
|
||||
excludeType: json['exclude-type'] as String?,
|
||||
expectedStatus: json['expected-status'],
|
||||
hidden: json['hidden'] as bool?,
|
||||
icon: json['icon'] as String?,
|
||||
);
|
||||
_ProxyGroup _$ProxyGroupFromJson(Map<String, dynamic> json) => _ProxyGroup(
|
||||
name: json['name'] as String,
|
||||
type: GroupType.parseProfileType(json['type'] as String),
|
||||
proxies: (json['proxies'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList(),
|
||||
use: (json['use'] as List<dynamic>?)?.map((e) => e as String).toList(),
|
||||
interval: (json['interval'] as num?)?.toInt(),
|
||||
lazy: json['lazy'] as bool?,
|
||||
url: json['url'] as String?,
|
||||
timeout: (json['timeout'] as num?)?.toInt(),
|
||||
maxFailedTimes: (json['max-failed-times'] as num?)?.toInt(),
|
||||
filter: json['filter'] as String?,
|
||||
excludeFilter: json['expected-filter'] as String?,
|
||||
excludeType: json['exclude-type'] as String?,
|
||||
expectedStatus: json['expected-status'],
|
||||
hidden: json['hidden'] as bool?,
|
||||
icon: json['icon'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProxyGroupImplToJson(_$ProxyGroupImpl instance) =>
|
||||
Map<String, dynamic> _$ProxyGroupToJson(_ProxyGroup instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'type': _$GroupTypeEnumMap[instance.type]!,
|
||||
@@ -53,106 +53,107 @@ const _$GroupTypeEnumMap = {
|
||||
GroupType.Relay: 'Relay',
|
||||
};
|
||||
|
||||
_$RuleProviderImpl _$$RuleProviderImplFromJson(Map<String, dynamic> json) =>
|
||||
_$RuleProviderImpl(
|
||||
name: json['name'] as String,
|
||||
);
|
||||
_RuleProvider _$RuleProviderFromJson(Map<String, dynamic> json) =>
|
||||
_RuleProvider(name: json['name'] as String);
|
||||
|
||||
Map<String, dynamic> _$$RuleProviderImplToJson(_$RuleProviderImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
};
|
||||
Map<String, dynamic> _$RuleProviderToJson(_RuleProvider instance) =>
|
||||
<String, dynamic>{'name': instance.name};
|
||||
|
||||
_$SnifferImpl _$$SnifferImplFromJson(Map<String, dynamic> json) =>
|
||||
_$SnifferImpl(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
overrideDest: json['override-destination'] as bool? ?? true,
|
||||
sniffing: (json['sniffing'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
forceDomain: (json['force-domain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
skipSrcAddress: (json['skip-src-address'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
skipDstAddress: (json['skip-dst-address'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
skipDomain: (json['skip-domain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
port: (json['port-whitelist'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
forceDnsMapping: json['force-dns-mapping'] as bool? ?? true,
|
||||
parsePureIp: json['parse-pure-ip'] as bool? ?? true,
|
||||
sniff: (json['sniff'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) =>
|
||||
MapEntry(k, SnifferConfig.fromJson(e as Map<String, dynamic>)),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
_Sniffer _$SnifferFromJson(Map<String, dynamic> json) => _Sniffer(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
overrideDest: json['override-destination'] as bool? ?? true,
|
||||
sniffing:
|
||||
(json['sniffing'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const [],
|
||||
forceDomain:
|
||||
(json['force-domain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
skipSrcAddress:
|
||||
(json['skip-src-address'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
skipDstAddress:
|
||||
(json['skip-dst-address'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
skipDomain:
|
||||
(json['skip-domain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
port:
|
||||
(json['port-whitelist'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
forceDnsMapping: json['force-dns-mapping'] as bool? ?? true,
|
||||
parsePureIp: json['parse-pure-ip'] as bool? ?? true,
|
||||
sniff:
|
||||
(json['sniff'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) =>
|
||||
MapEntry(k, SnifferConfig.fromJson(e as Map<String, dynamic>)),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SnifferImplToJson(_$SnifferImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'override-destination': instance.overrideDest,
|
||||
'sniffing': instance.sniffing,
|
||||
'force-domain': instance.forceDomain,
|
||||
'skip-src-address': instance.skipSrcAddress,
|
||||
'skip-dst-address': instance.skipDstAddress,
|
||||
'skip-domain': instance.skipDomain,
|
||||
'port-whitelist': instance.port,
|
||||
'force-dns-mapping': instance.forceDnsMapping,
|
||||
'parse-pure-ip': instance.parsePureIp,
|
||||
'sniff': instance.sniff,
|
||||
};
|
||||
Map<String, dynamic> _$SnifferToJson(_Sniffer instance) => <String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'override-destination': instance.overrideDest,
|
||||
'sniffing': instance.sniffing,
|
||||
'force-domain': instance.forceDomain,
|
||||
'skip-src-address': instance.skipSrcAddress,
|
||||
'skip-dst-address': instance.skipDstAddress,
|
||||
'skip-domain': instance.skipDomain,
|
||||
'port-whitelist': instance.port,
|
||||
'force-dns-mapping': instance.forceDnsMapping,
|
||||
'parse-pure-ip': instance.parsePureIp,
|
||||
'sniff': instance.sniff,
|
||||
};
|
||||
|
||||
_$SnifferConfigImpl _$$SnifferConfigImplFromJson(Map<String, dynamic> json) =>
|
||||
_$SnifferConfigImpl(
|
||||
_SnifferConfig _$SnifferConfigFromJson(Map<String, dynamic> json) =>
|
||||
_SnifferConfig(
|
||||
ports: json['ports'] == null
|
||||
? const []
|
||||
: _formJsonPorts(json['ports'] as List?),
|
||||
overrideDest: json['override-destination'] as bool?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SnifferConfigImplToJson(_$SnifferConfigImpl instance) =>
|
||||
Map<String, dynamic> _$SnifferConfigToJson(_SnifferConfig instance) =>
|
||||
<String, dynamic>{
|
||||
'ports': instance.ports,
|
||||
'override-destination': instance.overrideDest,
|
||||
};
|
||||
|
||||
_$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
device: json['device'] as String? ?? appName,
|
||||
autoRoute: json['auto-route'] as bool? ?? false,
|
||||
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
|
||||
TunStack.mixed,
|
||||
dnsHijack: (json['dns-hijack'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['any:53'],
|
||||
routeAddress: (json['route-address'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
_Tun _$TunFromJson(Map<String, dynamic> json) => _Tun(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
device: json['device'] as String? ?? appName,
|
||||
autoRoute: json['auto-route'] as bool? ?? false,
|
||||
stack:
|
||||
$enumDecodeNullable(_$TunStackEnumMap, json['stack']) ?? TunStack.mixed,
|
||||
dnsHijack:
|
||||
(json['dns-hijack'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['any:53'],
|
||||
routeAddress:
|
||||
(json['route-address'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TunImplToJson(_$TunImpl instance) => <String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'device': instance.device,
|
||||
'auto-route': instance.autoRoute,
|
||||
'stack': _$TunStackEnumMap[instance.stack]!,
|
||||
'dns-hijack': instance.dnsHijack,
|
||||
'route-address': instance.routeAddress,
|
||||
};
|
||||
Map<String, dynamic> _$TunToJson(_Tun instance) => <String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'device': instance.device,
|
||||
'auto-route': instance.autoRoute,
|
||||
'stack': _$TunStackEnumMap[instance.stack]!,
|
||||
'dns-hijack': instance.dnsHijack,
|
||||
'route-address': instance.routeAddress,
|
||||
};
|
||||
|
||||
const _$TunStackEnumMap = {
|
||||
TunStack.gvisor: 'gvisor',
|
||||
@@ -160,26 +161,23 @@ const _$TunStackEnumMap = {
|
||||
TunStack.mixed: 'mixed',
|
||||
};
|
||||
|
||||
_$FallbackFilterImpl _$$FallbackFilterImplFromJson(Map<String, dynamic> json) =>
|
||||
_$FallbackFilterImpl(
|
||||
geoip: json['geoip'] as bool? ?? true,
|
||||
geoipCode: json['geoip-code'] as String? ?? 'CN',
|
||||
geosite: (json['geosite'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['gfw'],
|
||||
ipcidr: (json['ipcidr'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['240.0.0.0/4'],
|
||||
domain: (json['domain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['+.google.com', '+.facebook.com', '+.youtube.com'],
|
||||
);
|
||||
_FallbackFilter _$FallbackFilterFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _FallbackFilter(
|
||||
geoip: json['geoip'] as bool? ?? true,
|
||||
geoipCode: json['geoip-code'] as String? ?? 'CN',
|
||||
geosite:
|
||||
(json['geosite'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const ['gfw'],
|
||||
ipcidr:
|
||||
(json['ipcidr'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const ['240.0.0.0/4'],
|
||||
domain:
|
||||
(json['domain'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const ['+.google.com', '+.facebook.com', '+.youtube.com'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$FallbackFilterImplToJson(
|
||||
_$FallbackFilterImpl instance) =>
|
||||
Map<String, dynamic> _$FallbackFilterToJson(_FallbackFilter instance) =>
|
||||
<String, dynamic>{
|
||||
'geoip': instance.geoip,
|
||||
'geoip-code': instance.geoipCode,
|
||||
@@ -188,74 +186,75 @@ Map<String, dynamic> _$$FallbackFilterImplToJson(
|
||||
'domain': instance.domain,
|
||||
};
|
||||
|
||||
_$DnsImpl _$$DnsImplFromJson(Map<String, dynamic> json) => _$DnsImpl(
|
||||
enable: json['enable'] as bool? ?? true,
|
||||
listen: json['listen'] as String? ?? '0.0.0.0:1053',
|
||||
preferH3: json['prefer-h3'] as bool? ?? false,
|
||||
useHosts: json['use-hosts'] as bool? ?? true,
|
||||
useSystemHosts: json['use-system-hosts'] as bool? ?? true,
|
||||
respectRules: json['respect-rules'] as bool? ?? false,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
defaultNameserver: (json['default-nameserver'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['223.5.5.5'],
|
||||
enhancedMode:
|
||||
$enumDecodeNullable(_$DnsModeEnumMap, json['enhanced-mode']) ??
|
||||
DnsMode.fakeIp,
|
||||
fakeIpRange: json['fake-ip-range'] as String? ?? '198.18.0.1/16',
|
||||
fakeIpFilter: (json['fake-ip-filter'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['*.lan', 'localhost.ptlogin2.qq.com'],
|
||||
nameserverPolicy:
|
||||
(json['nameserver-policy'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {
|
||||
'www.baidu.com': '114.114.114.114',
|
||||
'+.internal.crop.com': '10.0.0.1',
|
||||
'geosite:cn': 'https://doh.pub/dns-query'
|
||||
},
|
||||
nameserver: (json['nameserver'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [
|
||||
'https://doh.pub/dns-query',
|
||||
'https://dns.alidns.com/dns-query'
|
||||
],
|
||||
fallback: (json['fallback'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['tls://8.8.4.4', 'tls://1.1.1.1'],
|
||||
proxyServerNameserver: (json['proxy-server-nameserver'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['https://doh.pub/dns-query'],
|
||||
fallbackFilter: json['fallback-filter'] == null
|
||||
? const FallbackFilter()
|
||||
: FallbackFilter.fromJson(
|
||||
json['fallback-filter'] as Map<String, dynamic>),
|
||||
);
|
||||
_Dns _$DnsFromJson(Map<String, dynamic> json) => _Dns(
|
||||
enable: json['enable'] as bool? ?? true,
|
||||
listen: json['listen'] as String? ?? '0.0.0.0:1053',
|
||||
preferH3: json['prefer-h3'] as bool? ?? false,
|
||||
useHosts: json['use-hosts'] as bool? ?? true,
|
||||
useSystemHosts: json['use-system-hosts'] as bool? ?? true,
|
||||
respectRules: json['respect-rules'] as bool? ?? false,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
defaultNameserver:
|
||||
(json['default-nameserver'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['223.5.5.5'],
|
||||
enhancedMode:
|
||||
$enumDecodeNullable(_$DnsModeEnumMap, json['enhanced-mode']) ??
|
||||
DnsMode.fakeIp,
|
||||
fakeIpRange: json['fake-ip-range'] as String? ?? '198.18.0.1/16',
|
||||
fakeIpFilter:
|
||||
(json['fake-ip-filter'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['*.lan', 'localhost.ptlogin2.qq.com'],
|
||||
nameserverPolicy:
|
||||
(json['nameserver-policy'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {
|
||||
'www.baidu.com': '114.114.114.114',
|
||||
'+.internal.crop.com': '10.0.0.1',
|
||||
'geosite:cn': 'https://doh.pub/dns-query',
|
||||
},
|
||||
nameserver:
|
||||
(json['nameserver'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'],
|
||||
fallback:
|
||||
(json['fallback'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const ['tls://8.8.4.4', 'tls://1.1.1.1'],
|
||||
proxyServerNameserver:
|
||||
(json['proxy-server-nameserver'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const ['https://doh.pub/dns-query'],
|
||||
fallbackFilter: json['fallback-filter'] == null
|
||||
? const FallbackFilter()
|
||||
: FallbackFilter.fromJson(
|
||||
json['fallback-filter'] as Map<String, dynamic>,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DnsImplToJson(_$DnsImpl instance) => <String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'listen': instance.listen,
|
||||
'prefer-h3': instance.preferH3,
|
||||
'use-hosts': instance.useHosts,
|
||||
'use-system-hosts': instance.useSystemHosts,
|
||||
'respect-rules': instance.respectRules,
|
||||
'ipv6': instance.ipv6,
|
||||
'default-nameserver': instance.defaultNameserver,
|
||||
'enhanced-mode': _$DnsModeEnumMap[instance.enhancedMode]!,
|
||||
'fake-ip-range': instance.fakeIpRange,
|
||||
'fake-ip-filter': instance.fakeIpFilter,
|
||||
'nameserver-policy': instance.nameserverPolicy,
|
||||
'nameserver': instance.nameserver,
|
||||
'fallback': instance.fallback,
|
||||
'proxy-server-nameserver': instance.proxyServerNameserver,
|
||||
'fallback-filter': instance.fallbackFilter,
|
||||
};
|
||||
Map<String, dynamic> _$DnsToJson(_Dns instance) => <String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'listen': instance.listen,
|
||||
'prefer-h3': instance.preferH3,
|
||||
'use-hosts': instance.useHosts,
|
||||
'use-system-hosts': instance.useSystemHosts,
|
||||
'respect-rules': instance.respectRules,
|
||||
'ipv6': instance.ipv6,
|
||||
'default-nameserver': instance.defaultNameserver,
|
||||
'enhanced-mode': _$DnsModeEnumMap[instance.enhancedMode]!,
|
||||
'fake-ip-range': instance.fakeIpRange,
|
||||
'fake-ip-filter': instance.fakeIpFilter,
|
||||
'nameserver-policy': instance.nameserverPolicy,
|
||||
'nameserver': instance.nameserver,
|
||||
'fallback': instance.fallback,
|
||||
'proxy-server-nameserver': instance.proxyServerNameserver,
|
||||
'fallback-filter': instance.fallbackFilter,
|
||||
};
|
||||
|
||||
const _$DnsModeEnumMap = {
|
||||
DnsMode.normal: 'normal',
|
||||
@@ -264,51 +263,47 @@ const _$DnsModeEnumMap = {
|
||||
DnsMode.hosts: 'hosts',
|
||||
};
|
||||
|
||||
_$GeoXUrlImpl _$$GeoXUrlImplFromJson(Map<String, dynamic> json) =>
|
||||
_$GeoXUrlImpl(
|
||||
mmdb: json['mmdb'] as String? ??
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
|
||||
asn: json['asn'] as String? ??
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb',
|
||||
geoip: json['geoip'] as String? ??
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat',
|
||||
geosite: json['geosite'] as String? ??
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat',
|
||||
);
|
||||
_GeoXUrl _$GeoXUrlFromJson(Map<String, dynamic> json) => _GeoXUrl(
|
||||
mmdb:
|
||||
json['mmdb'] as String? ??
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb',
|
||||
asn:
|
||||
json['asn'] as String? ??
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb',
|
||||
geoip:
|
||||
json['geoip'] as String? ??
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat',
|
||||
geosite:
|
||||
json['geosite'] as String? ??
|
||||
'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$GeoXUrlImplToJson(_$GeoXUrlImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'mmdb': instance.mmdb,
|
||||
'asn': instance.asn,
|
||||
'geoip': instance.geoip,
|
||||
'geosite': instance.geosite,
|
||||
};
|
||||
Map<String, dynamic> _$GeoXUrlToJson(_GeoXUrl instance) => <String, dynamic>{
|
||||
'mmdb': instance.mmdb,
|
||||
'asn': instance.asn,
|
||||
'geoip': instance.geoip,
|
||||
'geosite': instance.geosite,
|
||||
};
|
||||
|
||||
_$RuleImpl _$$RuleImplFromJson(Map<String, dynamic> json) => _$RuleImpl(
|
||||
id: json['id'] as String,
|
||||
value: json['value'] as String,
|
||||
);
|
||||
_Rule _$RuleFromJson(Map<String, dynamic> json) =>
|
||||
_Rule(id: json['id'] as String, value: json['value'] as String);
|
||||
|
||||
Map<String, dynamic> _$$RuleImplToJson(_$RuleImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'value': instance.value,
|
||||
};
|
||||
Map<String, dynamic> _$RuleToJson(_Rule instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'value': instance.value,
|
||||
};
|
||||
|
||||
_$SubRuleImpl _$$SubRuleImplFromJson(Map<String, dynamic> json) =>
|
||||
_$SubRuleImpl(
|
||||
name: json['name'] as String,
|
||||
);
|
||||
_SubRule _$SubRuleFromJson(Map<String, dynamic> json) =>
|
||||
_SubRule(name: json['name'] as String);
|
||||
|
||||
Map<String, dynamic> _$$SubRuleImplToJson(_$SubRuleImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
};
|
||||
Map<String, dynamic> _$SubRuleToJson(_SubRule instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
};
|
||||
|
||||
_$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ClashConfigSnippetImpl(
|
||||
proxyGroups: (json['proxy-groups'] as List<dynamic>?)
|
||||
_ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) =>
|
||||
_ClashConfigSnippet(
|
||||
proxyGroups:
|
||||
(json['proxy-groups'] as List<dynamic>?)
|
||||
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
@@ -321,8 +316,7 @@ _$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
|
||||
: _genSubRules(json['sub-rules'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
|
||||
_$ClashConfigSnippetImpl instance) =>
|
||||
Map<String, dynamic> _$ClashConfigSnippetToJson(_ClashConfigSnippet instance) =>
|
||||
<String, dynamic>{
|
||||
'proxy-groups': instance.proxyGroups,
|
||||
'rules': instance.rule,
|
||||
@@ -330,56 +324,65 @@ Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
|
||||
'sub-rules': instance.subRules,
|
||||
};
|
||||
|
||||
_$ClashConfigImpl _$$ClashConfigImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ClashConfigImpl(
|
||||
mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort,
|
||||
socksPort: (json['socks-port'] as num?)?.toInt() ?? 0,
|
||||
port: (json['port'] as num?)?.toInt() ?? 0,
|
||||
redirPort: (json['redir-port'] as num?)?.toInt() ?? 0,
|
||||
tproxyPort: (json['tproxy-port'] as num?)?.toInt() ?? 0,
|
||||
mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule,
|
||||
allowLan: json['allow-lan'] as bool? ?? false,
|
||||
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['log-level']) ??
|
||||
LogLevel.error,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
findProcessMode: $enumDecodeNullable(
|
||||
_$FindProcessModeEnumMap, json['find-process-mode'],
|
||||
unknownValue: FindProcessMode.always) ??
|
||||
FindProcessMode.off,
|
||||
keepAliveInterval: (json['keep-alive-interval'] as num?)?.toInt() ??
|
||||
defaultKeepAliveInterval,
|
||||
unifiedDelay: json['unified-delay'] as bool? ?? true,
|
||||
tcpConcurrent: json['tcp-concurrent'] as bool? ?? true,
|
||||
tun: json['tun'] == null
|
||||
? defaultTun
|
||||
: Tun.safeFormJson(json['tun'] as Map<String, Object?>?),
|
||||
dns: json['dns'] == null
|
||||
? defaultDns
|
||||
: Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>),
|
||||
geoXUrl: json['geox-url'] == null
|
||||
? defaultGeoXUrl
|
||||
: GeoXUrl.safeFormJson(json['geox-url'] as Map<String, Object?>?),
|
||||
geodataLoader:
|
||||
$enumDecodeNullable(_$GeodataLoaderEnumMap, json['geodata-loader']) ??
|
||||
GeodataLoader.memconservative,
|
||||
proxyGroups: (json['proxy-groups'] as List<dynamic>?)
|
||||
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
rule:
|
||||
(json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const [],
|
||||
globalUa: json['global-ua'] as String?,
|
||||
externalController: $enumDecodeNullable(
|
||||
_$ExternalControllerStatusEnumMap, json['external-controller']) ??
|
||||
ExternalControllerStatus.close,
|
||||
hosts: (json['hosts'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
_ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => _ClashConfig(
|
||||
mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort,
|
||||
socksPort: (json['socks-port'] as num?)?.toInt() ?? 0,
|
||||
port: (json['port'] as num?)?.toInt() ?? 0,
|
||||
redirPort: (json['redir-port'] as num?)?.toInt() ?? 0,
|
||||
tproxyPort: (json['tproxy-port'] as num?)?.toInt() ?? 0,
|
||||
mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule,
|
||||
allowLan: json['allow-lan'] as bool? ?? false,
|
||||
logLevel:
|
||||
$enumDecodeNullable(_$LogLevelEnumMap, json['log-level']) ??
|
||||
LogLevel.error,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
findProcessMode:
|
||||
$enumDecodeNullable(
|
||||
_$FindProcessModeEnumMap,
|
||||
json['find-process-mode'],
|
||||
unknownValue: FindProcessMode.always,
|
||||
) ??
|
||||
FindProcessMode.always,
|
||||
keepAliveInterval:
|
||||
(json['keep-alive-interval'] as num?)?.toInt() ??
|
||||
defaultKeepAliveInterval,
|
||||
unifiedDelay: json['unified-delay'] as bool? ?? true,
|
||||
tcpConcurrent: json['tcp-concurrent'] as bool? ?? true,
|
||||
tun: json['tun'] == null
|
||||
? defaultTun
|
||||
: Tun.safeFormJson(json['tun'] as Map<String, Object?>?),
|
||||
dns: json['dns'] == null
|
||||
? defaultDns
|
||||
: Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>),
|
||||
geoXUrl: json['geox-url'] == null
|
||||
? defaultGeoXUrl
|
||||
: GeoXUrl.safeFormJson(json['geox-url'] as Map<String, Object?>?),
|
||||
geodataLoader:
|
||||
$enumDecodeNullable(_$GeodataLoaderEnumMap, json['geodata-loader']) ??
|
||||
GeodataLoader.memconservative,
|
||||
proxyGroups:
|
||||
(json['proxy-groups'] as List<dynamic>?)
|
||||
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
rule:
|
||||
(json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||
const [],
|
||||
globalUa: json['global-ua'] as String?,
|
||||
externalController:
|
||||
$enumDecodeNullable(
|
||||
_$ExternalControllerStatusEnumMap,
|
||||
json['external-controller'],
|
||||
) ??
|
||||
ExternalControllerStatus.close,
|
||||
hosts:
|
||||
(json['hosts'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ClashConfigImplToJson(_$ClashConfigImpl instance) =>
|
||||
Map<String, dynamic> _$ClashConfigToJson(_ClashConfig instance) =>
|
||||
<String, dynamic>{
|
||||
'mixed-port': instance.mixedPort,
|
||||
'socks-port': instance.socksPort,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,71 +6,69 @@ part of '../common.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$PackageImpl _$$PackageImplFromJson(Map<String, dynamic> json) =>
|
||||
_$PackageImpl(
|
||||
packageName: json['packageName'] as String,
|
||||
label: json['label'] as String,
|
||||
system: json['system'] as bool,
|
||||
internet: json['internet'] as bool,
|
||||
lastUpdateTime: (json['lastUpdateTime'] as num).toInt(),
|
||||
);
|
||||
_Package _$PackageFromJson(Map<String, dynamic> json) => _Package(
|
||||
packageName: json['packageName'] as String,
|
||||
label: json['label'] as String,
|
||||
system: json['system'] as bool,
|
||||
internet: json['internet'] as bool,
|
||||
lastUpdateTime: (json['lastUpdateTime'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$PackageImplToJson(_$PackageImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'packageName': instance.packageName,
|
||||
'label': instance.label,
|
||||
'system': instance.system,
|
||||
'internet': instance.internet,
|
||||
'lastUpdateTime': instance.lastUpdateTime,
|
||||
};
|
||||
Map<String, dynamic> _$PackageToJson(_Package instance) => <String, dynamic>{
|
||||
'packageName': instance.packageName,
|
||||
'label': instance.label,
|
||||
'system': instance.system,
|
||||
'internet': instance.internet,
|
||||
'lastUpdateTime': instance.lastUpdateTime,
|
||||
};
|
||||
|
||||
_$MetadataImpl _$$MetadataImplFromJson(Map<String, dynamic> json) =>
|
||||
_$MetadataImpl(
|
||||
uid: (json['uid'] as num?)?.toInt() ?? 0,
|
||||
network: json['network'] as String? ?? '',
|
||||
sourceIP: json['sourceIP'] as String? ?? '',
|
||||
sourcePort: json['sourcePort'] as String? ?? '',
|
||||
destinationIP: json['destinationIP'] as String? ?? '',
|
||||
destinationPort: json['destinationPort'] as String? ?? '',
|
||||
host: json['host'] as String? ?? '',
|
||||
dnsMode: $enumDecodeNullable(_$DnsModeEnumMap, json['dnsMode']),
|
||||
process: json['process'] as String? ?? '',
|
||||
processPath: json['processPath'] as String? ?? '',
|
||||
remoteDestination: json['remoteDestination'] as String? ?? '',
|
||||
sourceGeoIP: (json['sourceGeoIP'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
destinationGeoIP: (json['destinationGeoIP'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
destinationIPASN: json['destinationIPASN'] as String? ?? '',
|
||||
sourceIPASN: json['sourceIPASN'] as String? ?? '',
|
||||
specialRules: json['specialRules'] as String? ?? '',
|
||||
specialProxy: json['specialProxy'] as String? ?? '',
|
||||
);
|
||||
_Metadata _$MetadataFromJson(Map<String, dynamic> json) => _Metadata(
|
||||
uid: (json['uid'] as num?)?.toInt() ?? 0,
|
||||
network: json['network'] as String? ?? '',
|
||||
sourceIP: json['sourceIP'] as String? ?? '',
|
||||
sourcePort: json['sourcePort'] as String? ?? '',
|
||||
destinationIP: json['destinationIP'] as String? ?? '',
|
||||
destinationPort: json['destinationPort'] as String? ?? '',
|
||||
host: json['host'] as String? ?? '',
|
||||
dnsMode: $enumDecodeNullable(_$DnsModeEnumMap, json['dnsMode']),
|
||||
process: json['process'] as String? ?? '',
|
||||
processPath: json['processPath'] as String? ?? '',
|
||||
remoteDestination: json['remoteDestination'] as String? ?? '',
|
||||
sourceGeoIP:
|
||||
(json['sourceGeoIP'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
destinationGeoIP:
|
||||
(json['destinationGeoIP'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
destinationIPASN: json['destinationIPASN'] as String? ?? '',
|
||||
sourceIPASN: json['sourceIPASN'] as String? ?? '',
|
||||
specialRules: json['specialRules'] as String? ?? '',
|
||||
specialProxy: json['specialProxy'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$MetadataImplToJson(_$MetadataImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'uid': instance.uid,
|
||||
'network': instance.network,
|
||||
'sourceIP': instance.sourceIP,
|
||||
'sourcePort': instance.sourcePort,
|
||||
'destinationIP': instance.destinationIP,
|
||||
'destinationPort': instance.destinationPort,
|
||||
'host': instance.host,
|
||||
'dnsMode': _$DnsModeEnumMap[instance.dnsMode],
|
||||
'process': instance.process,
|
||||
'processPath': instance.processPath,
|
||||
'remoteDestination': instance.remoteDestination,
|
||||
'sourceGeoIP': instance.sourceGeoIP,
|
||||
'destinationGeoIP': instance.destinationGeoIP,
|
||||
'destinationIPASN': instance.destinationIPASN,
|
||||
'sourceIPASN': instance.sourceIPASN,
|
||||
'specialRules': instance.specialRules,
|
||||
'specialProxy': instance.specialProxy,
|
||||
};
|
||||
Map<String, dynamic> _$MetadataToJson(_Metadata instance) => <String, dynamic>{
|
||||
'uid': instance.uid,
|
||||
'network': instance.network,
|
||||
'sourceIP': instance.sourceIP,
|
||||
'sourcePort': instance.sourcePort,
|
||||
'destinationIP': instance.destinationIP,
|
||||
'destinationPort': instance.destinationPort,
|
||||
'host': instance.host,
|
||||
'dnsMode': _$DnsModeEnumMap[instance.dnsMode],
|
||||
'process': instance.process,
|
||||
'processPath': instance.processPath,
|
||||
'remoteDestination': instance.remoteDestination,
|
||||
'sourceGeoIP': instance.sourceGeoIP,
|
||||
'destinationGeoIP': instance.destinationGeoIP,
|
||||
'destinationIPASN': instance.destinationIPASN,
|
||||
'sourceIPASN': instance.sourceIPASN,
|
||||
'specialRules': instance.specialRules,
|
||||
'specialProxy': instance.specialProxy,
|
||||
};
|
||||
|
||||
const _$DnsModeEnumMap = {
|
||||
DnsMode.normal: 'normal',
|
||||
@@ -79,22 +77,20 @@ const _$DnsModeEnumMap = {
|
||||
DnsMode.hosts: 'hosts',
|
||||
};
|
||||
|
||||
_$TrackerInfoImpl _$$TrackerInfoImplFromJson(Map<String, dynamic> json) =>
|
||||
_$TrackerInfoImpl(
|
||||
id: json['id'] as String,
|
||||
upload: (json['upload'] as num?)?.toInt() ?? 0,
|
||||
download: (json['download'] as num?)?.toInt() ?? 0,
|
||||
start: DateTime.parse(json['start'] as String),
|
||||
metadata: Metadata.fromJson(json['metadata'] as Map<String, dynamic>),
|
||||
chains:
|
||||
(json['chains'] as List<dynamic>).map((e) => e as String).toList(),
|
||||
rule: json['rule'] as String,
|
||||
rulePayload: json['rulePayload'] as String,
|
||||
downloadSpeed: (json['downloadSpeed'] as num?)?.toInt(),
|
||||
uploadSpeed: (json['uploadSpeed'] as num?)?.toInt(),
|
||||
);
|
||||
_TrackerInfo _$TrackerInfoFromJson(Map<String, dynamic> json) => _TrackerInfo(
|
||||
id: json['id'] as String,
|
||||
upload: (json['upload'] as num?)?.toInt() ?? 0,
|
||||
download: (json['download'] as num?)?.toInt() ?? 0,
|
||||
start: DateTime.parse(json['start'] as String),
|
||||
metadata: Metadata.fromJson(json['metadata'] as Map<String, dynamic>),
|
||||
chains: (json['chains'] as List<dynamic>).map((e) => e as String).toList(),
|
||||
rule: json['rule'] as String,
|
||||
rulePayload: json['rulePayload'] as String,
|
||||
downloadSpeed: (json['downloadSpeed'] as num?)?.toInt(),
|
||||
uploadSpeed: (json['uploadSpeed'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TrackerInfoImplToJson(_$TrackerInfoImpl instance) =>
|
||||
Map<String, dynamic> _$TrackerInfoToJson(_TrackerInfo instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'upload': instance.upload,
|
||||
@@ -108,18 +104,18 @@ Map<String, dynamic> _$$TrackerInfoImplToJson(_$TrackerInfoImpl instance) =>
|
||||
'uploadSpeed': instance.uploadSpeed,
|
||||
};
|
||||
|
||||
_$LogImpl _$$LogImplFromJson(Map<String, dynamic> json) => _$LogImpl(
|
||||
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['LogLevel']) ??
|
||||
LogLevel.info,
|
||||
payload: json['Payload'] as String? ?? '',
|
||||
dateTime: _logDateTime(json['dateTime']),
|
||||
);
|
||||
_Log _$LogFromJson(Map<String, dynamic> json) => _Log(
|
||||
logLevel:
|
||||
$enumDecodeNullable(_$LogLevelEnumMap, json['LogLevel']) ?? LogLevel.info,
|
||||
payload: json['Payload'] as String? ?? '',
|
||||
dateTime: _logDateTime(json['dateTime']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$LogImplToJson(_$LogImpl instance) => <String, dynamic>{
|
||||
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
|
||||
'Payload': instance.payload,
|
||||
'dateTime': instance.dateTime,
|
||||
};
|
||||
Map<String, dynamic> _$LogToJson(_Log instance) => <String, dynamic>{
|
||||
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
|
||||
'Payload': instance.payload,
|
||||
'dateTime': instance.dateTime,
|
||||
};
|
||||
|
||||
const _$LogLevelEnumMap = {
|
||||
LogLevel.debug: 'debug',
|
||||
@@ -129,68 +125,74 @@ const _$LogLevelEnumMap = {
|
||||
LogLevel.silent: 'silent',
|
||||
};
|
||||
|
||||
_$DAVImpl _$$DAVImplFromJson(Map<String, dynamic> json) => _$DAVImpl(
|
||||
uri: json['uri'] as String,
|
||||
user: json['user'] as String,
|
||||
password: json['password'] as String,
|
||||
fileName: json['fileName'] as String? ?? defaultDavFileName,
|
||||
);
|
||||
_DAV _$DAVFromJson(Map<String, dynamic> json) => _DAV(
|
||||
uri: json['uri'] as String,
|
||||
user: json['user'] as String,
|
||||
password: json['password'] as String,
|
||||
fileName: json['fileName'] as String? ?? defaultDavFileName,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DAVImplToJson(_$DAVImpl instance) => <String, dynamic>{
|
||||
'uri': instance.uri,
|
||||
'user': instance.user,
|
||||
'password': instance.password,
|
||||
'fileName': instance.fileName,
|
||||
};
|
||||
Map<String, dynamic> _$DAVToJson(_DAV instance) => <String, dynamic>{
|
||||
'uri': instance.uri,
|
||||
'user': instance.user,
|
||||
'password': instance.password,
|
||||
'fileName': instance.fileName,
|
||||
};
|
||||
|
||||
_$VersionInfoImpl _$$VersionInfoImplFromJson(Map<String, dynamic> json) =>
|
||||
_$VersionInfoImpl(
|
||||
clashName: json['clashName'] as String? ?? '',
|
||||
version: json['version'] as String? ?? '',
|
||||
);
|
||||
_VersionInfo _$VersionInfoFromJson(Map<String, dynamic> json) => _VersionInfo(
|
||||
clashName: json['clashName'] as String? ?? '',
|
||||
version: json['version'] as String? ?? '',
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$VersionInfoImplToJson(_$VersionInfoImpl instance) =>
|
||||
Map<String, dynamic> _$VersionInfoToJson(_VersionInfo instance) =>
|
||||
<String, dynamic>{
|
||||
'clashName': instance.clashName,
|
||||
'version': instance.version,
|
||||
};
|
||||
|
||||
_$ProxyImpl _$$ProxyImplFromJson(Map<String, dynamic> json) => _$ProxyImpl(
|
||||
name: json['name'] as String,
|
||||
type: json['type'] as String,
|
||||
now: json['now'] as String?,
|
||||
);
|
||||
_Traffic _$TrafficFromJson(Map<String, dynamic> json) =>
|
||||
_Traffic(up: json['up'] as num? ?? 0, down: json['down'] as num? ?? 0);
|
||||
|
||||
Map<String, dynamic> _$$ProxyImplToJson(_$ProxyImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'type': instance.type,
|
||||
'now': instance.now,
|
||||
};
|
||||
Map<String, dynamic> _$TrafficToJson(_Traffic instance) => <String, dynamic>{
|
||||
'up': instance.up,
|
||||
'down': instance.down,
|
||||
};
|
||||
|
||||
_$GroupImpl _$$GroupImplFromJson(Map<String, dynamic> json) => _$GroupImpl(
|
||||
type: $enumDecode(_$GroupTypeEnumMap, json['type']),
|
||||
all: (json['all'] as List<dynamic>?)
|
||||
?.map((e) => Proxy.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
now: json['now'] as String?,
|
||||
hidden: json['hidden'] as bool?,
|
||||
testUrl: json['testUrl'] as String?,
|
||||
icon: json['icon'] as String? ?? '',
|
||||
name: json['name'] as String,
|
||||
);
|
||||
_Proxy _$ProxyFromJson(Map<String, dynamic> json) => _Proxy(
|
||||
name: json['name'] as String,
|
||||
type: json['type'] as String,
|
||||
now: json['now'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$GroupImplToJson(_$GroupImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'type': _$GroupTypeEnumMap[instance.type]!,
|
||||
'all': instance.all,
|
||||
'now': instance.now,
|
||||
'hidden': instance.hidden,
|
||||
'testUrl': instance.testUrl,
|
||||
'icon': instance.icon,
|
||||
'name': instance.name,
|
||||
};
|
||||
Map<String, dynamic> _$ProxyToJson(_Proxy instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'type': instance.type,
|
||||
'now': instance.now,
|
||||
};
|
||||
|
||||
_Group _$GroupFromJson(Map<String, dynamic> json) => _Group(
|
||||
type: $enumDecode(_$GroupTypeEnumMap, json['type']),
|
||||
all:
|
||||
(json['all'] as List<dynamic>?)
|
||||
?.map((e) => Proxy.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
now: json['now'] as String?,
|
||||
hidden: json['hidden'] as bool?,
|
||||
testUrl: json['testUrl'] as String?,
|
||||
icon: json['icon'] as String? ?? '',
|
||||
name: json['name'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GroupToJson(_Group instance) => <String, dynamic>{
|
||||
'type': _$GroupTypeEnumMap[instance.type]!,
|
||||
'all': instance.all,
|
||||
'now': instance.now,
|
||||
'hidden': instance.hidden,
|
||||
'testUrl': instance.testUrl,
|
||||
'icon': instance.icon,
|
||||
'name': instance.name,
|
||||
};
|
||||
|
||||
const _$GroupTypeEnumMap = {
|
||||
GroupType.Selector: 'Selector',
|
||||
@@ -200,22 +202,24 @@ const _$GroupTypeEnumMap = {
|
||||
GroupType.Relay: 'Relay',
|
||||
};
|
||||
|
||||
_$HotKeyActionImpl _$$HotKeyActionImplFromJson(Map<String, dynamic> json) =>
|
||||
_$HotKeyActionImpl(
|
||||
_HotKeyAction _$HotKeyActionFromJson(Map<String, dynamic> json) =>
|
||||
_HotKeyAction(
|
||||
action: $enumDecode(_$HotActionEnumMap, json['action']),
|
||||
key: (json['key'] as num?)?.toInt(),
|
||||
modifiers: (json['modifiers'] as List<dynamic>?)
|
||||
modifiers:
|
||||
(json['modifiers'] as List<dynamic>?)
|
||||
?.map((e) => $enumDecode(_$KeyboardModifierEnumMap, e))
|
||||
.toSet() ??
|
||||
const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$HotKeyActionImplToJson(_$HotKeyActionImpl instance) =>
|
||||
Map<String, dynamic> _$HotKeyActionToJson(_HotKeyAction instance) =>
|
||||
<String, dynamic>{
|
||||
'action': _$HotActionEnumMap[instance.action]!,
|
||||
'key': instance.key,
|
||||
'modifiers':
|
||||
instance.modifiers.map((e) => _$KeyboardModifierEnumMap[e]!).toList(),
|
||||
'modifiers': instance.modifiers
|
||||
.map((e) => _$KeyboardModifierEnumMap[e]!)
|
||||
.toList(),
|
||||
};
|
||||
|
||||
const _$HotActionEnumMap = {
|
||||
@@ -235,35 +239,30 @@ const _$KeyboardModifierEnumMap = {
|
||||
KeyboardModifier.shift: 'shift',
|
||||
};
|
||||
|
||||
_$TextPainterParamsImpl _$$TextPainterParamsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$TextPainterParamsImpl(
|
||||
text: json['text'] as String?,
|
||||
fontSize: (json['fontSize'] as num?)?.toDouble(),
|
||||
textScaleFactor: (json['textScaleFactor'] as num).toDouble(),
|
||||
maxWidth: (json['maxWidth'] as num?)?.toDouble() ?? double.infinity,
|
||||
maxLines: (json['maxLines'] as num?)?.toInt(),
|
||||
_AndroidState _$AndroidStateFromJson(Map<String, dynamic> json) =>
|
||||
_AndroidState(
|
||||
currentProfileName: json['currentProfileName'] as String,
|
||||
stopText: json['stopText'] as String,
|
||||
onlyStatisticsProxy: json['onlyStatisticsProxy'] as bool,
|
||||
crashlytics: json['crashlytics'] as bool,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TextPainterParamsImplToJson(
|
||||
_$TextPainterParamsImpl instance) =>
|
||||
Map<String, dynamic> _$AndroidStateToJson(_AndroidState instance) =>
|
||||
<String, dynamic>{
|
||||
'text': instance.text,
|
||||
'fontSize': instance.fontSize,
|
||||
'textScaleFactor': instance.textScaleFactor,
|
||||
'maxWidth': instance.maxWidth,
|
||||
'maxLines': instance.maxLines,
|
||||
'currentProfileName': instance.currentProfileName,
|
||||
'stopText': instance.stopText,
|
||||
'onlyStatisticsProxy': instance.onlyStatisticsProxy,
|
||||
'crashlytics': instance.crashlytics,
|
||||
};
|
||||
|
||||
_$ScriptImpl _$$ScriptImplFromJson(Map<String, dynamic> json) => _$ScriptImpl(
|
||||
id: json['id'] as String,
|
||||
label: json['label'] as String,
|
||||
content: json['content'] as String,
|
||||
);
|
||||
_Script _$ScriptFromJson(Map<String, dynamic> json) => _Script(
|
||||
id: json['id'] as String,
|
||||
label: json['label'] as String,
|
||||
content: json['content'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ScriptImplToJson(_$ScriptImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'label': instance.label,
|
||||
'content': instance.content,
|
||||
};
|
||||
Map<String, dynamic> _$ScriptToJson(_Script instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'label': instance.label,
|
||||
'content': instance.content,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,8 @@ part of '../config.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$AppSettingPropsImpl(
|
||||
_AppSettingProps _$AppSettingPropsFromJson(Map<String, dynamic> json) =>
|
||||
_AppSettingProps(
|
||||
locale: json['locale'] as String?,
|
||||
dashboardWidgets: json['dashboardWidgets'] == null
|
||||
? defaultDashboardWidgets
|
||||
@@ -24,16 +23,20 @@ _$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
|
||||
autoCheckUpdate: json['autoCheckUpdate'] as bool? ?? true,
|
||||
showLabel: json['showLabel'] as bool? ?? false,
|
||||
disclaimerAccepted: json['disclaimerAccepted'] as bool? ?? false,
|
||||
crashlyticsTip: json['crashlyticsTip'] as bool? ?? false,
|
||||
crashlytics: json['crashlytics'] as bool? ?? false,
|
||||
minimizeOnExit: json['minimizeOnExit'] as bool? ?? true,
|
||||
hidden: json['hidden'] as bool? ?? false,
|
||||
developerMode: json['developerMode'] as bool? ?? false,
|
||||
recoveryStrategy: $enumDecodeNullable(
|
||||
_$RecoveryStrategyEnumMap, json['recoveryStrategy']) ??
|
||||
recoveryStrategy:
|
||||
$enumDecodeNullable(
|
||||
_$RecoveryStrategyEnumMap,
|
||||
json['recoveryStrategy'],
|
||||
) ??
|
||||
RecoveryStrategy.compatible,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AppSettingPropsImplToJson(
|
||||
_$AppSettingPropsImpl instance) =>
|
||||
Map<String, dynamic> _$AppSettingPropsToJson(_AppSettingProps instance) =>
|
||||
<String, dynamic>{
|
||||
'locale': instance.locale,
|
||||
'dashboardWidgets': instance.dashboardWidgets
|
||||
@@ -50,6 +53,8 @@ Map<String, dynamic> _$$AppSettingPropsImplToJson(
|
||||
'autoCheckUpdate': instance.autoCheckUpdate,
|
||||
'showLabel': instance.showLabel,
|
||||
'disclaimerAccepted': instance.disclaimerAccepted,
|
||||
'crashlyticsTip': instance.crashlyticsTip,
|
||||
'crashlytics': instance.crashlytics,
|
||||
'minimizeOnExit': instance.minimizeOnExit,
|
||||
'hidden': instance.hidden,
|
||||
'developerMode': instance.developerMode,
|
||||
@@ -74,26 +79,30 @@ const _$DashboardWidgetEnumMap = {
|
||||
DashboardWidget.memoryInfo: 'memoryInfo',
|
||||
};
|
||||
|
||||
_$AccessControlImpl _$$AccessControlImplFromJson(Map<String, dynamic> json) =>
|
||||
_$AccessControlImpl(
|
||||
_AccessControl _$AccessControlFromJson(Map<String, dynamic> json) =>
|
||||
_AccessControl(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
mode: $enumDecodeNullable(_$AccessControlModeEnumMap, json['mode']) ??
|
||||
mode:
|
||||
$enumDecodeNullable(_$AccessControlModeEnumMap, json['mode']) ??
|
||||
AccessControlMode.rejectSelected,
|
||||
acceptList: (json['acceptList'] as List<dynamic>?)
|
||||
acceptList:
|
||||
(json['acceptList'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
rejectList: (json['rejectList'] as List<dynamic>?)
|
||||
rejectList:
|
||||
(json['rejectList'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
sort: $enumDecodeNullable(_$AccessSortTypeEnumMap, json['sort']) ??
|
||||
sort:
|
||||
$enumDecodeNullable(_$AccessSortTypeEnumMap, json['sort']) ??
|
||||
AccessSortType.none,
|
||||
isFilterSystemApp: json['isFilterSystemApp'] as bool? ?? true,
|
||||
isFilterNonInternetApp: json['isFilterNonInternetApp'] as bool? ?? true,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AccessControlImplToJson(_$AccessControlImpl instance) =>
|
||||
Map<String, dynamic> _$AccessControlToJson(_AccessControl instance) =>
|
||||
<String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'mode': _$AccessControlModeEnumMap[instance.mode]!,
|
||||
@@ -115,15 +124,14 @@ const _$AccessSortTypeEnumMap = {
|
||||
AccessSortType.time: 'time',
|
||||
};
|
||||
|
||||
_$WindowPropsImpl _$$WindowPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$WindowPropsImpl(
|
||||
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(),
|
||||
);
|
||||
_WindowProps _$WindowPropsFromJson(Map<String, dynamic> json) => _WindowProps(
|
||||
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(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$WindowPropsImplToJson(_$WindowPropsImpl instance) =>
|
||||
Map<String, dynamic> _$WindowPropsToJson(_WindowProps instance) =>
|
||||
<String, dynamic>{
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
@@ -131,40 +139,41 @@ Map<String, dynamic> _$$WindowPropsImplToJson(_$WindowPropsImpl instance) =>
|
||||
'left': instance.left,
|
||||
};
|
||||
|
||||
_$VpnPropsImpl _$$VpnPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$VpnPropsImpl(
|
||||
enable: json['enable'] as bool? ?? true,
|
||||
systemProxy: json['systemProxy'] as bool? ?? true,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
allowBypass: json['allowBypass'] as bool? ?? true,
|
||||
accessControl: json['accessControl'] == null
|
||||
? defaultAccessControl
|
||||
: AccessControl.fromJson(
|
||||
json['accessControl'] as Map<String, dynamic>),
|
||||
);
|
||||
_VpnProps _$VpnPropsFromJson(Map<String, dynamic> json) => _VpnProps(
|
||||
enable: json['enable'] as bool? ?? true,
|
||||
systemProxy: json['systemProxy'] as bool? ?? true,
|
||||
ipv6: json['ipv6'] as bool? ?? false,
|
||||
allowBypass: json['allowBypass'] as bool? ?? true,
|
||||
dnsHijacking: json['dnsHijacking'] as bool? ?? false,
|
||||
accessControl: json['accessControl'] == null
|
||||
? defaultAccessControl
|
||||
: AccessControl.fromJson(json['accessControl'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$VpnPropsImplToJson(_$VpnPropsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'systemProxy': instance.systemProxy,
|
||||
'ipv6': instance.ipv6,
|
||||
'allowBypass': instance.allowBypass,
|
||||
'accessControl': instance.accessControl,
|
||||
};
|
||||
Map<String, dynamic> _$VpnPropsToJson(_VpnProps instance) => <String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'systemProxy': instance.systemProxy,
|
||||
'ipv6': instance.ipv6,
|
||||
'allowBypass': instance.allowBypass,
|
||||
'dnsHijacking': instance.dnsHijacking,
|
||||
'accessControl': instance.accessControl,
|
||||
};
|
||||
|
||||
_$NetworkPropsImpl _$$NetworkPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$NetworkPropsImpl(
|
||||
_NetworkProps _$NetworkPropsFromJson(Map<String, dynamic> json) =>
|
||||
_NetworkProps(
|
||||
systemProxy: json['systemProxy'] as bool? ?? true,
|
||||
bypassDomain: (json['bypassDomain'] as List<dynamic>?)
|
||||
bypassDomain:
|
||||
(json['bypassDomain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
defaultBypassDomain,
|
||||
routeMode: $enumDecodeNullable(_$RouteModeEnumMap, json['routeMode']) ??
|
||||
routeMode:
|
||||
$enumDecodeNullable(_$RouteModeEnumMap, json['routeMode']) ??
|
||||
RouteMode.config,
|
||||
autoSetSystemDns: json['autoSetSystemDns'] as bool? ?? true,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$NetworkPropsImplToJson(_$NetworkPropsImpl instance) =>
|
||||
Map<String, dynamic> _$NetworkPropsToJson(_NetworkProps instance) =>
|
||||
<String, dynamic>{
|
||||
'systemProxy': instance.systemProxy,
|
||||
'bypassDomain': instance.bypassDomain,
|
||||
@@ -177,27 +186,31 @@ const _$RouteModeEnumMap = {
|
||||
RouteMode.config: 'config',
|
||||
};
|
||||
|
||||
_$ProxiesStyleImpl _$$ProxiesStyleImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ProxiesStyleImpl(
|
||||
type: $enumDecodeNullable(_$ProxiesTypeEnumMap, json['type']) ??
|
||||
_ProxiesStyle _$ProxiesStyleFromJson(Map<String, dynamic> json) =>
|
||||
_ProxiesStyle(
|
||||
type:
|
||||
$enumDecodeNullable(_$ProxiesTypeEnumMap, json['type']) ??
|
||||
ProxiesType.tab,
|
||||
sortType:
|
||||
$enumDecodeNullable(_$ProxiesSortTypeEnumMap, json['sortType']) ??
|
||||
ProxiesSortType.none,
|
||||
layout: $enumDecodeNullable(_$ProxiesLayoutEnumMap, json['layout']) ??
|
||||
ProxiesSortType.none,
|
||||
layout:
|
||||
$enumDecodeNullable(_$ProxiesLayoutEnumMap, json['layout']) ??
|
||||
ProxiesLayout.standard,
|
||||
iconStyle:
|
||||
$enumDecodeNullable(_$ProxiesIconStyleEnumMap, json['iconStyle']) ??
|
||||
ProxiesIconStyle.standard,
|
||||
cardType: $enumDecodeNullable(_$ProxyCardTypeEnumMap, json['cardType']) ??
|
||||
ProxiesIconStyle.standard,
|
||||
cardType:
|
||||
$enumDecodeNullable(_$ProxyCardTypeEnumMap, json['cardType']) ??
|
||||
ProxyCardType.expand,
|
||||
iconMap: (json['iconMap'] as Map<String, dynamic>?)?.map(
|
||||
iconMap:
|
||||
(json['iconMap'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProxiesStyleImplToJson(_$ProxiesStyleImpl instance) =>
|
||||
Map<String, dynamic> _$ProxiesStyleToJson(_ProxiesStyle instance) =>
|
||||
<String, dynamic>{
|
||||
'type': _$ProxiesTypeEnumMap[instance.type]!,
|
||||
'sortType': _$ProxiesSortTypeEnumMap[instance.sortType]!,
|
||||
@@ -207,10 +220,7 @@ Map<String, dynamic> _$$ProxiesStyleImplToJson(_$ProxiesStyleImpl instance) =>
|
||||
'iconMap': instance.iconMap,
|
||||
};
|
||||
|
||||
const _$ProxiesTypeEnumMap = {
|
||||
ProxiesType.tab: 'tab',
|
||||
ProxiesType.list: 'list',
|
||||
};
|
||||
const _$ProxiesTypeEnumMap = {ProxiesType.tab: 'tab', ProxiesType.list: 'list'};
|
||||
|
||||
const _$ProxiesSortTypeEnumMap = {
|
||||
ProxiesSortType.none: 'none',
|
||||
@@ -236,37 +246,37 @@ const _$ProxyCardTypeEnumMap = {
|
||||
ProxyCardType.min: 'min',
|
||||
};
|
||||
|
||||
_$TextScaleImpl _$$TextScaleImplFromJson(Map<String, dynamic> json) =>
|
||||
_$TextScaleImpl(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
scale: (json['scale'] as num?)?.toDouble() ?? 1.0,
|
||||
);
|
||||
_TextScale _$TextScaleFromJson(Map<String, dynamic> json) => _TextScale(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
scale: (json['scale'] as num?)?.toDouble() ?? 1.0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$TextScaleImplToJson(_$TextScaleImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'scale': instance.scale,
|
||||
};
|
||||
Map<String, dynamic> _$TextScaleToJson(_TextScale instance) =>
|
||||
<String, dynamic>{'enable': instance.enable, 'scale': instance.scale};
|
||||
|
||||
_$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ThemePropsImpl(
|
||||
primaryColor: (json['primaryColor'] as num?)?.toInt(),
|
||||
primaryColors: (json['primaryColors'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList() ??
|
||||
defaultPrimaryColors,
|
||||
themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
|
||||
ThemeMode.dark,
|
||||
schemeVariant: $enumDecodeNullable(
|
||||
_$DynamicSchemeVariantEnumMap, json['schemeVariant']) ??
|
||||
DynamicSchemeVariant.content,
|
||||
pureBlack: json['pureBlack'] as bool? ?? false,
|
||||
textScale: json['textScale'] == null
|
||||
? const TextScale()
|
||||
: TextScale.fromJson(json['textScale'] as Map<String, dynamic>),
|
||||
);
|
||||
_ThemeProps _$ThemePropsFromJson(Map<String, dynamic> json) => _ThemeProps(
|
||||
primaryColor: (json['primaryColor'] as num?)?.toInt(),
|
||||
primaryColors:
|
||||
(json['primaryColors'] as List<dynamic>?)
|
||||
?.map((e) => (e as num).toInt())
|
||||
.toList() ??
|
||||
defaultPrimaryColors,
|
||||
themeMode:
|
||||
$enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
|
||||
ThemeMode.dark,
|
||||
schemeVariant:
|
||||
$enumDecodeNullable(
|
||||
_$DynamicSchemeVariantEnumMap,
|
||||
json['schemeVariant'],
|
||||
) ??
|
||||
DynamicSchemeVariant.content,
|
||||
pureBlack: json['pureBlack'] as bool? ?? false,
|
||||
textScale: json['textScale'] == null
|
||||
? const TextScale()
|
||||
: TextScale.fromJson(json['textScale'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ThemePropsImplToJson(_$ThemePropsImpl instance) =>
|
||||
Map<String, dynamic> _$ThemePropsToJson(_ThemeProps instance) =>
|
||||
<String, dynamic>{
|
||||
'primaryColor': instance.primaryColor,
|
||||
'primaryColors': instance.primaryColors,
|
||||
@@ -294,77 +304,77 @@ const _$DynamicSchemeVariantEnumMap = {
|
||||
DynamicSchemeVariant.fruitSalad: 'fruitSalad',
|
||||
};
|
||||
|
||||
_$ScriptPropsImpl _$$ScriptPropsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ScriptPropsImpl(
|
||||
currentId: json['currentId'] as String?,
|
||||
scripts: (json['scripts'] as List<dynamic>?)
|
||||
?.map((e) => Script.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
_ScriptProps _$ScriptPropsFromJson(Map<String, dynamic> json) => _ScriptProps(
|
||||
currentId: json['currentId'] as String?,
|
||||
scripts:
|
||||
(json['scripts'] as List<dynamic>?)
|
||||
?.map((e) => Script.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ScriptPropsImplToJson(_$ScriptPropsImpl instance) =>
|
||||
Map<String, dynamic> _$ScriptPropsToJson(_ScriptProps instance) =>
|
||||
<String, dynamic>{
|
||||
'currentId': instance.currentId,
|
||||
'scripts': instance.scripts,
|
||||
};
|
||||
|
||||
_$ConfigImpl _$$ConfigImplFromJson(Map<String, dynamic> json) => _$ConfigImpl(
|
||||
appSetting: json['appSetting'] == null
|
||||
? defaultAppSettingProps
|
||||
: AppSettingProps.safeFromJson(
|
||||
json['appSetting'] as Map<String, Object?>?),
|
||||
profiles: (json['profiles'] as List<dynamic>?)
|
||||
?.map((e) => Profile.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
hotKeyActions: (json['hotKeyActions'] as List<dynamic>?)
|
||||
?.map((e) => HotKeyAction.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
currentProfileId: json['currentProfileId'] as String?,
|
||||
overrideDns: json['overrideDns'] as bool? ?? false,
|
||||
dav: json['dav'] == null
|
||||
? null
|
||||
: DAV.fromJson(json['dav'] as Map<String, dynamic>),
|
||||
networkProps: json['networkProps'] == null
|
||||
? defaultNetworkProps
|
||||
: NetworkProps.fromJson(
|
||||
json['networkProps'] as Map<String, dynamic>?),
|
||||
vpnProps: json['vpnProps'] == null
|
||||
? defaultVpnProps
|
||||
: VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?),
|
||||
themeProps:
|
||||
ThemeProps.safeFromJson(json['themeProps'] as Map<String, Object?>?),
|
||||
proxiesStyle: json['proxiesStyle'] == null
|
||||
? defaultProxiesStyle
|
||||
: ProxiesStyle.fromJson(
|
||||
json['proxiesStyle'] as Map<String, dynamic>?),
|
||||
windowProps: json['windowProps'] == null
|
||||
? defaultWindowProps
|
||||
: WindowProps.fromJson(json['windowProps'] as Map<String, dynamic>?),
|
||||
patchClashConfig: json['patchClashConfig'] == null
|
||||
? defaultClashConfig
|
||||
: ClashConfig.fromJson(
|
||||
json['patchClashConfig'] as Map<String, dynamic>),
|
||||
scriptProps: json['scriptProps'] == null
|
||||
? const ScriptProps()
|
||||
: ScriptProps.fromJson(json['scriptProps'] as Map<String, dynamic>),
|
||||
);
|
||||
_Config _$ConfigFromJson(Map<String, dynamic> json) => _Config(
|
||||
appSetting: json['appSetting'] == null
|
||||
? defaultAppSettingProps
|
||||
: AppSettingProps.safeFromJson(
|
||||
json['appSetting'] as Map<String, Object?>?,
|
||||
),
|
||||
profiles:
|
||||
(json['profiles'] as List<dynamic>?)
|
||||
?.map((e) => Profile.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
hotKeyActions:
|
||||
(json['hotKeyActions'] as List<dynamic>?)
|
||||
?.map((e) => HotKeyAction.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
currentProfileId: json['currentProfileId'] as String?,
|
||||
overrideDns: json['overrideDns'] as bool? ?? false,
|
||||
dav: json['dav'] == null
|
||||
? null
|
||||
: DAV.fromJson(json['dav'] as Map<String, dynamic>),
|
||||
networkProps: json['networkProps'] == null
|
||||
? defaultNetworkProps
|
||||
: NetworkProps.fromJson(json['networkProps'] as Map<String, dynamic>?),
|
||||
vpnProps: json['vpnProps'] == null
|
||||
? defaultVpnProps
|
||||
: VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?),
|
||||
themeProps: ThemeProps.safeFromJson(
|
||||
json['themeProps'] as Map<String, Object?>?,
|
||||
),
|
||||
proxiesStyle: json['proxiesStyle'] == null
|
||||
? defaultProxiesStyle
|
||||
: ProxiesStyle.fromJson(json['proxiesStyle'] as Map<String, dynamic>?),
|
||||
windowProps: json['windowProps'] == null
|
||||
? defaultWindowProps
|
||||
: WindowProps.fromJson(json['windowProps'] as Map<String, dynamic>?),
|
||||
patchClashConfig: json['patchClashConfig'] == null
|
||||
? defaultClashConfig
|
||||
: ClashConfig.fromJson(json['patchClashConfig'] as Map<String, dynamic>),
|
||||
scriptProps: json['scriptProps'] == null
|
||||
? const ScriptProps()
|
||||
: ScriptProps.fromJson(json['scriptProps'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ConfigImplToJson(_$ConfigImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'appSetting': instance.appSetting,
|
||||
'profiles': instance.profiles,
|
||||
'hotKeyActions': instance.hotKeyActions,
|
||||
'currentProfileId': instance.currentProfileId,
|
||||
'overrideDns': instance.overrideDns,
|
||||
'dav': instance.dav,
|
||||
'networkProps': instance.networkProps,
|
||||
'vpnProps': instance.vpnProps,
|
||||
'themeProps': instance.themeProps,
|
||||
'proxiesStyle': instance.proxiesStyle,
|
||||
'windowProps': instance.windowProps,
|
||||
'patchClashConfig': instance.patchClashConfig,
|
||||
'scriptProps': instance.scriptProps,
|
||||
};
|
||||
Map<String, dynamic> _$ConfigToJson(_Config instance) => <String, dynamic>{
|
||||
'appSetting': instance.appSetting,
|
||||
'profiles': instance.profiles,
|
||||
'hotKeyActions': instance.hotKeyActions,
|
||||
'currentProfileId': instance.currentProfileId,
|
||||
'overrideDns': instance.overrideDns,
|
||||
'dav': instance.dav,
|
||||
'networkProps': instance.networkProps,
|
||||
'vpnProps': instance.vpnProps,
|
||||
'themeProps': instance.themeProps,
|
||||
'proxiesStyle': instance.proxiesStyle,
|
||||
'windowProps': instance.windowProps,
|
||||
'patchClashConfig': instance.patchClashConfig,
|
||||
'scriptProps': instance.scriptProps,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,37 +6,38 @@ part of '../core.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$SetupParamsImpl _$$SetupParamsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$SetupParamsImpl(
|
||||
config: json['config'] as Map<String, dynamic>,
|
||||
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
|
||||
testUrl: json['test-url'] as String,
|
||||
);
|
||||
_SetupParams _$SetupParamsFromJson(Map<String, dynamic> json) => _SetupParams(
|
||||
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
|
||||
testUrl: json['test-url'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SetupParamsImplToJson(_$SetupParamsImpl instance) =>
|
||||
Map<String, dynamic> _$SetupParamsToJson(_SetupParams instance) =>
|
||||
<String, dynamic>{
|
||||
'config': instance.config,
|
||||
'selected-map': instance.selectedMap,
|
||||
'test-url': instance.testUrl,
|
||||
};
|
||||
|
||||
_$UpdateParamsImpl _$$UpdateParamsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$UpdateParamsImpl(
|
||||
_UpdateParams _$UpdateParamsFromJson(Map<String, dynamic> json) =>
|
||||
_UpdateParams(
|
||||
tun: Tun.fromJson(json['tun'] as Map<String, dynamic>),
|
||||
mixedPort: (json['mixed-port'] as num).toInt(),
|
||||
allowLan: json['allow-lan'] as bool,
|
||||
findProcessMode:
|
||||
$enumDecode(_$FindProcessModeEnumMap, json['find-process-mode']),
|
||||
findProcessMode: $enumDecode(
|
||||
_$FindProcessModeEnumMap,
|
||||
json['find-process-mode'],
|
||||
),
|
||||
mode: $enumDecode(_$ModeEnumMap, json['mode']),
|
||||
logLevel: $enumDecode(_$LogLevelEnumMap, json['log-level']),
|
||||
ipv6: json['ipv6'] as bool,
|
||||
tcpConcurrent: json['tcp-concurrent'] as bool,
|
||||
externalController: $enumDecode(
|
||||
_$ExternalControllerStatusEnumMap, json['external-controller']),
|
||||
_$ExternalControllerStatusEnumMap,
|
||||
json['external-controller'],
|
||||
),
|
||||
unifiedDelay: json['unified-delay'] as bool,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UpdateParamsImplToJson(_$UpdateParamsImpl instance) =>
|
||||
Map<String, dynamic> _$UpdateParamsToJson(_UpdateParams instance) =>
|
||||
<String, dynamic>{
|
||||
'tun': instance.tun,
|
||||
'mixed-port': instance.mixedPort,
|
||||
@@ -75,129 +76,103 @@ const _$ExternalControllerStatusEnumMap = {
|
||||
ExternalControllerStatus.open: '127.0.0.1:9090',
|
||||
};
|
||||
|
||||
_$CoreStateImpl _$$CoreStateImplFromJson(Map<String, dynamic> json) =>
|
||||
_$CoreStateImpl(
|
||||
vpnProps: VpnProps.fromJson(json['vpn-props'] as Map<String, dynamic>?),
|
||||
onlyStatisticsProxy: json['only-statistics-proxy'] as bool,
|
||||
currentProfileName: json['current-profile-name'] as String,
|
||||
bypassDomain: (json['bypass-domain'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
_VpnOptions _$VpnOptionsFromJson(Map<String, dynamic> json) => _VpnOptions(
|
||||
enable: json['enable'] as bool,
|
||||
port: (json['port'] as num).toInt(),
|
||||
ipv6: json['ipv6'] as bool,
|
||||
dnsHijacking: json['dnsHijacking'] as bool,
|
||||
accessControl: AccessControl.fromJson(
|
||||
json['accessControl'] as Map<String, dynamic>,
|
||||
),
|
||||
allowBypass: json['allowBypass'] as bool,
|
||||
systemProxy: json['systemProxy'] as bool,
|
||||
bypassDomain: (json['bypassDomain'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
stack: json['stack'] as String,
|
||||
routeAddress:
|
||||
(json['routeAddress'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$CoreStateImplToJson(_$CoreStateImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'vpn-props': instance.vpnProps,
|
||||
'only-statistics-proxy': instance.onlyStatisticsProxy,
|
||||
'current-profile-name': instance.currentProfileName,
|
||||
'bypass-domain': instance.bypassDomain,
|
||||
};
|
||||
|
||||
_$AndroidVpnOptionsImpl _$$AndroidVpnOptionsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$AndroidVpnOptionsImpl(
|
||||
enable: json['enable'] as bool,
|
||||
port: (json['port'] as num).toInt(),
|
||||
accessControl: json['accessControl'] == null
|
||||
? null
|
||||
: AccessControl.fromJson(
|
||||
json['accessControl'] as Map<String, dynamic>),
|
||||
allowBypass: json['allowBypass'] as bool,
|
||||
systemProxy: json['systemProxy'] as bool,
|
||||
bypassDomain: (json['bypassDomain'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
ipv4Address: json['ipv4Address'] as String,
|
||||
ipv6Address: json['ipv6Address'] as String,
|
||||
routeAddress: (json['routeAddress'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
const [],
|
||||
dnsServerAddress: json['dnsServerAddress'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AndroidVpnOptionsImplToJson(
|
||||
_$AndroidVpnOptionsImpl instance) =>
|
||||
Map<String, dynamic> _$VpnOptionsToJson(_VpnOptions instance) =>
|
||||
<String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'port': instance.port,
|
||||
'ipv6': instance.ipv6,
|
||||
'dnsHijacking': instance.dnsHijacking,
|
||||
'accessControl': instance.accessControl,
|
||||
'allowBypass': instance.allowBypass,
|
||||
'systemProxy': instance.systemProxy,
|
||||
'bypassDomain': instance.bypassDomain,
|
||||
'ipv4Address': instance.ipv4Address,
|
||||
'ipv6Address': instance.ipv6Address,
|
||||
'stack': instance.stack,
|
||||
'routeAddress': instance.routeAddress,
|
||||
'dnsServerAddress': instance.dnsServerAddress,
|
||||
};
|
||||
|
||||
_$InitParamsImpl _$$InitParamsImplFromJson(Map<String, dynamic> json) =>
|
||||
_$InitParamsImpl(
|
||||
homeDir: json['home-dir'] as String,
|
||||
version: (json['version'] as num).toInt(),
|
||||
);
|
||||
_InitParams _$InitParamsFromJson(Map<String, dynamic> json) => _InitParams(
|
||||
homeDir: json['home-dir'] as String,
|
||||
version: (json['version'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$InitParamsImplToJson(_$InitParamsImpl instance) =>
|
||||
Map<String, dynamic> _$InitParamsToJson(_InitParams instance) =>
|
||||
<String, dynamic>{
|
||||
'home-dir': instance.homeDir,
|
||||
'version': instance.version,
|
||||
};
|
||||
|
||||
_$ChangeProxyParamsImpl _$$ChangeProxyParamsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ChangeProxyParamsImpl(
|
||||
_ChangeProxyParams _$ChangeProxyParamsFromJson(Map<String, dynamic> json) =>
|
||||
_ChangeProxyParams(
|
||||
groupName: json['group-name'] as String,
|
||||
proxyName: json['proxy-name'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ChangeProxyParamsImplToJson(
|
||||
_$ChangeProxyParamsImpl instance) =>
|
||||
Map<String, dynamic> _$ChangeProxyParamsToJson(_ChangeProxyParams instance) =>
|
||||
<String, dynamic>{
|
||||
'group-name': instance.groupName,
|
||||
'proxy-name': instance.proxyName,
|
||||
};
|
||||
|
||||
_$UpdateGeoDataParamsImpl _$$UpdateGeoDataParamsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$UpdateGeoDataParamsImpl(
|
||||
_UpdateGeoDataParams _$UpdateGeoDataParamsFromJson(Map<String, dynamic> json) =>
|
||||
_UpdateGeoDataParams(
|
||||
geoType: json['geo-type'] as String,
|
||||
geoName: json['geo-name'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$UpdateGeoDataParamsImplToJson(
|
||||
_$UpdateGeoDataParamsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'geo-type': instance.geoType,
|
||||
'geo-name': instance.geoName,
|
||||
};
|
||||
Map<String, dynamic> _$UpdateGeoDataParamsToJson(
|
||||
_UpdateGeoDataParams instance,
|
||||
) => <String, dynamic>{
|
||||
'geo-type': instance.geoType,
|
||||
'geo-name': instance.geoName,
|
||||
};
|
||||
|
||||
_$AppMessageImpl _$$AppMessageImplFromJson(Map<String, dynamic> json) =>
|
||||
_$AppMessageImpl(
|
||||
type: $enumDecode(_$AppMessageTypeEnumMap, json['type']),
|
||||
data: json['data'],
|
||||
);
|
||||
_CoreEvent _$CoreEventFromJson(Map<String, dynamic> json) => _CoreEvent(
|
||||
type: $enumDecode(_$CoreEventTypeEnumMap, json['type']),
|
||||
data: json['data'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AppMessageImplToJson(_$AppMessageImpl instance) =>
|
||||
Map<String, dynamic> _$CoreEventToJson(_CoreEvent instance) =>
|
||||
<String, dynamic>{
|
||||
'type': _$AppMessageTypeEnumMap[instance.type]!,
|
||||
'type': _$CoreEventTypeEnumMap[instance.type]!,
|
||||
'data': instance.data,
|
||||
};
|
||||
|
||||
const _$AppMessageTypeEnumMap = {
|
||||
AppMessageType.log: 'log',
|
||||
AppMessageType.delay: 'delay',
|
||||
AppMessageType.request: 'request',
|
||||
AppMessageType.loaded: 'loaded',
|
||||
const _$CoreEventTypeEnumMap = {
|
||||
CoreEventType.log: 'log',
|
||||
CoreEventType.delay: 'delay',
|
||||
CoreEventType.request: 'request',
|
||||
CoreEventType.loaded: 'loaded',
|
||||
CoreEventType.crash: 'crash',
|
||||
};
|
||||
|
||||
_$InvokeMessageImpl _$$InvokeMessageImplFromJson(Map<String, dynamic> json) =>
|
||||
_$InvokeMessageImpl(
|
||||
_InvokeMessage _$InvokeMessageFromJson(Map<String, dynamic> json) =>
|
||||
_InvokeMessage(
|
||||
type: $enumDecode(_$InvokeMessageTypeEnumMap, json['type']),
|
||||
data: json['data'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$InvokeMessageImplToJson(_$InvokeMessageImpl instance) =>
|
||||
Map<String, dynamic> _$InvokeMessageToJson(_InvokeMessage instance) =>
|
||||
<String, dynamic>{
|
||||
'type': _$InvokeMessageTypeEnumMap[instance.type]!,
|
||||
'data': instance.data,
|
||||
@@ -208,63 +183,59 @@ const _$InvokeMessageTypeEnumMap = {
|
||||
InvokeMessageType.process: 'process',
|
||||
};
|
||||
|
||||
_$DelayImpl _$$DelayImplFromJson(Map<String, dynamic> json) => _$DelayImpl(
|
||||
name: json['name'] as String,
|
||||
url: json['url'] as String,
|
||||
value: (json['value'] as num?)?.toInt(),
|
||||
);
|
||||
_Delay _$DelayFromJson(Map<String, dynamic> json) => _Delay(
|
||||
name: json['name'] as String,
|
||||
url: json['url'] as String,
|
||||
value: (json['value'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$DelayImplToJson(_$DelayImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'url': instance.url,
|
||||
'value': instance.value,
|
||||
};
|
||||
Map<String, dynamic> _$DelayToJson(_Delay instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'url': instance.url,
|
||||
'value': instance.value,
|
||||
};
|
||||
|
||||
_$NowImpl _$$NowImplFromJson(Map<String, dynamic> json) => _$NowImpl(
|
||||
name: json['name'] as String,
|
||||
value: json['value'] as String,
|
||||
);
|
||||
_Now _$NowFromJson(Map<String, dynamic> json) =>
|
||||
_Now(name: json['name'] as String, value: json['value'] as String);
|
||||
|
||||
Map<String, dynamic> _$$NowImplToJson(_$NowImpl instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'value': instance.value,
|
||||
};
|
||||
Map<String, dynamic> _$NowToJson(_Now instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'value': instance.value,
|
||||
};
|
||||
|
||||
_$ProviderSubscriptionInfoImpl _$$ProviderSubscriptionInfoImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ProviderSubscriptionInfoImpl(
|
||||
upload: (json['UPLOAD'] as num?)?.toInt() ?? 0,
|
||||
download: (json['DOWNLOAD'] as num?)?.toInt() ?? 0,
|
||||
total: (json['TOTAL'] as num?)?.toInt() ?? 0,
|
||||
expire: (json['EXPIRE'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
_ProviderSubscriptionInfo _$ProviderSubscriptionInfoFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _ProviderSubscriptionInfo(
|
||||
upload: (json['UPLOAD'] as num?)?.toInt() ?? 0,
|
||||
download: (json['DOWNLOAD'] as num?)?.toInt() ?? 0,
|
||||
total: (json['TOTAL'] as num?)?.toInt() ?? 0,
|
||||
expire: (json['EXPIRE'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProviderSubscriptionInfoImplToJson(
|
||||
_$ProviderSubscriptionInfoImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'UPLOAD': instance.upload,
|
||||
'DOWNLOAD': instance.download,
|
||||
'TOTAL': instance.total,
|
||||
'EXPIRE': instance.expire,
|
||||
};
|
||||
Map<String, dynamic> _$ProviderSubscriptionInfoToJson(
|
||||
_ProviderSubscriptionInfo instance,
|
||||
) => <String, dynamic>{
|
||||
'UPLOAD': instance.upload,
|
||||
'DOWNLOAD': instance.download,
|
||||
'TOTAL': instance.total,
|
||||
'EXPIRE': instance.expire,
|
||||
};
|
||||
|
||||
_$ExternalProviderImpl _$$ExternalProviderImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$ExternalProviderImpl(
|
||||
_ExternalProvider _$ExternalProviderFromJson(Map<String, dynamic> json) =>
|
||||
_ExternalProvider(
|
||||
name: json['name'] as String,
|
||||
type: json['type'] as String,
|
||||
path: json['path'] as String?,
|
||||
count: (json['count'] as num).toInt(),
|
||||
subscriptionInfo: subscriptionInfoFormCore(
|
||||
json['subscription-info'] as Map<String, Object?>?),
|
||||
json['subscription-info'] as Map<String, Object?>?,
|
||||
),
|
||||
isUpdating: json['isUpdating'] as bool? ?? false,
|
||||
vehicleType: json['vehicle-type'] as String,
|
||||
updateAt: DateTime.parse(json['update-at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ExternalProviderImplToJson(
|
||||
_$ExternalProviderImpl instance) =>
|
||||
Map<String, dynamic> _$ExternalProviderToJson(_ExternalProvider instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'type': instance.type,
|
||||
@@ -276,18 +247,17 @@ Map<String, dynamic> _$$ExternalProviderImplToJson(
|
||||
'update-at': instance.updateAt.toIso8601String(),
|
||||
};
|
||||
|
||||
_$ActionImpl _$$ActionImplFromJson(Map<String, dynamic> json) => _$ActionImpl(
|
||||
method: $enumDecode(_$ActionMethodEnumMap, json['method']),
|
||||
data: json['data'],
|
||||
id: json['id'] as String,
|
||||
);
|
||||
_Action _$ActionFromJson(Map<String, dynamic> json) => _Action(
|
||||
method: $enumDecode(_$ActionMethodEnumMap, json['method']),
|
||||
data: json['data'],
|
||||
id: json['id'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ActionImplToJson(_$ActionImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'method': _$ActionMethodEnumMap[instance.method]!,
|
||||
'data': instance.data,
|
||||
'id': instance.id,
|
||||
};
|
||||
Map<String, dynamic> _$ActionToJson(_Action instance) => <String, dynamic>{
|
||||
'method': _$ActionMethodEnumMap[instance.method]!,
|
||||
'data': instance.data,
|
||||
'id': instance.id,
|
||||
};
|
||||
|
||||
const _$ActionMethodEnumMap = {
|
||||
ActionMethod.message: 'message',
|
||||
@@ -321,6 +291,7 @@ const _$ActionMethodEnumMap = {
|
||||
ActionMethod.getMemory: 'getMemory',
|
||||
ActionMethod.crash: 'crash',
|
||||
ActionMethod.setupConfig: 'setupConfig',
|
||||
ActionMethod.deleteFile: 'deleteFile',
|
||||
ActionMethod.setState: 'setState',
|
||||
ActionMethod.startTun: 'startTun',
|
||||
ActionMethod.stopTun: 'stopTun',
|
||||
@@ -330,16 +301,17 @@ const _$ActionMethodEnumMap = {
|
||||
ActionMethod.getCurrentProfileName: 'getCurrentProfileName',
|
||||
};
|
||||
|
||||
_$ActionResultImpl _$$ActionResultImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ActionResultImpl(
|
||||
_ActionResult _$ActionResultFromJson(Map<String, dynamic> json) =>
|
||||
_ActionResult(
|
||||
method: $enumDecode(_$ActionMethodEnumMap, json['method']),
|
||||
data: json['data'],
|
||||
id: json['id'] as String?,
|
||||
code: $enumDecodeNullable(_$ResultTypeEnumMap, json['code']) ??
|
||||
code:
|
||||
$enumDecodeNullable(_$ResultTypeEnumMap, json['code']) ??
|
||||
ResultType.success,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ActionResultImplToJson(_$ActionResultImpl instance) =>
|
||||
Map<String, dynamic> _$ActionResultToJson(_ActionResult instance) =>
|
||||
<String, dynamic>{
|
||||
'method': _$ActionMethodEnumMap[instance.method]!,
|
||||
'data': instance.data,
|
||||
@@ -347,7 +319,4 @@ Map<String, dynamic> _$$ActionResultImplToJson(_$ActionResultImpl instance) =>
|
||||
'code': _$ResultTypeEnumMap[instance.code]!,
|
||||
};
|
||||
|
||||
const _$ResultTypeEnumMap = {
|
||||
ResultType.success: 0,
|
||||
ResultType.error: -1,
|
||||
};
|
||||
const _$ResultTypeEnumMap = {ResultType.success: 0, ResultType.error: -1};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,17 +6,15 @@ part of '../profile.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$SubscriptionInfoImpl _$$SubscriptionInfoImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SubscriptionInfoImpl(
|
||||
_SubscriptionInfo _$SubscriptionInfoFromJson(Map<String, dynamic> json) =>
|
||||
_SubscriptionInfo(
|
||||
upload: (json['upload'] as num?)?.toInt() ?? 0,
|
||||
download: (json['download'] as num?)?.toInt() ?? 0,
|
||||
total: (json['total'] as num?)?.toInt() ?? 0,
|
||||
expire: (json['expire'] as num?)?.toInt() ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SubscriptionInfoImplToJson(
|
||||
_$SubscriptionInfoImpl instance) =>
|
||||
Map<String, dynamic> _$SubscriptionInfoToJson(_SubscriptionInfo instance) =>
|
||||
<String, dynamic>{
|
||||
'upload': instance.upload,
|
||||
'download': instance.download,
|
||||
@@ -24,79 +22,79 @@ Map<String, dynamic> _$$SubscriptionInfoImplToJson(
|
||||
'expire': instance.expire,
|
||||
};
|
||||
|
||||
_$ProfileImpl _$$ProfileImplFromJson(Map<String, dynamic> json) =>
|
||||
_$ProfileImpl(
|
||||
id: json['id'] as String,
|
||||
label: json['label'] as String?,
|
||||
currentGroupName: json['currentGroupName'] as String?,
|
||||
url: json['url'] as String? ?? '',
|
||||
lastUpdateDate: json['lastUpdateDate'] == null
|
||||
? null
|
||||
: DateTime.parse(json['lastUpdateDate'] as String),
|
||||
autoUpdateDuration:
|
||||
Duration(microseconds: (json['autoUpdateDuration'] as num).toInt()),
|
||||
subscriptionInfo: json['subscriptionInfo'] == null
|
||||
? null
|
||||
: SubscriptionInfo.fromJson(
|
||||
json['subscriptionInfo'] as Map<String, dynamic>),
|
||||
autoUpdate: json['autoUpdate'] as bool? ?? true,
|
||||
selectedMap: (json['selectedMap'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {},
|
||||
unfoldSet: (json['unfoldSet'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toSet() ??
|
||||
const {},
|
||||
overrideData: json['overrideData'] == null
|
||||
? const OverrideData()
|
||||
: OverrideData.fromJson(json['overrideData'] as Map<String, dynamic>),
|
||||
);
|
||||
_Profile _$ProfileFromJson(Map<String, dynamic> json) => _Profile(
|
||||
id: json['id'] as String,
|
||||
label: json['label'] as String?,
|
||||
currentGroupName: json['currentGroupName'] as String?,
|
||||
url: json['url'] as String? ?? '',
|
||||
lastUpdateDate: json['lastUpdateDate'] == null
|
||||
? null
|
||||
: DateTime.parse(json['lastUpdateDate'] as String),
|
||||
autoUpdateDuration: Duration(
|
||||
microseconds: (json['autoUpdateDuration'] as num).toInt(),
|
||||
),
|
||||
subscriptionInfo: json['subscriptionInfo'] == null
|
||||
? null
|
||||
: SubscriptionInfo.fromJson(
|
||||
json['subscriptionInfo'] as Map<String, dynamic>,
|
||||
),
|
||||
autoUpdate: json['autoUpdate'] as bool? ?? true,
|
||||
selectedMap:
|
||||
(json['selectedMap'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
const {},
|
||||
unfoldSet:
|
||||
(json['unfoldSet'] as List<dynamic>?)?.map((e) => e as String).toSet() ??
|
||||
const {},
|
||||
overrideData: json['overrideData'] == null
|
||||
? const OverrideData()
|
||||
: OverrideData.fromJson(json['overrideData'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ProfileImplToJson(_$ProfileImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'label': instance.label,
|
||||
'currentGroupName': instance.currentGroupName,
|
||||
'url': instance.url,
|
||||
'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(),
|
||||
'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds,
|
||||
'subscriptionInfo': instance.subscriptionInfo,
|
||||
'autoUpdate': instance.autoUpdate,
|
||||
'selectedMap': instance.selectedMap,
|
||||
'unfoldSet': instance.unfoldSet.toList(),
|
||||
'overrideData': instance.overrideData,
|
||||
};
|
||||
Map<String, dynamic> _$ProfileToJson(_Profile instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'label': instance.label,
|
||||
'currentGroupName': instance.currentGroupName,
|
||||
'url': instance.url,
|
||||
'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(),
|
||||
'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds,
|
||||
'subscriptionInfo': instance.subscriptionInfo,
|
||||
'autoUpdate': instance.autoUpdate,
|
||||
'selectedMap': instance.selectedMap,
|
||||
'unfoldSet': instance.unfoldSet.toList(),
|
||||
'overrideData': instance.overrideData,
|
||||
};
|
||||
|
||||
_$OverrideDataImpl _$$OverrideDataImplFromJson(Map<String, dynamic> json) =>
|
||||
_$OverrideDataImpl(
|
||||
_OverrideData _$OverrideDataFromJson(Map<String, dynamic> json) =>
|
||||
_OverrideData(
|
||||
enable: json['enable'] as bool? ?? false,
|
||||
rule: json['rule'] == null
|
||||
? const OverrideRule()
|
||||
: OverrideRule.fromJson(json['rule'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$OverrideDataImplToJson(_$OverrideDataImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'enable': instance.enable,
|
||||
'rule': instance.rule,
|
||||
};
|
||||
Map<String, dynamic> _$OverrideDataToJson(_OverrideData instance) =>
|
||||
<String, dynamic>{'enable': instance.enable, 'rule': instance.rule};
|
||||
|
||||
_$OverrideRuleImpl _$$OverrideRuleImplFromJson(Map<String, dynamic> json) =>
|
||||
_$OverrideRuleImpl(
|
||||
type: $enumDecodeNullable(_$OverrideRuleTypeEnumMap, json['type']) ??
|
||||
_OverrideRule _$OverrideRuleFromJson(Map<String, dynamic> json) =>
|
||||
_OverrideRule(
|
||||
type:
|
||||
$enumDecodeNullable(_$OverrideRuleTypeEnumMap, json['type']) ??
|
||||
OverrideRuleType.added,
|
||||
overrideRules: (json['overrideRules'] as List<dynamic>?)
|
||||
overrideRules:
|
||||
(json['overrideRules'] as List<dynamic>?)
|
||||
?.map((e) => Rule.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
addedRules: (json['addedRules'] as List<dynamic>?)
|
||||
addedRules:
|
||||
(json['addedRules'] as List<dynamic>?)
|
||||
?.map((e) => Rule.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$OverrideRuleImplToJson(_$OverrideRuleImpl instance) =>
|
||||
Map<String, dynamic> _$OverrideRuleToJson(_OverrideRule instance) =>
|
||||
<String, dynamic>{
|
||||
'type': _$OverrideRuleTypeEnumMap[instance.type]!,
|
||||
'overrideRules': instance.overrideRules,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,9 @@
|
||||
// ignore_for_file: invalid_annotation_target
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:fl_clash/clash/core.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/controller.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@@ -16,7 +15,7 @@ part 'generated/profile.g.dart';
|
||||
typedef SelectedMap = Map<String, String>;
|
||||
|
||||
@freezed
|
||||
class SubscriptionInfo with _$SubscriptionInfo {
|
||||
abstract class SubscriptionInfo with _$SubscriptionInfo {
|
||||
const factory SubscriptionInfo({
|
||||
@Default(0) int upload,
|
||||
@Default(0) int download,
|
||||
@@ -45,7 +44,7 @@ class SubscriptionInfo with _$SubscriptionInfo {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class Profile with _$Profile {
|
||||
abstract class Profile with _$Profile {
|
||||
const factory Profile({
|
||||
required String id,
|
||||
String? label,
|
||||
@@ -66,10 +65,7 @@ class Profile with _$Profile {
|
||||
factory Profile.fromJson(Map<String, Object?> json) =>
|
||||
_$ProfileFromJson(json);
|
||||
|
||||
factory Profile.normal({
|
||||
String? label,
|
||||
String url = '',
|
||||
}) {
|
||||
factory Profile.normal({String? label, String url = ''}) {
|
||||
return Profile(
|
||||
label: label,
|
||||
url: url,
|
||||
@@ -80,7 +76,7 @@ class Profile with _$Profile {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class OverrideData with _$OverrideData {
|
||||
abstract class OverrideData with _$OverrideData {
|
||||
const factory OverrideData({
|
||||
@Default(false) bool enable,
|
||||
@Default(OverrideRule()) OverrideRule rule,
|
||||
@@ -100,7 +96,7 @@ extension OverrideDataExt on OverrideData {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class OverrideRule with _$OverrideRule {
|
||||
abstract class OverrideRule with _$OverrideRule {
|
||||
const factory OverrideRule({
|
||||
@Default(OverrideRuleType.added) OverrideRuleType type,
|
||||
@Default([]) List<Rule> overrideRules,
|
||||
@@ -113,9 +109,9 @@ class OverrideRule with _$OverrideRule {
|
||||
|
||||
extension OverrideRuleExt on OverrideRule {
|
||||
List<Rule> get rules => switch (type == OverrideRuleType.override) {
|
||||
true => overrideRules,
|
||||
false => addedRules,
|
||||
};
|
||||
true => overrideRules,
|
||||
false => addedRules,
|
||||
};
|
||||
|
||||
OverrideRule updateRules(List<Rule> Function(List<Rule> rules) builder) {
|
||||
if (type == OverrideRuleType.added) {
|
||||
@@ -178,7 +174,7 @@ extension ProfileExtension on Profile {
|
||||
}
|
||||
|
||||
Future<Profile> saveFile(Uint8List bytes) async {
|
||||
final message = await clashCore.validateConfig(utf8.decode(bytes));
|
||||
final message = await coreController.validateConfig(utf8.decode(bytes));
|
||||
if (message.isNotEmpty) {
|
||||
throw message;
|
||||
}
|
||||
@@ -188,7 +184,7 @@ extension ProfileExtension on Profile {
|
||||
}
|
||||
|
||||
Future<Profile> saveFileWithString(String value) async {
|
||||
final message = await clashCore.validateConfig(value);
|
||||
final message = await coreController.validateConfig(value);
|
||||
if (message.isNotEmpty) {
|
||||
throw message;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -8,34 +7,23 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'generated/selector.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class VM2<A, B> with _$VM2<A, B> {
|
||||
const factory VM2({
|
||||
required A a,
|
||||
required B b,
|
||||
}) = _VM2;
|
||||
abstract class VM2<A, B> with _$VM2<A, B> {
|
||||
const factory VM2({required A a, required B b}) = _VM2;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class VM3<A, B, C> with _$VM3<A, B, C> {
|
||||
const factory VM3({
|
||||
required A a,
|
||||
required B b,
|
||||
required C c,
|
||||
}) = _VM3;
|
||||
abstract class VM3<A, B, C> with _$VM3<A, B, C> {
|
||||
const factory VM3({required A a, required B b, required C c}) = _VM3;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class VM4<A, B, C, D> with _$VM4<A, B, C, D> {
|
||||
const factory VM4({
|
||||
required A a,
|
||||
required B b,
|
||||
required C c,
|
||||
required D d,
|
||||
}) = _VM4;
|
||||
abstract class VM4<A, B, C, D> with _$VM4<A, B, C, D> {
|
||||
const factory VM4({required A a, required B b, required C c, required D d}) =
|
||||
_VM4;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class VM5<A, B, C, D, E> with _$VM5<A, B, C, D, E> {
|
||||
abstract class VM5<A, B, C, D, E> with _$VM5<A, B, C, D, E> {
|
||||
const factory VM5({
|
||||
required A a,
|
||||
required B b,
|
||||
@@ -46,7 +34,7 @@ class VM5<A, B, C, D, E> with _$VM5<A, B, C, D, E> {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class StartButtonSelectorState with _$StartButtonSelectorState {
|
||||
abstract class StartButtonSelectorState with _$StartButtonSelectorState {
|
||||
const factory StartButtonSelectorState({
|
||||
required bool isInit,
|
||||
required bool hasProfile,
|
||||
@@ -54,7 +42,7 @@ class StartButtonSelectorState with _$StartButtonSelectorState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProfilesSelectorState with _$ProfilesSelectorState {
|
||||
abstract class ProfilesSelectorState with _$ProfilesSelectorState {
|
||||
const factory ProfilesSelectorState({
|
||||
required List<Profile> profiles,
|
||||
required String? currentProfileId,
|
||||
@@ -63,7 +51,7 @@ class ProfilesSelectorState with _$ProfilesSelectorState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NetworkDetectionState with _$NetworkDetectionState {
|
||||
abstract class NetworkDetectionState with _$NetworkDetectionState {
|
||||
const factory NetworkDetectionState({
|
||||
required bool isLoading,
|
||||
required IpInfo? ipInfo,
|
||||
@@ -71,7 +59,7 @@ class NetworkDetectionState with _$NetworkDetectionState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class TrayState with _$TrayState {
|
||||
abstract class TrayState with _$TrayState {
|
||||
const factory TrayState({
|
||||
required Mode mode,
|
||||
required int port,
|
||||
@@ -87,7 +75,7 @@ class TrayState with _$TrayState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NavigationState with _$NavigationState {
|
||||
abstract class NavigationState with _$NavigationState {
|
||||
const factory NavigationState({
|
||||
required PageLabel pageLabel,
|
||||
required List<NavigationItem> navigationItems,
|
||||
@@ -98,45 +86,38 @@ class NavigationState with _$NavigationState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GroupsState with _$GroupsState {
|
||||
const factory GroupsState({
|
||||
required List<Group> value,
|
||||
}) = _GroupsState;
|
||||
abstract class GroupsState with _$GroupsState {
|
||||
const factory GroupsState({required List<Group> value}) = _GroupsState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class NavigationItemsState with _$NavigationItemsState {
|
||||
const factory NavigationItemsState({
|
||||
required List<NavigationItem> value,
|
||||
}) = _NavigationItemsState;
|
||||
abstract class NavigationItemsState with _$NavigationItemsState {
|
||||
const factory NavigationItemsState({required List<NavigationItem> value}) =
|
||||
_NavigationItemsState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProxiesListState with _$ProxiesListState {
|
||||
abstract class ProxiesListState with _$ProxiesListState {
|
||||
const factory ProxiesListState({
|
||||
required List<Group> groups,
|
||||
required Set<String> currentUnfoldSet,
|
||||
required ProxiesSortType proxiesSortType,
|
||||
required ProxyCardType proxyCardType,
|
||||
required num sortNum,
|
||||
required int columns,
|
||||
}) = _ProxiesListState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProxiesTabState with _$ProxiesTabState {
|
||||
abstract class ProxiesTabState with _$ProxiesTabState {
|
||||
const factory ProxiesTabState({
|
||||
required List<Group> groups,
|
||||
required String? currentGroupName,
|
||||
required ProxiesSortType proxiesSortType,
|
||||
required ProxyCardType proxyCardType,
|
||||
required num sortNum,
|
||||
required int columns,
|
||||
}) = _ProxiesTabState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProxyGroupSelectorState with _$ProxyGroupSelectorState {
|
||||
abstract class ProxyGroupSelectorState with _$ProxyGroupSelectorState {
|
||||
const factory ProxyGroupSelectorState({
|
||||
required String? testUrl,
|
||||
required ProxiesSortType proxiesSortType,
|
||||
@@ -149,14 +130,14 @@ class ProxyGroupSelectorState with _$ProxyGroupSelectorState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class MoreToolsSelectorState with _$MoreToolsSelectorState {
|
||||
abstract class MoreToolsSelectorState with _$MoreToolsSelectorState {
|
||||
const factory MoreToolsSelectorState({
|
||||
required List<NavigationItem> navigationItems,
|
||||
}) = _MoreToolsSelectorState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class PackageListSelectorState with _$PackageListSelectorState {
|
||||
abstract class PackageListSelectorState with _$PackageListSelectorState {
|
||||
const factory PackageListSelectorState({
|
||||
required List<Package> packages,
|
||||
required AccessControl accessControl,
|
||||
@@ -178,32 +159,26 @@ extension PackageListSelectorStateExt on PackageListSelectorState {
|
||||
|
||||
List<Package> getSortList(List<String> selectedList) {
|
||||
final sort = accessControl.sort;
|
||||
return list.sorted(
|
||||
(a, b) {
|
||||
return switch (sort) {
|
||||
AccessSortType.none => 0,
|
||||
AccessSortType.name => utils.sortByChar(
|
||||
utils.getPinyin(a.label),
|
||||
utils.getPinyin(b.label),
|
||||
),
|
||||
AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),
|
||||
};
|
||||
},
|
||||
).sorted(
|
||||
(a, b) {
|
||||
final isSelectA = selectedList.contains(a.packageName);
|
||||
final isSelectB = selectedList.contains(b.packageName);
|
||||
if (isSelectA && isSelectB) return 0;
|
||||
if (isSelectA) return -1;
|
||||
if (isSelectB) return 1;
|
||||
return 0;
|
||||
},
|
||||
);
|
||||
|
||||
return list.sorted((a, b) {
|
||||
final isSelectA = selectedList.contains(a.packageName);
|
||||
final isSelectB = selectedList.contains(b.packageName);
|
||||
|
||||
if (isSelectA != isSelectB) {
|
||||
return isSelectA ? -1 : 1;
|
||||
}
|
||||
return switch (sort) {
|
||||
AccessSortType.none => 0,
|
||||
AccessSortType.name => a.label.compareTo(b.label),
|
||||
AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProxiesListHeaderSelectorState with _$ProxiesListHeaderSelectorState {
|
||||
abstract class ProxiesListHeaderSelectorState
|
||||
with _$ProxiesListHeaderSelectorState {
|
||||
const factory ProxiesListHeaderSelectorState({
|
||||
required double offset,
|
||||
required int currentIndex,
|
||||
@@ -211,7 +186,7 @@ class ProxiesListHeaderSelectorState with _$ProxiesListHeaderSelectorState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProxiesActionsState with _$ProxiesActionsState {
|
||||
abstract class ProxiesActionsState with _$ProxiesActionsState {
|
||||
const factory ProxiesActionsState({
|
||||
required PageLabel pageLabel,
|
||||
required ProxiesType type,
|
||||
@@ -220,7 +195,7 @@ class ProxiesActionsState with _$ProxiesActionsState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProxyState with _$ProxyState {
|
||||
abstract class ProxyState with _$ProxyState {
|
||||
const factory ProxyState({
|
||||
required bool isStart,
|
||||
required bool systemProxy,
|
||||
@@ -230,7 +205,7 @@ class ProxyState with _$ProxyState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ClashConfigState with _$ClashConfigState {
|
||||
abstract class ClashConfigState with _$ClashConfigState {
|
||||
const factory ClashConfigState({
|
||||
required bool overrideDns,
|
||||
required ClashConfig clashConfig,
|
||||
@@ -240,23 +215,24 @@ class ClashConfigState with _$ClashConfigState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DashboardState with _$DashboardState {
|
||||
abstract class DashboardState with _$DashboardState {
|
||||
const factory DashboardState({
|
||||
required List<DashboardWidget> dashboardWidgets,
|
||||
required double viewWidth,
|
||||
required double contentWidth,
|
||||
}) = _DashboardState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProxyCardState with _$ProxyCardState {
|
||||
const factory ProxyCardState({
|
||||
abstract class SelectedProxyState with _$SelectedProxyState {
|
||||
const factory SelectedProxyState({
|
||||
required String proxyName,
|
||||
@Default(false) bool group,
|
||||
String? testUrl,
|
||||
}) = _ProxyCardState;
|
||||
}) = _SelectedProxyState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class VpnState with _$VpnState {
|
||||
abstract class VpnState with _$VpnState {
|
||||
const factory VpnState({
|
||||
required TunStack stack,
|
||||
required VpnProps vpnProps,
|
||||
@@ -264,10 +240,10 @@ class VpnState with _$VpnState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class ProfileOverrideStateModel with _$ProfileOverrideStateModel {
|
||||
const factory ProfileOverrideStateModel({
|
||||
ClashConfigSnippet? snippet,
|
||||
required Set<String> selectedRules,
|
||||
abstract class ProfileOverrideModel with _$ProfileOverrideModel {
|
||||
const factory ProfileOverrideModel({
|
||||
@Default(ClashConfigSnippet()) ClashConfigSnippet snippet,
|
||||
@Default({}) Set<String> selectedRules,
|
||||
OverrideData? overrideData,
|
||||
}) = _ProfileOverrideStateModel;
|
||||
}) = _ProfileOverrideModel;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'generated/widget.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class ActivateState with _$ActivateState {
|
||||
abstract class ActivateState with _$ActivateState {
|
||||
const factory ActivateState({
|
||||
required bool active,
|
||||
}) = _ActivateState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CommonMessage with _$CommonMessage {
|
||||
abstract class CommonMessage with _$CommonMessage {
|
||||
const factory CommonMessage({
|
||||
required String id,
|
||||
required String text,
|
||||
@@ -20,7 +20,7 @@ class CommonMessage with _$CommonMessage {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AppBarState with _$AppBarState {
|
||||
abstract class AppBarState with _$AppBarState {
|
||||
const factory AppBarState({
|
||||
@Default([]) List<Widget> actions,
|
||||
AppBarSearchState? searchState,
|
||||
@@ -29,7 +29,7 @@ class AppBarState with _$AppBarState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AppBarSearchState with _$AppBarSearchState {
|
||||
abstract class AppBarSearchState with _$AppBarSearchState {
|
||||
const factory AppBarSearchState({
|
||||
required Function(String) onSearch,
|
||||
@Default(null) String? query,
|
||||
@@ -37,7 +37,7 @@ class AppBarSearchState with _$AppBarSearchState {
|
||||
}
|
||||
|
||||
@freezed
|
||||
class AppBarEditState with _$AppBarEditState {
|
||||
abstract class AppBarEditState with _$AppBarEditState {
|
||||
const factory AppBarEditState({
|
||||
@Default(0) int editCount,
|
||||
required Function() onExit,
|
||||
|
||||
@@ -23,7 +23,11 @@ class EditorPage extends ConsumerStatefulWidget {
|
||||
final bool titleEditable;
|
||||
final Function(BuildContext context, String title, String content)? onSave;
|
||||
final Future<bool> Function(
|
||||
BuildContext context, String title, String content)? onPop;
|
||||
BuildContext context,
|
||||
String title,
|
||||
String content,
|
||||
)?
|
||||
onPop;
|
||||
|
||||
const EditorPage({
|
||||
super.key,
|
||||
@@ -33,9 +37,7 @@ class EditorPage extends ConsumerStatefulWidget {
|
||||
this.onSave,
|
||||
this.onPop,
|
||||
this.supportRemoteDownload = false,
|
||||
this.languages = const [
|
||||
Language.yaml,
|
||||
],
|
||||
this.languages = const [Language.yaml],
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -92,7 +94,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
Widget _wrapController(EditingValueChangeBuilder builder) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _controller,
|
||||
builder: (_, value, ___) {
|
||||
builder: (_, value, _) {
|
||||
return builder(value);
|
||||
},
|
||||
);
|
||||
@@ -101,7 +103,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
Widget _wrapTitleController(TextEditingValueChangeBuilder builder) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: _titleController,
|
||||
builder: (_, value, ___) {
|
||||
builder: (_, value, _) {
|
||||
return builder(value);
|
||||
},
|
||||
);
|
||||
@@ -154,7 +156,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
Widget build(BuildContext context) {
|
||||
final isMobileView = ref.watch(isMobileViewProvider);
|
||||
return CommonPopScope(
|
||||
onPop: () async {
|
||||
onPop: (context) async {
|
||||
if (widget.onPop == null) {
|
||||
return true;
|
||||
}
|
||||
@@ -185,7 +187,8 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
_wrapController(
|
||||
(value) => _wrapTitleController(
|
||||
(value) => IconButton(
|
||||
onPressed: _controller.text != widget.content ||
|
||||
onPressed:
|
||||
_controller.text != widget.content ||
|
||||
_titleController.text != widget.title
|
||||
? () {
|
||||
widget.onSave!(
|
||||
@@ -202,18 +205,14 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
if (widget.supportRemoteDownload)
|
||||
IconButton(
|
||||
onPressed: _handleImport,
|
||||
icon: Icon(
|
||||
Icons.arrow_downward,
|
||||
),
|
||||
icon: Icon(Icons.arrow_downward),
|
||||
),
|
||||
_wrapController(
|
||||
(value) => CommonPopupBox(
|
||||
targetBuilder: (open) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
open(
|
||||
offset: Offset(-20, 20),
|
||||
);
|
||||
open(offset: Offset(-20, 20));
|
||||
},
|
||||
icon: const Icon(Icons.more_vert),
|
||||
);
|
||||
@@ -248,9 +247,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
readOnly: readOnly,
|
||||
isMobileView: isMobileView,
|
||||
),
|
||||
padding: EdgeInsets.only(
|
||||
right: 16,
|
||||
),
|
||||
padding: EdgeInsets.only(right: 16),
|
||||
autocompleteSymbols: true,
|
||||
focusNode: _focusNode,
|
||||
scrollbarBuilder: (context, child, details) {
|
||||
@@ -260,26 +257,22 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
);
|
||||
},
|
||||
toolbarController: ContextMenuControllerImpl(),
|
||||
indicatorBuilder: (
|
||||
context,
|
||||
editingController,
|
||||
chunkController,
|
||||
notifier,
|
||||
) {
|
||||
return Row(
|
||||
children: [
|
||||
DefaultCodeLineNumber(
|
||||
controller: editingController,
|
||||
notifier: notifier,
|
||||
),
|
||||
DefaultCodeChunkIndicator(
|
||||
width: 20,
|
||||
controller: chunkController,
|
||||
notifier: notifier,
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
indicatorBuilder:
|
||||
(context, editingController, chunkController, notifier) {
|
||||
return Row(
|
||||
children: [
|
||||
DefaultCodeLineNumber(
|
||||
controller: editingController,
|
||||
notifier: notifier,
|
||||
),
|
||||
DefaultCodeChunkIndicator(
|
||||
width: 20,
|
||||
controller: chunkController,
|
||||
notifier: notifier,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),
|
||||
controller: _controller,
|
||||
style: CodeEditorStyle(
|
||||
@@ -288,13 +281,9 @@ class _EditorPageState extends ConsumerState<EditorPage> {
|
||||
codeTheme: CodeHighlightTheme(
|
||||
languages: {
|
||||
if (widget.languages.contains(Language.yaml))
|
||||
'yaml': CodeHighlightThemeMode(
|
||||
mode: langYaml,
|
||||
),
|
||||
'yaml': CodeHighlightThemeMode(mode: langYaml),
|
||||
if (widget.languages.contains(Language.javaScript))
|
||||
'javascript': CodeHighlightThemeMode(
|
||||
mode: langJavascript,
|
||||
),
|
||||
'javascript': CodeHighlightThemeMode(mode: langJavascript),
|
||||
},
|
||||
theme: atomOneLightTheme,
|
||||
),
|
||||
@@ -318,33 +307,24 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
|
||||
required this.controller,
|
||||
required this.readOnly,
|
||||
required this.isMobileView,
|
||||
}) : height = (isMobileView
|
||||
? _kDefaultFindPanelHeight * 2
|
||||
: _kDefaultFindPanelHeight) +
|
||||
8;
|
||||
}) : height =
|
||||
(isMobileView
|
||||
? _kDefaultFindPanelHeight * 2
|
||||
: _kDefaultFindPanelHeight) +
|
||||
8;
|
||||
|
||||
@override
|
||||
Size get preferredSize => Size(
|
||||
double.infinity,
|
||||
controller.value == null ? 0 : height,
|
||||
);
|
||||
Size get preferredSize =>
|
||||
Size(double.infinity, controller.value == null ? 0 : height);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (controller.value == null) {
|
||||
return const SizedBox(
|
||||
width: 0,
|
||||
height: 0,
|
||||
);
|
||||
return const SizedBox(width: 0, height: 0);
|
||||
}
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
horizontal: 16,
|
||||
),
|
||||
margin: EdgeInsets.only(
|
||||
bottom: 8,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
margin: EdgeInsets.only(bottom: 8),
|
||||
color: context.colorScheme.surface,
|
||||
alignment: Alignment.centerLeft,
|
||||
height: height,
|
||||
@@ -368,18 +348,13 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
|
||||
constraints: BoxConstraints(maxWidth: 360),
|
||||
child: _buildFindInput(context, value),
|
||||
),
|
||||
SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
],
|
||||
Text(
|
||||
result,
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
Text(result, style: context.textTheme.bodyMedium),
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
spacing: 8,
|
||||
spacing: 6,
|
||||
children: [
|
||||
_buildIconButton(
|
||||
onPressed: value.result == null
|
||||
@@ -397,21 +372,12 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
|
||||
},
|
||||
icon: Icons.arrow_downward,
|
||||
),
|
||||
SizedBox(
|
||||
width: 2,
|
||||
),
|
||||
SizedBox(width: 2),
|
||||
IconButton.filledTonal(
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: controller.close,
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStatePropertyAll(
|
||||
EdgeInsets.all(0),
|
||||
),
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
size: 16,
|
||||
),
|
||||
style: IconButton.styleFrom(padding: EdgeInsets.zero),
|
||||
icon: Icon(Icons.close, size: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -421,13 +387,7 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
|
||||
if (isMobileView) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
bar,
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
_buildFindInput(context, value),
|
||||
],
|
||||
children: [bar, SizedBox(height: 4), _buildFindInput(context, value)],
|
||||
);
|
||||
}
|
||||
return bar;
|
||||
@@ -469,11 +429,9 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
|
||||
controller.toggleRegex();
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -490,9 +448,7 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
|
||||
style: context.textTheme.bodyMedium,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 12),
|
||||
),
|
||||
onSubmitted: (_) {
|
||||
onSubmitted();
|
||||
@@ -521,27 +477,18 @@ class FindPanel extends StatelessWidget implements PreferredSizeWidget {
|
||||
: IconButton(
|
||||
onPressed: onPressed,
|
||||
padding: EdgeInsets.all(2),
|
||||
icon: Text(
|
||||
text,
|
||||
style: context.textTheme.bodySmall,
|
||||
),
|
||||
icon: Text(text, style: context.textTheme.bodySmall),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIconButton({
|
||||
required IconData icon,
|
||||
VoidCallback? onPressed,
|
||||
}) {
|
||||
Widget _buildIconButton({required IconData icon, VoidCallback? onPressed}) {
|
||||
return IconButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: onPressed,
|
||||
style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.all(0))),
|
||||
icon: Icon(
|
||||
icon,
|
||||
size: 16,
|
||||
),
|
||||
style: IconButton.styleFrom(padding: EdgeInsets.all(0)),
|
||||
icon: Icon(icon, size: 16),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -575,7 +522,7 @@ class ContextMenuControllerImpl implements SelectionToolbarController {
|
||||
builder: (context) => CodeEditorTapRegion(
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: controller,
|
||||
builder: (_, __, child) {
|
||||
builder: (_, _, child) {
|
||||
final isNotEmpty = controller.selectedText.isNotEmpty;
|
||||
final isAllSelected = controller.isAllSelected;
|
||||
final hasSelected = controller.selectedText.isNotEmpty;
|
||||
@@ -608,25 +555,25 @@ class ContextMenuControllerImpl implements SelectionToolbarController {
|
||||
return TextSelectionToolbar(
|
||||
anchorAbove: anchors.primaryAnchor,
|
||||
anchorBelow: anchors.secondaryAnchor ?? Offset.zero,
|
||||
children: menus.asMap().entries.map(
|
||||
(MapEntry<int, PopupMenuItemData> entry) {
|
||||
return TextSelectionToolbarTextButton(
|
||||
padding: TextSelectionToolbarTextButton.getPadding(
|
||||
entry.key,
|
||||
menus.length,
|
||||
),
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
onPressed: () {
|
||||
if (entry.value.onPressed == null) {
|
||||
return;
|
||||
}
|
||||
entry.value.onPressed!();
|
||||
_removeOverLayEntry();
|
||||
},
|
||||
child: Text(entry.value.label),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
children: menus.asMap().entries.map((
|
||||
MapEntry<int, PopupMenuItemData> entry,
|
||||
) {
|
||||
return TextSelectionToolbarTextButton(
|
||||
padding: TextSelectionToolbarTextButton.getPadding(
|
||||
entry.key,
|
||||
menus.length,
|
||||
),
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
onPressed: () {
|
||||
if (entry.value.onPressed == null) {
|
||||
return;
|
||||
}
|
||||
entry.value.onPressed!();
|
||||
_removeOverLayEntry();
|
||||
},
|
||||
child: Text(entry.value.label),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -662,8 +609,12 @@ class _NoInputBorder extends InputBorder {
|
||||
}
|
||||
|
||||
@override
|
||||
void paintInterior(Canvas canvas, Rect rect, Paint paint,
|
||||
{TextDirection? textDirection}) {
|
||||
void paintInterior(
|
||||
Canvas canvas,
|
||||
Rect rect,
|
||||
Paint paint, {
|
||||
TextDirection? textDirection,
|
||||
}) {
|
||||
canvas.drawRect(rect, paint);
|
||||
}
|
||||
|
||||
@@ -697,10 +648,7 @@ class _ImportOptionsDialogState extends State<_ImportOptionsDialog> {
|
||||
Widget build(BuildContext context) {
|
||||
return CommonDialog(
|
||||
title: appLocalizations.import,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 16,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
|
||||
child: Wrap(
|
||||
children: [
|
||||
ListItem(
|
||||
@@ -714,7 +662,7 @@ class _ImportOptionsDialogState extends State<_ImportOptionsDialog> {
|
||||
_handleOnTab(ImportOption.file);
|
||||
},
|
||||
title: Text(appLocalizations.importFile),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/manager/app_manager.dart';
|
||||
import 'package:fl_clash/providers/providers.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
@@ -15,88 +16,86 @@ class HomePage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return HomeBackScope(
|
||||
child: Material(
|
||||
color: context.colorScheme.surface,
|
||||
child: Consumer(
|
||||
builder: (context, ref, __) {
|
||||
final state = ref.watch(navigationStateProvider);
|
||||
final isMobile = state.viewMode == ViewMode.mobile;
|
||||
final navigationItems = state.navigationItems;
|
||||
final pageView = _HomePageView(pageBuilder: (_, index) {
|
||||
final navigationItem = state.navigationItems[index];
|
||||
final navigationView = navigationItem.builder(context);
|
||||
final view = isMobile
|
||||
? KeepScope(
|
||||
keep: navigationItem.keep,
|
||||
child: navigationView,
|
||||
)
|
||||
: KeepScope(
|
||||
keep: navigationItem.keep,
|
||||
child: Navigator(
|
||||
onGenerateRoute: (_) {
|
||||
return CommonRoute(
|
||||
builder: (_) => navigationView,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
return view;
|
||||
});
|
||||
final currentIndex = state.currentIndex;
|
||||
final bottomNavigationBar = NavigationBarTheme(
|
||||
data: _NavigationBarDefaultsM3(context),
|
||||
child: NavigationBar(
|
||||
destinations: navigationItems
|
||||
.map(
|
||||
(e) => NavigationDestination(
|
||||
icon: e.icon,
|
||||
label: Intl.message(e.label.name),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (index) {
|
||||
globalState.appController.toPage(
|
||||
navigationItems[index].label,
|
||||
return HomeBackScopeContainer(
|
||||
child: AppSidebarContainer(
|
||||
child: Material(
|
||||
color: context.colorScheme.surface,
|
||||
child: Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final state = ref.watch(navigationStateProvider);
|
||||
final isMobile = state.viewMode == ViewMode.mobile;
|
||||
final navigationItems = state.navigationItems;
|
||||
final pageView = _HomePageView(
|
||||
pageBuilder: (_, index) {
|
||||
final navigationItem = state.navigationItems[index];
|
||||
final navigationView = navigationItem.builder(context);
|
||||
final view = KeepScope(
|
||||
keep: navigationItem.keep,
|
||||
child: isMobile
|
||||
? navigationView
|
||||
: Navigator(
|
||||
pages: [MaterialPage(child: navigationView)],
|
||||
onDidRemovePage: (_) {},
|
||||
),
|
||||
);
|
||||
return view;
|
||||
},
|
||||
selectedIndex: currentIndex,
|
||||
),
|
||||
);
|
||||
if (isMobile) {
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: globalState.appState.systemUiOverlayStyle.copyWith(
|
||||
systemNavigationBarColor:
|
||||
context.colorScheme.surfaceContainer,
|
||||
);
|
||||
final currentIndex = state.currentIndex;
|
||||
final bottomNavigationBar = NavigationBarTheme(
|
||||
data: _NavigationBarDefaultsM3(context),
|
||||
child: NavigationBar(
|
||||
destinations: navigationItems
|
||||
.map(
|
||||
(e) => NavigationDestination(
|
||||
icon: e.icon,
|
||||
label: Intl.message(e.label.name),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onDestinationSelected: (index) {
|
||||
globalState.appController.toPage(
|
||||
navigationItems[index].label,
|
||||
);
|
||||
},
|
||||
selectedIndex: currentIndex,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: MediaQuery.removePadding(
|
||||
removeTop: false,
|
||||
removeBottom: true,
|
||||
);
|
||||
if (isMobile) {
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: globalState.appState.systemUiOverlayStyle.copyWith(
|
||||
systemNavigationBarColor:
|
||||
context.colorScheme.surfaceContainer,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: MediaQuery.removePadding(
|
||||
removeTop: false,
|
||||
removeBottom: true,
|
||||
removeLeft: true,
|
||||
removeRight: true,
|
||||
context: context,
|
||||
child: pageView,
|
||||
),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: false,
|
||||
removeLeft: true,
|
||||
removeRight: true,
|
||||
context: context,
|
||||
child: pageView,
|
||||
child: bottomNavigationBar,
|
||||
),
|
||||
),
|
||||
MediaQuery.removePadding(
|
||||
removeTop: true,
|
||||
removeBottom: false,
|
||||
removeLeft: true,
|
||||
removeRight: true,
|
||||
context: context,
|
||||
child: bottomNavigationBar,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return pageView;
|
||||
}
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return pageView;
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -106,9 +105,7 @@ class HomePage extends StatelessWidget {
|
||||
class _HomePageView extends ConsumerStatefulWidget {
|
||||
final IndexedWidgetBuilder pageBuilder;
|
||||
|
||||
const _HomePageView({
|
||||
required this.pageBuilder,
|
||||
});
|
||||
const _HomePageView({required this.pageBuilder});
|
||||
|
||||
@override
|
||||
ConsumerState createState() => _HomePageViewState();
|
||||
@@ -120,9 +117,7 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
_pageController = PageController(
|
||||
initialPage: _pageIndex,
|
||||
);
|
||||
_pageController = PageController(initialPage: _pageIndex);
|
||||
ref.listenManual(currentPageLabelProvider, (prev, next) {
|
||||
if (prev != next) {
|
||||
_toPage(next);
|
||||
@@ -142,8 +137,10 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _toPage(PageLabel pageLabel,
|
||||
[bool ignoreAnimateTo = false]) async {
|
||||
Future<void> _toPage(
|
||||
PageLabel pageLabel, [
|
||||
bool ignoreAnimateTo = false,
|
||||
]) async {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
@@ -178,8 +175,9 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final itemCount = ref.watch(currentNavigationItemsStateProvider
|
||||
.select((state) => state.value.length));
|
||||
final itemCount = ref.watch(
|
||||
currentNavigationItemsStateProvider.select((state) => state.value.length),
|
||||
);
|
||||
return PageView.builder(
|
||||
controller: _pageController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
@@ -193,11 +191,11 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
|
||||
|
||||
class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
|
||||
_NavigationBarDefaultsM3(this.context)
|
||||
: super(
|
||||
height: 80.0,
|
||||
elevation: 3.0,
|
||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||
);
|
||||
: super(
|
||||
height: 80.0,
|
||||
elevation: 3.0,
|
||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||
);
|
||||
|
||||
final BuildContext context;
|
||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||
@@ -220,8 +218,8 @@ class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
|
||||
color: states.contains(WidgetState.disabled)
|
||||
? _colors.onSurfaceVariant.opacity38
|
||||
: states.contains(WidgetState.selected)
|
||||
? _colors.onSecondaryContainer
|
||||
: _colors.onSurfaceVariant,
|
||||
? _colors.onSecondaryContainer
|
||||
: _colors.onSurfaceVariant,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -237,37 +235,38 @@ class _NavigationBarDefaultsM3 extends NavigationBarThemeData {
|
||||
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||
final TextStyle style = _textTheme.labelMedium!;
|
||||
return style.apply(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: states.contains(WidgetState.disabled)
|
||||
? _colors.onSurfaceVariant.opacity38
|
||||
: states.contains(WidgetState.selected)
|
||||
? _colors.onSurface
|
||||
: _colors.onSurfaceVariant);
|
||||
overflow: TextOverflow.ellipsis,
|
||||
color: states.contains(WidgetState.disabled)
|
||||
? _colors.onSurfaceVariant.opacity38
|
||||
: states.contains(WidgetState.selected)
|
||||
? _colors.onSurface
|
||||
: _colors.onSurfaceVariant,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class HomeBackScope extends StatelessWidget {
|
||||
class HomeBackScopeContainer extends ConsumerWidget {
|
||||
final Widget child;
|
||||
|
||||
const HomeBackScope({super.key, required this.child});
|
||||
const HomeBackScopeContainer({super.key, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (system.isAndroid) {
|
||||
return CommonPopScope(
|
||||
onPop: () async {
|
||||
final canPop = Navigator.canPop(context);
|
||||
if (canPop) {
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
await globalState.appController.handleBackOrExit();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
Widget build(BuildContext context, ref) {
|
||||
return CommonPopScope(
|
||||
onPop: (context) async {
|
||||
final pageLabel = ref.read(currentPageLabelProvider);
|
||||
final realContext =
|
||||
GlobalObjectKey(pageLabel).currentContext ?? context;
|
||||
final canPop = Navigator.canPop(realContext);
|
||||
if (canPop) {
|
||||
Navigator.of(realContext).pop();
|
||||
} else {
|
||||
await globalState.appController.handleBackOrExit();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,7 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
|
||||
void _handleBarcode(BarcodeCapture barcodeCapture) {
|
||||
final barcode = barcodeCapture.barcodes.first;
|
||||
if (barcode.type == BarcodeType.url) {
|
||||
Navigator.pop<String>(
|
||||
context,
|
||||
barcode.rawValue,
|
||||
);
|
||||
Navigator.pop<String>(context, barcode.rawValue);
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
@@ -78,16 +75,14 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
|
||||
scanWindow: scanWindow,
|
||||
),
|
||||
),
|
||||
CustomPaint(
|
||||
painter: ScannerOverlay(scanWindow: scanWindow),
|
||||
),
|
||||
CustomPaint(painter: ScannerOverlay(scanWindow: scanWindow)),
|
||||
AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
automaticallyImplyLeading: false,
|
||||
leading: IconButton(
|
||||
style: const ButtonStyle(
|
||||
iconSize: WidgetStatePropertyAll(32),
|
||||
foregroundColor: WidgetStatePropertyAll(Colors.white),
|
||||
style: IconButton.styleFrom(
|
||||
iconSize: 32,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
@@ -121,18 +116,16 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
|
||||
child: IconButton(
|
||||
color: Colors.white,
|
||||
icon: icon,
|
||||
style: ButtonStyle(
|
||||
foregroundColor:
|
||||
const WidgetStatePropertyAll(Colors.white),
|
||||
backgroundColor:
|
||||
WidgetStatePropertyAll(backgroundColor),
|
||||
style: IconButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: backgroundColor,
|
||||
),
|
||||
onPressed: () => controller.toggleTorch(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
@@ -140,9 +133,9 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: IconButton(
|
||||
color: Colors.white,
|
||||
style: const ButtonStyle(
|
||||
foregroundColor: WidgetStatePropertyAll(Colors.white),
|
||||
backgroundColor: WidgetStatePropertyAll(Colors.grey),
|
||||
style: IconButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Colors.grey,
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
iconSize: 32.0,
|
||||
@@ -166,10 +159,7 @@ class _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
class ScannerOverlay extends CustomPainter {
|
||||
const ScannerOverlay({
|
||||
required this.scanWindow,
|
||||
this.borderRadius = 12.0,
|
||||
});
|
||||
const ScannerOverlay({required this.scanWindow, this.borderRadius = 12.0});
|
||||
|
||||
final Rect scanWindow;
|
||||
final double borderRadius;
|
||||
@@ -179,8 +169,8 @@ class ScannerOverlay extends CustomPainter {
|
||||
final backgroundPath = Path()..addRect(Rect.largest);
|
||||
|
||||
final cutoutPath = Path()
|
||||
..addRRect(
|
||||
RRect.fromRectAndCorners(
|
||||
..addRSuperellipse(
|
||||
RSuperellipse.fromRectAndCorners(
|
||||
scanWindow,
|
||||
topLeft: Radius.circular(borderRadius),
|
||||
topRight: Radius.circular(borderRadius),
|
||||
@@ -205,7 +195,7 @@ class ScannerOverlay extends CustomPainter {
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 4.0;
|
||||
|
||||
final borderRect = RRect.fromRectAndCorners(
|
||||
final border = RSuperellipse.fromRectAndCorners(
|
||||
scanWindow,
|
||||
topLeft: Radius.circular(borderRadius),
|
||||
topRight: Radius.circular(borderRadius),
|
||||
@@ -214,7 +204,7 @@ class ScannerOverlay extends CustomPainter {
|
||||
);
|
||||
|
||||
canvas.drawPath(backgroundWithCutout, backgroundPaint);
|
||||
canvas.drawRRect(borderRect, borderPaint);
|
||||
canvas.drawRSuperellipse(border, borderPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/common/app_localizations.dart';
|
||||
import 'package:fl_clash/common/constant.dart';
|
||||
import 'package:fl_clash/common/system.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class App {
|
||||
static App? _instance;
|
||||
@@ -15,19 +16,13 @@ class App {
|
||||
Function()? onExit;
|
||||
|
||||
App._internal() {
|
||||
methodChannel = const MethodChannel('app');
|
||||
methodChannel = const MethodChannel('$packageName/app');
|
||||
methodChannel.setMethodCallHandler((call) async {
|
||||
switch (call.method) {
|
||||
case 'exit':
|
||||
if (onExit != null) {
|
||||
await onExit!();
|
||||
}
|
||||
case 'getText':
|
||||
try {
|
||||
return Intl.message(call.arguments as String);
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
default:
|
||||
throw MissingPluginException();
|
||||
}
|
||||
@@ -44,40 +39,48 @@ class App {
|
||||
}
|
||||
|
||||
Future<List<Package>> getPackages() async {
|
||||
final packagesString =
|
||||
await methodChannel.invokeMethod<String>('getPackages');
|
||||
final packagesString = await methodChannel.invokeMethod<String>(
|
||||
'getPackages',
|
||||
);
|
||||
return Isolate.run<List<Package>>(() {
|
||||
final List<dynamic> packagesRaw =
|
||||
packagesString != null ? json.decode(packagesString) : [];
|
||||
final List<dynamic> packagesRaw = packagesString != null
|
||||
? json.decode(packagesString)
|
||||
: [];
|
||||
return packagesRaw.map((e) => Package.fromJson(e)).toSet().toList();
|
||||
});
|
||||
}
|
||||
|
||||
Future<List<String>> getChinaPackageNames() async {
|
||||
final packageNamesString =
|
||||
await methodChannel.invokeMethod<String>('getChinaPackageNames');
|
||||
final packageNamesString = await methodChannel.invokeMethod<String>(
|
||||
'getChinaPackageNames',
|
||||
);
|
||||
return Isolate.run<List<String>>(() {
|
||||
final List<dynamic> packageNamesRaw =
|
||||
packageNamesString != null ? json.decode(packageNamesString) : [];
|
||||
final List<dynamic> packageNamesRaw = packageNamesString != null
|
||||
? json.decode(packageNamesString)
|
||||
: [];
|
||||
return packageNamesRaw.map((e) => e.toString()).toList();
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool?> requestNotificationsPermission() async {
|
||||
return await methodChannel.invokeMethod<bool>(
|
||||
'requestNotificationsPermission',
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> openFile(String path) async {
|
||||
return await methodChannel.invokeMethod<bool>('openFile', {
|
||||
'path': path,
|
||||
}) ??
|
||||
return await methodChannel.invokeMethod<bool>('openFile', {'path': path}) ??
|
||||
false;
|
||||
}
|
||||
|
||||
Future<ImageProvider?> getPackageIcon(String packageName) async {
|
||||
final base64 = await methodChannel.invokeMethod<String>('getPackageIcon', {
|
||||
final path = await methodChannel.invokeMethod<String>('getPackageIcon', {
|
||||
'packageName': packageName,
|
||||
});
|
||||
if (base64 == null) {
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
return MemoryImage(base64Decode(base64));
|
||||
return FileImage(File(path));
|
||||
}
|
||||
|
||||
Future<bool?> tip(String? message) async {
|
||||
|
||||
@@ -2,45 +2,120 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:fl_clash/common/constant.dart';
|
||||
import 'package:fl_clash/common/system.dart';
|
||||
import 'package:fl_clash/models/common.dart';
|
||||
import 'package:fl_clash/models/core.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../clash/lib.dart';
|
||||
abstract mixin class ServiceListener {
|
||||
void onServiceEvent(CoreEvent event) {}
|
||||
|
||||
void onServiceCrash(String message) {}
|
||||
}
|
||||
|
||||
class Service {
|
||||
static Service? _instance;
|
||||
late MethodChannel methodChannel;
|
||||
ReceivePort? receiver;
|
||||
|
||||
Service._internal() {
|
||||
methodChannel = const MethodChannel('service');
|
||||
}
|
||||
final ObserverList<ServiceListener> _listeners =
|
||||
ObserverList<ServiceListener>();
|
||||
|
||||
factory Service() {
|
||||
_instance ??= Service._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
Future<bool?> init() async {
|
||||
return await methodChannel.invokeMethod<bool>('init');
|
||||
}
|
||||
|
||||
Future<bool?> destroy() async {
|
||||
return await methodChannel.invokeMethod<bool>('destroy');
|
||||
}
|
||||
|
||||
Future<bool?> startVpn() async {
|
||||
final options = await clashLib?.getAndroidVpnOptions();
|
||||
return await methodChannel.invokeMethod<bool>('startVpn', {
|
||||
'data': json.encode(options),
|
||||
Service._internal() {
|
||||
methodChannel = const MethodChannel('$packageName/service');
|
||||
methodChannel.setMethodCallHandler((call) async {
|
||||
switch (call.method) {
|
||||
case 'getVpnOptions':
|
||||
return handleGetVpnOptions();
|
||||
case 'event':
|
||||
final data = call.arguments as String? ?? '';
|
||||
final result = ActionResult.fromJson(json.decode(data));
|
||||
for (final listener in _listeners) {
|
||||
listener.onServiceEvent(CoreEvent.fromJson(result.data));
|
||||
}
|
||||
break;
|
||||
case 'crash':
|
||||
final message = call.arguments as String? ?? '';
|
||||
for (final listener in _listeners) {
|
||||
listener.onServiceCrash(message);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw MissingPluginException();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool?> stopVpn() async {
|
||||
return await methodChannel.invokeMethod<bool>('stopVpn');
|
||||
Future<ActionResult?> invokeAction(Action action) async {
|
||||
final data = await methodChannel.invokeMethod<String>(
|
||||
'invokeAction',
|
||||
json.encode(action),
|
||||
);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
return ActionResult.fromJson(json.decode(data));
|
||||
}
|
||||
|
||||
String handleGetVpnOptions() {
|
||||
return json.encode(globalState.getVpnOptions());
|
||||
}
|
||||
|
||||
Future<bool> start() async {
|
||||
return await methodChannel.invokeMethod<bool>('start') ?? false;
|
||||
}
|
||||
|
||||
Future<bool> stop() async {
|
||||
return await methodChannel.invokeMethod<bool>('stop') ?? false;
|
||||
}
|
||||
|
||||
Future<String> syncAndroidState(AndroidState state) async {
|
||||
return await methodChannel.invokeMethod<String>(
|
||||
'syncState',
|
||||
json.encode(state),
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
Future<String> init() async {
|
||||
return await methodChannel.invokeMethod<String>(
|
||||
'init',
|
||||
!globalState.isService,
|
||||
) ??
|
||||
'';
|
||||
}
|
||||
|
||||
Future<bool> shutdown() async {
|
||||
return await methodChannel.invokeMethod<bool>('shutdown') ?? true;
|
||||
}
|
||||
|
||||
Future<DateTime?> getRunTime() async {
|
||||
final ms = await methodChannel.invokeMethod<int>('getRunTime') ?? 0;
|
||||
if (ms == 0) {
|
||||
return null;
|
||||
}
|
||||
return DateTime.fromMillisecondsSinceEpoch(ms);
|
||||
}
|
||||
|
||||
bool get hasListeners {
|
||||
return _listeners.isNotEmpty;
|
||||
}
|
||||
|
||||
void addListener(ServiceListener listener) {
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
void removeListener(ServiceListener listener) {
|
||||
_listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
Service? get service =>
|
||||
system.isAndroid && !globalState.isService ? Service() : null;
|
||||
Service? get service => system.isAndroid ? Service() : null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/common/constant.dart';
|
||||
import 'package:fl_clash/common/system.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -13,7 +14,7 @@ abstract mixin class TileListener {
|
||||
}
|
||||
|
||||
class Tile {
|
||||
final MethodChannel _channel = const MethodChannel('tile');
|
||||
final MethodChannel _channel = const MethodChannel('$packageName/tile');
|
||||
|
||||
Tile._() {
|
||||
_channel.setMethodCallHandler(_methodCallHandler);
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
abstract mixin class VpnListener {
|
||||
void onDnsChanged(String dns) {}
|
||||
}
|
||||
|
||||
class Vpn {
|
||||
static Vpn? _instance;
|
||||
late MethodChannel methodChannel;
|
||||
FutureOr<String> Function()? handleGetStartForegroundParams;
|
||||
|
||||
Vpn._internal() {
|
||||
methodChannel = const MethodChannel('vpn');
|
||||
methodChannel.setMethodCallHandler((call) async {
|
||||
switch (call.method) {
|
||||
case 'gc':
|
||||
clashCore.requestGc();
|
||||
case 'getStartForegroundParams':
|
||||
if (handleGetStartForegroundParams != null) {
|
||||
return await handleGetStartForegroundParams!();
|
||||
}
|
||||
return '';
|
||||
case 'status':
|
||||
return clashLibHandler?.getRunTime() != null;
|
||||
default:
|
||||
for (final VpnListener listener in _listeners) {
|
||||
switch (call.method) {
|
||||
case 'dnsChanged':
|
||||
final dns = call.arguments as String;
|
||||
listener.onDnsChanged(dns);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
factory Vpn() {
|
||||
_instance ??= Vpn._internal();
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
final ObserverList<VpnListener> _listeners = ObserverList<VpnListener>();
|
||||
|
||||
Future<bool?> start(AndroidVpnOptions options) async {
|
||||
return await methodChannel.invokeMethod<bool>('start', {
|
||||
'data': json.encode(options),
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool?> stop() async {
|
||||
return await methodChannel.invokeMethod<bool>('stop');
|
||||
}
|
||||
|
||||
void addListener(VpnListener listener) {
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
void removeListener(VpnListener listener) {
|
||||
_listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
Vpn? get vpn => globalState.isService ? Vpn() : null;
|
||||
@@ -3,7 +3,6 @@ import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/state.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'generated/app.g.dart';
|
||||
@@ -17,9 +16,7 @@ class RealTunEnable extends _$RealTunEnable with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
realTunEnable: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(realTunEnable: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,14 +28,12 @@ class Logs extends _$Logs with AutoDisposeNotifierMixin {
|
||||
}
|
||||
|
||||
void addLog(Log value) {
|
||||
state = state.copyWith()..add(value);
|
||||
this.value = state.copyWith()..add(value);
|
||||
}
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
logs: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(logs: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,18 +46,19 @@ class Requests extends _$Requests with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
requests: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(requests: value);
|
||||
}
|
||||
|
||||
void addRequest(TrackerInfo value) {
|
||||
state = state.copyWith()..add(value);
|
||||
this.value = state.copyWith()..add(value);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Providers extends _$Providers with AutoDisposeNotifierMixin {
|
||||
class Providers extends _$Providers with AnyNotifierMixin {
|
||||
@override
|
||||
List<ExternalProvider> get value => globalState.appState.providers;
|
||||
|
||||
@override
|
||||
List<ExternalProvider> build() {
|
||||
return globalState.appState.providers;
|
||||
@@ -70,16 +66,18 @@ class Providers extends _$Providers with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
providers: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(providers: value);
|
||||
}
|
||||
|
||||
void setProvider(ExternalProvider? provider) {
|
||||
if (!ref.mounted) {
|
||||
return;
|
||||
}
|
||||
if (provider == null) return;
|
||||
final index = state.indexWhere((item) => item.name == provider.name);
|
||||
final index = value.indexWhere((item) => item.name == provider.name);
|
||||
if (index == -1) return;
|
||||
state = List.from(state)..[index] = provider;
|
||||
final newState = List<ExternalProvider>.from(value)..[index] = provider;
|
||||
value = newState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,9 +90,7 @@ class Packages extends _$Packages with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
packages: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(packages: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,13 +104,11 @@ class SystemBrightness extends _$SystemBrightness
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
brightness: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(brightness: value);
|
||||
}
|
||||
|
||||
void setState(Brightness value) {
|
||||
state = value;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,17 +121,15 @@ class Traffics extends _$Traffics with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
traffics: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(traffics: value);
|
||||
}
|
||||
|
||||
void addTraffic(Traffic value) {
|
||||
state = state.copyWith()..add(value);
|
||||
this.value = state.copyWith()..add(value);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
state = state.copyWith()..clear();
|
||||
value = state.copyWith()..clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +142,7 @@ class TotalTraffic extends _$TotalTraffic with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
totalTraffic: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(totalTraffic: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,17 +155,7 @@ class LocalIp extends _$LocalIp with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
localIp: value,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
set state(String? value) {
|
||||
super.state = value;
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
localIp: state,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(localIp: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,13 +168,7 @@ class RunTime extends _$RunTime with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
runTime: value,
|
||||
);
|
||||
}
|
||||
|
||||
bool get isStart {
|
||||
return state != null;
|
||||
globalState.appState = globalState.appState.copyWith(runTime: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,14 +181,21 @@ class ViewSize extends _$ViewSize with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
viewSize: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(viewSize: value);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class SideWidth extends _$SideWidth with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
double build() {
|
||||
return globalState.appState.sideWidth;
|
||||
}
|
||||
|
||||
ViewMode get viewMode => utils.getViewMode(state.width);
|
||||
|
||||
bool get isMobileView => viewMode == ViewMode.mobile;
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(sideWidth: value);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@@ -246,9 +227,7 @@ class Init extends _$Init with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
isInit: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(isInit: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,9 +241,7 @@ class CurrentPageLabel extends _$CurrentPageLabel
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
pageLabel: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(pageLabel: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,9 +254,7 @@ class SortNum extends _$SortNum with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
sortNum: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(sortNum: value);
|
||||
}
|
||||
|
||||
int add() => state++;
|
||||
@@ -294,9 +269,7 @@ class CheckIpNum extends _$CheckIpNum with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
checkIpNum: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(checkIpNum: value);
|
||||
}
|
||||
|
||||
int add() => state++;
|
||||
@@ -311,9 +284,7 @@ class BackBlock extends _$BackBlock with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
backBlock: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(backBlock: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,9 +297,7 @@ class Loading extends _$Loading with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
loading: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(loading: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,9 +310,7 @@ class Version extends _$Version with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
version: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(version: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,9 +323,7 @@ class Groups extends _$Groups with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
groups: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(groups: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,9 +336,7 @@ class DelayDataSource extends _$DelayDataSource with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
delayMap: value,
|
||||
);
|
||||
globalState.appState = globalState.appState.copyWith(delayMap: value);
|
||||
}
|
||||
|
||||
void setDelay(Delay delay) {
|
||||
@@ -383,7 +346,7 @@ class DelayDataSource extends _$DelayDataSource with AutoDisposeNotifierMixin {
|
||||
newDelayMap[delay.url] = {};
|
||||
}
|
||||
newDelayMap[delay.url]![delay.name] = delay.value;
|
||||
state = newDelayMap;
|
||||
value = newDelayMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,3 +366,57 @@ class SystemUiOverlayStyleState extends _$SystemUiOverlayStyleState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ProfileOverrideState extends _$ProfileOverrideState
|
||||
with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
ProfileOverrideModel? build() {
|
||||
return globalState.appState.profileOverrideModel;
|
||||
}
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(
|
||||
profileOverrideModel: value,
|
||||
);
|
||||
}
|
||||
|
||||
void updateState(
|
||||
ProfileOverrideModel? Function(ProfileOverrideModel? state) builder,
|
||||
) {
|
||||
final value = builder(state);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@Riverpod(name: 'coreStatusProvider')
|
||||
class _CoreStatus extends _$CoreStatus with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
CoreStatus build() {
|
||||
return globalState.appState.coreStatus;
|
||||
}
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(coreStatus: value);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class QueryMap extends _$QueryMap with AutoDisposeNotifierMixin {
|
||||
@override
|
||||
Map<QueryTag, String> build() => globalState.appState.queryMap;
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.appState = globalState.appState.copyWith(queryMap: value);
|
||||
}
|
||||
|
||||
void updateQuery(QueryTag tag, String value) {
|
||||
this.value = Map.from(globalState.appState.queryMap)..[tag] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,11 @@ class AppSetting extends _$AppSetting with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
appSetting: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(appSetting: value);
|
||||
}
|
||||
|
||||
void updateState(AppSettingProps Function(AppSettingProps state) builder) {
|
||||
state = builder(state);
|
||||
value = builder(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +31,11 @@ class WindowSetting extends _$WindowSetting with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
windowProps: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(windowProps: value);
|
||||
}
|
||||
|
||||
void updateState(WindowProps Function(WindowProps state) builder) {
|
||||
state = builder(state);
|
||||
value = builder(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +48,11 @@ class VpnSetting extends _$VpnSetting with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
vpnProps: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(vpnProps: value);
|
||||
}
|
||||
|
||||
void updateState(VpnProps Function(VpnProps state) builder) {
|
||||
state = builder(state);
|
||||
value = builder(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,13 +65,11 @@ class NetworkSetting extends _$NetworkSetting with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
networkProps: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(networkProps: value);
|
||||
}
|
||||
|
||||
void updateState(NetworkProps Function(NetworkProps state) builder) {
|
||||
state = builder(state);
|
||||
value = builder(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,13 +82,11 @@ class ThemeSetting extends _$ThemeSetting with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
themeProps: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(themeProps: value);
|
||||
}
|
||||
|
||||
void updateState(ThemeProps Function(ThemeProps state) builder) {
|
||||
state = builder(state);
|
||||
value = builder(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,15 +99,15 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
profiles: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(profiles: value);
|
||||
}
|
||||
|
||||
String? _getLabel(String? label, String id) {
|
||||
final realLabel = label ?? id;
|
||||
final hasDup = state.indexWhere(
|
||||
(element) => element.label == realLabel && element.id != id) !=
|
||||
final hasDup =
|
||||
state.indexWhere(
|
||||
(element) => element.label == realLabel && element.id != id,
|
||||
) !=
|
||||
-1;
|
||||
if (hasDup) {
|
||||
return _getLabel(utils.getOverwriteLabel(realLabel), id);
|
||||
@@ -128,8 +118,9 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
|
||||
|
||||
void setProfile(Profile profile) {
|
||||
final List<Profile> profilesTemp = List.from(state);
|
||||
final index =
|
||||
profilesTemp.indexWhere((element) => element.id == profile.id);
|
||||
final index = profilesTemp.indexWhere(
|
||||
(element) => element.id == profile.id,
|
||||
);
|
||||
final updateProfile = profile.copyWith(
|
||||
label: _getLabel(profile.label, profile.id),
|
||||
);
|
||||
@@ -138,21 +129,23 @@ class Profiles extends _$Profiles with AutoDisposeNotifierMixin {
|
||||
} else {
|
||||
profilesTemp[index] = updateProfile;
|
||||
}
|
||||
state = profilesTemp;
|
||||
value = profilesTemp;
|
||||
}
|
||||
|
||||
void updateProfile(
|
||||
String profileId, Profile Function(Profile profile) builder) {
|
||||
String profileId,
|
||||
Profile Function(Profile profile) builder,
|
||||
) {
|
||||
final List<Profile> profilesTemp = List.from(state);
|
||||
final index = profilesTemp.indexWhere((element) => element.id == profileId);
|
||||
if (index != -1) {
|
||||
profilesTemp[index] = builder(profilesTemp[index]);
|
||||
}
|
||||
state = profilesTemp;
|
||||
value = profilesTemp;
|
||||
}
|
||||
|
||||
void deleteProfileById(String id) {
|
||||
state = state.where((element) => element.id != id).toList();
|
||||
value = state.where((element) => element.id != id).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,9 +159,7 @@ class CurrentProfileId extends _$CurrentProfileId
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
currentProfileId: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(currentProfileId: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,13 +172,11 @@ class AppDAVSetting extends _$AppDAVSetting with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
dav: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(dav: value);
|
||||
}
|
||||
|
||||
void updateState(DAV? Function(DAV? state) builder) {
|
||||
state = builder(state);
|
||||
value = builder(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,9 +189,7 @@ class OverrideDns extends _$OverrideDns with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
overrideDns: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(overrideDns: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,9 +202,7 @@ class HotKeyActions extends _$HotKeyActions with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
hotKeyActions: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(hotKeyActions: value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,13 +216,11 @@ class ProxiesStyleSetting extends _$ProxiesStyleSetting
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
proxiesStyle: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(proxiesStyle: value);
|
||||
}
|
||||
|
||||
void updateState(ProxiesStyle Function(ProxiesStyle state) builder) {
|
||||
state = builder(state);
|
||||
value = builder(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,9 +233,7 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
scriptProps: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(scriptProps: value);
|
||||
}
|
||||
|
||||
void setScript(Script script) {
|
||||
@@ -263,15 +244,11 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
|
||||
} else {
|
||||
list.add(script);
|
||||
}
|
||||
state = state.copyWith(
|
||||
scripts: list,
|
||||
);
|
||||
value = state.copyWith(scripts: list);
|
||||
}
|
||||
|
||||
void setId(String id) {
|
||||
state = state.copyWith(
|
||||
currentId: state.currentId != id ? id : null,
|
||||
);
|
||||
value = state.copyWith(currentId: state.currentId != id ? id : null);
|
||||
}
|
||||
|
||||
void del(String id) {
|
||||
@@ -281,10 +258,7 @@ class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
|
||||
list.removeAt(index);
|
||||
}
|
||||
final nextId = id == state.currentId ? null : state.currentId;
|
||||
state = state.copyWith(
|
||||
scripts: list,
|
||||
currentId: nextId,
|
||||
);
|
||||
state = state.copyWith(scripts: list, currentId: nextId);
|
||||
}
|
||||
|
||||
bool isExits(String label) {
|
||||
@@ -305,13 +279,11 @@ class PatchClashConfig extends _$PatchClashConfig
|
||||
if (newState == null) {
|
||||
return;
|
||||
}
|
||||
state = newState;
|
||||
value = newState;
|
||||
}
|
||||
|
||||
@override
|
||||
onUpdate(value) {
|
||||
globalState.config = globalState.config.copyWith(
|
||||
patchClashConfig: value,
|
||||
);
|
||||
globalState.config = globalState.config.copyWith(patchClashConfig: value);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,208 +6,693 @@ part of '../config.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$appSettingHash() => r'13a93334e18b97f5d52eb3e05bbc7b0b8a5c453e';
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
/// See also [AppSetting].
|
||||
@ProviderFor(AppSetting)
|
||||
final appSettingProvider =
|
||||
AutoDisposeNotifierProvider<AppSetting, AppSettingProps>.internal(
|
||||
AppSetting.new,
|
||||
name: r'appSettingProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$appSettingHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const appSettingProvider = AppSettingProvider._();
|
||||
|
||||
typedef _$AppSetting = AutoDisposeNotifier<AppSettingProps>;
|
||||
String _$windowSettingHash() => r'9bf31c7e08fab84213f31e249270f9d730bdf711';
|
||||
final class AppSettingProvider
|
||||
extends $NotifierProvider<AppSetting, AppSettingProps> {
|
||||
const AppSettingProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'appSettingProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$appSettingHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
AppSetting create() => AppSetting();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(AppSettingProps value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<AppSettingProps>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$appSettingHash() => r'7ec7fbf146e690dea42cf854fa4452b2652d8a46';
|
||||
|
||||
abstract class _$AppSetting extends $Notifier<AppSettingProps> {
|
||||
AppSettingProps build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<AppSettingProps, AppSettingProps>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AppSettingProps, AppSettingProps>,
|
||||
AppSettingProps,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [WindowSetting].
|
||||
@ProviderFor(WindowSetting)
|
||||
final windowSettingProvider =
|
||||
AutoDisposeNotifierProvider<WindowSetting, WindowProps>.internal(
|
||||
WindowSetting.new,
|
||||
name: r'windowSettingProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$windowSettingHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const windowSettingProvider = WindowSettingProvider._();
|
||||
|
||||
typedef _$WindowSetting = AutoDisposeNotifier<WindowProps>;
|
||||
String _$vpnSettingHash() => r'3dae8b56504bfb906aca546c5a5389d79d259a5e';
|
||||
final class WindowSettingProvider
|
||||
extends $NotifierProvider<WindowSetting, WindowProps> {
|
||||
const WindowSettingProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'windowSettingProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$windowSettingHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
WindowSetting create() => WindowSetting();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(WindowProps value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<WindowProps>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$windowSettingHash() => r'fc0e5c4ec95a57a24e0e656fc2fab6f31add31e7';
|
||||
|
||||
abstract class _$WindowSetting extends $Notifier<WindowProps> {
|
||||
WindowProps build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<WindowProps, WindowProps>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<WindowProps, WindowProps>,
|
||||
WindowProps,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [VpnSetting].
|
||||
@ProviderFor(VpnSetting)
|
||||
final vpnSettingProvider =
|
||||
AutoDisposeNotifierProvider<VpnSetting, VpnProps>.internal(
|
||||
VpnSetting.new,
|
||||
name: r'vpnSettingProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$vpnSettingHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const vpnSettingProvider = VpnSettingProvider._();
|
||||
|
||||
typedef _$VpnSetting = AutoDisposeNotifier<VpnProps>;
|
||||
String _$networkSettingHash() => r'5a30d4cbfaba94cc29ad08dc1771ebb368b4ba14';
|
||||
final class VpnSettingProvider extends $NotifierProvider<VpnSetting, VpnProps> {
|
||||
const VpnSettingProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'vpnSettingProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$vpnSettingHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
VpnSetting create() => VpnSetting();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(VpnProps value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<VpnProps>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$vpnSettingHash() => r'1dad4881ae7bcec76678585ac7b84f820b2ca92b';
|
||||
|
||||
abstract class _$VpnSetting extends $Notifier<VpnProps> {
|
||||
VpnProps build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<VpnProps, VpnProps>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<VpnProps, VpnProps>,
|
||||
VpnProps,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [NetworkSetting].
|
||||
@ProviderFor(NetworkSetting)
|
||||
final networkSettingProvider =
|
||||
AutoDisposeNotifierProvider<NetworkSetting, NetworkProps>.internal(
|
||||
NetworkSetting.new,
|
||||
name: r'networkSettingProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$networkSettingHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const networkSettingProvider = NetworkSettingProvider._();
|
||||
|
||||
typedef _$NetworkSetting = AutoDisposeNotifier<NetworkProps>;
|
||||
String _$themeSettingHash() => r'0b5620b696d73260d94f63cbfb65857acd2000f0';
|
||||
final class NetworkSettingProvider
|
||||
extends $NotifierProvider<NetworkSetting, NetworkProps> {
|
||||
const NetworkSettingProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'networkSettingProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$networkSettingHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
NetworkSetting create() => NetworkSetting();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(NetworkProps value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<NetworkProps>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$networkSettingHash() => r'6ac5959ad478247fd60329221743cccc7a7d010b';
|
||||
|
||||
abstract class _$NetworkSetting extends $Notifier<NetworkProps> {
|
||||
NetworkProps build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<NetworkProps, NetworkProps>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<NetworkProps, NetworkProps>,
|
||||
NetworkProps,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [ThemeSetting].
|
||||
@ProviderFor(ThemeSetting)
|
||||
final themeSettingProvider =
|
||||
AutoDisposeNotifierProvider<ThemeSetting, ThemeProps>.internal(
|
||||
ThemeSetting.new,
|
||||
name: r'themeSettingProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$themeSettingHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const themeSettingProvider = ThemeSettingProvider._();
|
||||
|
||||
typedef _$ThemeSetting = AutoDisposeNotifier<ThemeProps>;
|
||||
String _$profilesHash() => r'3203cc7de88b91fff86b79c75c2cacd8116fffb7';
|
||||
final class ThemeSettingProvider
|
||||
extends $NotifierProvider<ThemeSetting, ThemeProps> {
|
||||
const ThemeSettingProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'themeSettingProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$themeSettingHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ThemeSetting create() => ThemeSetting();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(ThemeProps value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<ThemeProps>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$themeSettingHash() => r'0ddad89cb63fc2b2094dd82262c76d972c2def5c';
|
||||
|
||||
abstract class _$ThemeSetting extends $Notifier<ThemeProps> {
|
||||
ThemeProps build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<ThemeProps, ThemeProps>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<ThemeProps, ThemeProps>,
|
||||
ThemeProps,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [Profiles].
|
||||
@ProviderFor(Profiles)
|
||||
final profilesProvider =
|
||||
AutoDisposeNotifierProvider<Profiles, List<Profile>>.internal(
|
||||
Profiles.new,
|
||||
name: r'profilesProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$profilesHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const profilesProvider = ProfilesProvider._();
|
||||
|
||||
final class ProfilesProvider
|
||||
extends $NotifierProvider<Profiles, List<Profile>> {
|
||||
const ProfilesProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'profilesProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$profilesHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
Profiles create() => Profiles();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(List<Profile> value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<List<Profile>>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$profilesHash() => r'aad57222a4a0bd16f2c70f9eb8ba0053d1a26d0f';
|
||||
|
||||
abstract class _$Profiles extends $Notifier<List<Profile>> {
|
||||
List<Profile> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<List<Profile>, List<Profile>>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<List<Profile>, List<Profile>>,
|
||||
List<Profile>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(CurrentProfileId)
|
||||
const currentProfileIdProvider = CurrentProfileIdProvider._();
|
||||
|
||||
final class CurrentProfileIdProvider
|
||||
extends $NotifierProvider<CurrentProfileId, String?> {
|
||||
const CurrentProfileIdProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'currentProfileIdProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$currentProfileIdHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
CurrentProfileId create() => CurrentProfileId();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(String? value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<String?>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _$Profiles = AutoDisposeNotifier<List<Profile>>;
|
||||
String _$currentProfileIdHash() => r'0c3e324e751aac1164da479e1796e826615bdcbe';
|
||||
|
||||
/// See also [CurrentProfileId].
|
||||
@ProviderFor(CurrentProfileId)
|
||||
final currentProfileIdProvider =
|
||||
AutoDisposeNotifierProvider<CurrentProfileId, String?>.internal(
|
||||
CurrentProfileId.new,
|
||||
name: r'currentProfileIdProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$currentProfileIdHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
abstract class _$CurrentProfileId extends $Notifier<String?> {
|
||||
String? build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<String?, String?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<String?, String?>,
|
||||
String?,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _$CurrentProfileId = AutoDisposeNotifier<String?>;
|
||||
String _$appDAVSettingHash() => r'4bf293ac0d1fba157f60df920b7ffd5afefaab26';
|
||||
|
||||
/// See also [AppDAVSetting].
|
||||
@ProviderFor(AppDAVSetting)
|
||||
final appDAVSettingProvider =
|
||||
AutoDisposeNotifierProvider<AppDAVSetting, DAV?>.internal(
|
||||
AppDAVSetting.new,
|
||||
name: r'appDAVSettingProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$appDAVSettingHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const appDAVSettingProvider = AppDAVSettingProvider._();
|
||||
|
||||
final class AppDAVSettingProvider
|
||||
extends $NotifierProvider<AppDAVSetting, DAV?> {
|
||||
const AppDAVSettingProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'appDAVSettingProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$appDAVSettingHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
AppDAVSetting create() => AppDAVSetting();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(DAV? value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<DAV?>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$appDAVSettingHash() => r'fa8de5d89d7a11f34f3f8e20b71cf164e5e11888';
|
||||
|
||||
abstract class _$AppDAVSetting extends $Notifier<DAV?> {
|
||||
DAV? build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<DAV?, DAV?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<DAV?, DAV?>,
|
||||
DAV?,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(OverrideDns)
|
||||
const overrideDnsProvider = OverrideDnsProvider._();
|
||||
|
||||
final class OverrideDnsProvider extends $NotifierProvider<OverrideDns, bool> {
|
||||
const OverrideDnsProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'overrideDnsProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$overrideDnsHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
OverrideDns create() => OverrideDns();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(bool value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<bool>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _$AppDAVSetting = AutoDisposeNotifier<DAV?>;
|
||||
String _$overrideDnsHash() => r'1fc914de471319bf1e003edf9627b8c646b641bf';
|
||||
|
||||
/// See also [OverrideDns].
|
||||
@ProviderFor(OverrideDns)
|
||||
final overrideDnsProvider =
|
||||
AutoDisposeNotifierProvider<OverrideDns, bool>.internal(
|
||||
OverrideDns.new,
|
||||
name: r'overrideDnsProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$overrideDnsHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
abstract class _$OverrideDns extends $Notifier<bool> {
|
||||
bool build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<bool, bool>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<bool, bool>,
|
||||
bool,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(HotKeyActions)
|
||||
const hotKeyActionsProvider = HotKeyActionsProvider._();
|
||||
|
||||
final class HotKeyActionsProvider
|
||||
extends $NotifierProvider<HotKeyActions, List<HotKeyAction>> {
|
||||
const HotKeyActionsProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'hotKeyActionsProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$hotKeyActionsHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
HotKeyActions create() => HotKeyActions();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(List<HotKeyAction> value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<List<HotKeyAction>>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _$OverrideDns = AutoDisposeNotifier<bool>;
|
||||
String _$hotKeyActionsHash() => r'1d308d61b74accebbb11b1771a55975760503691';
|
||||
|
||||
/// See also [HotKeyActions].
|
||||
@ProviderFor(HotKeyActions)
|
||||
final hotKeyActionsProvider =
|
||||
AutoDisposeNotifierProvider<HotKeyActions, List<HotKeyAction>>.internal(
|
||||
HotKeyActions.new,
|
||||
name: r'hotKeyActionsProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$hotKeyActionsHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
abstract class _$HotKeyActions extends $Notifier<List<HotKeyAction>> {
|
||||
List<HotKeyAction> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<List<HotKeyAction>, List<HotKeyAction>>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<List<HotKeyAction>, List<HotKeyAction>>,
|
||||
List<HotKeyAction>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _$HotKeyActions = AutoDisposeNotifier<List<HotKeyAction>>;
|
||||
String _$proxiesStyleSettingHash() =>
|
||||
r'54ebf20a8d4455b2d7a65824f375c4c02a5fba28';
|
||||
|
||||
/// See also [ProxiesStyleSetting].
|
||||
@ProviderFor(ProxiesStyleSetting)
|
||||
final proxiesStyleSettingProvider =
|
||||
AutoDisposeNotifierProvider<ProxiesStyleSetting, ProxiesStyle>.internal(
|
||||
ProxiesStyleSetting.new,
|
||||
name: r'proxiesStyleSettingProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$proxiesStyleSettingHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const proxiesStyleSettingProvider = ProxiesStyleSettingProvider._();
|
||||
|
||||
typedef _$ProxiesStyleSetting = AutoDisposeNotifier<ProxiesStyle>;
|
||||
String _$scriptStateHash() => r'afbb70d1dd7e577b2377ecd8ab35d0905c1d0e87';
|
||||
final class ProxiesStyleSettingProvider
|
||||
extends $NotifierProvider<ProxiesStyleSetting, ProxiesStyle> {
|
||||
const ProxiesStyleSettingProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'proxiesStyleSettingProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$proxiesStyleSettingHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ProxiesStyleSetting create() => ProxiesStyleSetting();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(ProxiesStyle value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<ProxiesStyle>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$proxiesStyleSettingHash() =>
|
||||
r'4ff62951ddc8289220191850516b6751ee69d642';
|
||||
|
||||
abstract class _$ProxiesStyleSetting extends $Notifier<ProxiesStyle> {
|
||||
ProxiesStyle build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<ProxiesStyle, ProxiesStyle>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<ProxiesStyle, ProxiesStyle>,
|
||||
ProxiesStyle,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [ScriptState].
|
||||
@ProviderFor(ScriptState)
|
||||
final scriptStateProvider =
|
||||
AutoDisposeNotifierProvider<ScriptState, ScriptProps>.internal(
|
||||
ScriptState.new,
|
||||
name: r'scriptStateProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$scriptStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const scriptStateProvider = ScriptStateProvider._();
|
||||
|
||||
typedef _$ScriptState = AutoDisposeNotifier<ScriptProps>;
|
||||
String _$patchClashConfigHash() => r'd9acdd0ace673fc1c1460b63d7a27c5787713c14';
|
||||
final class ScriptStateProvider
|
||||
extends $NotifierProvider<ScriptState, ScriptProps> {
|
||||
const ScriptStateProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'scriptStateProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$scriptStateHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ScriptState create() => ScriptState();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(ScriptProps value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<ScriptProps>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$scriptStateHash() => r'4770c34c3d24451fef95e372450e4a333b419977';
|
||||
|
||||
abstract class _$ScriptState extends $Notifier<ScriptProps> {
|
||||
ScriptProps build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<ScriptProps, ScriptProps>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<ScriptProps, ScriptProps>,
|
||||
ScriptProps,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [PatchClashConfig].
|
||||
@ProviderFor(PatchClashConfig)
|
||||
final patchClashConfigProvider =
|
||||
AutoDisposeNotifierProvider<PatchClashConfig, ClashConfig>.internal(
|
||||
PatchClashConfig.new,
|
||||
name: r'patchClashConfigProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$patchClashConfigHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
const patchClashConfigProvider = PatchClashConfigProvider._();
|
||||
|
||||
typedef _$PatchClashConfig = AutoDisposeNotifier<ClashConfig>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
final class PatchClashConfigProvider
|
||||
extends $NotifierProvider<PatchClashConfig, ClashConfig> {
|
||||
const PatchClashConfigProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'patchClashConfigProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$patchClashConfigHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
PatchClashConfig create() => PatchClashConfig();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(ClashConfig value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<ClashConfig>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$patchClashConfigHash() => r'b355bd89969d4d119631fdf117df230a71493fa8';
|
||||
|
||||
abstract class _$PatchClashConfig extends $Notifier<ClashConfig> {
|
||||
ClashConfig build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<ClashConfig, ClashConfig>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<ClashConfig, ClashConfig>,
|
||||
ClashConfig,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,17 +46,19 @@ Config configState(Ref ref) {
|
||||
|
||||
@riverpod
|
||||
GroupsState currentGroupsState(Ref ref) {
|
||||
final mode =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.mode));
|
||||
final mode = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.mode),
|
||||
);
|
||||
final groups = ref.watch(groupsProvider);
|
||||
return GroupsState(
|
||||
value: switch (mode) {
|
||||
Mode.direct => [],
|
||||
Mode.global => groups.toList(),
|
||||
Mode.rule => groups
|
||||
.where((item) => item.hidden == false)
|
||||
.where((element) => element.name != GroupName.GLOBAL.name)
|
||||
.toList(),
|
||||
Mode.rule =>
|
||||
groups
|
||||
.where((item) => item.hidden == false)
|
||||
.where((element) => element.name != GroupName.GLOBAL.name)
|
||||
.toList(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -64,11 +66,12 @@ GroupsState currentGroupsState(Ref ref) {
|
||||
@riverpod
|
||||
NavigationItemsState navigationItemsState(Ref ref) {
|
||||
final openLogs = ref.watch(appSettingProvider).openLogs;
|
||||
final hasProfiles =
|
||||
ref.watch(profilesProvider.select((state) => state.isNotEmpty));
|
||||
final hasProxies = ref.watch(currentGroupsStateProvider.select(
|
||||
(state) => state.value.isNotEmpty,
|
||||
));
|
||||
final hasProfiles = ref.watch(
|
||||
profilesProvider.select((state) => state.isNotEmpty),
|
||||
);
|
||||
final hasProxies = ref.watch(
|
||||
currentGroupsStateProvider.select((state) => state.value.isNotEmpty),
|
||||
);
|
||||
final isInit = ref.watch(initProvider);
|
||||
return NavigationItemsState(
|
||||
value: navigation.getItems(
|
||||
@@ -88,31 +91,15 @@ NavigationItemsState currentNavigationItemsState(Ref ref) {
|
||||
};
|
||||
return NavigationItemsState(
|
||||
value: navigationItemsState.value
|
||||
.where(
|
||||
(element) => element.modes.contains(navigationItemMode),
|
||||
)
|
||||
.where((element) => element.modes.contains(navigationItemMode))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
CoreState coreState(Ref ref) {
|
||||
final vpnProps = ref.watch(vpnSettingProvider);
|
||||
final currentProfile = ref.watch(currentProfileProvider);
|
||||
final onlyStatisticsProxy = ref.watch(appSettingProvider).onlyStatisticsProxy;
|
||||
return CoreState(
|
||||
vpnProps: vpnProps,
|
||||
onlyStatisticsProxy: onlyStatisticsProxy,
|
||||
currentProfileName: currentProfile?.label ?? currentProfile?.id ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
UpdateParams updateParams(Ref ref) {
|
||||
final routeMode = ref.watch(
|
||||
networkSettingProvider.select(
|
||||
(state) => state.routeMode,
|
||||
),
|
||||
networkSettingProvider.select((state) => state.routeMode),
|
||||
);
|
||||
return ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
@@ -135,12 +122,11 @@ UpdateParams updateParams(Ref ref) {
|
||||
@riverpod
|
||||
ProxyState proxyState(Ref ref) {
|
||||
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
|
||||
final vm2 = ref.watch(networkSettingProvider.select(
|
||||
(state) => VM2(
|
||||
a: state.systemProxy,
|
||||
b: state.bypassDomain,
|
||||
final vm2 = ref.watch(
|
||||
networkSettingProvider.select(
|
||||
(state) => VM2(a: state.systemProxy, b: state.bypassDomain),
|
||||
),
|
||||
));
|
||||
);
|
||||
final mixedPort = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.mixedPort),
|
||||
);
|
||||
@@ -156,20 +142,10 @@ ProxyState proxyState(Ref ref) {
|
||||
TrayState trayState(Ref ref) {
|
||||
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
|
||||
final networkProps = ref.watch(networkSettingProvider);
|
||||
final clashConfig = ref.watch(
|
||||
patchClashConfigProvider,
|
||||
);
|
||||
final appSetting = ref.watch(
|
||||
appSettingProvider,
|
||||
);
|
||||
final groups = ref
|
||||
.watch(
|
||||
currentGroupsStateProvider,
|
||||
)
|
||||
.value;
|
||||
final brightness = ref.watch(
|
||||
systemBrightnessProvider,
|
||||
);
|
||||
final clashConfig = ref.watch(patchClashConfigProvider);
|
||||
final appSetting = ref.watch(appSettingProvider);
|
||||
final groups = ref.watch(currentGroupsStateProvider).value;
|
||||
final brightness = ref.watch(systemBrightnessProvider);
|
||||
|
||||
final selectedMap = ref.watch(selectedMapProvider);
|
||||
|
||||
@@ -194,10 +170,7 @@ VpnState vpnState(Ref ref) {
|
||||
patchClashConfigProvider.select((state) => state.tun.stack),
|
||||
);
|
||||
|
||||
return VpnState(
|
||||
stack: stack,
|
||||
vpnProps: vpnProps,
|
||||
);
|
||||
return VpnState(stack: stack, vpnProps: vpnProps);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@@ -220,25 +193,33 @@ NavigationState navigationState(Ref ref) {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
DashboardState dashboardState(Ref ref) {
|
||||
final dashboardWidgets =
|
||||
ref.watch(appSettingProvider.select((state) => state.dashboardWidgets));
|
||||
double contentWidth(Ref ref) {
|
||||
final viewWidth = ref.watch(viewWidthProvider);
|
||||
final sideWidth = ref.watch(sideWidthProvider);
|
||||
return viewWidth - sideWidth;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
DashboardState dashboardState(Ref ref) {
|
||||
final dashboardWidgets = ref.watch(
|
||||
appSettingProvider.select((state) => state.dashboardWidgets),
|
||||
);
|
||||
final contentWidth = ref.watch(contentWidthProvider);
|
||||
return DashboardState(
|
||||
dashboardWidgets: dashboardWidgets,
|
||||
viewWidth: viewWidth,
|
||||
contentWidth: contentWidth,
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
ProxiesActionsState proxiesActionsState(Ref ref) {
|
||||
final pageLabel = ref.watch(currentPageLabelProvider);
|
||||
final hasProviders = ref.watch(providersProvider.select(
|
||||
(state) => state.isNotEmpty,
|
||||
));
|
||||
final type = ref.watch(proxiesStyleSettingProvider.select(
|
||||
(state) => state.type,
|
||||
));
|
||||
final hasProviders = ref.watch(
|
||||
providersProvider.select((state) => state.isNotEmpty),
|
||||
);
|
||||
final type = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.type),
|
||||
);
|
||||
return ProxiesActionsState(
|
||||
pageLabel: pageLabel,
|
||||
hasProviders: hasProviders,
|
||||
@@ -249,12 +230,10 @@ ProxiesActionsState proxiesActionsState(Ref ref) {
|
||||
@riverpod
|
||||
StartButtonSelectorState startButtonSelectorState(Ref ref) {
|
||||
final isInit = ref.watch(initProvider);
|
||||
final hasProfile =
|
||||
ref.watch(profilesProvider.select((state) => state.isNotEmpty));
|
||||
return StartButtonSelectorState(
|
||||
isInit: isInit,
|
||||
hasProfile: hasProfile,
|
||||
final hasProfile = ref.watch(
|
||||
profilesProvider.select((state) => state.isNotEmpty),
|
||||
);
|
||||
return StartButtonSelectorState(isInit: isInit, hasProfile: hasProfile);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@@ -262,9 +241,7 @@ ProfilesSelectorState profilesSelectorState(Ref ref) {
|
||||
final currentProfileId = ref.watch(currentProfileIdProvider);
|
||||
final profiles = ref.watch(profilesProvider);
|
||||
final columns = ref.watch(
|
||||
viewWidthProvider.select(
|
||||
(state) => utils.getProfilesColumns(state),
|
||||
),
|
||||
contentWidthProvider.select((state) => utils.getProfilesColumns(state)),
|
||||
);
|
||||
return ProfilesSelectorState(
|
||||
profiles: profiles,
|
||||
@@ -281,75 +258,61 @@ GroupsState filterGroupsState(Ref ref, String query) {
|
||||
}
|
||||
final lowQuery = query.toLowerCase();
|
||||
final groups = currentGroups.value
|
||||
.map(
|
||||
(group) {
|
||||
return group.copyWith(
|
||||
all: group.all
|
||||
.where((proxy) => proxy.name.toLowerCase().contains(lowQuery))
|
||||
.toList());
|
||||
},
|
||||
)
|
||||
.where(
|
||||
(group) => group.all.isNotEmpty,
|
||||
)
|
||||
.map((group) {
|
||||
return group.copyWith(
|
||||
all: group.all
|
||||
.where((proxy) => proxy.name.toLowerCase().contains(lowQuery))
|
||||
.toList(),
|
||||
);
|
||||
})
|
||||
.where((group) => group.all.isNotEmpty)
|
||||
.toList();
|
||||
return GroupsState(value: groups);
|
||||
}
|
||||
|
||||
@Riverpod(dependencies: [Query])
|
||||
@riverpod
|
||||
ProxiesListState proxiesListState(Ref ref) {
|
||||
final query = ref.watch(queryProvider);
|
||||
final query = ref.watch(queryProvider(QueryTag.proxies));
|
||||
final currentGroups = ref.watch(filterGroupsStateProvider(query));
|
||||
final currentUnfoldSet = ref.watch(unfoldSetProvider);
|
||||
final vm2 = ref.watch(
|
||||
proxiesStyleSettingProvider.select(
|
||||
(state) => VM2(
|
||||
a: state.sortType,
|
||||
b: state.cardType,
|
||||
),
|
||||
),
|
||||
final cardType = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.cardType),
|
||||
);
|
||||
|
||||
final sortNum = ref.watch(sortNumProvider);
|
||||
final columns = ref.watch(getProxiesColumnsProvider);
|
||||
return ProxiesListState(
|
||||
groups: currentGroups.value,
|
||||
currentUnfoldSet: currentUnfoldSet,
|
||||
proxiesSortType: vm2.a,
|
||||
proxyCardType: vm2.b,
|
||||
sortNum: sortNum,
|
||||
proxyCardType: cardType,
|
||||
columns: columns,
|
||||
);
|
||||
}
|
||||
|
||||
@Riverpod(dependencies: [Query])
|
||||
@riverpod
|
||||
ProxiesTabState proxiesTabState(Ref ref) {
|
||||
final query = ref.watch(queryProvider);
|
||||
final query = ref.watch(queryProvider(QueryTag.proxies));
|
||||
final currentGroups = ref.watch(filterGroupsStateProvider(query));
|
||||
final currentGroupName = ref.watch(currentProfileProvider.select(
|
||||
(state) => state?.currentGroupName,
|
||||
));
|
||||
final vm2 = ref.watch(
|
||||
proxiesStyleSettingProvider.select(
|
||||
(state) => VM2(
|
||||
a: state.sortType,
|
||||
b: state.cardType,
|
||||
),
|
||||
),
|
||||
final currentGroupName = ref.watch(
|
||||
currentProfileProvider.select((state) => state?.currentGroupName),
|
||||
);
|
||||
final cardType = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.cardType),
|
||||
);
|
||||
final sortNum = ref.watch(sortNumProvider);
|
||||
final columns = ref.watch(getProxiesColumnsProvider);
|
||||
return ProxiesTabState(
|
||||
groups: currentGroups.value,
|
||||
currentGroupName: currentGroupName,
|
||||
proxiesSortType: vm2.a,
|
||||
proxyCardType: vm2.b,
|
||||
sortNum: sortNum,
|
||||
proxyCardType: cardType,
|
||||
columns: columns,
|
||||
);
|
||||
}
|
||||
|
||||
@Riverpod(dependencies: [proxiesTabState])
|
||||
@riverpod
|
||||
bool isStart(Ref ref) {
|
||||
return ref.watch(runTimeProvider.select((state) => state != null));
|
||||
}
|
||||
|
||||
@riverpod
|
||||
VM2<List<String>, String?> proxiesTabControllerState(Ref ref) {
|
||||
return ref.watch(
|
||||
proxiesTabStateProvider.select(
|
||||
@@ -367,9 +330,7 @@ ProxyGroupSelectorState proxyGroupSelectorState(
|
||||
String groupName,
|
||||
String query,
|
||||
) {
|
||||
final proxiesStyle = ref.watch(
|
||||
proxiesStyleSettingProvider,
|
||||
);
|
||||
final proxiesStyle = ref.watch(proxiesStyleSettingProvider);
|
||||
final group = ref.watch(
|
||||
currentGroupsStateProvider.select(
|
||||
(state) => state.value.getGroup(groupName),
|
||||
@@ -378,7 +339,8 @@ ProxyGroupSelectorState proxyGroupSelectorState(
|
||||
final sortNum = ref.watch(sortNumProvider);
|
||||
final columns = ref.watch(getProxiesColumnsProvider);
|
||||
final lowQuery = query.toLowerCase();
|
||||
final proxies = group?.all.where((item) {
|
||||
final proxies =
|
||||
group?.all.where((item) {
|
||||
return item.name.toLowerCase().contains(lowQuery);
|
||||
}).toList() ??
|
||||
[];
|
||||
@@ -396,8 +358,9 @@ ProxyGroupSelectorState proxyGroupSelectorState(
|
||||
@riverpod
|
||||
PackageListSelectorState packageListSelectorState(Ref ref) {
|
||||
final packages = ref.watch(packagesProvider);
|
||||
final accessControl =
|
||||
ref.watch(vpnSettingProvider.select((state) => state.accessControl));
|
||||
final accessControl = ref.watch(
|
||||
vpnSettingProvider.select((state) => state.accessControl),
|
||||
);
|
||||
return PackageListSelectorState(
|
||||
packages: packages,
|
||||
accessControl: accessControl,
|
||||
@@ -407,18 +370,19 @@ PackageListSelectorState packageListSelectorState(Ref ref) {
|
||||
@riverpod
|
||||
MoreToolsSelectorState moreToolsSelectorState(Ref ref) {
|
||||
final viewMode = ref.watch(viewModeProvider);
|
||||
final navigationItems =
|
||||
ref.watch(navigationItemsStateProvider.select((state) {
|
||||
return state.value.where((element) {
|
||||
final isMore = element.modes.contains(NavigationItemMode.more);
|
||||
final isDesktop = element.modes.contains(NavigationItemMode.desktop);
|
||||
if (isMore && !isDesktop) return true;
|
||||
if (viewMode != ViewMode.mobile || !isMore) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
}));
|
||||
final navigationItems = ref.watch(
|
||||
navigationItemsStateProvider.select((state) {
|
||||
return state.value.where((element) {
|
||||
final isMore = element.modes.contains(NavigationItemMode.more);
|
||||
final isDesktop = element.modes.contains(NavigationItemMode.desktop);
|
||||
if (isMore && !isDesktop) return true;
|
||||
if (viewMode != ViewMode.mobile || !isMore) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
}),
|
||||
);
|
||||
|
||||
return MoreToolsSelectorState(navigationItems: navigationItems);
|
||||
}
|
||||
@@ -447,26 +411,16 @@ String getRealTestUrl(Ref ref, [String? testUrl]) {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
int? getDelay(
|
||||
Ref ref, {
|
||||
required String proxyName,
|
||||
String? testUrl,
|
||||
}) {
|
||||
int? getDelay(Ref ref, {required String proxyName, String? testUrl}) {
|
||||
final currentTestUrl = ref.watch(getRealTestUrlProvider(testUrl));
|
||||
final proxyCardState = ref.watch(
|
||||
getProxyCardStateProvider(
|
||||
proxyName,
|
||||
),
|
||||
);
|
||||
final proxyState = ref.watch(realSelectedProxyStateProvider(proxyName));
|
||||
final delay = ref.watch(
|
||||
delayDataSourceProvider.select(
|
||||
(state) {
|
||||
final delayMap =
|
||||
state[proxyCardState.testUrl.getSafeValue(currentTestUrl)];
|
||||
return delayMap?[proxyCardState.proxyName];
|
||||
},
|
||||
),
|
||||
delayDataSourceProvider.select((state) {
|
||||
final delayMap = state[proxyState.testUrl.getSafeValue(currentTestUrl)];
|
||||
return delayMap?[proxyState.proxyName];
|
||||
}),
|
||||
);
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
@@ -489,71 +443,46 @@ Set<String> unfoldSet(Ref ref) {
|
||||
@riverpod
|
||||
HotKeyAction getHotKeyAction(Ref ref, HotAction hotAction) {
|
||||
return ref.watch(
|
||||
hotKeyActionsProvider.select(
|
||||
(state) {
|
||||
final index = state.indexWhere((item) => item.action == hotAction);
|
||||
return index != -1
|
||||
? state[index]
|
||||
: HotKeyAction(
|
||||
action: hotAction,
|
||||
);
|
||||
},
|
||||
),
|
||||
hotKeyActionsProvider.select((state) {
|
||||
final index = state.indexWhere((item) => item.action == hotAction);
|
||||
return index != -1 ? state[index] : HotKeyAction(action: hotAction);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Profile? currentProfile(Ref ref) {
|
||||
final profileId = ref.watch(currentProfileIdProvider);
|
||||
return ref
|
||||
.watch(profilesProvider.select((state) => state.getProfile(profileId)));
|
||||
}
|
||||
|
||||
@riverpod
|
||||
int getProxiesColumns(Ref ref) {
|
||||
final viewWidth = ref.watch(viewWidthProvider);
|
||||
final proxiesLayout =
|
||||
ref.watch(proxiesStyleSettingProvider.select((state) => state.layout));
|
||||
return utils.getProxiesColumns(viewWidth, proxiesLayout);
|
||||
}
|
||||
|
||||
ProxyCardState _getProxyCardState(
|
||||
List<Group> groups,
|
||||
SelectedMap selectedMap,
|
||||
ProxyCardState proxyDelayState,
|
||||
) {
|
||||
if (proxyDelayState.proxyName.isEmpty) return proxyDelayState;
|
||||
final index =
|
||||
groups.indexWhere((element) => element.name == proxyDelayState.proxyName);
|
||||
if (index == -1) return proxyDelayState;
|
||||
final group = groups[index];
|
||||
final currentSelectedName = group
|
||||
.getCurrentSelectedName(selectedMap[proxyDelayState.proxyName] ?? '');
|
||||
if (currentSelectedName.isEmpty) {
|
||||
return proxyDelayState;
|
||||
}
|
||||
return _getProxyCardState(
|
||||
groups,
|
||||
selectedMap,
|
||||
proxyDelayState.copyWith(
|
||||
proxyName: currentSelectedName,
|
||||
testUrl: group.testUrl,
|
||||
),
|
||||
return ref.watch(
|
||||
profilesProvider.select((state) => state.getProfile(profileId)),
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
ProxyCardState getProxyCardState(Ref ref, String proxyName) {
|
||||
int getProxiesColumns(Ref ref) {
|
||||
final contentWidth = ref.watch(contentWidthProvider);
|
||||
final proxiesLayout = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.layout),
|
||||
);
|
||||
return utils.getProxiesColumns(contentWidth, proxiesLayout);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
SelectedProxyState realSelectedProxyState(Ref ref, String proxyName) {
|
||||
final groups = ref.watch(groupsProvider);
|
||||
final selectedMap = ref.watch(selectedMapProvider);
|
||||
return _getProxyCardState(
|
||||
groups, selectedMap, ProxyCardState(proxyName: proxyName));
|
||||
return computeRealSelectedProxyState(
|
||||
proxyName,
|
||||
groups: groups,
|
||||
selectedMap: selectedMap,
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
String? getProxyName(Ref ref, String groupName) {
|
||||
final proxyName =
|
||||
ref.watch(selectedMapProvider.select((state) => state[groupName]));
|
||||
final proxyName = ref.watch(
|
||||
selectedMapProvider.select((state) => state[groupName]),
|
||||
);
|
||||
return proxyName;
|
||||
}
|
||||
|
||||
@@ -561,9 +490,7 @@ String? getProxyName(Ref ref, String groupName) {
|
||||
String? getSelectedProxyName(Ref ref, String groupName) {
|
||||
final proxyName = ref.watch(getProxyNameProvider(groupName));
|
||||
final group = ref.watch(
|
||||
groupsProvider.select(
|
||||
(state) => state.getGroup(groupName),
|
||||
),
|
||||
groupsProvider.select((state) => state.getGroup(groupName)),
|
||||
);
|
||||
return group?.getCurrentSelectedName(proxyName ?? '');
|
||||
}
|
||||
@@ -577,32 +504,11 @@ String getProxyDesc(Ref ref, Proxy proxy) {
|
||||
final groups = ref.watch(groupsProvider);
|
||||
final index = groups.indexWhere((element) => element.name == proxy.name);
|
||||
if (index == -1) return proxy.type;
|
||||
final state = ref.watch(getProxyCardStateProvider(proxy.name));
|
||||
final state = ref.watch(realSelectedProxyStateProvider(proxy.name));
|
||||
return "${proxy.type}(${state.proxyName.isNotEmpty ? state.proxyName : '*'})";
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class ProfileOverrideState extends _$ProfileOverrideState {
|
||||
@override
|
||||
ProfileOverrideStateModel build() {
|
||||
return ProfileOverrideStateModel(
|
||||
selectedRules: {},
|
||||
);
|
||||
}
|
||||
|
||||
void updateState(
|
||||
ProfileOverrideStateModel? Function(ProfileOverrideStateModel state)
|
||||
builder,
|
||||
) {
|
||||
final value = builder(state);
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
state = value;
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
OverrideData? getProfileOverrideData(Ref ref, String profileId) {
|
||||
return ref.watch(
|
||||
@@ -615,12 +521,10 @@ OverrideData? getProfileOverrideData(Ref ref, String profileId) {
|
||||
@riverpod
|
||||
VM2? layoutChange(Ref ref) {
|
||||
final viewWidth = ref.watch(viewWidthProvider);
|
||||
final textScale =
|
||||
ref.watch(themeSettingProvider.select((state) => state.textScale));
|
||||
return VM2(
|
||||
a: viewWidth,
|
||||
b: textScale,
|
||||
final textScale = ref.watch(
|
||||
themeSettingProvider.select((state) => state.textScale),
|
||||
);
|
||||
return VM2(a: viewWidth, b: textScale);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@@ -632,10 +536,7 @@ VM2<int, bool> checkIp(Ref ref) {
|
||||
state.dashboardWidgets.contains(DashboardWidget.networkDetection),
|
||||
),
|
||||
);
|
||||
return VM2(
|
||||
a: checkIpNum,
|
||||
b: containsDetection,
|
||||
);
|
||||
return VM2(a: checkIpNum, b: containsDetection);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@@ -647,10 +548,7 @@ ColorScheme genColorScheme(
|
||||
}) {
|
||||
final vm2 = ref.watch(
|
||||
themeSettingProvider.select(
|
||||
(state) => VM2(
|
||||
a: state.primaryColor,
|
||||
b: state.schemeVariant,
|
||||
),
|
||||
(state) => VM2(a: state.primaryColor, b: state.schemeVariant),
|
||||
),
|
||||
);
|
||||
if (color == null && (ignoreConfig == true || vm2.a == null)) {
|
||||
@@ -658,7 +556,8 @@ ColorScheme genColorScheme(
|
||||
// return globalState.corePalette!.toColorScheme(brightness: brightness);
|
||||
// }
|
||||
return ColorScheme.fromSeed(
|
||||
seedColor: globalState.corePalette
|
||||
seedColor:
|
||||
globalState.corePalette
|
||||
?.toColorScheme(brightness: brightness)
|
||||
.primary ??
|
||||
globalState.accentColor,
|
||||
@@ -677,24 +576,20 @@ ColorScheme genColorScheme(
|
||||
VM3<String?, String?, Dns?> needSetup(Ref ref) {
|
||||
final profileId = ref.watch(currentProfileIdProvider);
|
||||
final content = ref.watch(
|
||||
scriptStateProvider.select((state) => state.currentScript?.content));
|
||||
scriptStateProvider.select((state) => state.currentScript?.content),
|
||||
);
|
||||
final overrideDns = ref.watch(overrideDnsProvider);
|
||||
final dns = overrideDns == true
|
||||
? ref.watch(patchClashConfigProvider.select(
|
||||
(state) => state.dns,
|
||||
))
|
||||
? ref.watch(patchClashConfigProvider.select((state) => state.dns))
|
||||
: null;
|
||||
return VM3(
|
||||
a: profileId,
|
||||
b: content,
|
||||
c: dns,
|
||||
);
|
||||
return VM3(a: profileId, b: content, c: dns);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Brightness currentBrightness(Ref ref) {
|
||||
final themeMode =
|
||||
ref.watch(themeSettingProvider.select((state) => state.themeMode));
|
||||
final themeMode = ref.watch(
|
||||
themeSettingProvider.select((state) => state.themeMode),
|
||||
);
|
||||
final systemBrightness = ref.watch(systemBrightnessProvider);
|
||||
return switch (themeMode) {
|
||||
ThemeMode.system => systemBrightness,
|
||||
@@ -708,18 +603,46 @@ VM2<bool, bool> autoSetSystemDnsState(Ref ref) {
|
||||
final isStart = ref.watch(runTimeProvider.select((state) => state != null));
|
||||
final realTunEnable = ref.watch(realTunEnableProvider);
|
||||
final autoSetSystemDns = ref.watch(
|
||||
networkSettingProvider.select(
|
||||
(state) => state.autoSetSystemDns,
|
||||
),
|
||||
networkSettingProvider.select((state) => state.autoSetSystemDns),
|
||||
);
|
||||
return VM2(
|
||||
a: isStart ? realTunEnable : false,
|
||||
b: autoSetSystemDns,
|
||||
return VM2(a: isStart ? realTunEnable : false, b: autoSetSystemDns);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
VM3<bool, int, ProxiesSortType> needUpdateGroups(Ref ref) {
|
||||
final isProxies = ref.watch(
|
||||
currentPageLabelProvider.select((state) => state == PageLabel.proxies),
|
||||
);
|
||||
final sortNum = ref.watch(sortNumProvider);
|
||||
final sortType = ref.watch(
|
||||
proxiesStyleSettingProvider.select((state) => state.sortType),
|
||||
);
|
||||
return VM3(a: isProxies, b: sortNum, c: sortType);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
AndroidState androidState(Ref ref) {
|
||||
final currentProfileName = ref.watch(
|
||||
currentProfileProvider.select((state) => state?.label ?? ''),
|
||||
);
|
||||
final onlyStatisticsProxy = ref.watch(
|
||||
appSettingProvider.select((state) => state.onlyStatisticsProxy),
|
||||
);
|
||||
ref.watch((appSettingProvider).select((state) => state.locale));
|
||||
final crashlytics = ref.watch(
|
||||
(appSettingProvider).select((state) => state.crashlytics),
|
||||
);
|
||||
return AndroidState(
|
||||
currentProfileName: currentProfileName,
|
||||
onlyStatisticsProxy: onlyStatisticsProxy,
|
||||
stopText: appLocalizations.stop,
|
||||
crashlytics: crashlytics,
|
||||
);
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Query extends _$Query with AutoDisposeNotifierMixin {
|
||||
class Query extends _$Query {
|
||||
@override
|
||||
String build() => '';
|
||||
String build(QueryTag id) =>
|
||||
ref.watch(queryMapProvider.select((state) => state[id] ?? ''));
|
||||
}
|
||||
|
||||
215
lib/state.dart
215
lib/state.dart
@@ -1,12 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi' show Pointer;
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/theme.dart';
|
||||
import 'package:fl_clash/core/core.dart';
|
||||
import 'package:fl_clash/enum/enum.dart';
|
||||
import 'package:fl_clash/l10n/l10n.dart';
|
||||
import 'package:fl_clash/plugins/service.dart';
|
||||
@@ -16,6 +18,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_js/flutter_js.dart';
|
||||
import 'package:material_color_utilities/palettes/core_palette.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'common/common.dart';
|
||||
@@ -27,10 +30,6 @@ typedef UpdateTasks = List<FutureOr Function()>;
|
||||
class GlobalState {
|
||||
static GlobalState? _instance;
|
||||
Map<CacheTag, FixedMap<String, double>> computeHeightMapCache = {};
|
||||
|
||||
// Map<CacheTag, double> computeScrollPositionCache = {};
|
||||
// final Map<String, double> scrollPositionCache = {};
|
||||
bool isService = false;
|
||||
Timer? timer;
|
||||
Timer? groupsUpdateTimer;
|
||||
late Config config;
|
||||
@@ -47,9 +46,9 @@ class GlobalState {
|
||||
UpdateTasks tasks = [];
|
||||
final navigatorKey = GlobalKey<NavigatorState>();
|
||||
AppController? _appController;
|
||||
|
||||
// GlobalKey<CommonScaffoldState> homeScaffoldKey = GlobalKey();
|
||||
bool isInit = false;
|
||||
bool isUserDisconnected = false;
|
||||
bool isService = false;
|
||||
|
||||
bool get isStart => startTime != null && startTime!.isBeforeNow;
|
||||
|
||||
@@ -82,22 +81,54 @@ class GlobalState {
|
||||
);
|
||||
await _initDynamicColor();
|
||||
await init();
|
||||
await window?.init(version);
|
||||
_shakingStore();
|
||||
}
|
||||
|
||||
Future<void> _shakingStore() async {
|
||||
final profileIds = config.profiles.map((item) => item.id);
|
||||
final providersRootPath = await appPath.getProvidersRootPath();
|
||||
final profilesRootPath = await appPath.profilesPath;
|
||||
Isolate.run(() async {
|
||||
final profilesDir = Directory(profilesRootPath);
|
||||
final providersDir = Directory(providersRootPath);
|
||||
final List<FileSystemEntity> entities = [];
|
||||
if (await profilesDir.exists()) {
|
||||
entities.addAll(
|
||||
profilesDir.listSync().where(
|
||||
(item) => !item.path.contains('providers'),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (await providersDir.exists()) {
|
||||
entities.addAll(providersDir.listSync());
|
||||
}
|
||||
final deleteFutures = entities.map((entity) async {
|
||||
if (!profileIds.contains(basenameWithoutExtension(entity.path))) {
|
||||
final res = await coreController.deleteFile(entity.path);
|
||||
if (res.isNotEmpty) {
|
||||
throw res;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
await Future.wait(deleteFutures);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _initDynamicColor() async {
|
||||
try {
|
||||
corePalette = await DynamicColorPlugin.getCorePalette();
|
||||
accentColor = await DynamicColorPlugin.getAccentColor() ??
|
||||
accentColor =
|
||||
await DynamicColorPlugin.getAccentColor() ??
|
||||
Color(defaultPrimaryColor);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
packageInfo = await PackageInfo.fromPlatform();
|
||||
config = await preferences.getConfig() ??
|
||||
Config(
|
||||
themeProps: defaultThemeProps,
|
||||
);
|
||||
config =
|
||||
await preferences.getConfig() ?? Config(themeProps: defaultThemeProps);
|
||||
await globalState.migrateOldData(config);
|
||||
await AppLocalizations.load(
|
||||
utils.getLocaleForString(config.appSetting.locale) ??
|
||||
@@ -112,6 +143,9 @@ class GlobalState {
|
||||
if (tasks != null) {
|
||||
this.tasks = tasks;
|
||||
}
|
||||
if (this.tasks.isEmpty) {
|
||||
return;
|
||||
}
|
||||
await executorUpdateTask();
|
||||
timer = Timer(const Duration(seconds: 1), () async {
|
||||
startUpdateTasks();
|
||||
@@ -133,29 +167,33 @@ class GlobalState {
|
||||
|
||||
Future<void> handleStart([UpdateTasks? tasks]) async {
|
||||
startTime ??= DateTime.now();
|
||||
await clashCore.startListener();
|
||||
await service?.startVpn();
|
||||
await coreController.startListener();
|
||||
await service?.start();
|
||||
startUpdateTasks(tasks);
|
||||
}
|
||||
|
||||
Future updateStartTime() async {
|
||||
startTime = await clashLib?.getRunTime();
|
||||
startTime = await service?.getRunTime();
|
||||
}
|
||||
|
||||
Future handleStop() async {
|
||||
startTime = null;
|
||||
await clashCore.stopListener();
|
||||
await service?.stopVpn();
|
||||
await coreController.stopListener();
|
||||
await service?.stop();
|
||||
stopUpdateTasks();
|
||||
}
|
||||
|
||||
Future<bool?> showMessage({
|
||||
String? title,
|
||||
required InlineSpan message,
|
||||
BuildContext? context,
|
||||
String? title,
|
||||
String? confirmText,
|
||||
bool cancelable = true,
|
||||
bool? dismissible,
|
||||
}) async {
|
||||
return await showCommonDialog<bool>(
|
||||
context: context,
|
||||
dismissible: dismissible,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return CommonDialog(
|
||||
@@ -173,7 +211,7 @@ class GlobalState {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: Text(confirmText ?? appLocalizations.confirm),
|
||||
)
|
||||
),
|
||||
],
|
||||
child: Container(
|
||||
width: 300,
|
||||
@@ -184,9 +222,7 @@ class GlobalState {
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
children: [message],
|
||||
),
|
||||
style: const TextStyle(
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
style: const TextStyle(overflow: TextOverflow.visible),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -196,36 +232,34 @@ class GlobalState {
|
||||
);
|
||||
}
|
||||
|
||||
// Future<Map<String, dynamic>> getProfileMap(String id) async {
|
||||
// final profilePath = await appPath.getProfilePath(id);
|
||||
// final res = await Isolate.run<Result<dynamic>>(() async {
|
||||
// try {
|
||||
// final file = File(profilePath);
|
||||
// if (!await file.exists()) {
|
||||
// return Result.error("");
|
||||
// }
|
||||
// final value = await file.readAsString();
|
||||
// return Result.success(utils.convertYamlNode(loadYaml(value)));
|
||||
// } catch (e) {
|
||||
// return Result.error(e.toString());
|
||||
// }
|
||||
// });
|
||||
// if (res.isSuccess) {
|
||||
// return res.data as Map<String, dynamic>;
|
||||
// } else {
|
||||
// throw res.message;
|
||||
// }
|
||||
// }
|
||||
VpnOptions getVpnOptions() {
|
||||
final vpnProps = config.vpnProps;
|
||||
final networkProps = config.networkProps;
|
||||
final port = config.patchClashConfig.mixedPort;
|
||||
return VpnOptions(
|
||||
stack: config.patchClashConfig.tun.stack.name,
|
||||
enable: vpnProps.enable,
|
||||
systemProxy: networkProps.systemProxy,
|
||||
port: port,
|
||||
ipv6: vpnProps.ipv6,
|
||||
dnsHijacking: vpnProps.dnsHijacking,
|
||||
accessControl: vpnProps.accessControl,
|
||||
allowBypass: vpnProps.allowBypass,
|
||||
bypassDomain: networkProps.bypassDomain,
|
||||
);
|
||||
}
|
||||
|
||||
Future<T?> showCommonDialog<T>({
|
||||
required Widget child,
|
||||
bool dismissible = true,
|
||||
BuildContext? context,
|
||||
bool? dismissible,
|
||||
}) async {
|
||||
return await showModal<T>(
|
||||
context: navigatorKey.currentState!.context,
|
||||
useRootNavigator: false,
|
||||
context: context ?? globalState.navigatorKey.currentContext!,
|
||||
configuration: FadeScaleTransitionConfiguration(
|
||||
barrierColor: Colors.black38,
|
||||
barrierDismissible: dismissible,
|
||||
barrierDismissible: dismissible ?? true,
|
||||
),
|
||||
builder: (_) => child,
|
||||
filter: commonFilter,
|
||||
@@ -254,38 +288,50 @@ class GlobalState {
|
||||
Future<void> migrateOldData(Config config) async {
|
||||
final clashConfig = await preferences.getClashConfig();
|
||||
if (clashConfig != null) {
|
||||
config = config.copyWith(
|
||||
patchClashConfig: clashConfig,
|
||||
);
|
||||
config = config.copyWith(patchClashConfig: clashConfig);
|
||||
preferences.clearClashConfig();
|
||||
preferences.saveConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
CoreState getCoreState() {
|
||||
final currentProfile = config.currentProfile;
|
||||
return CoreState(
|
||||
vpnProps: config.vpnProps,
|
||||
onlyStatisticsProxy: config.appSetting.onlyStatisticsProxy,
|
||||
currentProfileName: currentProfile?.label ?? currentProfile?.id ?? '',
|
||||
bypassDomain: config.networkProps.bypassDomain,
|
||||
);
|
||||
}
|
||||
|
||||
Future<SetupParams> getSetupParams({
|
||||
required ClashConfig pathConfig,
|
||||
}) async {
|
||||
final clashConfig = await patchRawConfig(
|
||||
patchConfig: pathConfig,
|
||||
);
|
||||
Future<SetupParams> getSetupParams() async {
|
||||
final params = SetupParams(
|
||||
config: clashConfig,
|
||||
selectedMap: config.currentProfile?.selectedMap ?? {},
|
||||
testUrl: config.appSetting.testUrl,
|
||||
);
|
||||
return params;
|
||||
}
|
||||
|
||||
Future<void> genConfigFile(ClashConfig pathConfig) async {
|
||||
final configFilePath = await appPath.configFilePath;
|
||||
final config = await patchRawConfig(patchConfig: pathConfig);
|
||||
final res = await Isolate.run<String>(() async {
|
||||
try {
|
||||
final res = json.encode(config);
|
||||
final file = File(configFilePath);
|
||||
if (!await file.exists()) {
|
||||
await file.create(recursive: true);
|
||||
}
|
||||
await file.writeAsString(res);
|
||||
return '';
|
||||
} catch (e) {
|
||||
return e.toString();
|
||||
}
|
||||
});
|
||||
if (res.isNotEmpty) {
|
||||
throw res;
|
||||
}
|
||||
}
|
||||
|
||||
AndroidState getAndroidState() {
|
||||
return AndroidState(
|
||||
currentProfileName: config.currentProfile?.label ?? '',
|
||||
onlyStatisticsProxy: config.appSetting.onlyStatisticsProxy,
|
||||
stopText: appLocalizations.stop,
|
||||
crashlytics: config.appSetting.crashlytics,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> patchRawConfig({
|
||||
required ClashConfig patchConfig,
|
||||
}) async {
|
||||
@@ -390,7 +436,8 @@ class GlobalState {
|
||||
if (overrideDns || !isEnableDns) {
|
||||
final dns = switch (!isEnableDns) {
|
||||
true => realPatchConfig.dns.copyWith(
|
||||
nameserver: [...realPatchConfig.dns.nameserver, 'system://']),
|
||||
nameserver: [...realPatchConfig.dns.nameserver, 'system://'],
|
||||
),
|
||||
false => realPatchConfig.dns,
|
||||
};
|
||||
rawConfig['dns'] = dns.toJson();
|
||||
@@ -400,7 +447,7 @@ class GlobalState {
|
||||
entry.value.splitByMultipleSeparators;
|
||||
}
|
||||
}
|
||||
var rules = [];
|
||||
List rules = [];
|
||||
if (rawConfig['rules'] != null) {
|
||||
rules = rawConfig['rules'];
|
||||
}
|
||||
@@ -419,10 +466,7 @@ class GlobalState {
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getProfileConfig(String profileId) async {
|
||||
final configMap = await switch (clashLibHandler != null) {
|
||||
true => clashLibHandler!.getConfig(profileId),
|
||||
false => clashCore.getConfig(profileId),
|
||||
};
|
||||
final configMap = await coreController.getConfig(profileId);
|
||||
configMap['rules'] = configMap['rule'];
|
||||
configMap.remove('rule');
|
||||
return configMap;
|
||||
@@ -464,10 +508,7 @@ class DetectionState {
|
||||
CancelToken? cancelToken;
|
||||
|
||||
final state = ValueNotifier<NetworkDetectionState>(
|
||||
const NetworkDetectionState(
|
||||
isLoading: true,
|
||||
ipInfo: null,
|
||||
),
|
||||
const NetworkDetectionState(isLoading: true, ipInfo: null),
|
||||
);
|
||||
|
||||
DetectionState._internal();
|
||||
@@ -481,9 +522,7 @@ class DetectionState {
|
||||
debouncer.call(
|
||||
FunctionTag.checkIp,
|
||||
_checkIp,
|
||||
duration: Duration(
|
||||
milliseconds: 1200,
|
||||
),
|
||||
duration: Duration(milliseconds: 1200),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -504,10 +543,7 @@ class DetectionState {
|
||||
return;
|
||||
}
|
||||
_clearSetTimeoutTimer();
|
||||
state.value = state.value.copyWith(
|
||||
isLoading: true,
|
||||
ipInfo: null,
|
||||
);
|
||||
state.value = state.value.copyWith(isLoading: true, ipInfo: null);
|
||||
_preIsStart = isStart;
|
||||
if (cancelToken != null) {
|
||||
cancelToken!.cancel();
|
||||
@@ -516,26 +552,17 @@ class DetectionState {
|
||||
cancelToken = CancelToken();
|
||||
final res = await request.checkIp(cancelToken: cancelToken);
|
||||
if (res.isError) {
|
||||
state.value = state.value.copyWith(
|
||||
isLoading: true,
|
||||
ipInfo: null,
|
||||
);
|
||||
state.value = state.value.copyWith(isLoading: true, ipInfo: null);
|
||||
return;
|
||||
}
|
||||
final ipInfo = res.data;
|
||||
if (ipInfo != null) {
|
||||
state.value = state.value.copyWith(
|
||||
isLoading: false,
|
||||
ipInfo: ipInfo,
|
||||
);
|
||||
state.value = state.value.copyWith(isLoading: false, ipInfo: ipInfo);
|
||||
return;
|
||||
}
|
||||
_clearSetTimeoutTimer();
|
||||
_setTimeoutTimer = Timer(const Duration(milliseconds: 300), () {
|
||||
state.value = state.value.copyWith(
|
||||
isLoading: false,
|
||||
ipInfo: null,
|
||||
);
|
||||
state.value = state.value.copyWith(isLoading: false, ipInfo: null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -51,18 +51,14 @@ class AboutView extends StatelessWidget {
|
||||
ListItem(
|
||||
title: const Text('Telegram'),
|
||||
onTap: () {
|
||||
globalState.openUrl(
|
||||
'https://t.me/FlClash',
|
||||
);
|
||||
globalState.openUrl('https://t.me/FlClash');
|
||||
},
|
||||
trailing: const Icon(Icons.launch),
|
||||
),
|
||||
ListItem(
|
||||
title: Text(appLocalizations.project),
|
||||
onTap: () {
|
||||
globalState.openUrl(
|
||||
'https://github.com/$repository',
|
||||
);
|
||||
globalState.openUrl('https://github.com/$repository');
|
||||
},
|
||||
trailing: const Icon(Icons.launch),
|
||||
),
|
||||
@@ -103,13 +99,11 @@ class AboutView extends StatelessWidget {
|
||||
spacing: 24,
|
||||
children: [
|
||||
for (final contributor in contributors)
|
||||
Avatar(
|
||||
contributor: contributor,
|
||||
),
|
||||
Avatar(contributor: contributor),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -121,46 +115,50 @@ class AboutView extends StatelessWidget {
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Consumer(builder: (_, ref, ___) {
|
||||
return _DeveloperModeDetector(
|
||||
child: Wrap(
|
||||
spacing: 16,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Image.asset(
|
||||
'assets/images/icon.png',
|
||||
width: 64,
|
||||
height: 64,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
appName,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
Consumer(
|
||||
builder: (_, ref, _) {
|
||||
return _DeveloperModeDetector(
|
||||
child: Wrap(
|
||||
spacing: 16,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Image.asset(
|
||||
'assets/images/icon.png',
|
||||
width: 64,
|
||||
height: 64,
|
||||
),
|
||||
Text(
|
||||
globalState.packageInfo.version,
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
onEnterDeveloperMode: () {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(developerMode: true),
|
||||
);
|
||||
context.showNotifier(appLocalizations.developerModeEnableTip);
|
||||
},
|
||||
);
|
||||
}),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
appName,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
Text(
|
||||
globalState.packageInfo.version,
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
onEnterDeveloperMode: () {
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(developerMode: true),
|
||||
);
|
||||
context.showNotifier(
|
||||
appLocalizations.developerModeEnableTip,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
appLocalizations.desc,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
@@ -168,17 +166,12 @@ class AboutView extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
..._buildContributorsSection(),
|
||||
..._buildMoreSection(context),
|
||||
];
|
||||
return Padding(
|
||||
padding: kMaterialListPadding.copyWith(
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
),
|
||||
padding: kMaterialListPadding.copyWith(top: 16, bottom: 16),
|
||||
child: generateListView(items),
|
||||
);
|
||||
}
|
||||
@@ -187,10 +180,7 @@ class AboutView extends StatelessWidget {
|
||||
class Avatar extends StatelessWidget {
|
||||
final Contributor contributor;
|
||||
|
||||
const Avatar({
|
||||
super.key,
|
||||
required this.contributor,
|
||||
});
|
||||
const Avatar({super.key, required this.contributor});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -201,18 +191,11 @@ class Avatar extends StatelessWidget {
|
||||
width: 36,
|
||||
height: 36,
|
||||
child: CircleAvatar(
|
||||
foregroundImage: AssetImage(
|
||||
contributor.avatar,
|
||||
),
|
||||
foregroundImage: AssetImage(contributor.avatar),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
contributor.name,
|
||||
style: context.textTheme.bodySmall,
|
||||
)
|
||||
const SizedBox(height: 4),
|
||||
Text(contributor.name, style: context.textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
@@ -264,9 +247,6 @@ class _DeveloperModeDetectorState extends State<_DeveloperModeDetector> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: _handleTap,
|
||||
child: widget.child,
|
||||
);
|
||||
return GestureDetector(onTap: _handleTap, child: widget.child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,11 +55,9 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
rejectList: rejectList,
|
||||
),
|
||||
).then(
|
||||
(_) => setState(
|
||||
() {
|
||||
_updateInitList();
|
||||
},
|
||||
),
|
||||
(_) => setState(() {
|
||||
_updateInitList();
|
||||
}),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.search),
|
||||
@@ -70,39 +68,38 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
required bool isSelectedAll,
|
||||
required List<String> allValueList,
|
||||
}) {
|
||||
final tooltip = isSelectedAll
|
||||
? appLocalizations.cancelSelectAll
|
||||
: appLocalizations.selectAll;
|
||||
return IconButton(
|
||||
tooltip: tooltip,
|
||||
onPressed: () {
|
||||
ref.read(vpnSettingProvider.notifier).updateState((state) {
|
||||
final isAccept =
|
||||
state.accessControl.mode == AccessControlMode.acceptSelected;
|
||||
if (isSelectedAll) {
|
||||
return switch (isAccept) {
|
||||
true => state.copyWith.accessControl(
|
||||
acceptList: [],
|
||||
),
|
||||
false => state.copyWith.accessControl(
|
||||
rejectList: [],
|
||||
),
|
||||
};
|
||||
} else {
|
||||
return switch (isAccept) {
|
||||
true => state.copyWith.accessControl(
|
||||
acceptList: allValueList,
|
||||
),
|
||||
false => state.copyWith.accessControl(
|
||||
rejectList: allValueList,
|
||||
),
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
icon: isSelectedAll
|
||||
? const Icon(Icons.deselect)
|
||||
: const Icon(Icons.select_all),
|
||||
onPressed() {
|
||||
ref.read(vpnSettingProvider.notifier).updateState((state) {
|
||||
final isAccept =
|
||||
state.accessControl.mode == AccessControlMode.acceptSelected;
|
||||
if (isSelectedAll) {
|
||||
return switch (isAccept) {
|
||||
true => state.copyWith.accessControl(acceptList: []),
|
||||
false => state.copyWith.accessControl(rejectList: []),
|
||||
};
|
||||
} else {
|
||||
return switch (isAccept) {
|
||||
true => state.copyWith.accessControl(acceptList: allValueList),
|
||||
false => state.copyWith.accessControl(rejectList: allValueList),
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return FadeRotationScaleBox(
|
||||
child: isSelectedAll
|
||||
? IconButton(
|
||||
key: ValueKey(true),
|
||||
tooltip: appLocalizations.cancelSelectAll,
|
||||
onPressed: onPressed,
|
||||
icon: const Icon(Icons.deselect),
|
||||
)
|
||||
: IconButton(
|
||||
key: ValueKey(false),
|
||||
tooltip: appLocalizations.selectAll,
|
||||
onPressed: onPressed,
|
||||
icon: const Icon(Icons.select_all),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,20 +113,21 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
if (commonScaffoldState?.mounted != true) return;
|
||||
final selectedPackageNames =
|
||||
(await globalState.appController.safeRun<List<String>>(
|
||||
needLoading: true,
|
||||
() async {
|
||||
return await app?.getChinaPackageNames() ?? [];
|
||||
},
|
||||
))
|
||||
?.toSet() ??
|
||||
{};
|
||||
needLoading: true,
|
||||
() async {
|
||||
return await app?.getChinaPackageNames() ?? [];
|
||||
},
|
||||
))?.toSet() ??
|
||||
{};
|
||||
final acceptList = packageNames
|
||||
.where((item) => !selectedPackageNames.contains(item))
|
||||
.toList();
|
||||
final rejectList = packageNames
|
||||
.where((item) => selectedPackageNames.contains(item))
|
||||
.toList();
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
acceptList: acceptList,
|
||||
rejectList: rejectList,
|
||||
@@ -142,9 +140,7 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
onPressed: () async {
|
||||
final res = await showSheet<int>(
|
||||
context: context,
|
||||
props: SheetProps(
|
||||
isScrollControlled: true,
|
||||
),
|
||||
props: SheetProps(isScrollControlled: true),
|
||||
builder: (_, type) {
|
||||
return AdaptiveSheetScaffold(
|
||||
type: type,
|
||||
@@ -168,14 +164,10 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
valueList.remove(package.packageName);
|
||||
}
|
||||
ref.read(vpnSettingProvider.notifier).updateState((state) {
|
||||
return switch (
|
||||
state.accessControl.mode == AccessControlMode.acceptSelected) {
|
||||
true => state.copyWith.accessControl(
|
||||
acceptList: valueList,
|
||||
),
|
||||
false => state.copyWith.accessControl(
|
||||
rejectList: valueList,
|
||||
),
|
||||
return switch (state.accessControl.mode ==
|
||||
AccessControlMode.acceptSelected) {
|
||||
true => state.copyWith.accessControl(acceptList: valueList),
|
||||
false => state.copyWith.accessControl(rejectList: valueList),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -206,10 +198,10 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
delegate: SwitchDelegate(
|
||||
value: accessControl.enable,
|
||||
onChanged: (enable) {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
enable: enable,
|
||||
),
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.accessControl(enable: enable),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -217,9 +209,7 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Divider(
|
||||
height: 12,
|
||||
),
|
||||
child: Divider(height: 12),
|
||||
),
|
||||
Flexible(
|
||||
child: DisabledMask(
|
||||
@@ -255,17 +245,13 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Flexible(
|
||||
child: SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
),
|
||||
const Flexible(child: SizedBox(width: 8)),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'${valueList.length}',
|
||||
@@ -273,18 +259,16 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
.textTheme
|
||||
.labelLarge
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(describe),
|
||||
)
|
||||
Flexible(child: Text(describe)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -293,9 +277,7 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: _buildSearchButton(),
|
||||
),
|
||||
Flexible(child: _buildSearchButton()),
|
||||
Flexible(
|
||||
child: _buildSelectedAllButton(
|
||||
isSelectedAll:
|
||||
@@ -303,9 +285,7 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
allValueList: packageNameList,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: _buildSettingButton(),
|
||||
),
|
||||
Flexible(child: _buildSettingButton()),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -315,41 +295,42 @@ class _AccessViewState extends ConsumerState<AccessView> {
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: FutureBuilder(
|
||||
future: _completer.future,
|
||||
builder: (_, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
return packages.isEmpty
|
||||
? NullStatus(
|
||||
label: appLocalizations.noData,
|
||||
)
|
||||
: CommonScrollBar(
|
||||
future: _completer.future,
|
||||
builder: (_, snapshot) {
|
||||
if (snapshot.connectionState != ConnectionState.done) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return packages.isEmpty
|
||||
? NullStatus(label: appLocalizations.noData)
|
||||
: CommonScrollBar(
|
||||
controller: _controller,
|
||||
child: ListView.builder(
|
||||
controller: _controller,
|
||||
child: ListView.builder(
|
||||
controller: _controller,
|
||||
itemCount: packages.length,
|
||||
itemExtent: 72,
|
||||
itemBuilder: (_, index) {
|
||||
final package = packages[index];
|
||||
return PackageListItem(
|
||||
key: Key(package.packageName),
|
||||
package: package,
|
||||
value: valueList
|
||||
.contains(package.packageName),
|
||||
isActive: accessControl.enable,
|
||||
onChanged: (value) {
|
||||
_handleSelected(
|
||||
valueList, package, value);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
)
|
||||
itemCount: packages.length,
|
||||
itemExtent: 72,
|
||||
itemBuilder: (_, index) {
|
||||
final package = packages[index];
|
||||
return PackageListItem(
|
||||
key: Key(package.packageName),
|
||||
package: package,
|
||||
value: valueList.contains(
|
||||
package.packageName,
|
||||
),
|
||||
isActive: accessControl.enable,
|
||||
onChanged: (value) {
|
||||
_handleSelected(
|
||||
valueList,
|
||||
package,
|
||||
value,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -400,22 +381,15 @@ class PackageListItem extends StatelessWidget {
|
||||
),
|
||||
title: Text(
|
||||
package.label,
|
||||
style: const TextStyle(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
style: const TextStyle(overflow: TextOverflow.ellipsis),
|
||||
maxLines: 1,
|
||||
),
|
||||
subtitle: Text(
|
||||
package.packageName,
|
||||
style: const TextStyle(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
style: const TextStyle(overflow: TextOverflow.ellipsis),
|
||||
maxLines: 1,
|
||||
),
|
||||
delegate: CheckboxDelegate(
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
delegate: CheckboxDelegate(value: value, onChanged: onChanged),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -444,9 +418,7 @@ class AccessControlSearchDelegate extends SearchDelegate {
|
||||
},
|
||||
icon: const Icon(Icons.clear),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
)
|
||||
const SizedBox(width: 8),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -461,21 +433,21 @@ class AccessControlSearchDelegate extends SearchDelegate {
|
||||
}
|
||||
|
||||
void _handleSelected(
|
||||
WidgetRef ref, List<String> valueList, Package package, bool? value) {
|
||||
WidgetRef ref,
|
||||
List<String> valueList,
|
||||
Package package,
|
||||
bool? value,
|
||||
) {
|
||||
if (value == true) {
|
||||
valueList.add(package.packageName);
|
||||
} else {
|
||||
valueList.remove(package.packageName);
|
||||
}
|
||||
ref.read(vpnSettingProvider.notifier).updateState((state) {
|
||||
return switch (
|
||||
state.accessControl.mode == AccessControlMode.acceptSelected) {
|
||||
true => state.copyWith.accessControl(
|
||||
acceptList: valueList,
|
||||
),
|
||||
false => state.copyWith.accessControl(
|
||||
rejectList: valueList,
|
||||
),
|
||||
return switch (state.accessControl.mode ==
|
||||
AccessControlMode.acceptSelected) {
|
||||
true => state.copyWith.accessControl(acceptList: valueList),
|
||||
false => state.copyWith.accessControl(rejectList: valueList),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -483,7 +455,7 @@ class AccessControlSearchDelegate extends SearchDelegate {
|
||||
Widget _packageList() {
|
||||
final lowQuery = query.toLowerCase();
|
||||
return Consumer(
|
||||
builder: (context, ref, __) {
|
||||
builder: (context, ref, _) {
|
||||
final vm3 = ref.watch(
|
||||
packageListSelectorStateProvider.select(
|
||||
(state) => VM3(
|
||||
@@ -521,12 +493,7 @@ class AccessControlSearchDelegate extends SearchDelegate {
|
||||
value: valueList.contains(package.packageName),
|
||||
isActive: isAccessControl,
|
||||
onChanged: (value) {
|
||||
_handleSelected(
|
||||
ref,
|
||||
valueList,
|
||||
package,
|
||||
value,
|
||||
);
|
||||
_handleSelected(ref, valueList, package, value);
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -548,9 +515,7 @@ class AccessControlSearchDelegate extends SearchDelegate {
|
||||
}
|
||||
|
||||
class AccessControlPanel extends ConsumerStatefulWidget {
|
||||
const AccessControlPanel({
|
||||
super.key,
|
||||
});
|
||||
const AccessControlPanel({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState createState() => _AccessControlPanelState();
|
||||
@@ -595,7 +560,7 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
builder: (_, ref, _) {
|
||||
final accessControlMode = ref.watch(
|
||||
vpnSettingProvider.select((state) => state.accessControl.mode),
|
||||
);
|
||||
@@ -610,18 +575,19 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
),
|
||||
isSelected: accessControlMode == item,
|
||||
onPressed: () {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
mode: item,
|
||||
),
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) =>
|
||||
state.copyWith.accessControl(mode: item),
|
||||
);
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -634,7 +600,7 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
builder: (_, ref, _) {
|
||||
final accessSortType = ref.watch(
|
||||
vpnSettingProvider.select((state) => state.accessControl.sort),
|
||||
);
|
||||
@@ -649,10 +615,11 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
),
|
||||
isSelected: accessSortType == item,
|
||||
onPressed: () {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
sort: item,
|
||||
),
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) =>
|
||||
state.copyWith.accessControl(sort: item),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -673,7 +640,7 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
builder: (_, ref, _) {
|
||||
final vm2 = ref.watch(
|
||||
vpnSettingProvider.select(
|
||||
(state) => VM2(
|
||||
@@ -689,7 +656,9 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
appLocalizations.systemApp,
|
||||
isSelected: vm2.a == false,
|
||||
onPressed: () {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
isFilterSystemApp: !vm2.a,
|
||||
),
|
||||
@@ -700,18 +669,20 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
appLocalizations.noNetworkApp,
|
||||
isSelected: vm2.b == false,
|
||||
onPressed: () {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.accessControl(
|
||||
isFilterNonInternetApp: !vm2.b,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -719,31 +690,25 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
Future<void> _copyToClipboard() async {
|
||||
await globalState.appController.safeRun(() {
|
||||
final data = globalState.config.vpnProps.accessControl.toJson();
|
||||
Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: json.encode(data),
|
||||
),
|
||||
);
|
||||
Clipboard.setData(ClipboardData(text: json.encode(data)));
|
||||
});
|
||||
if (!mounted) return;
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
Future<void> _pasteToClipboard() async {
|
||||
await globalState.appController.safeRun(
|
||||
() async {
|
||||
final data = await Clipboard.getData('text/plain');
|
||||
final text = data?.text;
|
||||
if (text == null) return;
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
accessControl: AccessControl.fromJson(
|
||||
json.decode(text),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
await globalState.appController.safeRun(() async {
|
||||
final data = await Clipboard.getData('text/plain');
|
||||
final text = data?.text;
|
||||
if (text == null) return;
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
accessControl: AccessControl.fromJson(json.decode(text)),
|
||||
),
|
||||
);
|
||||
});
|
||||
if (!mounted) return;
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
@@ -753,9 +718,7 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
title: appLocalizations.action,
|
||||
items: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Wrap(
|
||||
runSpacing: 16,
|
||||
spacing: 16,
|
||||
@@ -776,10 +739,10 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
|
||||
avatar: const Icon(Icons.content_copy),
|
||||
label: appLocalizations.clipboardExport,
|
||||
onPressed: _copyToClipboard,
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,11 +19,9 @@ class CloseConnectionsItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: closeConnections,
|
||||
onChanged: (value) async {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
closeConnections: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(closeConnections: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -44,10 +42,10 @@ class UsageItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: onlyStatisticsProxy,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
onlyStatisticsProxy: value,
|
||||
),
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(onlyStatisticsProxy: value),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -69,11 +67,9 @@ class MinimizeItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: minimizeOnExit,
|
||||
onChanged: (bool value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
minimizeOnExit: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(minimizeOnExit: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -94,11 +90,9 @@ class AutoLaunchItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: autoLaunch,
|
||||
onChanged: (bool value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
autoLaunch: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(autoLaunch: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -119,11 +113,9 @@ class SilentLaunchItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: silentLaunch,
|
||||
onChanged: (bool value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
silentLaunch: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(silentLaunch: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -144,11 +136,9 @@ class AutoRunItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: autoRun,
|
||||
onChanged: (bool value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
autoRun: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(autoRun: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -169,11 +159,9 @@ class HiddenItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: hidden,
|
||||
onChanged: (value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
hidden: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(hidden: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -194,11 +182,9 @@ class AnimateTabItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: isAnimateToPage,
|
||||
onChanged: (value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
isAnimateToPage: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(isAnimateToPage: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -219,11 +205,32 @@ class OpenLogsItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: openLogs,
|
||||
onChanged: (bool value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
openLogs: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(openLogs: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CrashlyticsItem extends ConsumerWidget {
|
||||
const CrashlyticsItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final crashlytics = ref.watch(
|
||||
appSettingProvider.select((state) => state.crashlytics),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.crashlytics),
|
||||
subtitle: Text(appLocalizations.crashlyticsTip),
|
||||
delegate: SwitchDelegate(
|
||||
value: crashlytics,
|
||||
onChanged: (bool value) {
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(crashlytics: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -244,11 +251,9 @@ class AutoCheckUpdateItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: autoCheckUpdate,
|
||||
onChanged: (bool value) {
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
autoCheckUpdate: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(autoCheckUpdate: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -267,18 +272,14 @@ class ApplicationSettingView extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
List<Widget> items = [
|
||||
MinimizeItem(),
|
||||
if (system.isDesktop) ...[
|
||||
AutoLaunchItem(),
|
||||
SilentLaunchItem(),
|
||||
],
|
||||
if (system.isDesktop) ...[AutoLaunchItem(), SilentLaunchItem()],
|
||||
AutoRunItem(),
|
||||
if (system.isAndroid) ...[
|
||||
HiddenItem(),
|
||||
],
|
||||
if (system.isAndroid) ...[HiddenItem()],
|
||||
AnimateTabItem(),
|
||||
OpenLogsItem(),
|
||||
CloseConnectionsItem(),
|
||||
UsageItem(),
|
||||
if (system.isAndroid) CrashlyticsItem(),
|
||||
AutoCheckUpdateItem(),
|
||||
];
|
||||
return ListView.separated(
|
||||
@@ -286,10 +287,8 @@ class ApplicationSettingView extends StatelessWidget {
|
||||
final item = items[index];
|
||||
return item;
|
||||
},
|
||||
separatorBuilder: (_, __) {
|
||||
return const Divider(
|
||||
height: 0,
|
||||
);
|
||||
separatorBuilder: (_, _) {
|
||||
return const Divider(height: 0);
|
||||
},
|
||||
itemCount: items.length,
|
||||
);
|
||||
|
||||
@@ -20,9 +20,7 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
|
||||
Future<void> _showAddWebDAV(DAV? dav) async {
|
||||
await globalState.showCommonDialog<String>(
|
||||
child: WebDAVFormDialog(
|
||||
dav: dav?.copyWith(),
|
||||
),
|
||||
child: WebDAVFormDialog(dav: dav?.copyWith()),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,7 +62,9 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
}
|
||||
|
||||
Future<void> _handleRecoveryOnWebDAV(
|
||||
BuildContext context, DAVClient client) async {
|
||||
BuildContext context,
|
||||
DAVClient client,
|
||||
) async {
|
||||
final recoveryOption = await globalState.showCommonDialog<RecoveryOption>(
|
||||
child: const RecoveryOptionsDialog(),
|
||||
);
|
||||
@@ -84,6 +84,7 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
return true;
|
||||
},
|
||||
title: appLocalizations.backup,
|
||||
needLoading: true,
|
||||
);
|
||||
if (res != true) return;
|
||||
globalState.showMessage(
|
||||
@@ -92,9 +93,7 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _recoveryOnLocal(
|
||||
RecoveryOption recoveryOption,
|
||||
) async {
|
||||
Future<void> _recoveryOnLocal(RecoveryOption recoveryOption) async {
|
||||
final file = await picker.pickerFile();
|
||||
final data = file?.bytes;
|
||||
if (data == null) return;
|
||||
@@ -128,35 +127,29 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(appDAVSettingProvider.notifier).updateState(
|
||||
(state) => state?.copyWith(
|
||||
fileName: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appDAVSettingProvider.notifier)
|
||||
.updateState((state) => state?.copyWith(fileName: value));
|
||||
}
|
||||
|
||||
Future<void> _handleUpdateRecoveryStrategy(WidgetRef ref) async {
|
||||
final recoveryStrategy = ref.read(appSettingProvider.select(
|
||||
(state) => state.recoveryStrategy,
|
||||
));
|
||||
final recoveryStrategy = ref.read(
|
||||
appSettingProvider.select((state) => state.recoveryStrategy),
|
||||
);
|
||||
final res = await globalState.showCommonDialog(
|
||||
child: OptionsDialog<RecoveryStrategy>(
|
||||
title: appLocalizations.recoveryStrategy,
|
||||
options: RecoveryStrategy.values,
|
||||
textBuilder: (mode) => Intl.message(
|
||||
'recoveryStrategy_${mode.name}',
|
||||
),
|
||||
textBuilder: (mode) => Intl.message('recoveryStrategy_${mode.name}'),
|
||||
value: recoveryStrategy,
|
||||
),
|
||||
);
|
||||
if (res == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
recoveryStrategy: res,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(recoveryStrategy: res));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -175,9 +168,7 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
onPressed: () {
|
||||
_showAddWebDAV(dav);
|
||||
},
|
||||
child: Text(
|
||||
appLocalizations.bind,
|
||||
),
|
||||
child: Text(appLocalizations.bind),
|
||||
),
|
||||
)
|
||||
else ...[
|
||||
@@ -203,23 +194,23 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
child: FadeThroughBox(
|
||||
child:
|
||||
snapshot.connectionState != ConnectionState.done
|
||||
? const SizedBox(
|
||||
width: 12,
|
||||
height: 12,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: snapshot.data == true
|
||||
? Colors.green
|
||||
: Colors.red,
|
||||
),
|
||||
width: 12,
|
||||
height: 12,
|
||||
),
|
||||
? const SizedBox(
|
||||
width: 12,
|
||||
height: 12,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 1,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: snapshot.data == true
|
||||
? Colors.green
|
||||
: Colors.red,
|
||||
),
|
||||
width: 12,
|
||||
height: 12,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -231,14 +222,10 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
onPressed: () {
|
||||
_showAddWebDAV(dav);
|
||||
},
|
||||
child: Text(
|
||||
appLocalizations.edit,
|
||||
),
|
||||
child: Text(appLocalizations.edit),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
ListItem.input(
|
||||
title: Text(appLocalizations.file),
|
||||
subtitle: Text(dav.fileName),
|
||||
@@ -282,25 +269,27 @@ class BackupAndRecovery extends ConsumerWidget {
|
||||
subtitle: Text(appLocalizations.localRecoveryDesc),
|
||||
),
|
||||
ListHeader(title: appLocalizations.options),
|
||||
Consumer(builder: (_, ref, __) {
|
||||
final recoveryStrategy = ref.watch(appSettingProvider.select(
|
||||
(state) => state.recoveryStrategy,
|
||||
));
|
||||
return ListItem(
|
||||
onTap: () {
|
||||
_handleUpdateRecoveryStrategy(ref);
|
||||
},
|
||||
title: Text(appLocalizations.recoveryStrategy),
|
||||
trailing: FilledButton(
|
||||
onPressed: () {
|
||||
Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final recoveryStrategy = ref.watch(
|
||||
appSettingProvider.select((state) => state.recoveryStrategy),
|
||||
);
|
||||
return ListItem(
|
||||
onTap: () {
|
||||
_handleUpdateRecoveryStrategy(ref);
|
||||
},
|
||||
child: Text(
|
||||
Intl.message('recoveryStrategy_${recoveryStrategy.name}'),
|
||||
title: Text(appLocalizations.recoveryStrategy),
|
||||
trailing: FilledButton(
|
||||
onPressed: () {
|
||||
_handleUpdateRecoveryStrategy(ref);
|
||||
},
|
||||
child: Text(
|
||||
Intl.message('recoveryStrategy_${recoveryStrategy.name}'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -323,10 +312,7 @@ class _RecoveryOptionsDialogState extends State<RecoveryOptionsDialog> {
|
||||
Widget build(BuildContext context) {
|
||||
return CommonDialog(
|
||||
title: appLocalizations.recovery,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 16,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
|
||||
child: Wrap(
|
||||
children: [
|
||||
ListItem(
|
||||
@@ -340,7 +326,7 @@ class _RecoveryOptionsDialogState extends State<RecoveryOptionsDialog> {
|
||||
_handleOnTab(RecoveryOption.all);
|
||||
},
|
||||
title: Text(appLocalizations.recoveryAll),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -398,14 +384,8 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
|
||||
title: appLocalizations.webDAVConfiguration,
|
||||
actions: [
|
||||
if (widget.dav != null)
|
||||
TextButton(
|
||||
onPressed: _delete,
|
||||
child: Text(appLocalizations.delete),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: _submit,
|
||||
child: Text(appLocalizations.save),
|
||||
)
|
||||
TextButton(onPressed: _delete, child: Text(appLocalizations.delete)),
|
||||
TextButton(onPressed: _submit, child: Text(appLocalizations.save)),
|
||||
],
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
@@ -445,7 +425,7 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _obscureController,
|
||||
builder: (_, obscure, __) {
|
||||
builder: (_, obscure, _) {
|
||||
return TextFormField(
|
||||
controller: passwordController,
|
||||
obscureText: obscure,
|
||||
@@ -464,8 +444,9 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
|
||||
),
|
||||
validator: (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations
|
||||
.emptyTip(appLocalizations.password);
|
||||
return appLocalizations.emptyTip(
|
||||
appLocalizations.password,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -27,9 +27,7 @@ class _ConfigViewState extends State<ConfigView> {
|
||||
leading: const Icon(Icons.build),
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.general,
|
||||
widget: generateListView(
|
||||
generalItems,
|
||||
),
|
||||
widget: generateListView(generalItems),
|
||||
blur: false,
|
||||
),
|
||||
),
|
||||
@@ -41,35 +39,35 @@ class _ConfigViewState extends State<ConfigView> {
|
||||
title: appLocalizations.network,
|
||||
blur: false,
|
||||
actions: [
|
||||
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(vpnSettingProvider.notifier).updateState(
|
||||
(state) => defaultVpnProps.copyWith(
|
||||
accessControl: state.accessControl,
|
||||
),
|
||||
);
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
tun: defaultTun,
|
||||
),
|
||||
);
|
||||
},
|
||||
tooltip: appLocalizations.reset,
|
||||
icon: const Icon(
|
||||
Icons.replay,
|
||||
),
|
||||
);
|
||||
})
|
||||
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(vpnSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => defaultVpnProps.copyWith(
|
||||
accessControl: state.accessControl,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(tun: defaultTun),
|
||||
);
|
||||
},
|
||||
tooltip: appLocalizations.reset,
|
||||
icon: const Icon(Icons.replay),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
widget: const NetworkListView(),
|
||||
),
|
||||
@@ -81,44 +79,34 @@ class _ConfigViewState extends State<ConfigView> {
|
||||
delegate: OpenDelegate(
|
||||
title: 'DNS',
|
||||
actions: [
|
||||
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,
|
||||
),
|
||||
);
|
||||
})
|
||||
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,
|
||||
),
|
||||
)
|
||||
),
|
||||
];
|
||||
return generateListView(
|
||||
items
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
return generateListView(items.separated(const Divider(height: 0)).toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,9 @@ class StatusItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final enable =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.dns.enable));
|
||||
final enable = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.enable),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.status),
|
||||
subtitle: Text(appLocalizations.statusDesc),
|
||||
@@ -51,8 +52,9 @@ class ListenItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final listen =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.dns.listen));
|
||||
final listen = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.listen),
|
||||
);
|
||||
return ListItem.input(
|
||||
title: Text(appLocalizations.listen),
|
||||
subtitle: Text(listen),
|
||||
@@ -83,8 +85,9 @@ class PreferH3Item extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final preferH3 = ref
|
||||
.watch(patchClashConfigProvider.select((state) => state.dns.preferH3));
|
||||
final preferH3 = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.preferH3),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
title: const Text('PreferH3'),
|
||||
subtitle: Text(appLocalizations.preferH3Desc),
|
||||
@@ -218,10 +221,11 @@ class FakeIpFilterItem extends StatelessWidget {
|
||||
blur: false,
|
||||
title: appLocalizations.fakeipFilter,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
builder: (_, ref, _) {
|
||||
final fakeIpFilter = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fakeIpFilter),
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.fakeIpFilter,
|
||||
),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.fakeipFilter,
|
||||
@@ -230,9 +234,10 @@ class FakeIpFilterItem extends StatelessWidget {
|
||||
onChange: (items) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith.dns(
|
||||
fakeIpFilter: List.from(items),
|
||||
));
|
||||
.updateState(
|
||||
(state) =>
|
||||
state.copyWith.dns(fakeIpFilter: List.from(items)),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -253,24 +258,29 @@ class DefaultNameserverItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
blur: false,
|
||||
title: appLocalizations.defaultNameserver,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final defaultNameserver = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.defaultNameserver),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.defaultNameserver,
|
||||
items: defaultNameserver,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.dns(
|
||||
defaultNameserver: List.from(items),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
widget: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final defaultNameserver = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.defaultNameserver,
|
||||
),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.defaultNameserver,
|
||||
items: defaultNameserver,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns(
|
||||
defaultNameserver: List.from(items),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -287,23 +297,26 @@ class NameserverItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
title: appLocalizations.nameserver,
|
||||
blur: false,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final nameserver = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.nameserver),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.nameserver,
|
||||
items: nameserver,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.dns(
|
||||
nameserver: List.from(items),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
widget: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final nameserver = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.nameserver),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.nameserver,
|
||||
items: nameserver,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) =>
|
||||
state.copyWith.dns(nameserver: List.from(items)),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -346,9 +359,9 @@ class UseSystemHostsItem extends ConsumerWidget {
|
||||
onChanged: (bool value) async {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith.dns(
|
||||
useSystemHosts: value,
|
||||
));
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns(useSystemHosts: value),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -366,25 +379,28 @@ class NameserverPolicyItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
blur: false,
|
||||
title: appLocalizations.nameserverPolicy,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final nameserverPolicy = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.nameserverPolicy),
|
||||
);
|
||||
return MapInputPage(
|
||||
title: appLocalizations.nameserverPolicy,
|
||||
map: nameserverPolicy,
|
||||
titleBuilder: (item) => Text(item.key),
|
||||
subtitleBuilder: (item) => Text(item.value),
|
||||
onChange: (value) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.dns(
|
||||
nameserverPolicy: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
widget: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final nameserverPolicy = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.nameserverPolicy,
|
||||
),
|
||||
);
|
||||
return MapInputPage(
|
||||
title: appLocalizations.nameserverPolicy,
|
||||
map: nameserverPolicy,
|
||||
titleBuilder: (item) => Text(item.key),
|
||||
subtitleBuilder: (item) => Text(item.value),
|
||||
onChange: (value) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns(nameserverPolicy: value),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -402,17 +418,20 @@ class ProxyServerNameserverItem extends StatelessWidget {
|
||||
blur: false,
|
||||
title: appLocalizations.proxyNameserver,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
builder: (_, ref, _) {
|
||||
final proxyServerNameserver = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.proxyServerNameserver),
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.proxyServerNameserver,
|
||||
),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.proxyNameserver,
|
||||
items: proxyServerNameserver,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns(
|
||||
proxyServerNameserver: List.from(items),
|
||||
),
|
||||
@@ -437,23 +456,25 @@ class FallbackItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
blur: false,
|
||||
title: appLocalizations.fallback,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final fallback = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.fallback),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.fallback,
|
||||
items: fallback,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.dns(
|
||||
fallback: List.from(items),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
widget: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final fallback = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.dns.fallback),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.fallback,
|
||||
items: fallback,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns(fallback: List.from(items)),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -465,8 +486,9 @@ class GeoipItem extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final geoip = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fallbackFilter.geoip),
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.fallbackFilter.geoip,
|
||||
),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
title: const Text('Geoip'),
|
||||
@@ -475,9 +497,9 @@ class GeoipItem extends ConsumerWidget {
|
||||
onChanged: (bool value) async {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith.dns.fallbackFilter(
|
||||
geoip: value,
|
||||
));
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns.fallbackFilter(geoip: value),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -490,8 +512,9 @@ class GeoipCodeItem extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final geoipCode = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fallbackFilter.geoipCode),
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.fallbackFilter.geoipCode,
|
||||
),
|
||||
);
|
||||
return ListItem.input(
|
||||
title: Text(appLocalizations.geoipCode),
|
||||
@@ -509,10 +532,10 @@ class GeoipCodeItem extends ConsumerWidget {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.dns.fallbackFilter(
|
||||
geoipCode: value,
|
||||
),
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns.fallbackFilter(geoipCode: value),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -530,24 +553,29 @@ class GeositeItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
blur: false,
|
||||
title: 'Geosite',
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final geosite = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fallbackFilter.geosite),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: 'Geosite',
|
||||
items: geosite,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.dns.fallbackFilter(
|
||||
geosite: List.from(items),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
widget: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final geosite = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.fallbackFilter.geosite,
|
||||
),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: 'Geosite',
|
||||
items: geosite,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns.fallbackFilter(
|
||||
geosite: List.from(items),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -563,24 +591,29 @@ class IpcidrItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
blur: false,
|
||||
title: appLocalizations.ipcidr,
|
||||
widget: Consumer(builder: (_, ref, ___) {
|
||||
final ipcidr = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fallbackFilter.ipcidr),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.ipcidr,
|
||||
items: ipcidr,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith.dns.fallbackFilter(
|
||||
widget: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final ipcidr = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.fallbackFilter.ipcidr,
|
||||
),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.ipcidr,
|
||||
items: ipcidr,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns.fallbackFilter(
|
||||
ipcidr: List.from(items),
|
||||
));
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -596,24 +629,29 @@ class DomainItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
blur: false,
|
||||
title: appLocalizations.domain,
|
||||
widget: Consumer(builder: (_, ref, __) {
|
||||
final domain = ref.watch(
|
||||
patchClashConfigProvider
|
||||
.select((state) => state.dns.fallbackFilter.domain),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.domain,
|
||||
items: domain,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.dns.fallbackFilter(
|
||||
domain: List.from(items),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
widget: Consumer(
|
||||
builder: (_, ref, _) {
|
||||
final domain = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.dns.fallbackFilter.domain,
|
||||
),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.domain,
|
||||
items: domain,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith.dns.fallbackFilter(
|
||||
domain: List.from(items),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -680,8 +718,6 @@ class DnsListView extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
return generateListView(
|
||||
dnsItems,
|
||||
);
|
||||
return generateListView(dnsItems);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,9 @@ class LogLevelItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final logLevel =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.logLevel));
|
||||
final logLevel = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.logLevel),
|
||||
);
|
||||
return ListItem<LogLevel>.options(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: Text(appLocalizations.logLevel),
|
||||
@@ -25,11 +26,9 @@ class LogLevelItem extends ConsumerWidget {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
logLevel: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(logLevel: value));
|
||||
},
|
||||
textBuilder: (logLevel) => logLevel.name,
|
||||
value: logLevel,
|
||||
@@ -43,26 +42,21 @@ class UaItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final globalUa =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.globalUa));
|
||||
final globalUa = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.globalUa),
|
||||
);
|
||||
return ListItem<String?>.options(
|
||||
leading: const Icon(Icons.computer_outlined),
|
||||
title: const Text('UA'),
|
||||
subtitle: Text(globalUa ?? appLocalizations.defaultText),
|
||||
delegate: OptionsDelegate<String?>(
|
||||
title: 'UA',
|
||||
options: [
|
||||
null,
|
||||
'clash-verge/v1.6.6',
|
||||
'ClashforWindows/0.19.23',
|
||||
],
|
||||
options: [null, 'clash-verge/v2.4.2', 'ClashforWindows/0.19.23'],
|
||||
value: globalUa,
|
||||
onChanged: (value) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
globalUa: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(globalUa: value));
|
||||
},
|
||||
textBuilder: (ua) => ua ?? appLocalizations.defaultText,
|
||||
),
|
||||
@@ -76,7 +70,8 @@ class KeepAliveIntervalItem extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final keepAliveInterval = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.keepAliveInterval));
|
||||
patchClashConfigProvider.select((state) => state.keepAliveInterval),
|
||||
);
|
||||
return ListItem.input(
|
||||
leading: const Icon(Icons.timer_outlined),
|
||||
title: Text(appLocalizations.keepAliveIntervalDesc),
|
||||
@@ -101,10 +96,10 @@ class KeepAliveIntervalItem extends ConsumerWidget {
|
||||
return;
|
||||
}
|
||||
final intValue = int.parse(value);
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
keepAliveInterval: intValue,
|
||||
),
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(keepAliveInterval: intValue),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -117,8 +112,9 @@ class TestUrlItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final testUrl =
|
||||
ref.watch(appSettingProvider.select((state) => state.testUrl));
|
||||
final testUrl = ref.watch(
|
||||
appSettingProvider.select((state) => state.testUrl),
|
||||
);
|
||||
return ListItem.input(
|
||||
leading: const Icon(Icons.timeline),
|
||||
title: Text(appLocalizations.testUrl),
|
||||
@@ -140,11 +136,9 @@ class TestUrlItem extends ConsumerWidget {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(appSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
testUrl: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(appSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(testUrl: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -155,16 +149,15 @@ class PortItem extends ConsumerWidget {
|
||||
const PortItem({super.key});
|
||||
|
||||
Future<void> handleShowPortDialog() async {
|
||||
await globalState.showCommonDialog(
|
||||
child: _PortDialog(),
|
||||
);
|
||||
await globalState.showCommonDialog(child: _PortDialog());
|
||||
// inputDelegate.onChanged(value);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final mixedPort =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.mixedPort));
|
||||
final mixedPort = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.mixedPort),
|
||||
);
|
||||
return ListItem(
|
||||
leading: const Icon(Icons.adjust_outlined),
|
||||
title: Text(appLocalizations.port),
|
||||
@@ -218,20 +211,19 @@ class HostsItem extends StatelessWidget {
|
||||
blur: false,
|
||||
title: 'Hosts',
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
final hosts = ref
|
||||
.watch(patchClashConfigProvider.select((state) => state.hosts));
|
||||
builder: (_, ref, _) {
|
||||
final hosts = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.hosts),
|
||||
);
|
||||
return MapInputPage(
|
||||
title: 'Hosts',
|
||||
map: hosts,
|
||||
titleBuilder: (item) => Text(item.key),
|
||||
subtitleBuilder: (item) => Text(item.value),
|
||||
onChange: (value) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
hosts: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(hosts: value));
|
||||
},
|
||||
);
|
||||
},
|
||||
@@ -246,8 +238,9 @@ class Ipv6Item extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final ipv6 =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.ipv6));
|
||||
final ipv6 = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.ipv6),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.water_outlined),
|
||||
title: const Text('IPv6'),
|
||||
@@ -255,11 +248,9 @@ class Ipv6Item extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: ipv6,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
ipv6: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(ipv6: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -271,8 +262,9 @@ class AllowLanItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final allowLan =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.allowLan));
|
||||
final allowLan = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.allowLan),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.device_hub),
|
||||
title: Text(appLocalizations.allowLan),
|
||||
@@ -280,11 +272,9 @@ class AllowLanItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: allowLan,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
allowLan: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(allowLan: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -296,8 +286,9 @@ class UnifiedDelayItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final unifiedDelay = ref
|
||||
.watch(patchClashConfigProvider.select((state) => state.unifiedDelay));
|
||||
final unifiedDelay = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.unifiedDelay),
|
||||
);
|
||||
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.compress_outlined),
|
||||
@@ -306,11 +297,9 @@ class UnifiedDelayItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: unifiedDelay,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
unifiedDelay: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(unifiedDelay: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -322,8 +311,11 @@ class FindProcessItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final findProcess = ref.watch(patchClashConfigProvider
|
||||
.select((state) => state.findProcessMode == FindProcessMode.always));
|
||||
final findProcess = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.findProcessMode == FindProcessMode.always,
|
||||
),
|
||||
);
|
||||
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.polymer_outlined),
|
||||
@@ -332,10 +324,13 @@ class FindProcessItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: findProcess,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
findProcessMode:
|
||||
value ? FindProcessMode.always : FindProcessMode.off,
|
||||
findProcessMode: value
|
||||
? FindProcessMode.always
|
||||
: FindProcessMode.off,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -349,8 +344,9 @@ class TcpConcurrentItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final tcpConcurrent = ref
|
||||
.watch(patchClashConfigProvider.select((state) => state.tcpConcurrent));
|
||||
final tcpConcurrent = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.tcpConcurrent),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.double_arrow_outlined),
|
||||
title: Text(appLocalizations.tcpConcurrent),
|
||||
@@ -358,11 +354,9 @@ class TcpConcurrentItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: tcpConcurrent,
|
||||
onChanged: (value) async {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
tcpConcurrent: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith(tcpConcurrent: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -374,8 +368,11 @@ class GeodataLoaderItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final isMemconservative = ref.watch(patchClashConfigProvider.select(
|
||||
(state) => state.geodataLoader == GeodataLoader.memconservative));
|
||||
final isMemconservative = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.geodataLoader == GeodataLoader.memconservative,
|
||||
),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.memory),
|
||||
title: Text(appLocalizations.geodataLoader),
|
||||
@@ -383,7 +380,9 @@ class GeodataLoaderItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: isMemconservative,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
geodataLoader: value
|
||||
? GeodataLoader.memconservative
|
||||
@@ -401,8 +400,11 @@ class ExternalControllerItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final hasExternalController = ref.watch(patchClashConfigProvider.select(
|
||||
(state) => state.externalController == ExternalControllerStatus.open));
|
||||
final hasExternalController = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.externalController == ExternalControllerStatus.open,
|
||||
),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
leading: const Icon(Icons.api_outlined),
|
||||
title: Text(appLocalizations.externalController),
|
||||
@@ -410,7 +412,9 @@ class ExternalControllerItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: hasExternalController,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
externalController: value
|
||||
? ExternalControllerStatus.open
|
||||
@@ -437,13 +441,7 @@ final generalItems = <Widget>[
|
||||
TcpConcurrentItem(),
|
||||
GeodataLoaderItem(),
|
||||
ExternalControllerItem(),
|
||||
]
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
].separated(const Divider(height: 0)).toList();
|
||||
|
||||
class _PortDialog extends ConsumerStatefulWidget {
|
||||
const _PortDialog();
|
||||
@@ -465,42 +463,34 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final vm5 = ref.read(patchClashConfigProvider.select((state) {
|
||||
return VM5(
|
||||
a: state.mixedPort,
|
||||
b: state.port,
|
||||
c: state.socksPort,
|
||||
d: state.redirPort,
|
||||
e: state.tproxyPort,
|
||||
);
|
||||
}));
|
||||
_mixedPortController = TextEditingController(
|
||||
text: vm5.a.toString(),
|
||||
);
|
||||
_portController = TextEditingController(
|
||||
text: vm5.b.toString(),
|
||||
);
|
||||
_socksPortController = TextEditingController(
|
||||
text: vm5.c.toString(),
|
||||
);
|
||||
_redirPortController = TextEditingController(
|
||||
text: vm5.d.toString(),
|
||||
);
|
||||
_tProxyPortController = TextEditingController(
|
||||
text: vm5.e.toString(),
|
||||
final vm5 = ref.read(
|
||||
patchClashConfigProvider.select((state) {
|
||||
return VM5(
|
||||
a: state.mixedPort,
|
||||
b: state.port,
|
||||
c: state.socksPort,
|
||||
d: state.redirPort,
|
||||
e: state.tproxyPort,
|
||||
);
|
||||
}),
|
||||
);
|
||||
_mixedPortController = TextEditingController(text: vm5.a.toString());
|
||||
_portController = TextEditingController(text: vm5.b.toString());
|
||||
_socksPortController = TextEditingController(text: vm5.c.toString());
|
||||
_redirPortController = TextEditingController(text: vm5.d.toString());
|
||||
_tProxyPortController = TextEditingController(text: vm5.e.toString());
|
||||
}
|
||||
|
||||
Future<void> _handleReset() async {
|
||||
final res = await globalState.showMessage(
|
||||
message: TextSpan(
|
||||
text: appLocalizations.resetTip,
|
||||
),
|
||||
message: TextSpan(text: appLocalizations.resetTip),
|
||||
);
|
||||
if (res != true) {
|
||||
return;
|
||||
}
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
mixedPort: 7890,
|
||||
port: 0,
|
||||
@@ -516,7 +506,9 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
|
||||
void _handleUpdate() {
|
||||
if (_formKey.currentState?.validate() == false) return;
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(
|
||||
mixedPort: int.parse(_mixedPortController.text),
|
||||
port: int.parse(_portController.text),
|
||||
@@ -544,9 +536,7 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
children: [
|
||||
IconButton.filledTonal(
|
||||
onPressed: _handleMore,
|
||||
icon: CommonExpandIcon(
|
||||
expand: _isMore,
|
||||
),
|
||||
icon: CommonExpandIcon(expand: _isMore),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
@@ -554,17 +544,15 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
onPressed: _handleReset,
|
||||
child: Text(appLocalizations.reset),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
TextButton(
|
||||
onPressed: _handleUpdate,
|
||||
child: Text(appLocalizations.submit),
|
||||
)
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
child: Form(
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
@@ -592,23 +580,26 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations
|
||||
.emptyTip(appLocalizations.mixedPort);
|
||||
return appLocalizations.emptyTip(
|
||||
appLocalizations.mixedPort,
|
||||
);
|
||||
}
|
||||
final port = int.tryParse(value);
|
||||
if (port == null) {
|
||||
return appLocalizations
|
||||
.numberTip(appLocalizations.mixedPort);
|
||||
return appLocalizations.numberTip(
|
||||
appLocalizations.mixedPort,
|
||||
);
|
||||
}
|
||||
if (port < 1024 || port > 49151) {
|
||||
return appLocalizations
|
||||
.portTip(appLocalizations.mixedPort);
|
||||
return appLocalizations.portTip(
|
||||
appLocalizations.mixedPort,
|
||||
);
|
||||
}
|
||||
final ports = [
|
||||
_portController.text,
|
||||
_socksPortController.text,
|
||||
_tProxyPortController.text,
|
||||
_redirPortController.text
|
||||
_redirPortController.text,
|
||||
].map((item) => item.trim());
|
||||
if (ports.contains(value.trim())) {
|
||||
return appLocalizations.portConflictTip;
|
||||
@@ -649,7 +640,7 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
_mixedPortController.text,
|
||||
_socksPortController.text,
|
||||
_tProxyPortController.text,
|
||||
_redirPortController.text
|
||||
_redirPortController.text,
|
||||
].map((item) => item.trim());
|
||||
if (ports.contains(value.trim())) {
|
||||
return appLocalizations.portConflictTip;
|
||||
@@ -671,26 +662,29 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations
|
||||
.emptyTip(appLocalizations.socksPort);
|
||||
return appLocalizations.emptyTip(
|
||||
appLocalizations.socksPort,
|
||||
);
|
||||
}
|
||||
final port = int.tryParse(value);
|
||||
if (port == null) {
|
||||
return appLocalizations
|
||||
.numberTip(appLocalizations.socksPort);
|
||||
return appLocalizations.numberTip(
|
||||
appLocalizations.socksPort,
|
||||
);
|
||||
}
|
||||
if (port == 0) {
|
||||
return null;
|
||||
}
|
||||
if (port < 1024 || port > 49151) {
|
||||
return appLocalizations
|
||||
.portTip(appLocalizations.socksPort);
|
||||
return appLocalizations.portTip(
|
||||
appLocalizations.socksPort,
|
||||
);
|
||||
}
|
||||
final ports = [
|
||||
_portController.text,
|
||||
_mixedPortController.text,
|
||||
_tProxyPortController.text,
|
||||
_redirPortController.text
|
||||
_redirPortController.text,
|
||||
].map((item) => item.trim());
|
||||
if (ports.contains(value.trim())) {
|
||||
return appLocalizations.portConflictTip;
|
||||
@@ -712,26 +706,29 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations
|
||||
.emptyTip(appLocalizations.redirPort);
|
||||
return appLocalizations.emptyTip(
|
||||
appLocalizations.redirPort,
|
||||
);
|
||||
}
|
||||
final port = int.tryParse(value);
|
||||
if (port == null) {
|
||||
return appLocalizations
|
||||
.numberTip(appLocalizations.redirPort);
|
||||
return appLocalizations.numberTip(
|
||||
appLocalizations.redirPort,
|
||||
);
|
||||
}
|
||||
if (port == 0) {
|
||||
return null;
|
||||
}
|
||||
if (port < 1024 || port > 49151) {
|
||||
return appLocalizations
|
||||
.portTip(appLocalizations.redirPort);
|
||||
return appLocalizations.portTip(
|
||||
appLocalizations.redirPort,
|
||||
);
|
||||
}
|
||||
final ports = [
|
||||
_portController.text,
|
||||
_socksPortController.text,
|
||||
_tProxyPortController.text,
|
||||
_mixedPortController.text
|
||||
_mixedPortController.text,
|
||||
].map((item) => item.trim());
|
||||
if (ports.contains(value.trim())) {
|
||||
return appLocalizations.portConflictTip;
|
||||
@@ -753,13 +750,15 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return appLocalizations
|
||||
.emptyTip(appLocalizations.tproxyPort);
|
||||
return appLocalizations.emptyTip(
|
||||
appLocalizations.tproxyPort,
|
||||
);
|
||||
}
|
||||
final port = int.tryParse(value);
|
||||
if (port == null) {
|
||||
return appLocalizations
|
||||
.numberTip(appLocalizations.tproxyPort);
|
||||
return appLocalizations.numberTip(
|
||||
appLocalizations.tproxyPort,
|
||||
);
|
||||
}
|
||||
if (port == 0) {
|
||||
return null;
|
||||
@@ -773,7 +772,7 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
_portController.text,
|
||||
_socksPortController.text,
|
||||
_mixedPortController.text,
|
||||
_redirPortController.text
|
||||
_redirPortController.text,
|
||||
].map((item) => item.trim());
|
||||
if (ports.contains(value.trim())) {
|
||||
return appLocalizations.portConflictTip;
|
||||
@@ -782,7 +781,7 @@ class _PortDialogState extends ConsumerState<_PortDialog> {
|
||||
return null;
|
||||
},
|
||||
),
|
||||
]
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -13,19 +13,18 @@ class VPNItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final enable =
|
||||
ref.watch(vpnSettingProvider.select((state) => state.enable));
|
||||
final enable = ref.watch(
|
||||
vpnSettingProvider.select((state) => state.enable),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
title: const Text('VPN'),
|
||||
subtitle: Text(appLocalizations.vpnEnableDesc),
|
||||
delegate: SwitchDelegate(
|
||||
value: enable,
|
||||
onChanged: (value) async {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
enable: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(enable: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -37,8 +36,9 @@ class TUNItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final enable =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.tun.enable));
|
||||
final enable = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.tun.enable),
|
||||
);
|
||||
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.tun),
|
||||
@@ -46,11 +46,9 @@ class TUNItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: enable,
|
||||
onChanged: (value) async {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.tun(
|
||||
enable: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith.tun(enable: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -62,19 +60,18 @@ class AllowBypassItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final allowBypass =
|
||||
ref.watch(vpnSettingProvider.select((state) => state.allowBypass));
|
||||
final allowBypass = ref.watch(
|
||||
vpnSettingProvider.select((state) => state.allowBypass),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.allowBypass),
|
||||
subtitle: Text(appLocalizations.allowBypassDesc),
|
||||
delegate: SwitchDelegate(
|
||||
value: allowBypass,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
allowBypass: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(allowBypass: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -86,19 +83,18 @@ class VpnSystemProxyItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final systemProxy =
|
||||
ref.watch(vpnSettingProvider.select((state) => state.systemProxy));
|
||||
final systemProxy = ref.watch(
|
||||
vpnSettingProvider.select((state) => state.systemProxy),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.systemProxy),
|
||||
subtitle: Text(appLocalizations.systemProxyDesc),
|
||||
delegate: SwitchDelegate(
|
||||
value: systemProxy,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
systemProxy: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(systemProxy: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -110,8 +106,9 @@ class SystemProxyItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final systemProxy =
|
||||
ref.watch(networkSettingProvider.select((state) => state.systemProxy));
|
||||
final systemProxy = ref.watch(
|
||||
networkSettingProvider.select((state) => state.systemProxy),
|
||||
);
|
||||
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.systemProxy),
|
||||
@@ -119,11 +116,9 @@ class SystemProxyItem extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: systemProxy,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(networkSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
systemProxy: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(networkSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(systemProxy: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -142,11 +137,9 @@ class Ipv6Item extends ConsumerWidget {
|
||||
delegate: SwitchDelegate(
|
||||
value: ipv6,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(vpnSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
ipv6: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(ipv6: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -159,17 +152,16 @@ class AutoSetSystemDnsItem extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final autoSetSystemDns = ref.watch(
|
||||
networkSettingProvider.select((state) => state.autoSetSystemDns));
|
||||
networkSettingProvider.select((state) => state.autoSetSystemDns),
|
||||
);
|
||||
return ListItem.switchItem(
|
||||
title: Text(appLocalizations.autoSetSystemDns),
|
||||
delegate: SwitchDelegate(
|
||||
value: autoSetSystemDns,
|
||||
onChanged: (bool value) async {
|
||||
ref.read(networkSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
autoSetSystemDns: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(networkSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(autoSetSystemDns: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -181,8 +173,9 @@ class TunStackItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final stack =
|
||||
ref.watch(patchClashConfigProvider.select((state) => state.tun.stack));
|
||||
final stack = ref.watch(
|
||||
patchClashConfigProvider.select((state) => state.tun.stack),
|
||||
);
|
||||
|
||||
return ListItem.options(
|
||||
title: Text(appLocalizations.stackMode),
|
||||
@@ -195,11 +188,9 @@ class TunStackItem extends ConsumerWidget {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.tun(
|
||||
stack: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState((state) => state.copyWith.tun(stack: value));
|
||||
},
|
||||
title: appLocalizations.stackMode,
|
||||
),
|
||||
@@ -218,45 +209,45 @@ class BypassDomainItem extends StatelessWidget {
|
||||
delegate: OpenDelegate(
|
||||
blur: false,
|
||||
actions: [
|
||||
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(networkSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
bypassDomain: defaultBypassDomain,
|
||||
),
|
||||
);
|
||||
},
|
||||
tooltip: appLocalizations.reset,
|
||||
icon: const Icon(
|
||||
Icons.replay,
|
||||
),
|
||||
);
|
||||
})
|
||||
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(networkSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) =>
|
||||
state.copyWith(bypassDomain: defaultBypassDomain),
|
||||
);
|
||||
},
|
||||
tooltip: appLocalizations.reset,
|
||||
icon: const Icon(Icons.replay),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
title: appLocalizations.bypassDomain,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
builder: (_, ref, _) {
|
||||
final bypassDomain = ref.watch(
|
||||
networkSettingProvider.select((state) => state.bypassDomain));
|
||||
networkSettingProvider.select((state) => state.bypassDomain),
|
||||
);
|
||||
return ListInputPage(
|
||||
title: appLocalizations.bypassDomain,
|
||||
items: bypassDomain,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref.read(networkSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
bypassDomain: List.from(items),
|
||||
),
|
||||
ref
|
||||
.read(networkSettingProvider.notifier)
|
||||
.updateState(
|
||||
(state) => state.copyWith(bypassDomain: List.from(items)),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -267,13 +258,36 @@ class BypassDomainItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DNSHijackingItem extends ConsumerWidget {
|
||||
const DNSHijackingItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final dnsHijacking = ref.watch(
|
||||
vpnSettingProvider.select((state) => state.dnsHijacking),
|
||||
);
|
||||
return ListItem<RouteMode>.switchItem(
|
||||
title: Text(appLocalizations.dnsHijacking),
|
||||
delegate: SwitchDelegate(
|
||||
value: dnsHijacking,
|
||||
onChanged: (value) async {
|
||||
ref
|
||||
.read(vpnSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(dnsHijacking: value));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RouteModeItem extends ConsumerWidget {
|
||||
const RouteModeItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final routeMode =
|
||||
ref.watch(networkSettingProvider.select((state) => state.routeMode));
|
||||
final routeMode = ref.watch(
|
||||
networkSettingProvider.select((state) => state.routeMode),
|
||||
);
|
||||
return ListItem<RouteMode>.options(
|
||||
title: Text(appLocalizations.routeMode),
|
||||
subtitle: Text(Intl.message('routeMode_${routeMode.name}')),
|
||||
@@ -284,15 +298,11 @@ class RouteModeItem extends ConsumerWidget {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
ref.read(networkSettingProvider.notifier).updateState(
|
||||
(state) => state.copyWith(
|
||||
routeMode: value,
|
||||
),
|
||||
);
|
||||
ref
|
||||
.read(networkSettingProvider.notifier)
|
||||
.updateState((state) => state.copyWith(routeMode: value));
|
||||
},
|
||||
textBuilder: (routeMode) => Intl.message(
|
||||
'routeMode_${routeMode.name}',
|
||||
),
|
||||
textBuilder: (routeMode) => Intl.message('routeMode_${routeMode.name}'),
|
||||
value: routeMode,
|
||||
),
|
||||
);
|
||||
@@ -304,8 +314,11 @@ class RouteAddressItem extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final bypassPrivate = ref.watch(networkSettingProvider
|
||||
.select((state) => state.routeMode == RouteMode.bypassPrivate));
|
||||
final bypassPrivate = ref.watch(
|
||||
networkSettingProvider.select(
|
||||
(state) => state.routeMode == RouteMode.bypassPrivate,
|
||||
),
|
||||
);
|
||||
if (bypassPrivate) {
|
||||
return Container();
|
||||
}
|
||||
@@ -317,7 +330,7 @@ class RouteAddressItem extends ConsumerWidget {
|
||||
maxWidth: 360,
|
||||
title: appLocalizations.routeAddress,
|
||||
widget: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
builder: (_, ref, _) {
|
||||
final routeAddress = ref.watch(
|
||||
patchClashConfigProvider.select(
|
||||
(state) => state.tun.routeAddress,
|
||||
@@ -328,10 +341,11 @@ class RouteAddressItem extends ConsumerWidget {
|
||||
items: routeAddress,
|
||||
titleBuilder: (item) => Text(item),
|
||||
onChange: (items) {
|
||||
ref.read(patchClashConfigProvider.notifier).updateState(
|
||||
(state) => state.copyWith.tun(
|
||||
routeAddress: List.from(items),
|
||||
),
|
||||
ref
|
||||
.read(patchClashConfigProvider.notifier)
|
||||
.updateState(
|
||||
(state) =>
|
||||
state.copyWith.tun(routeAddress: List.from(items)),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -352,15 +366,13 @@ final networkItems = [
|
||||
const BypassDomainItem(),
|
||||
const AllowBypassItem(),
|
||||
const Ipv6Item(),
|
||||
const DNSHijackingItem(),
|
||||
],
|
||||
),
|
||||
if (system.isDesktop)
|
||||
...generateSection(
|
||||
title: appLocalizations.system,
|
||||
items: [
|
||||
SystemProxyItem(),
|
||||
BypassDomainItem(),
|
||||
],
|
||||
items: [SystemProxyItem(), BypassDomainItem()],
|
||||
),
|
||||
...generateSection(
|
||||
title: appLocalizations.options,
|
||||
@@ -371,7 +383,7 @@ final networkItems = [
|
||||
if (!system.isDesktop) ...[
|
||||
const RouteModeItem(),
|
||||
const RouteAddressItem(),
|
||||
]
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
@@ -381,8 +393,6 @@ class NetworkListView extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return generateListView(
|
||||
networkItems,
|
||||
);
|
||||
return generateListView(networkItems);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fl_clash/clash/clash.dart';
|
||||
import 'package:fl_clash/common/common.dart';
|
||||
import 'package:fl_clash/core/controller.dart';
|
||||
import 'package:fl_clash/models/models.dart';
|
||||
import 'package:fl_clash/widgets/widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -28,7 +28,7 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
|
||||
return [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
clashCore.closeConnections();
|
||||
coreController.closeConnections();
|
||||
await _updateConnections();
|
||||
},
|
||||
icon: const Icon(Icons.delete_sweep_outlined),
|
||||
@@ -43,8 +43,9 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
|
||||
}
|
||||
|
||||
void _onKeywordsUpdate(List<String> keywords) {
|
||||
_connectionsStateNotifier.value =
|
||||
_connectionsStateNotifier.value.copyWith(keywords: keywords);
|
||||
_connectionsStateNotifier.value = _connectionsStateNotifier.value.copyWith(
|
||||
keywords: keywords,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _updateConnectionsTask() async {
|
||||
@@ -66,12 +67,12 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
|
||||
|
||||
Future<void> _updateConnections() async {
|
||||
_connectionsStateNotifier.value = _connectionsStateNotifier.value.copyWith(
|
||||
trackerInfos: await clashCore.getConnections(),
|
||||
trackerInfos: await coreController.getConnections(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleBlockConnection(String id) async {
|
||||
clashCore.closeConnection(id);
|
||||
coreController.closeConnection(id);
|
||||
await _updateConnections();
|
||||
}
|
||||
|
||||
@@ -93,7 +94,7 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
|
||||
actions: _buildActions(),
|
||||
body: ValueListenableBuilder<TrackerInfosState>(
|
||||
valueListenable: _connectionsStateNotifier,
|
||||
builder: (context, state, __) {
|
||||
builder: (context, state, _) {
|
||||
final connections = state.list;
|
||||
if (connections.isEmpty) {
|
||||
return NullStatus(
|
||||
@@ -111,9 +112,7 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
|
||||
trailing: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
style: ButtonStyle(
|
||||
minimumSize: WidgetStatePropertyAll(Size.zero),
|
||||
),
|
||||
style: IconButton.styleFrom(minimumSize: Size.zero),
|
||||
icon: const Icon(Icons.block),
|
||||
onPressed: () {
|
||||
_handleBlockConnection(trackerInfo.id);
|
||||
@@ -124,11 +123,7 @@ class _ConnectionsViewState extends ConsumerState<ConnectionsView> {
|
||||
),
|
||||
),
|
||||
)
|
||||
.separated(
|
||||
const Divider(
|
||||
height: 0,
|
||||
),
|
||||
)
|
||||
.separated(const Divider(height: 0))
|
||||
.toList();
|
||||
return ListView.builder(
|
||||
controller: _scrollController,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user