add warp config, update to flutter 1.22
This commit is contained in:
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -15,7 +15,7 @@ on:
|
|||||||
env:
|
env:
|
||||||
IS_GITHUB_ACTIONS: 1
|
IS_GITHUB_ACTIONS: 1
|
||||||
CHANNEL: "${{ inputs.channel }}"
|
CHANNEL: "${{ inputs.channel }}"
|
||||||
FLUTTER_VERSION: '3.19.x'
|
FLUTTER_VERSION: '3.22.x'
|
||||||
NDK_VERSION: r26b
|
NDK_VERSION: r26b
|
||||||
UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}"
|
UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}"
|
||||||
TAG_NAME: "${{ inputs.tag-name }}"
|
TAG_NAME: "${{ inputs.tag-name }}"
|
||||||
|
|||||||
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
@@ -1,6 +1,16 @@
|
|||||||
{
|
{
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "go Package",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"cwd": "./libcore",
|
||||||
|
"program": "./libcore/cli/main.go",
|
||||||
|
"args": ["build","-c","a.txt","-d","b.txt","--full-config"] ,
|
||||||
|
"buildFlags": "-tags with_clash_api,with_gvisor,with_quic,with_wireguard,with_grpc,with_ech,with_utls,with_reality_server"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Hiddify Dev",
|
"name": "Hiddify Dev",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
|
|||||||
21
.vscode/settings.json
vendored
Normal file
21
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"dart.lineLength": 250,
|
||||||
|
"[dart]": {
|
||||||
|
"editor.defaultFormatter": "Dart-Code.dart-code",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnType": true,
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.rulers": [
|
||||||
|
250
|
||||||
|
],
|
||||||
|
"editor.detectIndentation": false,
|
||||||
|
"editor.selectionHighlight": false,
|
||||||
|
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||||
|
"editor.suggestSelection": "first",
|
||||||
|
"editor.tabCompletion": "onlySnippets",
|
||||||
|
"editor.wordBasedSuggestions": "off"
|
||||||
|
},
|
||||||
|
|
||||||
|
"html.format.wrapLineLength": 250,
|
||||||
|
|
||||||
|
}
|
||||||
@@ -78,8 +78,10 @@
|
|||||||
"permissionRequest": "Permission to camera to scan QR Code"
|
"permissionRequest": "Permission to camera to scan QR Code"
|
||||||
},
|
},
|
||||||
"manually": "Manual Entry",
|
"manually": "Manual Entry",
|
||||||
|
"addWarp": "Add Warp",
|
||||||
"addingProfileMsg": "Adding Profile",
|
"addingProfileMsg": "Adding Profile",
|
||||||
"failureMsg": "Failed to Add Profile"
|
"failureMsg": "Failed to Add Profile"
|
||||||
|
|
||||||
},
|
},
|
||||||
"update": {
|
"update": {
|
||||||
"buttonTxt": "Update",
|
"buttonTxt": "Update",
|
||||||
@@ -162,8 +164,8 @@
|
|||||||
"requiresRestartMsg": "For this to take effect restart the app",
|
"requiresRestartMsg": "For this to take effect restart the app",
|
||||||
"experimental": "Experimental",
|
"experimental": "Experimental",
|
||||||
"experimentalMsg": "Features with Experimental flag are still in development and might cause issues.",
|
"experimentalMsg": "Features with Experimental flag are still in development and might cause issues.",
|
||||||
"exportOptions": "Export Options to Clipboard",
|
"exportOptions": "Copy Anonymous Options to Clipboard",
|
||||||
"exportAllOptions": "Export Options to Clipboard (Debug)",
|
"exportAllOptions": "Copy All Options to Clipboard",
|
||||||
"importOptions": "Import Options From Clipboard",
|
"importOptions": "Import Options From Clipboard",
|
||||||
"importOptionsMsg": "This will rewrite all config options with provided values. Are you sure?",
|
"importOptionsMsg": "This will rewrite all config options with provided values. Are you sure?",
|
||||||
"general": {
|
"general": {
|
||||||
|
|||||||
@@ -162,8 +162,8 @@
|
|||||||
"requiresRestartMsg": "برای اعمال این تنظیم، برنامه را دوباره راهاندازی کنید",
|
"requiresRestartMsg": "برای اعمال این تنظیم، برنامه را دوباره راهاندازی کنید",
|
||||||
"experimental": "آزمایشی",
|
"experimental": "آزمایشی",
|
||||||
"experimentalMsg": "تنظیماتی که عنوان آزمایشی دارند، همچنان در دست توسعه هستند و فعالسازی آنها میتواند باعث بروز مشکلاتی شود. ",
|
"experimentalMsg": "تنظیماتی که عنوان آزمایشی دارند، همچنان در دست توسعه هستند و فعالسازی آنها میتواند باعث بروز مشکلاتی شود. ",
|
||||||
"exportOptions": "صادر کردن تنظیمات به کلیپبورد",
|
"exportOptions": "کپی تنظیمات ساده به کلیپبورد",
|
||||||
"exportAllOptions": "صادر کردن تنظیمات به کلیپبورد (اشکالزدایی)",
|
"exportAllOptions": "کپی همه تنظیمات به کلیپبورد",
|
||||||
"importOptions": "وارد کردن تنظیمات از کلیپبورد",
|
"importOptions": "وارد کردن تنظیمات از کلیپبورد",
|
||||||
"importOptionsMsg": "این اقدام همهی تنظیمات پیکربندی را با مقادیر اولیه بازنویسی میکند. آیا مطمئن هستید؟",
|
"importOptionsMsg": "این اقدام همهی تنظیمات پیکربندی را با مقادیر اولیه بازنویسی میکند. آیا مطمئن هستید؟",
|
||||||
"general": {
|
"general": {
|
||||||
|
|||||||
@@ -162,8 +162,7 @@
|
|||||||
"requiresRestartMsg": "Чтобы применить изменения, перезапустите приложение.",
|
"requiresRestartMsg": "Чтобы применить изменения, перезапустите приложение.",
|
||||||
"experimental": "Экспериментальный",
|
"experimental": "Экспериментальный",
|
||||||
"experimentalMsg": "Функции с флагом «Экспериментально» все еще находятся в разработке и могут вызвать проблемы.",
|
"experimentalMsg": "Функции с флагом «Экспериментально» все еще находятся в разработке и могут вызвать проблемы.",
|
||||||
"exportOptions": "Экспорт параметров в буфер обмена",
|
|
||||||
"exportAllOptions": "Экспорт параметров в буфер обмена (отладка)",
|
|
||||||
"importOptions": "Импорт параметров из буфера обмена",
|
"importOptions": "Импорт параметров из буфера обмена",
|
||||||
"importOptionsMsg": "Это перезапишет все параметры конфига предоставленными значениями. Вы уверены?",
|
"importOptionsMsg": "Это перезапишет все параметры конфига предоставленными значениями. Вы уверены?",
|
||||||
"general": {
|
"general": {
|
||||||
|
|||||||
@@ -162,8 +162,7 @@
|
|||||||
"requiresRestartMsg": "要使其生效,请重新启动应用程序",
|
"requiresRestartMsg": "要使其生效,请重新启动应用程序",
|
||||||
"experimental": "实验性选项",
|
"experimental": "实验性选项",
|
||||||
"experimentalMsg": "带有实验标志的功能仍在开发中,可能会出现问题。",
|
"experimentalMsg": "带有实验标志的功能仍在开发中,可能会出现问题。",
|
||||||
"exportOptions": "导出选项到剪切板",
|
|
||||||
"exportAllOptions": "导出选项到剪切板(用于调试)",
|
|
||||||
"importOptions": "从剪切板导入选项",
|
"importOptions": "从剪切板导入选项",
|
||||||
"importOptionsMsg": "这将使用提供的值重写所有配置选项。您确定吗?",
|
"importOptionsMsg": "这将使用提供的值重写所有配置选项。您确定吗?",
|
||||||
"general": {
|
"general": {
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ class AnalyticsController extends _$AnalyticsController with AppLogger {
|
|||||||
return _preferences.getBool(enableAnalyticsPrefKey) ?? true;
|
return _preferences.getBool(enableAnalyticsPrefKey) ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPreferences get _preferences =>
|
SharedPreferences get _preferences => ref.read(sharedPreferencesProvider).requireValue;
|
||||||
ref.read(sharedPreferencesProvider).requireValue;
|
|
||||||
|
|
||||||
Future<void> enableAnalytics() async {
|
Future<void> enableAnalytics() async {
|
||||||
if (state case AsyncData(value: final enabled)) {
|
if (state case AsyncData(value: final enabled)) {
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ abstract class ConfigOptions {
|
|||||||
validator: (value) => value.isNotBlank,
|
validator: (value) => value.isNotBlank,
|
||||||
);
|
);
|
||||||
|
|
||||||
static final remoteDnsDomainStrategy =
|
static final remoteDnsDomainStrategy = PreferencesNotifier.create<DomainStrategy, String>(
|
||||||
PreferencesNotifier.create<DomainStrategy, String>(
|
|
||||||
"remote-dns-domain-strategy",
|
"remote-dns-domain-strategy",
|
||||||
DomainStrategy.auto,
|
DomainStrategy.auto,
|
||||||
mapFrom: (value) => DomainStrategy.values.firstWhere((e) => e.key == value),
|
mapFrom: (value) => DomainStrategy.values.firstWhere((e) => e.key == value),
|
||||||
@@ -65,8 +64,7 @@ abstract class ConfigOptions {
|
|||||||
validator: (value) => value.isNotBlank,
|
validator: (value) => value.isNotBlank,
|
||||||
);
|
);
|
||||||
|
|
||||||
static final directDnsDomainStrategy =
|
static final directDnsDomainStrategy = PreferencesNotifier.create<DomainStrategy, String>(
|
||||||
PreferencesNotifier.create<DomainStrategy, String>(
|
|
||||||
"direct-dns-domain-strategy",
|
"direct-dns-domain-strategy",
|
||||||
DomainStrategy.auto,
|
DomainStrategy.auto,
|
||||||
mapFrom: (value) => DomainStrategy.values.firstWhere((e) => e.key == value),
|
mapFrom: (value) => DomainStrategy.values.firstWhere((e) => e.key == value),
|
||||||
@@ -91,8 +89,7 @@ abstract class ConfigOptions {
|
|||||||
validator: (value) => isPort(value.toString()),
|
validator: (value) => isPort(value.toString()),
|
||||||
);
|
);
|
||||||
|
|
||||||
static final tunImplementation =
|
static final tunImplementation = PreferencesNotifier.create<TunImplementation, String>(
|
||||||
PreferencesNotifier.create<TunImplementation, String>(
|
|
||||||
"tun-implementation",
|
"tun-implementation",
|
||||||
TunImplementation.mixed,
|
TunImplementation.mixed,
|
||||||
mapFrom: TunImplementation.values.byName,
|
mapFrom: TunImplementation.values.byName,
|
||||||
@@ -101,8 +98,7 @@ abstract class ConfigOptions {
|
|||||||
|
|
||||||
static final mtu = PreferencesNotifier.create<int, int>("mtu", 9000);
|
static final mtu = PreferencesNotifier.create<int, int>("mtu", 9000);
|
||||||
|
|
||||||
static final strictRoute =
|
static final strictRoute = PreferencesNotifier.create<bool, bool>("strict-route", true);
|
||||||
PreferencesNotifier.create<bool, bool>("strict-route", true);
|
|
||||||
|
|
||||||
static final connectionTestUrl = PreferencesNotifier.create<String, String>(
|
static final connectionTestUrl = PreferencesNotifier.create<String, String>(
|
||||||
"connection-test-url",
|
"connection-test-url",
|
||||||
@@ -128,8 +124,7 @@ abstract class ConfigOptions {
|
|||||||
validator: (value) => isPort(value.toString()),
|
validator: (value) => isPort(value.toString()),
|
||||||
);
|
);
|
||||||
|
|
||||||
static final bypassLan =
|
static final bypassLan = PreferencesNotifier.create<bool, bool>("bypass-lan", false);
|
||||||
PreferencesNotifier.create<bool, bool>("bypass-lan", false);
|
|
||||||
|
|
||||||
static final allowConnectionFromLan = PreferencesNotifier.create<bool, bool>(
|
static final allowConnectionFromLan = PreferencesNotifier.create<bool, bool>(
|
||||||
"allow-connection-from-lan",
|
"allow-connection-from-lan",
|
||||||
@@ -156,16 +151,14 @@ abstract class ConfigOptions {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
static final tlsFragmentSize =
|
static final tlsFragmentSize = PreferencesNotifier.create<OptionalRange, String>(
|
||||||
PreferencesNotifier.create<OptionalRange, String>(
|
|
||||||
"tls-fragment-size",
|
"tls-fragment-size",
|
||||||
const OptionalRange(min: 1, max: 500),
|
const OptionalRange(min: 1, max: 500),
|
||||||
mapFrom: OptionalRange.parse,
|
mapFrom: OptionalRange.parse,
|
||||||
mapTo: const OptionalRangeJsonConverter().toJson,
|
mapTo: const OptionalRangeJsonConverter().toJson,
|
||||||
);
|
);
|
||||||
|
|
||||||
static final tlsFragmentSleep =
|
static final tlsFragmentSleep = PreferencesNotifier.create<OptionalRange, String>(
|
||||||
PreferencesNotifier.create<OptionalRange, String>(
|
|
||||||
"tls-fragment-sleep",
|
"tls-fragment-sleep",
|
||||||
const OptionalRange(min: 0, max: 500),
|
const OptionalRange(min: 0, max: 500),
|
||||||
mapFrom: OptionalRange.parse,
|
mapFrom: OptionalRange.parse,
|
||||||
@@ -182,8 +175,7 @@ abstract class ConfigOptions {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
static final tlsPaddingSize =
|
static final tlsPaddingSize = PreferencesNotifier.create<OptionalRange, String>(
|
||||||
PreferencesNotifier.create<OptionalRange, String>(
|
|
||||||
"tls-padding-size",
|
"tls-padding-size",
|
||||||
const OptionalRange(min: 1, max: 1500),
|
const OptionalRange(min: 1, max: 1500),
|
||||||
mapFrom: OptionalRange.parse,
|
mapFrom: OptionalRange.parse,
|
||||||
@@ -218,8 +210,7 @@ abstract class ConfigOptions {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
static final warpDetourMode =
|
static final warpDetourMode = PreferencesNotifier.create<WarpDetourMode, String>(
|
||||||
PreferencesNotifier.create<WarpDetourMode, String>(
|
|
||||||
"warp-detour-mode",
|
"warp-detour-mode",
|
||||||
WarpDetourMode.proxyOverWarp,
|
WarpDetourMode.proxyOverWarp,
|
||||||
mapFrom: WarpDetourMode.values.byName,
|
mapFrom: WarpDetourMode.values.byName,
|
||||||
@@ -230,16 +221,28 @@ abstract class ConfigOptions {
|
|||||||
"warp-license-key",
|
"warp-license-key",
|
||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
|
static final warp2LicenseKey = PreferencesNotifier.create<String, String>(
|
||||||
|
"warp2s-license-key",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
static final warpAccountId = PreferencesNotifier.create<String, String>(
|
static final warpAccountId = PreferencesNotifier.create<String, String>(
|
||||||
"warp-account-id",
|
"warp-account-id",
|
||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
|
static final warp2AccountId = PreferencesNotifier.create<String, String>(
|
||||||
|
"warp2-account-id",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
static final warpAccessToken = PreferencesNotifier.create<String, String>(
|
static final warpAccessToken = PreferencesNotifier.create<String, String>(
|
||||||
"warp-access-token",
|
"warp-access-token",
|
||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
|
static final warp2AccessToken = PreferencesNotifier.create<String, String>(
|
||||||
|
"warp2-access-token",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
static final warpCleanIp = PreferencesNotifier.create<String, String>(
|
static final warpCleanIp = PreferencesNotifier.create<String, String>(
|
||||||
"warp-clean-ip",
|
"warp-clean-ip",
|
||||||
@@ -259,8 +262,7 @@ abstract class ConfigOptions {
|
|||||||
mapTo: const OptionalRangeJsonConverter().toJson,
|
mapTo: const OptionalRangeJsonConverter().toJson,
|
||||||
);
|
);
|
||||||
|
|
||||||
static final warpNoiseDelay =
|
static final warpNoiseDelay = PreferencesNotifier.create<OptionalRange, String>(
|
||||||
PreferencesNotifier.create<OptionalRange, String>(
|
|
||||||
"warp-noise-delay",
|
"warp-noise-delay",
|
||||||
const OptionalRange(min: 20, max: 200),
|
const OptionalRange(min: 20, max: 200),
|
||||||
mapFrom: (value) => OptionalRange.parse(value, allowEmpty: true),
|
mapFrom: (value) => OptionalRange.parse(value, allowEmpty: true),
|
||||||
@@ -271,6 +273,10 @@ abstract class ConfigOptions {
|
|||||||
"warp-wireguard-config",
|
"warp-wireguard-config",
|
||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
|
static final warp2WireguardConfig = PreferencesNotifier.create<String, String>(
|
||||||
|
"warp2-wireguard-config",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
static final hasExperimentalFeatures = Provider.autoDispose<bool>(
|
static final hasExperimentalFeatures = Provider.autoDispose<bool>(
|
||||||
(ref) {
|
(ref) {
|
||||||
@@ -278,13 +284,7 @@ abstract class ConfigOptions {
|
|||||||
if (PlatformUtils.isDesktop && mode == ServiceMode.tun) {
|
if (PlatformUtils.isDesktop && mode == ServiceMode.tun) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (ref.watch(enableTlsFragment) ||
|
if (ref.watch(enableTlsFragment) || ref.watch(enableTlsMixedSniCase) || ref.watch(enableTlsPadding) || ref.watch(enableMux) || ref.watch(enableWarp) || ref.watch(bypassLan) || ref.watch(allowConnectionFromLan)) {
|
||||||
ref.watch(enableTlsMixedSniCase) ||
|
|
||||||
ref.watch(enableTlsPadding) ||
|
|
||||||
ref.watch(enableMux) ||
|
|
||||||
ref.watch(enableWarp) ||
|
|
||||||
ref.watch(bypassLan) ||
|
|
||||||
ref.watch(allowConnectionFromLan)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,10 +298,13 @@ abstract class ConfigOptions {
|
|||||||
"warp.access-token",
|
"warp.access-token",
|
||||||
"warp.account-id",
|
"warp.account-id",
|
||||||
"warp.wireguard-config",
|
"warp.wireguard-config",
|
||||||
|
"warp2.license-key",
|
||||||
|
"warp2.access-token",
|
||||||
|
"warp2.account-id",
|
||||||
|
"warp2.wireguard-config",
|
||||||
};
|
};
|
||||||
|
|
||||||
static final Map<String, StateNotifierProvider<PreferencesNotifier, dynamic>>
|
static final Map<String, StateNotifierProvider<PreferencesNotifier, dynamic>> preferences = {
|
||||||
preferences = {
|
|
||||||
"service-mode": serviceMode,
|
"service-mode": serviceMode,
|
||||||
"log-level": logLevel,
|
"log-level": logLevel,
|
||||||
"resolve-destination": resolveDestination,
|
"resolve-destination": resolveDestination,
|
||||||
@@ -348,6 +351,10 @@ abstract class ConfigOptions {
|
|||||||
"warp.noise": warpNoise,
|
"warp.noise": warpNoise,
|
||||||
"warp.noise-delay": warpNoiseDelay,
|
"warp.noise-delay": warpNoiseDelay,
|
||||||
"warp.wireguard-config": warpWireguardConfig,
|
"warp.wireguard-config": warpWireguardConfig,
|
||||||
|
"warp2.license-key": warp2LicenseKey,
|
||||||
|
"warp2.account-id": warp2AccountId,
|
||||||
|
"warp2.access-token": warp2AccessToken,
|
||||||
|
"warp2.wireguard-config": warp2WireguardConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
static final singboxConfigOptions = FutureProvider<SingboxConfigOption>(
|
static final singboxConfigOptions = FutureProvider<SingboxConfigOption>(
|
||||||
@@ -386,8 +393,7 @@ abstract class ConfigOptions {
|
|||||||
};
|
};
|
||||||
|
|
||||||
final geoAssetsRepo = await ref.watch(geoAssetRepositoryProvider.future);
|
final geoAssetsRepo = await ref.watch(geoAssetRepositoryProvider.future);
|
||||||
final geoAssets =
|
final geoAssets = await geoAssetsRepo.getActivePair().getOrElse((l) => throw l).run();
|
||||||
await geoAssetsRepo.getActivePair().getOrElse((l) => throw l).run();
|
|
||||||
|
|
||||||
final mode = ref.watch(serviceMode);
|
final mode = ref.watch(serviceMode);
|
||||||
return SingboxConfigOption(
|
return SingboxConfigOption(
|
||||||
@@ -443,6 +449,18 @@ abstract class ConfigOptions {
|
|||||||
noise: ref.watch(warpNoise),
|
noise: ref.watch(warpNoise),
|
||||||
noiseDelay: ref.watch(warpNoiseDelay),
|
noiseDelay: ref.watch(warpNoiseDelay),
|
||||||
),
|
),
|
||||||
|
warp2: SingboxWarpOption(
|
||||||
|
enable: ref.watch(enableWarp),
|
||||||
|
mode: ref.watch(warpDetourMode),
|
||||||
|
wireguardConfig: ref.watch(warp2WireguardConfig),
|
||||||
|
licenseKey: ref.watch(warp2LicenseKey),
|
||||||
|
accountId: ref.watch(warp2AccountId),
|
||||||
|
accessToken: ref.watch(warp2AccessToken),
|
||||||
|
cleanIp: ref.watch(warpCleanIp),
|
||||||
|
cleanPort: ref.watch(warpPort),
|
||||||
|
noise: ref.watch(warpNoise),
|
||||||
|
noiseDelay: ref.watch(warpNoiseDelay),
|
||||||
|
),
|
||||||
geoipPath: ref.watch(geoAssetPathResolverProvider).relativePath(
|
geoipPath: ref.watch(geoAssetPathResolverProvider).relativePath(
|
||||||
geoAssets.geoip.providerName,
|
geoAssets.geoip.providerName,
|
||||||
geoAssets.geoip.fileName,
|
geoAssets.geoip.fileName,
|
||||||
@@ -470,8 +488,7 @@ class ConfigOptionRepository with ExceptionHandler, InfraLogger {
|
|||||||
final GeoAssetRepository geoAssetRepository;
|
final GeoAssetRepository geoAssetRepository;
|
||||||
final GeoAssetPathResolver geoAssetPathResolver;
|
final GeoAssetPathResolver geoAssetPathResolver;
|
||||||
|
|
||||||
TaskEither<ConfigOptionFailure, SingboxConfigOption>
|
TaskEither<ConfigOptionFailure, SingboxConfigOption> getFullSingboxConfigOption() {
|
||||||
getFullSingboxConfigOption() {
|
|
||||||
return exceptionHandler(
|
return exceptionHandler(
|
||||||
() async {
|
() async {
|
||||||
return right(await getConfigOptions());
|
return right(await getConfigOptions());
|
||||||
|
|||||||
@@ -26,20 +26,14 @@ class WarpOptionNotifier extends _$WarpOptionNotifier with AppLogger {
|
|||||||
|
|
||||||
return WarpOptions(
|
return WarpOptions(
|
||||||
consentGiven: consent,
|
consentGiven: consent,
|
||||||
configGeneration: hasWarpConfig
|
configGeneration: hasWarpConfig ? const AsyncValue.data("") : AsyncError(const MissingWarpConfigFailure(), StackTrace.current),
|
||||||
? const AsyncValue.data("")
|
|
||||||
: AsyncError(const MissingWarpConfigFailure(), StackTrace.current),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPreferences get _prefs =>
|
SharedPreferences get _prefs => ref.read(sharedPreferencesProvider).requireValue;
|
||||||
ref.read(sharedPreferencesProvider).requireValue;
|
|
||||||
|
|
||||||
Future<void> agree() async {
|
Future<void> agree() async {
|
||||||
await ref
|
await ref.read(sharedPreferencesProvider).requireValue.setBool(warpConsentGiven, true);
|
||||||
.read(sharedPreferencesProvider)
|
|
||||||
.requireValue
|
|
||||||
.setBool(warpConsentGiven, true);
|
|
||||||
state = state.copyWith(consentGiven: true);
|
state = state.copyWith(consentGiven: true);
|
||||||
await generateWarpConfig();
|
await generateWarpConfig();
|
||||||
}
|
}
|
||||||
@@ -59,15 +53,33 @@ class WarpOptionNotifier extends _$WarpOptionNotifier with AppLogger {
|
|||||||
.getOrElse((l) => throw l)
|
.getOrElse((l) => throw l)
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
await ref
|
await ref.read(ConfigOptions.warpAccountId.notifier).update(warp.accountId);
|
||||||
.read(ConfigOptions.warpAccountId.notifier)
|
await ref.read(ConfigOptions.warpAccessToken.notifier).update(warp.accessToken);
|
||||||
.update(warp.accountId);
|
await ref.read(ConfigOptions.warpWireguardConfig.notifier).update(warp.wireguardConfig);
|
||||||
await ref
|
return warp.log;
|
||||||
.read(ConfigOptions.warpAccessToken.notifier)
|
});
|
||||||
.update(warp.accessToken);
|
|
||||||
await ref
|
state = state.copyWith(configGeneration: result);
|
||||||
.read(ConfigOptions.warpWireguardConfig.notifier)
|
}
|
||||||
.update(warp.wireguardConfig);
|
|
||||||
|
Future<void> generateWarp2Config() async {
|
||||||
|
if (state.configGeneration.isLoading) return;
|
||||||
|
state = state.copyWith(configGeneration: const AsyncLoading());
|
||||||
|
|
||||||
|
final result = await AsyncValue.guard(() async {
|
||||||
|
final warp = await ref
|
||||||
|
.read(singboxServiceProvider)
|
||||||
|
.generateWarpConfig(
|
||||||
|
licenseKey: ref.read(ConfigOptions.warpLicenseKey),
|
||||||
|
previousAccountId: ref.read(ConfigOptions.warp2AccountId),
|
||||||
|
previousAccessToken: ref.read(ConfigOptions.warp2AccessToken),
|
||||||
|
)
|
||||||
|
.getOrElse((l) => throw l)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
await ref.read(ConfigOptions.warp2AccountId.notifier).update(warp.accountId);
|
||||||
|
await ref.read(ConfigOptions.warp2AccessToken.notifier).update(warp.accessToken);
|
||||||
|
await ref.read(ConfigOptions.warp2WireguardConfig.notifier).update(warp.wireguardConfig);
|
||||||
return warp.log;
|
return warp.log;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -23,17 +23,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:humanizer/humanizer.dart';
|
import 'package:humanizer/humanizer.dart';
|
||||||
|
|
||||||
enum ConfigOptionSection {
|
enum ConfigOptionSection {
|
||||||
warp;
|
warp,
|
||||||
|
fragment;
|
||||||
|
|
||||||
static final _warpKey = GlobalKey(debugLabel: "warp-section-key");
|
static final _warpKey = GlobalKey(debugLabel: "warp-section-key");
|
||||||
|
static final _fragmentKey = GlobalKey(debugLabel: "fragment-section-key");
|
||||||
|
|
||||||
GlobalKey get key => switch (this) { _ => _warpKey };
|
GlobalKey get key => switch (this) {
|
||||||
|
ConfigOptionSection.warp => _warpKey,
|
||||||
|
ConfigOptionSection.fragment => _fragmentKey,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfigOptionsPage extends HookConsumerWidget {
|
class ConfigOptionsPage extends HookConsumerWidget {
|
||||||
ConfigOptionsPage({super.key, String? section})
|
ConfigOptionsPage({super.key, String? section}) : section = section != null ? ConfigOptionSection.values.byName(section) : null;
|
||||||
: section =
|
|
||||||
section != null ? ConfigOptionSection.values.byName(section) : null;
|
|
||||||
|
|
||||||
final ConfigOptionSection? section;
|
final ConfigOptionSection? section;
|
||||||
|
|
||||||
@@ -47,14 +50,10 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
if (section != null) {
|
if (section != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance.addPostFrameCallback(
|
||||||
(_) {
|
(_) {
|
||||||
final box =
|
final box = section!.key.currentContext?.findRenderObject() as RenderBox?;
|
||||||
section!.key.currentContext?.findRenderObject() as RenderBox?;
|
|
||||||
final offset = box?.localToGlobal(Offset.zero);
|
final offset = box?.localToGlobal(Offset.zero);
|
||||||
if (offset == null) return;
|
if (offset == null) return;
|
||||||
final height = scrollController.offset +
|
final height = scrollController.offset + offset.dy - MediaQueryData.fromView(View.of(context)).padding.top - kToolbarHeight;
|
||||||
offset.dy -
|
|
||||||
MediaQueryData.fromView(View.of(context)).padding.top -
|
|
||||||
kToolbarHeight;
|
|
||||||
scrollController.animateTo(
|
scrollController.animateTo(
|
||||||
height,
|
height,
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
@@ -83,36 +82,26 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
return [
|
return [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () async => ref
|
onTap: () async => ref.read(configOptionNotifierProvider.notifier).exportJsonToClipboard().then((success) {
|
||||||
.read(configOptionNotifierProvider.notifier)
|
|
||||||
.exportJsonToClipboard()
|
|
||||||
.then((success) {
|
|
||||||
if (success) {
|
if (success) {
|
||||||
ref
|
ref.read(inAppNotificationControllerProvider).showSuccessToast(
|
||||||
.read(inAppNotificationControllerProvider)
|
|
||||||
.showSuccessToast(
|
|
||||||
t.general.clipboardExportSuccessMsg,
|
t.general.clipboardExportSuccessMsg,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
child: Text(t.settings.exportOptions),
|
child: Text(t.settings.exportOptions),
|
||||||
),
|
),
|
||||||
if (ref.watch(debugModeNotifierProvider))
|
// if (ref.watch(debugModeNotifierProvider))
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () async => ref
|
onTap: () async => ref.read(configOptionNotifierProvider.notifier).exportJsonToClipboard(excludePrivate: false).then((success) {
|
||||||
.read(configOptionNotifierProvider.notifier)
|
if (success) {
|
||||||
.exportJsonToClipboard(excludePrivate: false)
|
ref.read(inAppNotificationControllerProvider).showSuccessToast(
|
||||||
.then((success) {
|
t.general.clipboardExportSuccessMsg,
|
||||||
if (success) {
|
);
|
||||||
ref
|
}
|
||||||
.read(inAppNotificationControllerProvider)
|
}),
|
||||||
.showSuccessToast(
|
child: Text(t.settings.exportAllOptions),
|
||||||
t.general.clipboardExportSuccessMsg,
|
),
|
||||||
);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
child: Text(t.settings.exportAllOptions),
|
|
||||||
),
|
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final shouldImport = await showConfirmationDialog(
|
final shouldImport = await showConfirmationDialog(
|
||||||
@@ -121,9 +110,7 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
message: t.settings.importOptionsMsg,
|
message: t.settings.importOptionsMsg,
|
||||||
);
|
);
|
||||||
if (shouldImport) {
|
if (shouldImport) {
|
||||||
await ref
|
await ref.read(configOptionNotifierProvider.notifier).importFromClipboard();
|
||||||
.read(configOptionNotifierProvider.notifier)
|
|
||||||
.importFromClipboard();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text(t.settings.importOptions),
|
child: Text(t.settings.importOptions),
|
||||||
@@ -131,9 +118,7 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Text(t.config.resetBtn),
|
child: Text(t.config.resetBtn),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await ref
|
await ref.read(configOptionNotifierProvider.notifier).resetOption();
|
||||||
.read(configOptionNotifierProvider.notifier)
|
|
||||||
.resetOption();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@@ -158,15 +143,12 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(experimental(t.config.bypassLan)),
|
title: Text(experimental(t.config.bypassLan)),
|
||||||
value: ref.watch(ConfigOptions.bypassLan),
|
value: ref.watch(ConfigOptions.bypassLan),
|
||||||
onChanged:
|
onChanged: ref.watch(ConfigOptions.bypassLan.notifier).update,
|
||||||
ref.watch(ConfigOptions.bypassLan.notifier).update,
|
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(t.config.resolveDestination),
|
title: Text(t.config.resolveDestination),
|
||||||
value: ref.watch(ConfigOptions.resolveDestination),
|
value: ref.watch(ConfigOptions.resolveDestination),
|
||||||
onChanged: ref
|
onChanged: ref.watch(ConfigOptions.resolveDestination.notifier).update,
|
||||||
.watch(ConfigOptions.resolveDestination.notifier)
|
|
||||||
.update,
|
|
||||||
),
|
),
|
||||||
ChoicePreferenceWidget(
|
ChoicePreferenceWidget(
|
||||||
selected: ref.watch(ConfigOptions.ipv6Mode),
|
selected: ref.watch(ConfigOptions.ipv6Mode),
|
||||||
@@ -179,28 +161,24 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
SettingsSection(t.config.section.dns),
|
SettingsSection(t.config.section.dns),
|
||||||
ValuePreferenceWidget(
|
ValuePreferenceWidget(
|
||||||
value: ref.watch(ConfigOptions.remoteDnsAddress),
|
value: ref.watch(ConfigOptions.remoteDnsAddress),
|
||||||
preferences:
|
preferences: ref.watch(ConfigOptions.remoteDnsAddress.notifier),
|
||||||
ref.watch(ConfigOptions.remoteDnsAddress.notifier),
|
|
||||||
title: t.config.remoteDnsAddress,
|
title: t.config.remoteDnsAddress,
|
||||||
),
|
),
|
||||||
ChoicePreferenceWidget(
|
ChoicePreferenceWidget(
|
||||||
selected: ref.watch(ConfigOptions.remoteDnsDomainStrategy),
|
selected: ref.watch(ConfigOptions.remoteDnsDomainStrategy),
|
||||||
preferences: ref
|
preferences: ref.watch(ConfigOptions.remoteDnsDomainStrategy.notifier),
|
||||||
.watch(ConfigOptions.remoteDnsDomainStrategy.notifier),
|
|
||||||
choices: DomainStrategy.values,
|
choices: DomainStrategy.values,
|
||||||
title: t.config.remoteDnsDomainStrategy,
|
title: t.config.remoteDnsDomainStrategy,
|
||||||
presentChoice: (value) => value.displayName,
|
presentChoice: (value) => value.displayName,
|
||||||
),
|
),
|
||||||
ValuePreferenceWidget(
|
ValuePreferenceWidget(
|
||||||
value: ref.watch(ConfigOptions.directDnsAddress),
|
value: ref.watch(ConfigOptions.directDnsAddress),
|
||||||
preferences:
|
preferences: ref.watch(ConfigOptions.directDnsAddress.notifier),
|
||||||
ref.watch(ConfigOptions.directDnsAddress.notifier),
|
|
||||||
title: t.config.directDnsAddress,
|
title: t.config.directDnsAddress,
|
||||||
),
|
),
|
||||||
ChoicePreferenceWidget(
|
ChoicePreferenceWidget(
|
||||||
selected: ref.watch(ConfigOptions.directDnsDomainStrategy),
|
selected: ref.watch(ConfigOptions.directDnsDomainStrategy),
|
||||||
preferences: ref
|
preferences: ref.watch(ConfigOptions.directDnsDomainStrategy.notifier),
|
||||||
.watch(ConfigOptions.directDnsDomainStrategy.notifier),
|
|
||||||
choices: DomainStrategy.values,
|
choices: DomainStrategy.values,
|
||||||
title: t.config.directDnsDomainStrategy,
|
title: t.config.directDnsDomainStrategy,
|
||||||
presentChoice: (value) => value.displayName,
|
presentChoice: (value) => value.displayName,
|
||||||
@@ -208,9 +186,7 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(t.config.enableDnsRouting),
|
title: Text(t.config.enableDnsRouting),
|
||||||
value: ref.watch(ConfigOptions.enableDnsRouting),
|
value: ref.watch(ConfigOptions.enableDnsRouting),
|
||||||
onChanged: ref
|
onChanged: ref.watch(ConfigOptions.enableDnsRouting.notifier).update,
|
||||||
.watch(ConfigOptions.enableDnsRouting.notifier)
|
|
||||||
.update,
|
|
||||||
),
|
),
|
||||||
// const SettingsDivider(),
|
// const SettingsDivider(),
|
||||||
// SettingsSection(experimental(t.config.section.mux)),
|
// SettingsSection(experimental(t.config.section.mux)),
|
||||||
@@ -247,13 +223,11 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(t.config.strictRoute),
|
title: Text(t.config.strictRoute),
|
||||||
value: ref.watch(ConfigOptions.strictRoute),
|
value: ref.watch(ConfigOptions.strictRoute),
|
||||||
onChanged:
|
onChanged: ref.watch(ConfigOptions.strictRoute.notifier).update,
|
||||||
ref.watch(ConfigOptions.strictRoute.notifier).update,
|
|
||||||
),
|
),
|
||||||
ChoicePreferenceWidget(
|
ChoicePreferenceWidget(
|
||||||
selected: ref.watch(ConfigOptions.tunImplementation),
|
selected: ref.watch(ConfigOptions.tunImplementation),
|
||||||
preferences:
|
preferences: ref.watch(ConfigOptions.tunImplementation.notifier),
|
||||||
ref.watch(ConfigOptions.tunImplementation.notifier),
|
|
||||||
choices: TunImplementation.values,
|
choices: TunImplementation.values,
|
||||||
title: t.config.tunImplementation,
|
title: t.config.tunImplementation,
|
||||||
presentChoice: (value) => value.name,
|
presentChoice: (value) => value.name,
|
||||||
@@ -287,25 +261,21 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
experimental(t.config.allowConnectionFromLan),
|
experimental(t.config.allowConnectionFromLan),
|
||||||
),
|
),
|
||||||
value: ref.watch(ConfigOptions.allowConnectionFromLan),
|
value: ref.watch(ConfigOptions.allowConnectionFromLan),
|
||||||
onChanged: ref
|
onChanged: ref.read(ConfigOptions.allowConnectionFromLan.notifier).update,
|
||||||
.read(ConfigOptions.allowConnectionFromLan.notifier)
|
|
||||||
.update,
|
|
||||||
),
|
),
|
||||||
const SettingsDivider(),
|
const SettingsDivider(),
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
experimental(t.config.section.tlsTricks),
|
experimental(t.config.section.tlsTricks),
|
||||||
|
key: ConfigOptionSection._fragmentKey,
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(t.config.enableTlsFragment),
|
title: Text(t.config.enableTlsFragment),
|
||||||
value: ref.watch(ConfigOptions.enableTlsFragment),
|
value: ref.watch(ConfigOptions.enableTlsFragment),
|
||||||
onChanged: ref
|
onChanged: ref.watch(ConfigOptions.enableTlsFragment.notifier).update,
|
||||||
.watch(ConfigOptions.enableTlsFragment.notifier)
|
|
||||||
.update,
|
|
||||||
),
|
),
|
||||||
ValuePreferenceWidget(
|
ValuePreferenceWidget(
|
||||||
value: ref.watch(ConfigOptions.tlsFragmentSize),
|
value: ref.watch(ConfigOptions.tlsFragmentSize),
|
||||||
preferences:
|
preferences: ref.watch(ConfigOptions.tlsFragmentSize.notifier),
|
||||||
ref.watch(ConfigOptions.tlsFragmentSize.notifier),
|
|
||||||
title: t.config.tlsFragmentSize,
|
title: t.config.tlsFragmentSize,
|
||||||
inputToValue: OptionalRange.tryParse,
|
inputToValue: OptionalRange.tryParse,
|
||||||
presentValue: (value) => value.present(t),
|
presentValue: (value) => value.present(t),
|
||||||
@@ -313,8 +283,7 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
ValuePreferenceWidget(
|
ValuePreferenceWidget(
|
||||||
value: ref.watch(ConfigOptions.tlsFragmentSleep),
|
value: ref.watch(ConfigOptions.tlsFragmentSleep),
|
||||||
preferences:
|
preferences: ref.watch(ConfigOptions.tlsFragmentSleep.notifier),
|
||||||
ref.watch(ConfigOptions.tlsFragmentSleep.notifier),
|
|
||||||
title: t.config.tlsFragmentSleep,
|
title: t.config.tlsFragmentSleep,
|
||||||
inputToValue: OptionalRange.tryParse,
|
inputToValue: OptionalRange.tryParse,
|
||||||
presentValue: (value) => value.present(t),
|
presentValue: (value) => value.present(t),
|
||||||
@@ -323,21 +292,16 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(t.config.enableTlsMixedSniCase),
|
title: Text(t.config.enableTlsMixedSniCase),
|
||||||
value: ref.watch(ConfigOptions.enableTlsMixedSniCase),
|
value: ref.watch(ConfigOptions.enableTlsMixedSniCase),
|
||||||
onChanged: ref
|
onChanged: ref.watch(ConfigOptions.enableTlsMixedSniCase.notifier).update,
|
||||||
.watch(ConfigOptions.enableTlsMixedSniCase.notifier)
|
|
||||||
.update,
|
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
title: Text(t.config.enableTlsPadding),
|
title: Text(t.config.enableTlsPadding),
|
||||||
value: ref.watch(ConfigOptions.enableTlsPadding),
|
value: ref.watch(ConfigOptions.enableTlsPadding),
|
||||||
onChanged: ref
|
onChanged: ref.watch(ConfigOptions.enableTlsPadding.notifier).update,
|
||||||
.watch(ConfigOptions.enableTlsPadding.notifier)
|
|
||||||
.update,
|
|
||||||
),
|
),
|
||||||
ValuePreferenceWidget(
|
ValuePreferenceWidget(
|
||||||
value: ref.watch(ConfigOptions.tlsPaddingSize),
|
value: ref.watch(ConfigOptions.tlsPaddingSize),
|
||||||
preferences:
|
preferences: ref.watch(ConfigOptions.tlsPaddingSize.notifier),
|
||||||
ref.watch(ConfigOptions.tlsPaddingSize.notifier),
|
|
||||||
title: t.config.tlsPaddingSize,
|
title: t.config.tlsPaddingSize,
|
||||||
inputToValue: OptionalRange.tryParse,
|
inputToValue: OptionalRange.tryParse,
|
||||||
presentValue: (value) => value.format(),
|
presentValue: (value) => value.format(),
|
||||||
@@ -350,38 +314,26 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
|||||||
SettingsSection(t.config.section.misc),
|
SettingsSection(t.config.section.misc),
|
||||||
ValuePreferenceWidget(
|
ValuePreferenceWidget(
|
||||||
value: ref.watch(ConfigOptions.connectionTestUrl),
|
value: ref.watch(ConfigOptions.connectionTestUrl),
|
||||||
preferences:
|
preferences: ref.watch(ConfigOptions.connectionTestUrl.notifier),
|
||||||
ref.watch(ConfigOptions.connectionTestUrl.notifier),
|
|
||||||
title: t.config.connectionTestUrl,
|
title: t.config.connectionTestUrl,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(t.config.urlTestInterval),
|
title: Text(t.config.urlTestInterval),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
ref
|
ref.watch(ConfigOptions.urlTestInterval).toApproximateTime(isRelativeToNow: false),
|
||||||
.watch(ConfigOptions.urlTestInterval)
|
|
||||||
.toApproximateTime(isRelativeToNow: false),
|
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final urlTestInterval = await SettingsSliderDialog(
|
final urlTestInterval = await SettingsSliderDialog(
|
||||||
title: t.config.urlTestInterval,
|
title: t.config.urlTestInterval,
|
||||||
initialValue: ref
|
initialValue: ref.watch(ConfigOptions.urlTestInterval).inMinutes.coerceIn(0, 60).toDouble(),
|
||||||
.watch(ConfigOptions.urlTestInterval)
|
onReset: ref.read(ConfigOptions.urlTestInterval.notifier).reset,
|
||||||
.inMinutes
|
|
||||||
.coerceIn(0, 60)
|
|
||||||
.toDouble(),
|
|
||||||
onReset: ref
|
|
||||||
.read(ConfigOptions.urlTestInterval.notifier)
|
|
||||||
.reset,
|
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 60,
|
max: 60,
|
||||||
divisions: 60,
|
divisions: 60,
|
||||||
labelGen: (value) => Duration(minutes: value.toInt())
|
labelGen: (value) => Duration(minutes: value.toInt()).toApproximateTime(isRelativeToNow: false),
|
||||||
.toApproximateTime(isRelativeToNow: false),
|
|
||||||
).show(context);
|
).show(context);
|
||||||
if (urlTestInterval == null) return;
|
if (urlTestInterval == null) return;
|
||||||
await ref
|
await ref.read(ConfigOptions.urlTestInterval.notifier).update(Duration(minutes: urlTestInterval.toInt()));
|
||||||
.read(ConfigOptions.urlTestInterval.notifier)
|
|
||||||
.update(Duration(minutes: urlTestInterval.toInt()));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ValuePreferenceWidget(
|
ValuePreferenceWidget(
|
||||||
|
|||||||
@@ -63,17 +63,15 @@ class WarpOptionsTiles extends HookConsumerWidget {
|
|||||||
AsyncLoading() => const LinearProgressIndicator(),
|
AsyncLoading() => const LinearProgressIndicator(),
|
||||||
AsyncError() => Text(
|
AsyncError() => Text(
|
||||||
t.config.missingWarpConfig,
|
t.config.missingWarpConfig,
|
||||||
style:
|
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||||
TextStyle(color: Theme.of(context).colorScheme.error),
|
|
||||||
),
|
),
|
||||||
_ => null,
|
_ => null,
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
enabled: canChangeOptions,
|
enabled: canChangeOptions,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await ref
|
await ref.read(warpOptionNotifierProvider.notifier).generateWarpConfig();
|
||||||
.read(warpOptionNotifierProvider.notifier)
|
await ref.read(warpOptionNotifierProvider.notifier).generateWarp2Config();
|
||||||
.generateWarpConfig();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ChoicePreferenceWidget(
|
ChoicePreferenceWidget(
|
||||||
@@ -111,8 +109,7 @@ class WarpOptionsTiles extends HookConsumerWidget {
|
|||||||
preferences: ref.watch(ConfigOptions.warpNoise.notifier),
|
preferences: ref.watch(ConfigOptions.warpNoise.notifier),
|
||||||
enabled: canChangeOptions,
|
enabled: canChangeOptions,
|
||||||
title: t.config.warpNoise,
|
title: t.config.warpNoise,
|
||||||
inputToValue: (input) =>
|
inputToValue: (input) => OptionalRange.tryParse(input, allowEmpty: true),
|
||||||
OptionalRange.tryParse(input, allowEmpty: true),
|
|
||||||
presentValue: (value) => value.present(t),
|
presentValue: (value) => value.present(t),
|
||||||
formatInputValue: (value) => value.format(),
|
formatInputValue: (value) => value.format(),
|
||||||
),
|
),
|
||||||
@@ -121,8 +118,7 @@ class WarpOptionsTiles extends HookConsumerWidget {
|
|||||||
preferences: ref.watch(ConfigOptions.warpNoiseDelay.notifier),
|
preferences: ref.watch(ConfigOptions.warpNoiseDelay.notifier),
|
||||||
enabled: canChangeOptions,
|
enabled: canChangeOptions,
|
||||||
title: t.config.warpNoiseDelay,
|
title: t.config.warpNoiseDelay,
|
||||||
inputToValue: (input) =>
|
inputToValue: (input) => OptionalRange.tryParse(input, allowEmpty: true),
|
||||||
OptionalRange.tryParse(input, allowEmpty: true),
|
|
||||||
presentValue: (value) => value.present(t),
|
presentValue: (value) => value.present(t),
|
||||||
formatInputValue: (value) => value.format(),
|
formatInputValue: (value) => value.format(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ class QuickSettingsModal extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final t = ref.watch(translationsProvider);
|
final t = ref.watch(translationsProvider);
|
||||||
|
|
||||||
final warpPrefaceCompleted =
|
final warpPrefaceCompleted = ref.watch(warpOptionNotifierProvider).consentGiven;
|
||||||
ref.watch(warpOptionNotifierProvider).consentGiven;
|
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -33,37 +32,41 @@ class QuickSettingsModal extends HookConsumerWidget {
|
|||||||
e.presentShort(t),
|
e.presentShort(t),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
tooltip:
|
tooltip: e.isExperimental ? t.settings.experimental : null,
|
||||||
e.isExperimental ? t.settings.experimental : null,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
selected: {ref.watch(ConfigOptions.serviceMode)},
|
selected: {ref.watch(ConfigOptions.serviceMode)},
|
||||||
onSelectionChanged: (newSet) => ref
|
onSelectionChanged: (newSet) => ref.read(ConfigOptions.serviceMode.notifier).update(newSet.first),
|
||||||
.read(ConfigOptions.serviceMode.notifier)
|
|
||||||
.update(newSet.first),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
if (warpPrefaceCompleted)
|
if (warpPrefaceCompleted)
|
||||||
SwitchListTile(
|
GestureDetector(
|
||||||
value: ref.watch(ConfigOptions.enableWarp),
|
onLongPress: () {
|
||||||
onChanged: ref.watch(ConfigOptions.enableWarp.notifier).update,
|
ConfigOptionsRoute(section: ConfigOptionSection.warp.name).go(context);
|
||||||
title: Text(t.config.enableWarp),
|
},
|
||||||
|
child: SwitchListTile(
|
||||||
|
value: ref.watch(ConfigOptions.enableWarp),
|
||||||
|
onChanged: ref.watch(ConfigOptions.enableWarp.notifier).update,
|
||||||
|
title: Text(t.config.enableWarp),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(t.config.setupWarp),
|
title: Text(t.config.setupWarp),
|
||||||
trailing: const Icon(FluentIcons.chevron_right_24_regular),
|
trailing: const Icon(FluentIcons.chevron_right_24_regular),
|
||||||
onTap: () =>
|
onTap: () => ConfigOptionsRoute(section: ConfigOptionSection.warp.name).go(context),
|
||||||
ConfigOptionsRoute(section: ConfigOptionSection.warp.name)
|
),
|
||||||
.go(context),
|
GestureDetector(
|
||||||
|
onLongPress: () {
|
||||||
|
ConfigOptionsRoute(section: ConfigOptionSection.fragment.name).go(context);
|
||||||
|
},
|
||||||
|
child: SwitchListTile(
|
||||||
|
value: ref.watch(ConfigOptions.enableTlsFragment),
|
||||||
|
onChanged: ref.watch(ConfigOptions.enableTlsFragment.notifier).update,
|
||||||
|
title: Text(t.config.enableTlsFragment),
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
|
||||||
value: ref.watch(ConfigOptions.enableTlsFragment),
|
|
||||||
onChanged:
|
|
||||||
ref.watch(ConfigOptions.enableTlsFragment.notifier).update,
|
|
||||||
title: Text(t.config.enableTlsFragment),
|
|
||||||
),
|
),
|
||||||
// SwitchListTile(
|
// SwitchListTile(
|
||||||
// value: ref.watch(ConfigOptions.enableMux),
|
// value: ref.watch(ConfigOptions.enableMux),
|
||||||
|
|||||||
@@ -5,11 +5,16 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hiddify/core/localization/translations.dart';
|
import 'package:hiddify/core/localization/translations.dart';
|
||||||
|
import 'package:hiddify/core/preferences/preferences_provider.dart';
|
||||||
import 'package:hiddify/core/router/router.dart';
|
import 'package:hiddify/core/router/router.dart';
|
||||||
import 'package:hiddify/features/common/qr_code_scanner_screen.dart';
|
import 'package:hiddify/features/common/qr_code_scanner_screen.dart';
|
||||||
|
import 'package:hiddify/features/config_option/notifier/warp_option_notifier.dart';
|
||||||
|
import 'package:hiddify/features/config_option/overview/config_options_page.dart';
|
||||||
|
import 'package:hiddify/features/config_option/overview/warp_options_widgets.dart';
|
||||||
import 'package:hiddify/features/profile/notifier/profile_notifier.dart';
|
import 'package:hiddify/features/profile/notifier/profile_notifier.dart';
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class AddProfileModal extends HookConsumerWidget {
|
class AddProfileModal extends HookConsumerWidget {
|
||||||
const AddProfileModal({
|
const AddProfileModal({
|
||||||
@@ -17,7 +22,7 @@ class AddProfileModal extends HookConsumerWidget {
|
|||||||
this.url,
|
this.url,
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
});
|
});
|
||||||
|
static const warpConsentGiven = "warp_consent_given";
|
||||||
final String? url;
|
final String? url;
|
||||||
final ScrollController? scrollController;
|
final ScrollController? scrollController;
|
||||||
|
|
||||||
@@ -58,8 +63,7 @@ class AddProfileModal extends HookConsumerWidget {
|
|||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
// temporary solution, aspect ratio widget relies on height and in a row there no height!
|
// temporary solution, aspect ratio widget relies on height and in a row there no height!
|
||||||
final buttonWidth =
|
final buttonWidth = constraints.maxWidth / 2 - (buttonsPadding + (buttonsGap / 2));
|
||||||
constraints.maxWidth / 2 - (buttonsPadding + (buttonsGap / 2));
|
|
||||||
|
|
||||||
return AnimatedCrossFade(
|
return AnimatedCrossFade(
|
||||||
firstChild: SizedBox(
|
firstChild: SizedBox(
|
||||||
@@ -93,8 +97,7 @@ class AddProfileModal extends HookConsumerWidget {
|
|||||||
secondChild: Column(
|
secondChild: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: buttonsPadding),
|
||||||
const EdgeInsets.symmetric(horizontal: buttonsPadding),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
_Button(
|
_Button(
|
||||||
@@ -103,13 +106,9 @@ class AddProfileModal extends HookConsumerWidget {
|
|||||||
icon: FluentIcons.clipboard_paste_24_regular,
|
icon: FluentIcons.clipboard_paste_24_regular,
|
||||||
size: buttonWidth,
|
size: buttonWidth,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final captureResult =
|
final captureResult = await Clipboard.getData(Clipboard.kTextPlain).then((value) => value?.text ?? '');
|
||||||
await Clipboard.getData(Clipboard.kTextPlain)
|
|
||||||
.then((value) => value?.text ?? '');
|
|
||||||
if (addProfileState.isLoading) return;
|
if (addProfileState.isLoading) return;
|
||||||
ref
|
ref.read(addProfileProvider.notifier).add(captureResult);
|
||||||
.read(addProfileProvider.notifier)
|
|
||||||
.add(captureResult);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Gap(buttonsGap),
|
const Gap(buttonsGap),
|
||||||
@@ -120,8 +119,7 @@ class AddProfileModal extends HookConsumerWidget {
|
|||||||
icon: FluentIcons.qr_code_24_regular,
|
icon: FluentIcons.qr_code_24_regular,
|
||||||
size: buttonWidth,
|
size: buttonWidth,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final cr =
|
final cr = await QRCodeScannerScreen().open(context);
|
||||||
await QRCodeScannerScreen().open(context);
|
|
||||||
|
|
||||||
if (cr == null) return;
|
if (cr == null) return;
|
||||||
if (addProfileState.isLoading) return;
|
if (addProfileState.isLoading) return;
|
||||||
@@ -142,56 +140,118 @@ class AddProfileModal extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!PlatformUtils.isDesktop)
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.symmetric(
|
||||||
padding: const EdgeInsets.symmetric(
|
horizontal: buttonsPadding,
|
||||||
horizontal: buttonsPadding,
|
vertical: 16,
|
||||||
vertical: 16,
|
),
|
||||||
),
|
child: Column(
|
||||||
child: Semantics(
|
children: [
|
||||||
button: true,
|
Semantics(
|
||||||
child: SizedBox(
|
button: true,
|
||||||
height: 36,
|
child: SizedBox(
|
||||||
child: Material(
|
height: 36,
|
||||||
key: const ValueKey("add_manually_button"),
|
child: Material(
|
||||||
elevation: 8,
|
key: const ValueKey("add_warp_button"),
|
||||||
color: theme.colorScheme.surface,
|
elevation: 8,
|
||||||
surfaceTintColor: theme.colorScheme.surfaceTint,
|
color: theme.colorScheme.surface,
|
||||||
shadowColor: Colors.transparent,
|
surfaceTintColor: theme.colorScheme.surfaceTint,
|
||||||
borderRadius: BorderRadius.circular(8),
|
shadowColor: Colors.transparent,
|
||||||
clipBehavior: Clip.antiAlias,
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: InkWell(
|
clipBehavior: Clip.antiAlias,
|
||||||
onTap: () async {
|
child: InkWell(
|
||||||
context.pop();
|
onTap: () async {
|
||||||
await const NewProfileRoute().push(context);
|
Future.microtask(() async {
|
||||||
},
|
context.pop();
|
||||||
child: Row(
|
final _prefs = ref.read(sharedPreferencesProvider).requireValue;
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
final consent = _prefs.getBool(warpConsentGiven) ?? false;
|
||||||
children: [
|
if (!consent) {
|
||||||
Icon(
|
final agreed = await showDialog<bool>(
|
||||||
FluentIcons.add_24_regular,
|
context: context,
|
||||||
color: theme.colorScheme.primary,
|
builder: (context) => const WarpLicenseAgreementModal(),
|
||||||
),
|
);
|
||||||
const Gap(8),
|
|
||||||
Text(
|
if (agreed ?? false) {
|
||||||
t.profile.add.manually,
|
await ref.read(warpOptionNotifierProvider.notifier).agree();
|
||||||
style: theme.textTheme.labelLarge?.copyWith(
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final accountId = _prefs.getString("warp2-account-id");
|
||||||
|
final accessToken = _prefs.getString("warp2-access-token");
|
||||||
|
final hasWarp2Config = accountId != null && accessToken != null;
|
||||||
|
|
||||||
|
if (!hasWarp2Config) {
|
||||||
|
await ref.read(warpOptionNotifierProvider.notifier).generateWarp2Config();
|
||||||
|
}
|
||||||
|
await ref.read(addProfileProvider.notifier).add("#profile-title: Hiddify WARP\nwarp://p2@auto#Remote&&detour=warp://p1@auto#Local"); //
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
FluentIcons.add_24_regular,
|
||||||
color: theme.colorScheme.primary,
|
color: theme.colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
],
|
Text(
|
||||||
|
t.profile.add.addWarp,
|
||||||
|
style: theme.textTheme.labelLarge?.copyWith(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (!PlatformUtils.isDesktop) const SizedBox(height: 16), // Spacing between the buttons
|
||||||
|
if (!PlatformUtils.isDesktop)
|
||||||
|
Semantics(
|
||||||
|
button: true,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 36,
|
||||||
|
child: Material(
|
||||||
|
key: const ValueKey("add_manually_button"),
|
||||||
|
elevation: 8,
|
||||||
|
color: theme.colorScheme.surface,
|
||||||
|
surfaceTintColor: theme.colorScheme.surfaceTint,
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
context.pop();
|
||||||
|
await const NewProfileRoute().push(context);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
FluentIcons.add_24_regular,
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
t.profile.add.manually,
|
||||||
|
style: theme.textTheme.labelLarge?.copyWith(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const Gap(24),
|
const Gap(24),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
crossFadeState: addProfileState.isLoading
|
crossFadeState: addProfileState.isLoading ? CrossFadeState.showFirst : CrossFadeState.showSecond,
|
||||||
? CrossFadeState.showFirst
|
|
||||||
: CrossFadeState.showSecond,
|
|
||||||
duration: const Duration(milliseconds: 250),
|
duration: const Duration(milliseconds: 250),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ class AddProfile extends _$AddProfile with AppLogger {
|
|||||||
return const AsyncData(null);
|
return const AsyncData(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileRepository get _profilesRepo =>
|
ProfileRepository get _profilesRepo => ref.read(profileRepositoryProvider).requireValue;
|
||||||
ref.read(profileRepositoryProvider).requireValue;
|
|
||||||
CancelToken? _cancelToken;
|
CancelToken? _cancelToken;
|
||||||
|
|
||||||
Future<void> add(String rawInput) async {
|
Future<void> add(String rawInput) async {
|
||||||
@@ -57,8 +56,7 @@ class AddProfile extends _$AddProfile with AppLogger {
|
|||||||
state = await AsyncValue.guard(
|
state = await AsyncValue.guard(
|
||||||
() async {
|
() async {
|
||||||
final activeProfile = await ref.read(activeProfileProvider.future);
|
final activeProfile = await ref.read(activeProfileProvider.future);
|
||||||
final markAsActive =
|
final markAsActive = activeProfile == null || ref.read(Preferences.markNewProfileActive);
|
||||||
activeProfile == null || ref.read(Preferences.markNewProfileActive);
|
|
||||||
final TaskEither<ProfileFailure, Unit> task;
|
final TaskEither<ProfileFailure, Unit> task;
|
||||||
if (LinkParser.parse(rawInput) case (final link)?) {
|
if (LinkParser.parse(rawInput) case (final link)?) {
|
||||||
loggy.debug("adding profile, url: [${link.url}]");
|
loggy.debug("adding profile, url: [${link.url}]");
|
||||||
@@ -70,7 +68,11 @@ class AddProfile extends _$AddProfile with AppLogger {
|
|||||||
} else if (LinkParser.protocol(rawInput) case (final parsed)?) {
|
} else if (LinkParser.protocol(rawInput) case (final parsed)?) {
|
||||||
loggy.debug("adding profile, content");
|
loggy.debug("adding profile, content");
|
||||||
var name = parsed.name;
|
var name = parsed.name;
|
||||||
|
var oldItem = await _profilesRepo.getByName(name);
|
||||||
|
if (name == "Hiddify WARP" && oldItem != null) {
|
||||||
|
_profilesRepo.setAsActive(oldItem.id).run();
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
while (await _profilesRepo.getByName(name) != null) {
|
while (await _profilesRepo.getByName(name) != null) {
|
||||||
name += '${randomInt(0, 9).run()}';
|
name += '${randomInt(0, 9).run()}';
|
||||||
}
|
}
|
||||||
@@ -122,8 +124,7 @@ class UpdateProfile extends _$UpdateProfile with AppLogger {
|
|||||||
return const AsyncData(null);
|
return const AsyncData(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileRepository get _profilesRepo =>
|
ProfileRepository get _profilesRepo => ref.read(profileRepositoryProvider).requireValue;
|
||||||
ref.read(profileRepositoryProvider).requireValue;
|
|
||||||
|
|
||||||
Future<void> updateProfile(RemoteProfileEntity profile) async {
|
Future<void> updateProfile(RemoteProfileEntity profile) async {
|
||||||
if (state.isLoading) return;
|
if (state.isLoading) return;
|
||||||
@@ -143,9 +144,7 @@ class UpdateProfile extends _$UpdateProfile with AppLogger {
|
|||||||
|
|
||||||
await ref.read(activeProfileProvider.future).then((active) async {
|
await ref.read(activeProfileProvider.future).then((active) async {
|
||||||
if (active != null && active.id == profile.id) {
|
if (active != null && active.id == profile.id) {
|
||||||
await ref
|
await ref.read(connectionNotifierProvider.notifier).reconnect(profile);
|
||||||
.read(connectionNotifierProvider.notifier)
|
|
||||||
.reconnect(profile);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return unit;
|
return unit;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:combine/combine.dart';
|
|
||||||
import 'package:dartx/dartx.dart';
|
import 'package:dartx/dartx.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
import 'package:hiddify/core/haptic/haptic_service.dart';
|
import 'package:hiddify/core/haptic/haptic_service.dart';
|
||||||
import 'package:hiddify/core/localization/translations.dart';
|
import 'package:hiddify/core/localization/translations.dart';
|
||||||
import 'package:hiddify/core/preferences/preferences_provider.dart';
|
import 'package:hiddify/core/preferences/preferences_provider.dart';
|
||||||
@@ -85,46 +85,41 @@ class ProxiesOverviewNotifier extends _$ProxiesOverviewNotifier with AppLogger {
|
|||||||
List<ProxyGroupEntity> proxies,
|
List<ProxyGroupEntity> proxies,
|
||||||
ProxiesSort sortBy,
|
ProxiesSort sortBy,
|
||||||
) async {
|
) async {
|
||||||
return CombineWorker().execute(
|
final groupWithSelected = {
|
||||||
() {
|
for (final o in proxies) o.tag: o.selected,
|
||||||
final groupWithSelected = {
|
};
|
||||||
for (final o in proxies) o.tag: o.selected,
|
final sortedProxies = <ProxyGroupEntity>[];
|
||||||
};
|
for (final group in proxies) {
|
||||||
final sortedProxies = <ProxyGroupEntity>[];
|
final sortedItems = switch (sortBy) {
|
||||||
for (final group in proxies) {
|
ProxiesSort.name => group.items.sortedWith((a, b) {
|
||||||
final sortedItems = switch (sortBy) {
|
if (a.type.isGroup && !b.type.isGroup) return -1;
|
||||||
ProxiesSort.name => group.items.sortedWith((a, b) {
|
if (!a.type.isGroup && b.type.isGroup) return 1;
|
||||||
if (a.type.isGroup && !b.type.isGroup) return -1;
|
return a.tag.compareTo(b.tag);
|
||||||
if (!a.type.isGroup && b.type.isGroup) return 1;
|
}),
|
||||||
return a.tag.compareTo(b.tag);
|
ProxiesSort.delay => group.items.sortedWith((a, b) {
|
||||||
}),
|
if (a.type.isGroup && !b.type.isGroup) return -1;
|
||||||
ProxiesSort.delay => group.items.sortedWith((a, b) {
|
if (!a.type.isGroup && b.type.isGroup) return 1;
|
||||||
if (a.type.isGroup && !b.type.isGroup) return -1;
|
|
||||||
if (!a.type.isGroup && b.type.isGroup) return 1;
|
|
||||||
|
|
||||||
final ai = a.urlTestDelay;
|
final ai = a.urlTestDelay;
|
||||||
final bi = b.urlTestDelay;
|
final bi = b.urlTestDelay;
|
||||||
if (ai == 0 && bi == 0) return -1;
|
if (ai == 0 && bi == 0) return -1;
|
||||||
if (ai == 0 && bi > 0) return 1;
|
if (ai == 0 && bi > 0) return 1;
|
||||||
if (ai > 0 && bi == 0) return -1;
|
if (ai > 0 && bi == 0) return -1;
|
||||||
return ai.compareTo(bi);
|
return ai.compareTo(bi);
|
||||||
}),
|
}),
|
||||||
ProxiesSort.unsorted => group.items,
|
ProxiesSort.unsorted => group.items,
|
||||||
};
|
};
|
||||||
final items = <ProxyItemEntity>[];
|
final items = <ProxyItemEntity>[];
|
||||||
for (final item in sortedItems) {
|
for (final item in sortedItems) {
|
||||||
if (groupWithSelected.keys.contains(item.tag)) {
|
if (groupWithSelected.keys.contains(item.tag)) {
|
||||||
items
|
items.add(item.copyWith(selectedTag: groupWithSelected[item.tag]));
|
||||||
.add(item.copyWith(selectedTag: groupWithSelected[item.tag]));
|
} else {
|
||||||
} else {
|
items.add(item);
|
||||||
items.add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sortedProxies.add(group.copyWith(items: items));
|
|
||||||
}
|
}
|
||||||
return sortedProxies;
|
}
|
||||||
},
|
sortedProxies.add(group.copyWith(items: items));
|
||||||
);
|
}
|
||||||
|
return sortedProxies;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeProxy(String groupTag, String outboundTag) async {
|
Future<void> changeProxy(String groupTag, String outboundTag) async {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class SingboxConfigOption with _$SingboxConfigOption {
|
|||||||
required SingboxMuxOption mux,
|
required SingboxMuxOption mux,
|
||||||
required SingboxTlsTricks tlsTricks,
|
required SingboxTlsTricks tlsTricks,
|
||||||
required SingboxWarpOption warp,
|
required SingboxWarpOption warp,
|
||||||
|
required SingboxWarpOption warp2,
|
||||||
}) = _SingboxConfigOption;
|
}) = _SingboxConfigOption;
|
||||||
|
|
||||||
String format() {
|
String format() {
|
||||||
@@ -55,8 +56,7 @@ class SingboxConfigOption with _$SingboxConfigOption {
|
|||||||
return encoder.convert(toJson());
|
return encoder.convert(toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
factory SingboxConfigOption.fromJson(Map<String, dynamic> json) =>
|
factory SingboxConfigOption.fromJson(Map<String, dynamic> json) => _$SingboxConfigOptionFromJson(json);
|
||||||
_$SingboxConfigOptionFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -75,8 +75,7 @@ class SingboxWarpOption with _$SingboxWarpOption {
|
|||||||
@OptionalRangeJsonConverter() required OptionalRange noiseDelay,
|
@OptionalRangeJsonConverter() required OptionalRange noiseDelay,
|
||||||
}) = _SingboxWarpOption;
|
}) = _SingboxWarpOption;
|
||||||
|
|
||||||
factory SingboxWarpOption.fromJson(Map<String, dynamic> json) =>
|
factory SingboxWarpOption.fromJson(Map<String, dynamic> json) => _$SingboxWarpOptionFromJson(json);
|
||||||
_$SingboxWarpOptionFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -89,8 +88,7 @@ class SingboxMuxOption with _$SingboxMuxOption {
|
|||||||
required MuxProtocol protocol,
|
required MuxProtocol protocol,
|
||||||
}) = _SingboxMuxOption;
|
}) = _SingboxMuxOption;
|
||||||
|
|
||||||
factory SingboxMuxOption.fromJson(Map<String, dynamic> json) =>
|
factory SingboxMuxOption.fromJson(Map<String, dynamic> json) => _$SingboxMuxOptionFromJson(json);
|
||||||
_$SingboxMuxOptionFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@@ -105,6 +103,5 @@ class SingboxTlsTricks with _$SingboxTlsTricks {
|
|||||||
@OptionalRangeJsonConverter() required OptionalRange paddingSize,
|
@OptionalRangeJsonConverter() required OptionalRange paddingSize,
|
||||||
}) = _SingboxTlsTricks;
|
}) = _SingboxTlsTricks;
|
||||||
|
|
||||||
factory SingboxTlsTricks.fromJson(Map<String, dynamic> json) =>
|
factory SingboxTlsTricks.fromJson(Map<String, dynamic> json) => _$SingboxTlsTricksFromJson(json);
|
||||||
_$SingboxTlsTricksFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'dart:ffi';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:combine/combine.dart';
|
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:fpdart/fpdart.dart';
|
import 'package:fpdart/fpdart.dart';
|
||||||
import 'package:hiddify/core/model/directories.dart';
|
import 'package:hiddify/core/model/directories.dart';
|
||||||
@@ -52,10 +51,7 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
loggy.debug("initializing");
|
loggy.debug("initializing");
|
||||||
_statusReceiver = ReceivePort('service status receiver');
|
_statusReceiver = ReceivePort('service status receiver');
|
||||||
final source = _statusReceiver
|
final source = _statusReceiver.asBroadcastStream().map((event) => jsonDecode(event as String)).map(SingboxStatus.fromEvent);
|
||||||
.asBroadcastStream()
|
|
||||||
.map((event) => jsonDecode(event as String))
|
|
||||||
.map(SingboxStatus.fromEvent);
|
|
||||||
_status = ValueConnectableStream.seeded(
|
_status = ValueConnectableStream.seeded(
|
||||||
source,
|
source,
|
||||||
const SingboxStopped(),
|
const SingboxStopped(),
|
||||||
@@ -69,8 +65,8 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
) {
|
) {
|
||||||
final port = _statusReceiver.sendPort.nativePort;
|
final port = _statusReceiver.sendPort.nativePort;
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
_box.setupOnce(NativeApi.initializeApiDLData);
|
_box.setupOnce(NativeApi.initializeApiDLData);
|
||||||
final err = _box
|
final err = _box
|
||||||
.setup(
|
.setup(
|
||||||
@@ -98,8 +94,8 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
bool debug,
|
bool debug,
|
||||||
) {
|
) {
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final err = _box
|
final err = _box
|
||||||
.parse(
|
.parse(
|
||||||
path.toNativeUtf8().cast(),
|
path.toNativeUtf8().cast(),
|
||||||
@@ -120,13 +116,10 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> changeOptions(SingboxConfigOption options) {
|
TaskEither<String, Unit> changeOptions(SingboxConfigOption options) {
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final json = jsonEncode(options.toJson());
|
final json = jsonEncode(options.toJson());
|
||||||
final err = _box
|
final err = _box.changeConfigOptions(json.toNativeUtf8().cast()).cast<Utf8>().toDartString();
|
||||||
.changeConfigOptions(json.toNativeUtf8().cast())
|
|
||||||
.cast<Utf8>()
|
|
||||||
.toDartString();
|
|
||||||
if (err.isNotEmpty) {
|
if (err.isNotEmpty) {
|
||||||
return left(err);
|
return left(err);
|
||||||
}
|
}
|
||||||
@@ -141,8 +134,8 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
String path,
|
String path,
|
||||||
) {
|
) {
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final response = _box
|
final response = _box
|
||||||
.generateConfig(
|
.generateConfig(
|
||||||
path.toNativeUtf8().cast(),
|
path.toNativeUtf8().cast(),
|
||||||
@@ -166,8 +159,8 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
) {
|
) {
|
||||||
loggy.debug("starting, memory limit: [${!disableMemoryLimit}]");
|
loggy.debug("starting, memory limit: [${!disableMemoryLimit}]");
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final err = _box
|
final err = _box
|
||||||
.start(
|
.start(
|
||||||
configPath.toNativeUtf8().cast(),
|
configPath.toNativeUtf8().cast(),
|
||||||
@@ -187,8 +180,8 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> stop() {
|
TaskEither<String, Unit> stop() {
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final err = _box.stop().cast<Utf8>().toDartString();
|
final err = _box.stop().cast<Utf8>().toDartString();
|
||||||
if (err.isNotEmpty) {
|
if (err.isNotEmpty) {
|
||||||
return left(err);
|
return left(err);
|
||||||
@@ -207,8 +200,8 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
) {
|
) {
|
||||||
loggy.debug("restarting, memory limit: [${!disableMemoryLimit}]");
|
loggy.debug("restarting, memory limit: [${!disableMemoryLimit}]");
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final err = _box
|
final err = _box
|
||||||
.restart(
|
.restart(
|
||||||
configPath.toNativeUtf8().cast(),
|
configPath.toNativeUtf8().cast(),
|
||||||
@@ -265,10 +258,7 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
final err = _box
|
final err = _box.startCommandClient(1, receiver.sendPort.nativePort).cast<Utf8>().toDartString();
|
||||||
.startCommandClient(1, receiver.sendPort.nativePort)
|
|
||||||
.cast<Utf8>()
|
|
||||||
.toDartString();
|
|
||||||
if (err.isNotEmpty) {
|
if (err.isNotEmpty) {
|
||||||
loggy.error("error starting status command: $err");
|
loggy.error("error starting status command: $err");
|
||||||
throw err;
|
throw err;
|
||||||
@@ -310,10 +300,7 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final err = _box
|
final err = _box.startCommandClient(5, receiver.sendPort.nativePort).cast<Utf8>().toDartString();
|
||||||
.startCommandClient(5, receiver.sendPort.nativePort)
|
|
||||||
.cast<Utf8>()
|
|
||||||
.toDartString();
|
|
||||||
if (err.isNotEmpty) {
|
if (err.isNotEmpty) {
|
||||||
logger.error("error starting group command: $err");
|
logger.error("error starting group command: $err");
|
||||||
throw err;
|
throw err;
|
||||||
@@ -357,10 +344,7 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final err = _box
|
final err = _box.startCommandClient(13, receiver.sendPort.nativePort).cast<Utf8>().toDartString();
|
||||||
.startCommandClient(13, receiver.sendPort.nativePort)
|
|
||||||
.cast<Utf8>()
|
|
||||||
.toDartString();
|
|
||||||
if (err.isNotEmpty) {
|
if (err.isNotEmpty) {
|
||||||
logger.error("error starting: $err");
|
logger.error("error starting: $err");
|
||||||
throw err;
|
throw err;
|
||||||
@@ -376,8 +360,8 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> selectOutbound(String groupTag, String outboundTag) {
|
TaskEither<String, Unit> selectOutbound(String groupTag, String outboundTag) {
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final err = _box
|
final err = _box
|
||||||
.selectOutbound(
|
.selectOutbound(
|
||||||
groupTag.toNativeUtf8().cast(),
|
groupTag.toNativeUtf8().cast(),
|
||||||
@@ -397,12 +381,9 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> urlTest(String groupTag) {
|
TaskEither<String, Unit> urlTest(String groupTag) {
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final err = _box
|
final err = _box.urlTest(groupTag.toNativeUtf8().cast()).cast<Utf8>().toDartString();
|
||||||
.urlTest(groupTag.toNativeUtf8().cast())
|
|
||||||
.cast<Utf8>()
|
|
||||||
.toDartString();
|
|
||||||
if (err.isNotEmpty) {
|
if (err.isNotEmpty) {
|
||||||
return left(err);
|
return left(err);
|
||||||
}
|
}
|
||||||
@@ -418,9 +399,7 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
@override
|
@override
|
||||||
Stream<List<String>> watchLogs(String path) async* {
|
Stream<List<String>> watchLogs(String path) async* {
|
||||||
yield await _readLogFile(File(path));
|
yield await _readLogFile(File(path));
|
||||||
yield* Watcher(path, pollingDelay: const Duration(seconds: 1))
|
yield* Watcher(path, pollingDelay: const Duration(seconds: 1)).events.asyncMap((event) async {
|
||||||
.events
|
|
||||||
.asyncMap((event) async {
|
|
||||||
if (event.type == ChangeType.MODIFY) {
|
if (event.type == ChangeType.MODIFY) {
|
||||||
await _readLogFile(File(path));
|
await _readLogFile(File(path));
|
||||||
}
|
}
|
||||||
@@ -431,17 +410,18 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> clearLogs() {
|
TaskEither<String, Unit> clearLogs() {
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() async {
|
() => Future.microtask(
|
||||||
_logBuffer.clear();
|
() async {
|
||||||
return right(unit);
|
_logBuffer.clear();
|
||||||
},
|
return right(unit);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> _readLogFile(File file) async {
|
Future<List<String>> _readLogFile(File file) async {
|
||||||
if (_logFilePosition == 0 && file.lengthSync() == 0) return [];
|
if (_logFilePosition == 0 && file.lengthSync() == 0) return [];
|
||||||
final content =
|
final content = await file.openRead(_logFilePosition).transform(utf8.decoder).join();
|
||||||
await file.openRead(_logFilePosition).transform(utf8.decoder).join();
|
|
||||||
_logFilePosition = file.lengthSync();
|
_logFilePosition = file.lengthSync();
|
||||||
final lines = const LineSplitter().convert(content);
|
final lines = const LineSplitter().convert(content);
|
||||||
if (lines.length > 300) {
|
if (lines.length > 300) {
|
||||||
@@ -464,8 +444,8 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
}) {
|
}) {
|
||||||
loggy.debug("generating warp config");
|
loggy.debug("generating warp config");
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => Future.microtask(
|
||||||
() {
|
() async {
|
||||||
final response = _box
|
final response = _box
|
||||||
.generateWarpConfig(
|
.generateWarpConfig(
|
||||||
licenseKey.toNativeUtf8().cast(),
|
licenseKey.toNativeUtf8().cast(),
|
||||||
|
|||||||
2
libcore
2
libcore
Submodule libcore updated: b265886306...bc48ec07a8
55
pubspec.lock
55
pubspec.lock
@@ -218,14 +218,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
combine:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: combine
|
|
||||||
sha256: "8b52083c822a614a448fdd307e78c05266080e9747604b61fca5ddfe736a6c1e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.5.7-0.1.pre"
|
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -801,11 +793,12 @@ packages:
|
|||||||
humanizer:
|
humanizer:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: humanizer
|
path: "."
|
||||||
sha256: "08728a4b6d62accd7d09e668bd54e81e6e09a82c8cfda30553224b3eb868d4f2"
|
ref: up-version
|
||||||
url: "https://pub.dev"
|
resolved-ref: "8ae61d68357fae197be7ee71d67ccb9498b9d5c7"
|
||||||
source: hosted
|
url: "https://github.com/alex-relov/humanizer"
|
||||||
version: "2.2.0"
|
source: git
|
||||||
|
version: "2.3.0"
|
||||||
iconsax_flutter:
|
iconsax_flutter:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -850,10 +843,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.1"
|
version: "0.19.0"
|
||||||
io:
|
io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -922,26 +915,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.0"
|
version: "10.0.4"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "3.0.3"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_testing
|
name: leak_tracker_testing
|
||||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "3.0.1"
|
||||||
lint:
|
lint:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -1002,10 +995,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.12.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1703,26 +1696,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f
|
sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.24.9"
|
version: "1.25.2"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.1"
|
version: "0.7.0"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a
|
sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.9"
|
version: "0.6.0"
|
||||||
time:
|
time:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1935,10 +1928,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "13.0.0"
|
version: "14.2.1"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
10
pubspec.yaml
10
pubspec.yaml
@@ -12,7 +12,7 @@ dependencies:
|
|||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
intl: ^0.18.1
|
intl: ^0.19.0
|
||||||
slang: ^3.30.1
|
slang: ^3.30.1
|
||||||
slang_flutter: ^3.30.0
|
slang_flutter: ^3.30.0
|
||||||
fpdart: ^1.1.0
|
fpdart: ^1.1.0
|
||||||
@@ -40,7 +40,7 @@ dependencies:
|
|||||||
launch_at_startup: ^0.2.2
|
launch_at_startup: ^0.2.2
|
||||||
sentry_flutter: ^7.16.1
|
sentry_flutter: ^7.16.1
|
||||||
sentry_dart_plugin: ^1.7.1
|
sentry_dart_plugin: ^1.7.1
|
||||||
combine: ^0.5.7-0.1.pre
|
|
||||||
path: ^1.8.3
|
path: ^1.8.3
|
||||||
loggy: ^2.0.3
|
loggy: ^2.0.3
|
||||||
flutter_loggy: ^2.0.2
|
flutter_loggy: ^2.0.2
|
||||||
@@ -58,7 +58,11 @@ dependencies:
|
|||||||
percent_indicator: ^4.2.3
|
percent_indicator: ^4.2.3
|
||||||
sliver_tools: ^0.2.12
|
sliver_tools: ^0.2.12
|
||||||
flutter_adaptive_scaffold: ^0.1.8
|
flutter_adaptive_scaffold: ^0.1.8
|
||||||
humanizer: ^2.2.0
|
# humanizer: ^2.2.0
|
||||||
|
humanizer:
|
||||||
|
git:
|
||||||
|
url: https://github.com/alex-relov/humanizer
|
||||||
|
ref: up-version
|
||||||
upgrader: ^9.0.0
|
upgrader: ^9.0.0
|
||||||
toastification: ^1.2.1
|
toastification: ^1.2.1
|
||||||
version: ^3.0.2
|
version: ^3.0.2
|
||||||
|
|||||||
Reference in New Issue
Block a user