mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-06-11 08:59:36 +00:00
Merge branch 'beta'
This commit is contained in:
commit
c78e5704bd
23 changed files with 940 additions and 83 deletions
|
@ -11,28 +11,28 @@ PODS:
|
|||
- FMDB/standard (2.7.5)
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- Sentry/HybridSDK (8.9.1):
|
||||
- SentryPrivate (= 8.9.1)
|
||||
- Sentry/HybridSDK (8.15.2):
|
||||
- SentryPrivate (= 8.15.2)
|
||||
- sentry_flutter (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- Sentry/HybridSDK (= 8.9.1)
|
||||
- SentryPrivate (8.9.1)
|
||||
- Sentry/HybridSDK (= 8.15.2)
|
||||
- SentryPrivate (8.15.2)
|
||||
- sqflite (0.0.3):
|
||||
- Flutter
|
||||
- FMDB (>= 2.7.5)
|
||||
- sqlite3 (3.43.1):
|
||||
- sqlite3/common (= 3.43.1)
|
||||
- sqlite3/common (3.43.1)
|
||||
- sqlite3/fts5 (3.43.1):
|
||||
- sqlite3 (3.44.0):
|
||||
- sqlite3/common (= 3.44.0)
|
||||
- sqlite3/common (3.44.0)
|
||||
- sqlite3/fts5 (3.44.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/perf-threadsafe (3.43.1):
|
||||
- sqlite3/perf-threadsafe (3.44.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/rtree (3.43.1):
|
||||
- sqlite3/rtree (3.44.0):
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
- sqlite3 (~> 3.43.0)
|
||||
- sqlite3 (~> 3.44.0)
|
||||
- sqlite3/fts5
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
|
@ -83,21 +83,21 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||
flutter_web_browser: 7bccaafbb0c5b8862afe7bcd158f15557109f61f
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||
Sentry: e3203780941722a1fcfee99e351de14244c7f806
|
||||
sentry_flutter: 8f0ffd53088e6a4d50c095852c5cad9e4405025c
|
||||
SentryPrivate: 5e3683390f66611fc7c6215e27645873adb55d13
|
||||
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
||||
Sentry: 6f5742b4c47c17c9adcf265f6f328cf4a0ed1923
|
||||
sentry_flutter: 2c309a1d4b45e59d02cfa15795705687f1e2081b
|
||||
SentryPrivate: b2f7996f37781080f04a946eb4e377ff63c64195
|
||||
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||
sqlite3: e0a0623a33a20a47cb5921552aebc6e9e437dc91
|
||||
sqlite3_flutter_libs: 878ccbdcfd7b7cb41a774ec238223d876880c5ec
|
||||
sqlite3: 6e2d4a4879854d0ec86b476bf3c3e30870bac273
|
||||
sqlite3_flutter_libs: eb769059df0356dc52ddda040f09cacc9391a7cf
|
||||
store_checker: 359c5051d9ec30ff0a8fa39eb5ec9df021bb745d
|
||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
|
||||
|
||||
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
||||
|
||||
COCOAPODS: 1.12.1
|
||||
COCOAPODS: 1.14.3
|
||||
|
|
|
@ -690,5 +690,28 @@
|
|||
"topUpstreams": "Top upstreams",
|
||||
"averageUpstreamResponseTime": "Average upstream response time",
|
||||
"dhcpNotAvailable": "The DHCP server is not available.",
|
||||
"osServerInstalledIncompatible": "The OS where the server is installed is not compatible with this feature."
|
||||
"osServerInstalledIncompatible": "The OS where the server is installed is not compatible with this feature.",
|
||||
"resetSettings": "Reset settings",
|
||||
"resetEncryptionSettingsDescription": "Are you sure you want to reset to default values the encryption settings?",
|
||||
"resettingConfig": "Resetting configuration...",
|
||||
"configurationResetSuccessfully": "Configuration resetted successfully",
|
||||
"configurationResetError": "The configuration couldn't be resetted",
|
||||
"testUpstreamDnsServers": "Test upstream DNS servers",
|
||||
"errorTestUpstreamDns": "Error when testing upstream DNS servers.",
|
||||
"useCustomIpEdns": "Use custom IP for EDNS",
|
||||
"useCustomIpEdnsDescription": "Allow to use custom IP for EDNS",
|
||||
"sortingOptions": "Sorting options",
|
||||
"fromHighestToLowest": "From highest to lowest",
|
||||
"fromLowestToHighest": "From lowest to highest",
|
||||
"queryLogsAndStatistics": "Query logs and statistics",
|
||||
"ignoreClientQueryLog": "Ignore this client in query log",
|
||||
"ignoreClientStatistics": "Ignore this client in statistics",
|
||||
"savingChanges": "Saving changes...",
|
||||
"fallbackDnsServers": "Fallback DNS servers",
|
||||
"fallbackDnsServersDescription": "Configure fallback DNS servers",
|
||||
"fallbackDnsServersInfo": "List of fallback DNS servers used when upstream DNS servers are not responding. The syntax is the same as in the main upstreams field above.",
|
||||
"noFallbackDnsAdded": "No fallback DNS servers added.",
|
||||
"blockedResponseTtl": "Blocked response TTL",
|
||||
"blockedResponseTtlDescription": "Specifies for how many seconds the clients should cache a filtered response",
|
||||
"invalidValue": "Invalid value"
|
||||
}
|
|
@ -690,5 +690,28 @@
|
|||
"topUpstreams": "DNS de subida más frecuentes",
|
||||
"averageUpstreamResponseTime": "Tiempo promedio de respuesta upstream",
|
||||
"dhcpNotAvailable": "El servidor DHCP no está disponible.",
|
||||
"osServerInstalledIncompatible": "El SO donde el servidor está instalado no es compatible con esta característica."
|
||||
"osServerInstalledIncompatible": "El SO donde el servidor está instalado no es compatible con esta característica.",
|
||||
"resetSettings": "Resetear configuración",
|
||||
"resetEncryptionSettingsDescription": "Estás seguro que deseas restaurar a valores por defecto la configuración de encriptación?",
|
||||
"resettingConfig": "Reseteando configuración...",
|
||||
"configurationResetSuccessfully": "Configuración reseteada correctamente",
|
||||
"configurationResetError": "La configuración no ha podido ser reseteada",
|
||||
"testUpstreamDnsServers": "Probar servidores DNS de subida",
|
||||
"errorTestUpstreamDns": "Error al probar los servidores DNS de subida.",
|
||||
"useCustomIpEdns": "Usar IP personalizada para EDNS",
|
||||
"useCustomIpEdnsDescription": "Permitir usar IP personalizada para EDNS",
|
||||
"sortingOptions": "Opciones de ordenación",
|
||||
"fromHighestToLowest": "De mayor a menor",
|
||||
"fromLowestToHighest": "De menor a mayor",
|
||||
"queryLogsAndStatistics": "Registro de consultas y estadísticas",
|
||||
"ignoreClientQueryLog": "Ignorar este cliente en el registro de consultas",
|
||||
"ignoreClientStatistics": "Ignorar este cliente en las estadísticas",
|
||||
"savingChanges": "Guardando cambios...",
|
||||
"fallbackDnsServers": "Servidores DNS alternativos",
|
||||
"fallbackDnsServersDescription": "Configura los servidores DNS alternativos",
|
||||
"fallbackDnsServersInfo": "Lista de servidores DNS alternativos utilizados cuando los servidores DNS de subida no responden. La sintaxis es la misma que en el campo de los principales DNS de subida anterior.",
|
||||
"noFallbackDnsAdded": "No hay servidores DNS alternativos añadidos.",
|
||||
"blockedResponseTtl": "Respuesta TTL bloqueada",
|
||||
"blockedResponseTtlDescription": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada",
|
||||
"invalidValue": "Valor no válido"
|
||||
}
|
|
@ -87,6 +87,8 @@ class Client {
|
|||
final bool useGlobalBlockedServices;
|
||||
final bool useGlobalSettings;
|
||||
final SafeSearch? safeSearch;
|
||||
final bool? ignoreQuerylog;
|
||||
final bool? ignoreStatistics;
|
||||
|
||||
Client({
|
||||
required this.name,
|
||||
|
@ -100,6 +102,8 @@ class Client {
|
|||
required this.useGlobalBlockedServices,
|
||||
required this.useGlobalSettings,
|
||||
required this.safeSearch,
|
||||
required this.ignoreQuerylog,
|
||||
required this.ignoreStatistics,
|
||||
});
|
||||
|
||||
factory Client.fromJson(Map<String, dynamic> json) => Client(
|
||||
|
@ -115,7 +119,9 @@ class Client {
|
|||
useGlobalSettings: json["use_global_settings"],
|
||||
safeSearch: json["safe_search"] != null
|
||||
? SafeSearch.fromJson(json["safe_search"])
|
||||
: null
|
||||
: null,
|
||||
ignoreQuerylog: json["ignore_querylog"],
|
||||
ignoreStatistics: json["ignore_statistics"]
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
@ -130,5 +136,7 @@ class Client {
|
|||
"safe_search": safeSearch,
|
||||
"use_global_blocked_services": useGlobalBlockedServices,
|
||||
"use_global_settings": useGlobalSettings,
|
||||
"ignore_querylog": ignoreQuerylog,
|
||||
"ignore_statistics": ignoreStatistics,
|
||||
};
|
||||
}
|
|
@ -2,10 +2,13 @@ class DnsInfo {
|
|||
List<String> upstreamDns;
|
||||
String? upstreamDnsFile;
|
||||
List<String> bootstrapDns;
|
||||
List<String>? fallbackDns;
|
||||
bool protectionEnabled;
|
||||
int ratelimit;
|
||||
String blockingMode;
|
||||
bool ednsCsEnabled;
|
||||
bool? ednsCsUseCustom;
|
||||
String? ednsCsCustomIp;
|
||||
bool dnssecEnabled;
|
||||
bool disableIpv6;
|
||||
String? upstreamMode;
|
||||
|
@ -19,15 +22,19 @@ class DnsInfo {
|
|||
String blockingIpv4;
|
||||
String blockingIpv6;
|
||||
List<String> defaultLocalPtrUpstreams;
|
||||
int? blockedResponseTtl;
|
||||
|
||||
DnsInfo({
|
||||
required this.upstreamDns,
|
||||
required this.upstreamDnsFile,
|
||||
required this.bootstrapDns,
|
||||
required this.fallbackDns,
|
||||
required this.protectionEnabled,
|
||||
required this.ratelimit,
|
||||
required this.blockingMode,
|
||||
required this.ednsCsEnabled,
|
||||
required this.ednsCsUseCustom,
|
||||
required this.ednsCsCustomIp,
|
||||
required this.dnssecEnabled,
|
||||
required this.disableIpv6,
|
||||
required this.upstreamMode,
|
||||
|
@ -41,16 +48,20 @@ class DnsInfo {
|
|||
required this.blockingIpv4,
|
||||
required this.blockingIpv6,
|
||||
required this.defaultLocalPtrUpstreams,
|
||||
required this.blockedResponseTtl,
|
||||
});
|
||||
|
||||
factory DnsInfo.fromJson(Map<String, dynamic> json) => DnsInfo(
|
||||
upstreamDns: json["upstream_dns"] != null ? List<String>.from(json["upstream_dns"].map((x) => x)) : [],
|
||||
upstreamDnsFile: json["upstream_dns_file"],
|
||||
bootstrapDns: json["bootstrap_dns"] != null ? List<String>.from(json["bootstrap_dns"].map((x) => x)) : [],
|
||||
fallbackDns: json["fallback_dns"] != null ? List<String>.from(json["fallback_dns"].map((x) => x)) : [],
|
||||
protectionEnabled: json["protection_enabled"],
|
||||
ratelimit: json["ratelimit"],
|
||||
blockingMode: json["blocking_mode"],
|
||||
ednsCsEnabled: json["edns_cs_enabled"],
|
||||
ednsCsUseCustom: json["edns_cs_use_custom"],
|
||||
ednsCsCustomIp: json["edns_cs_custom_ip"],
|
||||
dnssecEnabled: json["dnssec_enabled"],
|
||||
disableIpv6: json["disable_ipv6"],
|
||||
upstreamMode: json["upstream_mode"],
|
||||
|
@ -64,16 +75,20 @@ class DnsInfo {
|
|||
blockingIpv4: json["blocking_ipv4"],
|
||||
blockingIpv6: json["blocking_ipv6"],
|
||||
defaultLocalPtrUpstreams: json["default_local_ptr_upstreams"] != null ? List<String>.from(json["default_local_ptr_upstreams"].map((x) => x)) : [],
|
||||
blockedResponseTtl: json["blocked_response_ttl"]
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"upstream_dns": List<dynamic>.from(upstreamDns.map((x) => x)),
|
||||
"upstream_dns_file": upstreamDnsFile,
|
||||
"bootstrap_dns": List<dynamic>.from(bootstrapDns.map((x) => x)),
|
||||
"fallback_dns": List<dynamic>.from(bootstrapDns.map((x) => x)),
|
||||
"protection_enabled": protectionEnabled,
|
||||
"ratelimit": ratelimit,
|
||||
"blocking_mode": blockingMode,
|
||||
"edns_cs_enabled": ednsCsEnabled,
|
||||
"edns_cs_use_custom": ednsCsUseCustom,
|
||||
"edns_cs_custom_ip": ednsCsCustomIp,
|
||||
"dnssec_enabled": dnssecEnabled,
|
||||
"disable_ipv6": disableIpv6,
|
||||
"upstream_mode": upstreamMode,
|
||||
|
@ -87,5 +102,6 @@ class DnsInfo {
|
|||
"blocking_ipv4": blockingIpv4,
|
||||
"blocking_ipv6": blockingIpv6,
|
||||
"default_local_ptr_upstreams": List<dynamic>.from(defaultLocalPtrUpstreams.map((x) => x)),
|
||||
"blocked_response_ttl": blockedResponseTtl
|
||||
};
|
||||
}
|
||||
|
|
|
@ -112,6 +112,22 @@ class DnsProvider with ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse> saveFallbackDnsConfig(Map<String, dynamic> value) async {
|
||||
final result = await _serversProvider!.apiClient2!.setDnsConfig(
|
||||
data: value
|
||||
);
|
||||
|
||||
if (result.successful == true) {
|
||||
DnsInfo data = dnsInfo!;
|
||||
data.bootstrapDns = List<String>.from(value['fallback_dns']);
|
||||
setDnsInfoData(data);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse> saveCacheCacheConfig(Map<String, dynamic> value) async {
|
||||
final result = await _serversProvider!.apiClient2!.setDnsConfig(
|
||||
data: value
|
||||
|
@ -145,6 +161,7 @@ class DnsProvider with ChangeNotifier {
|
|||
data.blockingMode = value['blocking_mode'];
|
||||
data.blockingIpv4 = value['blocking_ipv4'];
|
||||
data.blockingIpv6 = value['blocking_ipv6'];
|
||||
data.blockedResponseTtl = value['blocked_response_ttl'];
|
||||
setDnsInfoData(data);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ class AddedList extends StatefulWidget {
|
|||
final bool splitView;
|
||||
|
||||
const AddedList({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.scrollController,
|
||||
required this.data,
|
||||
required this.onClientSelected,
|
||||
this.selectedClient,
|
||||
required this.splitView
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<AddedList> createState() => _AddedListState();
|
||||
|
@ -75,7 +75,7 @@ class _AddedListState extends State<AddedList> {
|
|||
|
||||
void confirmEditClient(Client client) async {
|
||||
ProcessModal processModal = ProcessModal();
|
||||
processModal.open(AppLocalizations.of(context)!.addingClient);
|
||||
processModal.open(AppLocalizations.of(context)!.savingChanges);
|
||||
|
||||
final result = await clientsProvider.editClient(client);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/custom_switch_list_tile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
|
@ -45,6 +46,10 @@ class ClientForm extends StatelessWidget {
|
|||
final void Function(bool) updateEnableSafeSearch;
|
||||
final void Function(SafeSearch) updateSafeSearch;
|
||||
final void Function(bool) updateUseGlobalSettingsServices;
|
||||
final bool ignoreClientQueryLog;
|
||||
final void Function(bool) updateIgnoreClientQueryLog;
|
||||
final bool ignoreClientStatistics;
|
||||
final void Function(bool) updateIgnoreClientStatistics;
|
||||
|
||||
const ClientForm({
|
||||
super.key,
|
||||
|
@ -75,6 +80,10 @@ class ClientForm extends StatelessWidget {
|
|||
required this.updateEnableSafeSearch,
|
||||
required this.updateSafeSearch,
|
||||
required this.updateUseGlobalSettingsServices,
|
||||
required this.ignoreClientQueryLog,
|
||||
required this.ignoreClientStatistics,
|
||||
required this.updateIgnoreClientQueryLog,
|
||||
required this.updateIgnoreClientStatistics,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -217,6 +226,28 @@ class ClientForm extends StatelessWidget {
|
|||
)
|
||||
: null,
|
||||
),
|
||||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.queryLogsAndStatistics,
|
||||
padding: const EdgeInsets.all(24),
|
||||
),
|
||||
CustomSwitchListTile(
|
||||
title: AppLocalizations.of(context)!.ignoreClientQueryLog,
|
||||
value: ignoreClientQueryLog,
|
||||
onChanged: updateIgnoreClientQueryLog,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 4
|
||||
),
|
||||
),
|
||||
CustomSwitchListTile(
|
||||
title: AppLocalizations.of(context)!.ignoreClientStatistics,
|
||||
value: ignoreClientStatistics,
|
||||
onChanged: updateIgnoreClientStatistics,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 4
|
||||
),
|
||||
),
|
||||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.blockedServices,
|
||||
padding: const EdgeInsets.all(24),
|
||||
|
|
|
@ -73,6 +73,9 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
|
||||
List<ControllerListItem> upstreamServers = [];
|
||||
|
||||
bool _ignoreClientQueryLog = false;
|
||||
bool _ignoreClientStatistics = false;
|
||||
|
||||
void enableDisableGlobalSettingsFiltering() {
|
||||
if (useGlobalSettingsFiltering == true) {
|
||||
setState(() {
|
||||
|
@ -120,6 +123,8 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
id: uuid.v4(),
|
||||
controller: TextEditingController(text: e)
|
||||
)).toList();
|
||||
_ignoreClientQueryLog = widget.client!.ignoreQuerylog ?? false;
|
||||
_ignoreClientStatistics = widget.client!.ignoreStatistics ?? false;
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
@ -140,7 +145,9 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
useGlobalBlockedServices: useGlobalSettingsServices,
|
||||
blockedServices: blockedServices,
|
||||
upstreams: List<String>.from(upstreamServers.map((e) => e.controller.text)),
|
||||
tags: selectedTags
|
||||
tags: selectedTags,
|
||||
ignoreQuerylog: _ignoreClientQueryLog,
|
||||
ignoreStatistics: _ignoreClientStatistics
|
||||
);
|
||||
widget.onConfirm(client);
|
||||
}
|
||||
|
@ -214,6 +221,10 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
updateEnableSafeSearch: (v) => setState(() => enableSafeSearch = v),
|
||||
updateSafeSearch: (v) => setState(() => safeSearch = v),
|
||||
updateUseGlobalSettingsServices: (v) => setState(() => useGlobalSettingsServices = v),
|
||||
ignoreClientQueryLog: _ignoreClientQueryLog,
|
||||
ignoreClientStatistics: _ignoreClientStatistics,
|
||||
updateIgnoreClientQueryLog: (v) => setState(() => _ignoreClientQueryLog = v),
|
||||
updateIgnoreClientStatistics: (v) => setState(() => _ignoreClientStatistics = v),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -281,6 +292,10 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
updateEnableSafeSearch: (v) => setState(() => enableSafeSearch = v),
|
||||
updateSafeSearch: (v) => setState(() => safeSearch = v),
|
||||
updateUseGlobalSettingsServices: (v) => setState(() => useGlobalSettingsServices = v),
|
||||
ignoreClientQueryLog: _ignoreClientQueryLog,
|
||||
ignoreClientStatistics: _ignoreClientStatistics,
|
||||
updateIgnoreClientQueryLog: (v) => setState(() => _ignoreClientQueryLog = v),
|
||||
updateIgnoreClientStatistics: (v) => setState(() => _ignoreClientStatistics = v),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
@ -16,9 +18,9 @@ class HomeAppBar extends StatelessWidget {
|
|||
final bool innerBoxScrolled;
|
||||
|
||||
const HomeAppBar({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.innerBoxScrolled
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -118,6 +120,14 @@ class HomeAppBar extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
actions: [
|
||||
if (!(Platform.isAndroid || Platform.isIOS)) ...[
|
||||
IconButton(
|
||||
onPressed: () => statusProvider.getServerStatus(),
|
||||
icon: const Icon(Icons.refresh_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.refresh,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
|
|
|
@ -15,6 +15,8 @@ import 'package:adguard_home_manager/constants/enums.dart';
|
|||
import 'package:adguard_home_manager/functions/number_format.dart';
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
|
||||
enum _SortingOptions { highestToLowest, lowestToHighest }
|
||||
|
||||
class TopItemsScreen extends StatefulWidget {
|
||||
final HomeTopItems type;
|
||||
final String title;
|
||||
|
@ -44,6 +46,7 @@ class TopItemsScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _TopItemsScreenState extends State<TopItemsScreen> {
|
||||
_SortingOptions _sortingOptions = _SortingOptions.highestToLowest;
|
||||
bool searchActive = false;
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
|
||||
|
@ -68,6 +71,10 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
|||
for (var element in data) {
|
||||
total = total + double.parse(element.values.toList()[0].toString());
|
||||
}
|
||||
|
||||
final sortedValues = _sortingOptions == _SortingOptions.lowestToHighest
|
||||
? screenData.reversed.toList()
|
||||
: screenData.toList();
|
||||
|
||||
if (widget.isFullscreen == true) {
|
||||
return Dialog.fullscreen(
|
||||
|
@ -119,6 +126,53 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
|||
icon: const Icon(Icons.clear_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.clearSearch,
|
||||
),
|
||||
PopupMenuButton(
|
||||
icon: const Icon(Icons.sort_rounded),
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
onTap: () => setState(() => _sortingOptions = _SortingOptions.highestToLowest),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.arrow_downward_rounded),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(AppLocalizations.of(context)!.fromHighestToLowest)
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Icon(
|
||||
_sortingOptions == _SortingOptions.highestToLowest
|
||||
? Icons.radio_button_checked_rounded
|
||||
: Icons.radio_button_unchecked_rounded,
|
||||
color: _sortingOptions == _SortingOptions.highestToLowest
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
PopupMenuItem(
|
||||
onTap: () => setState(() => _sortingOptions = _SortingOptions.lowestToHighest),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.arrow_upward_rounded),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(AppLocalizations.of(context)!.fromLowestToHighest)
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Icon(
|
||||
_sortingOptions == _SortingOptions.lowestToHighest
|
||||
? Icons.radio_button_checked_rounded
|
||||
: Icons.radio_button_unchecked_rounded,
|
||||
color: _sortingOptions == _SortingOptions.lowestToHighest
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8)
|
||||
],
|
||||
),
|
||||
|
@ -128,7 +182,7 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
|||
isClient: widget.isClient,
|
||||
onTapEntry: widget.onTapEntry,
|
||||
options: widget.options,
|
||||
screenData: screenData,
|
||||
screenData: sortedValues,
|
||||
total: total,
|
||||
withProgressBar: widget.withProgressBar,
|
||||
),
|
||||
|
@ -193,7 +247,7 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
|||
isClient: widget.isClient,
|
||||
onTapEntry: widget.onTapEntry,
|
||||
options: widget.options,
|
||||
screenData: screenData,
|
||||
screenData: sortedValues,
|
||||
total: total,
|
||||
withProgressBar: widget.withProgressBar,
|
||||
),
|
||||
|
|
|
@ -49,6 +49,7 @@ class _AccessSettingsState extends State<AccessSettings> with TickerProviderStat
|
|||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
sliver: SliverSafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
sliver: SliverAppBar(
|
||||
title: Text(AppLocalizations.of(context)!.accessSettings),
|
||||
pinned: true,
|
||||
|
@ -56,10 +57,7 @@ class _AccessSettingsState extends State<AccessSettings> with TickerProviderStat
|
|||
centerTitle: false,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size(double.maxFinite, 50),
|
||||
child: _Tabs(tabController: _tabController)
|
||||
)
|
||||
bottom: _Tabs(tabController: _tabController)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -92,7 +90,7 @@ class _AccessSettingsState extends State<AccessSettings> with TickerProviderStat
|
|||
}
|
||||
}
|
||||
|
||||
class _Tabs extends StatelessWidget {
|
||||
class _Tabs extends StatelessWidget implements PreferredSizeWidget {
|
||||
final TabController tabController;
|
||||
|
||||
const _Tabs({
|
||||
|
@ -137,6 +135,9 @@ class _Tabs extends StatelessWidget {
|
|||
]
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
||||
|
||||
class _TabsView extends StatelessWidget {
|
||||
|
|
|
@ -5,6 +5,8 @@ import 'package:flutter_split_view/flutter_split_view.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/screens/settings/dns/fallback_dns.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/dns/test_upstream_dns_modal.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/dns/clear_dns_cache_dialog.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/dns/cache_config.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/dns/dns_server_settings.dart';
|
||||
|
@ -84,6 +86,14 @@ class _DnsSettingsState extends State<DnsSettings> {
|
|||
title: Text(AppLocalizations.of(context)!.dnsSettings),
|
||||
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => const TestUpstreamDnsModal()
|
||||
),
|
||||
icon: const Icon(Icons.upload_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.testUpstreamDnsServers,
|
||||
),
|
||||
PopupMenuButton(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
|
@ -158,6 +168,12 @@ class _DnsSettingsState extends State<DnsSettings> {
|
|||
onTap: () => navigate(const BootstrapDnsScreen()),
|
||||
icon: Icons.dns_rounded,
|
||||
),
|
||||
if (dnsProvider.dnsInfo!.fallbackDns != null) CustomListTile(
|
||||
title: AppLocalizations.of(context)!.fallbackDnsServers,
|
||||
subtitle: AppLocalizations.of(context)!.fallbackDnsServersDescription,
|
||||
onTap: () => navigate(const FallbackDnsScreen()),
|
||||
icon: Icons.alt_route_rounded,
|
||||
),
|
||||
CustomListTile(
|
||||
title: AppLocalizations.of(context)!.privateReverseDnsServers,
|
||||
subtitle: AppLocalizations.of(context)!.privateReverseDnsDescription,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
@ -24,7 +25,12 @@ class DnsServerSettingsScreen extends StatefulWidget {
|
|||
class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
||||
final TextEditingController limitRequestsController = TextEditingController();
|
||||
String? limitRequestsError;
|
||||
final _expandableCustomEdns = ExpandableController();
|
||||
final _expandableEdnsIp = ExpandableController();
|
||||
bool enableEdns = false;
|
||||
bool useCustomIpEdns = false;
|
||||
final _customIpEdnsController = TextEditingController();
|
||||
String? ednsIpError;
|
||||
bool enableDnssec = false;
|
||||
bool disableIpv6Resolving = false;
|
||||
|
||||
|
@ -35,6 +41,9 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
final TextEditingController ipv6controller = TextEditingController();
|
||||
String? ipv6error;
|
||||
|
||||
final _ttlController = TextEditingController();
|
||||
String? _ttlError;
|
||||
|
||||
bool isDataValid = false;
|
||||
|
||||
void validateIpv4(String value) {
|
||||
|
@ -48,6 +57,17 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
validateData();
|
||||
}
|
||||
|
||||
void validateEdns(String value) {
|
||||
RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$');
|
||||
if (ipAddress.hasMatch(value) == true) {
|
||||
setState(() => ednsIpError = null);
|
||||
}
|
||||
else {
|
||||
setState(() => ednsIpError = AppLocalizations.of(context)!.ipNotValid);
|
||||
}
|
||||
validateData();
|
||||
}
|
||||
|
||||
void validateIpv6(String value) {
|
||||
RegExp ipAddress = RegExp(r'(?:^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$)|(?:^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$)');
|
||||
if (ipAddress.hasMatch(value) == true) {
|
||||
|
@ -72,7 +92,9 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
ipv6controller.text != '' &&
|
||||
ipv6error == null
|
||||
)
|
||||
) == true
|
||||
) == true &&
|
||||
ednsIpError == null &&
|
||||
_ttlError == null
|
||||
) {
|
||||
setState(() => isDataValid = true);
|
||||
}
|
||||
|
@ -81,18 +103,36 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
void validateNumber(String value) {
|
||||
final regex = RegExp(r'^(\d)+$');
|
||||
if (regex.hasMatch(value) == true) {
|
||||
setState(() => _ttlError = null);
|
||||
}
|
||||
else {
|
||||
setState(() => _ttlError = AppLocalizations.of(context)!.invalidValue);
|
||||
}
|
||||
validateData();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final dnsProvider = Provider.of<DnsProvider>(context, listen: false);
|
||||
|
||||
limitRequestsController.text = dnsProvider.dnsInfo!.ratelimit.toString();
|
||||
enableEdns = dnsProvider.dnsInfo!.ednsCsEnabled;
|
||||
useCustomIpEdns = dnsProvider.dnsInfo!.ednsCsUseCustom ?? false;
|
||||
_customIpEdnsController.text = dnsProvider.dnsInfo!.ednsCsCustomIp ?? "";
|
||||
if (dnsProvider.dnsInfo!.ednsCsEnabled == true) _expandableCustomEdns.toggle();
|
||||
if (dnsProvider.dnsInfo!.ednsCsUseCustom == true) _expandableEdnsIp.toggle();
|
||||
enableDnssec = dnsProvider.dnsInfo!.dnssecEnabled;
|
||||
disableIpv6Resolving = dnsProvider.dnsInfo!.disableIpv6;
|
||||
blockingMode = dnsProvider.dnsInfo!.blockingMode;
|
||||
ipv4controller.text = dnsProvider.dnsInfo!.blockingIpv4;
|
||||
ipv6controller.text = dnsProvider.dnsInfo!.blockingIpv6;
|
||||
isDataValid = true;
|
||||
_ttlController.text = dnsProvider.dnsInfo!.blockedResponseTtl != null
|
||||
? dnsProvider.dnsInfo!.blockedResponseTtl.toString()
|
||||
: "";
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -109,11 +149,14 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
final result = await dnsProvider.saveDnsServerConfig({
|
||||
"ratelimit": int.parse(limitRequestsController.text),
|
||||
"edns_cs_enabled": enableEdns,
|
||||
"edns_cs_use_custom": useCustomIpEdns,
|
||||
"edns_cs_custom_ip": _customIpEdnsController.text,
|
||||
"dnssec_enabled": enableDnssec,
|
||||
"disable_ipv6": disableIpv6Resolving,
|
||||
"blocking_mode": blockingMode,
|
||||
"blocking_ipv4": ipv4controller.text,
|
||||
"blocking_ipv6": ipv6controller.text
|
||||
"blocking_ipv6": ipv6controller.text,
|
||||
"blocked_response_ttl": int.parse(_ttlController.text)
|
||||
});
|
||||
|
||||
processModal.close();
|
||||
|
@ -200,10 +243,78 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
const SizedBox(height: 10),
|
||||
CustomSwitchListTile(
|
||||
value: enableEdns,
|
||||
onChanged: (value) => setState(() => enableEdns = value),
|
||||
onChanged: (value) => setState(() {
|
||||
enableEdns = value;
|
||||
_expandableCustomEdns.toggle();
|
||||
if (value == false) {
|
||||
useCustomIpEdns = false;
|
||||
if (_expandableEdnsIp.expanded == true) _expandableEdnsIp.toggle();
|
||||
_customIpEdnsController.text = "";
|
||||
ednsIpError = null;
|
||||
}
|
||||
validateData();
|
||||
}),
|
||||
title: AppLocalizations.of(context)!.enableEdns,
|
||||
subtitle: AppLocalizations.of(context)!.enableEdnsDescription,
|
||||
),
|
||||
ExpandableNotifier(
|
||||
controller: _expandableCustomEdns,
|
||||
child: Expandable(
|
||||
collapsed: const SizedBox(),
|
||||
expanded: Column(
|
||||
children: [
|
||||
CustomSwitchListTile(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 50,
|
||||
top: 12,
|
||||
bottom: 12,
|
||||
right: 16
|
||||
),
|
||||
value: useCustomIpEdns,
|
||||
onChanged: (value) => setState(() {
|
||||
useCustomIpEdns = value;
|
||||
_expandableEdnsIp.toggle();
|
||||
if (useCustomIpEdns == false) {
|
||||
_customIpEdnsController.text = "";
|
||||
ednsIpError = null;
|
||||
}
|
||||
validateData();
|
||||
}),
|
||||
title: AppLocalizations.of(context)!.useCustomIpEdns,
|
||||
subtitle: AppLocalizations.of(context)!.useCustomIpEdnsDescription,
|
||||
),
|
||||
ExpandableNotifier(
|
||||
controller: _expandableEdnsIp,
|
||||
child: Expandable(
|
||||
collapsed: const SizedBox(),
|
||||
expanded: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16,
|
||||
bottom: 16,
|
||||
right: 16,
|
||||
left: 70
|
||||
),
|
||||
child: TextFormField(
|
||||
controller: _customIpEdnsController,
|
||||
onChanged: validateEdns,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.link_rounded),
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10)
|
||||
)
|
||||
),
|
||||
errorText: ednsIpError,
|
||||
labelText: AppLocalizations.of(context)!.ipAddress,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
CustomSwitchListTile(
|
||||
value: enableDnssec,
|
||||
onChanged: (value) => setState(() => enableDnssec = value),
|
||||
|
@ -300,8 +411,28 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
keyboardType: TextInputType.number,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30)
|
||||
]
|
||||
const SizedBox(height: 30),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: TextFormField(
|
||||
controller: _ttlController,
|
||||
onChanged: validateNumber,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.timer_rounded),
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10)
|
||||
)
|
||||
),
|
||||
errorText: _ttlError,
|
||||
labelText: AppLocalizations.of(context)!.blockedResponseTtl,
|
||||
helperText: AppLocalizations.of(context)!.blockedResponseTtlDescription,
|
||||
helperMaxLines: 2,
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
224
lib/screens/settings/dns/fallback_dns.dart
Normal file
224
lib/screens/settings/dns/fallback_dns.dart
Normal file
|
@ -0,0 +1,224 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/functions/desktop_mode.dart';
|
||||
import 'package:adguard_home_manager/classes/process_modal.dart';
|
||||
import 'package:adguard_home_manager/providers/dns_provider.dart';
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class FallbackDnsScreen extends StatefulWidget {
|
||||
const FallbackDnsScreen({super.key});
|
||||
|
||||
@override
|
||||
State<FallbackDnsScreen> createState() => _FallbackDnsScreenState();
|
||||
}
|
||||
|
||||
class _FallbackDnsScreenState extends State<FallbackDnsScreen> {
|
||||
List<Map<String, dynamic>> fallbackControllers = [];
|
||||
|
||||
bool validValues = false;
|
||||
|
||||
void validateIp(Map<String, dynamic> field, String value) {
|
||||
RegExp ipAddress = RegExp(r'(?:^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$)|(?:^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$)');
|
||||
if (ipAddress.hasMatch(value) == true) {
|
||||
setState(() => field['error'] = null);
|
||||
}
|
||||
else {
|
||||
setState(() => field['error'] = AppLocalizations.of(context)!.invalidIp);
|
||||
}
|
||||
checkValidValues();
|
||||
}
|
||||
|
||||
void checkValidValues() {
|
||||
if (
|
||||
fallbackControllers.isNotEmpty &&
|
||||
fallbackControllers.every((element) => element['controller'].text != '') &&
|
||||
fallbackControllers.every((element) => element['error'] == null)
|
||||
) {
|
||||
setState(() => validValues = true);
|
||||
}
|
||||
else {
|
||||
setState(() => validValues = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final dnsProvider = Provider.of<DnsProvider>(context, listen: false);
|
||||
|
||||
for (var item in dnsProvider.dnsInfo!.fallbackDns!) {
|
||||
final controller = TextEditingController();
|
||||
controller.text = item;
|
||||
fallbackControllers.add({
|
||||
'controller': controller,
|
||||
'error': null
|
||||
});
|
||||
}
|
||||
validValues = true;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dnsProvider = Provider.of<DnsProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
|
||||
void saveData() async {
|
||||
ProcessModal processModal = ProcessModal();
|
||||
processModal.open(AppLocalizations.of(context)!.savingConfig);
|
||||
|
||||
final result = await dnsProvider.saveFallbackDnsConfig({
|
||||
"fallback_dns": fallbackControllers.map((e) => e['controller'].text).toList(),
|
||||
});
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (result.successful == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.dnsConfigSaved,
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else if (result.successful == false && result.statusCode == 400) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.someValueNotValid,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.dnsConfigNotSaved,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.fallbackDnsServers),
|
||||
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: validValues == true
|
||||
? () => saveData()
|
||||
: null,
|
||||
icon: const Icon(Icons.save_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.save,
|
||||
),
|
||||
const SizedBox(width: 10)
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
children: [
|
||||
Card(
|
||||
margin: const EdgeInsets.only(
|
||||
left: 16, right: 16, bottom: 20
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.info_rounded,
|
||||
color: Theme.of(context).listTileTheme.iconColor,
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Flexible(
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.fallbackDnsServersInfo,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (fallbackControllers.isEmpty) Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.noFallbackDnsAdded,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontSize: 16
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
...fallbackControllers.map((c) => Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16, right: 6, bottom: 20
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: c['controller'],
|
||||
onChanged: (value) => validateIp(c, value),
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.dns_rounded),
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10)
|
||||
)
|
||||
),
|
||||
errorText: c['error'],
|
||||
labelText: AppLocalizations.of(context)!.dnsServer,
|
||||
)
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() => fallbackControllers = fallbackControllers.where((con) => con != c).toList());
|
||||
checkValidValues();
|
||||
},
|
||||
icon: const Icon(Icons.remove_circle_outline)
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
setState(() => fallbackControllers.add({
|
||||
'controller': TextEditingController(),
|
||||
'error': null
|
||||
}));
|
||||
checkValidValues();
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: Text(AppLocalizations.of(context)!.addItem)
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
168
lib/screens/settings/dns/test_upstream_dns_modal.dart
Normal file
168
lib/screens/settings/dns/test_upstream_dns_modal.dart
Normal file
|
@ -0,0 +1,168 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/providers/dns_provider.dart';
|
||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||
|
||||
class _Item {
|
||||
final String url;
|
||||
final bool value;
|
||||
|
||||
const _Item({
|
||||
required this.url,
|
||||
required this.value
|
||||
});
|
||||
}
|
||||
|
||||
class TestUpstreamDnsModal extends StatefulWidget {
|
||||
const TestUpstreamDnsModal({super.key});
|
||||
|
||||
@override
|
||||
State<TestUpstreamDnsModal> createState() => _TestUpstreamDnsModalState();
|
||||
}
|
||||
|
||||
class _TestUpstreamDnsModalState extends State<TestUpstreamDnsModal> {
|
||||
LoadStatus loadStatus = LoadStatus.loading;
|
||||
List<_Item>? values;
|
||||
|
||||
void checkDns() async {
|
||||
final dnsProvider = Provider.of<DnsProvider>(context, listen: false);
|
||||
final result = await Provider.of<ServersProvider>(context, listen: false).apiClient2!.testUpstreamDns(
|
||||
body: {
|
||||
"bootstrap_dns": dnsProvider.dnsInfo!.bootstrapDns,
|
||||
"fallback_dns": [],
|
||||
"private_upstream": dnsProvider.dnsInfo!.defaultLocalPtrUpstreams,
|
||||
"upstream_dns": dnsProvider.dnsInfo!.upstreamDns
|
||||
}
|
||||
);
|
||||
if (!mounted) return;
|
||||
if (result.successful == true) {
|
||||
setState(() {
|
||||
values = List<_Item>.from(
|
||||
(result.content as Map<String, dynamic>).entries.map((e) => _Item(
|
||||
url: e.key,
|
||||
value: e.value == "OK" ? true : false
|
||||
))
|
||||
);
|
||||
loadStatus = LoadStatus.loaded;
|
||||
});
|
||||
}
|
||||
else {
|
||||
setState(() => loadStatus = LoadStatus.error);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
checkDns();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.upload_rounded,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.testUpstreamDnsServers,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 500
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
switch (loadStatus) {
|
||||
case LoadStatus.loading:
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircularProgressIndicator()
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
case LoadStatus.loaded:
|
||||
return SingleChildScrollView(
|
||||
child: Wrap(
|
||||
children: values!.map((v) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
v.url,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
...[
|
||||
const SizedBox(width: 8),
|
||||
if (v.value == true) const Icon(
|
||||
Icons.check_circle_rounded,
|
||||
color: Colors.green,
|
||||
size: 16,
|
||||
),
|
||||
if (v.value == false) const Icon(
|
||||
Icons.cancel_rounded,
|
||||
color: Colors.red,
|
||||
)
|
||||
]
|
||||
],
|
||||
),
|
||||
)).toList(),
|
||||
),
|
||||
);
|
||||
|
||||
case LoadStatus.error:
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
size: 30,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.errorTestUpstreamDns,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(AppLocalizations.of(context)!.close)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -186,7 +186,7 @@ class _UpstreamDnsScreenState extends State<UpstreamDnsScreen> {
|
|||
icon: const Icon(Icons.save_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.save,
|
||||
),
|
||||
const SizedBox(width: 10)
|
||||
const SizedBox(width: 8)
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
|
|
|
@ -6,8 +6,8 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|||
|
||||
import 'package:adguard_home_manager/widgets/section_label.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_switch_list_tile.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/encryption/config_error_modal.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/encryption/status.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/encryption/reset_settings_modal.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/encryption/custom_text_field.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/encryption/master_switch.dart';
|
||||
import 'package:adguard_home_manager/screens/settings/encryption/encryption_functions.dart';
|
||||
|
@ -254,6 +254,45 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
|
|||
}
|
||||
}
|
||||
|
||||
void resetSettings() async {
|
||||
ProcessModal processModal = ProcessModal();
|
||||
processModal.open(AppLocalizations.of(context)!.resettingConfig);
|
||||
|
||||
final result = await serversProvider.apiClient2!.saveEncryptionSettings(
|
||||
data: {
|
||||
"enabled": false,
|
||||
"server_name": "",
|
||||
"force_https": false,
|
||||
"port_https": 443,
|
||||
"port_dns_over_tls": 853,
|
||||
"port_dns_over_quic": 853,
|
||||
"certificate_chain": "",
|
||||
"private_key": "",
|
||||
"private_key_saved": false,
|
||||
"certificate_path": "",
|
||||
"private_key_path": "",
|
||||
}
|
||||
);
|
||||
if (!mounted) return;
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (result.successful == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.configurationResetSuccessfully,
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.configurationResetError,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.encryptionSettings),
|
||||
|
@ -261,17 +300,12 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
|
|||
centerTitle: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: certKeyValidApi == 2 && (validDataError != null || encryptionResultMessage != null)
|
||||
? () => {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => EncryptionErrorModal(
|
||||
error: validDataError ?? encryptionResultMessage ?? AppLocalizations.of(context)!.unknownError
|
||||
)
|
||||
)
|
||||
} : null,
|
||||
icon: generateStatus(context, appConfigProvider, localValidationValid, certKeyValidApi, formEdited),
|
||||
tooltip: generateStatusString(context, localValidationValid, certKeyValidApi)
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => ResetSettingsModal(onConfirm: resetSettings)
|
||||
),
|
||||
icon: const Icon(Icons.restore_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.resetSettings,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: localValidationValid ?
|
||||
|
@ -311,6 +345,26 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
|
|||
case LoadStatus.loaded:
|
||||
return ListView(
|
||||
children: [
|
||||
if (certKeyValidApi == 2 && (validDataError != null || encryptionResultMessage != null)) Card(
|
||||
margin: const EdgeInsets.all(16),
|
||||
color: Colors.red.withOpacity(0.2),
|
||||
elevation: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(validDataError ?? encryptionResultMessage ?? AppLocalizations.of(context)!.unknownError)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
EncryptionMasterSwitch(
|
||||
value: enabled,
|
||||
onChange: (value) {
|
||||
|
|
52
lib/screens/settings/encryption/reset_settings_modal.dart
Normal file
52
lib/screens/settings/encryption/reset_settings_modal.dart
Normal file
|
@ -0,0 +1,52 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class ResetSettingsModal extends StatelessWidget {
|
||||
final void Function() onConfirm;
|
||||
|
||||
const ResetSettingsModal({
|
||||
super.key,
|
||||
required this.onConfirm,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.restore_rounded,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.resetSettings,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!.resetEncryptionSettingsDescription,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(AppLocalizations.of(context)!.cancel)
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
onConfirm();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.confirm)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -853,4 +853,18 @@ class ApiClientV2 {
|
|||
);
|
||||
return ApiResponse(successful: result.successful);
|
||||
}
|
||||
|
||||
Future<ApiResponse> testUpstreamDns({
|
||||
required Map<String, dynamic> body
|
||||
}) async {
|
||||
final result = await HttpRequestClient.post(
|
||||
urlPath: '/test_upstream_dns',
|
||||
server: server,
|
||||
body: body
|
||||
);
|
||||
return ApiResponse(
|
||||
successful: result.successful,
|
||||
content: result.body != null ? jsonDecode(result.body!) : null
|
||||
);
|
||||
}
|
||||
}
|
|
@ -88,10 +88,10 @@ SPEC CHECKSUMS:
|
|||
SentryPrivate: b2f7996f37781080f04a946eb4e377ff63c64195
|
||||
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
|
||||
sqlite3: 6e2d4a4879854d0ec86b476bf3c3e30870bac273
|
||||
sqlite3_flutter_libs: 5b7e226d522d67be60d7ade93f5aa11ebc0cd796
|
||||
sqlite3_flutter_libs: a25f3a0f522fdcd8fef6a4a50a3d681dd43d8dea
|
||||
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
|
||||
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195
|
||||
|
||||
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
|
||||
|
||||
COCOAPODS: 1.12.1
|
||||
COCOAPODS: 1.14.3
|
||||
|
|
42
pubspec.lock
42
pubspec.lock
|
@ -13,10 +13,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: animations
|
||||
sha256: ef57563eed3620bd5d75ad96189846aca1e033c0c45fc9a7d26e80ab02b88a70
|
||||
sha256: "708e4b68c23228c264b038fe7003a2f5d01ce85fc64d8cae090e86b27fcea6c5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
version: "2.0.10"
|
||||
ansicolor:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -271,10 +271,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_native_splash
|
||||
sha256: c4d899312b36e7454bedfd0a4740275837b99e532d81c8477579d8183db1de6c
|
||||
sha256: "141b20f15a2c4fe6e33c49257ca1bc114fc5c500b04fcbc8d75016bb86af672f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.6"
|
||||
version: "2.3.8"
|
||||
flutter_reorderable_list:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -330,10 +330,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -474,10 +474,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6
|
||||
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.1"
|
||||
version: "6.0.2"
|
||||
pie_chart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -575,18 +575,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: sqflite_common_ffi
|
||||
sha256: "35d2fce1e971707c227cc4775cc017d5eafe06c2654c3435ebd5c3ad6c170f5f"
|
||||
sha256: "873677ee78738a723d1ded4ccb23980581998d873d30ee9c331f6a81748663ff"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0+4"
|
||||
version: "2.3.1"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqlite3
|
||||
sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb
|
||||
sha256: "8922805564b78eb7aa9386c10056d377a541ac7270dc6a1589176277ebb4d15d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.2.0"
|
||||
sqlite3_flutter_libs:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -671,10 +671,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
|
||||
sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.1"
|
||||
version: "6.2.2"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -719,10 +719,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
|
||||
sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.2.2"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -783,10 +783,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f"
|
||||
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.0"
|
||||
version: "5.1.1"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -808,10 +808,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556
|
||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.2"
|
||||
version: "6.5.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -822,4 +822,4 @@ packages:
|
|||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.13.0"
|
||||
flutter: ">=3.16.0"
|
||||
|
|
12
pubspec.yaml
12
pubspec.yaml
|
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 2.12.4+115
|
||||
version: 2.13.0+117
|
||||
|
||||
environment:
|
||||
sdk: '>=2.18.1 <3.0.0'
|
||||
|
@ -44,7 +44,7 @@ dependencies:
|
|||
package_info_plus: ^5.0.1
|
||||
flutter_displaymode: ^0.6.0
|
||||
dynamic_color: ^1.6.8
|
||||
animations: ^2.0.8
|
||||
animations: ^2.0.10
|
||||
device_info_plus: ^9.1.1
|
||||
uuid: ^4.2.1
|
||||
expandable: ^5.0.1
|
||||
|
@ -58,7 +58,7 @@ dependencies:
|
|||
html: ^0.15.4
|
||||
flutter_html: ^3.0.0-beta.2
|
||||
sqlite3_flutter_libs: ^0.5.18
|
||||
sqflite_common_ffi: ^2.3.0+4
|
||||
sqflite_common_ffi: ^2.3.1
|
||||
window_size:
|
||||
git:
|
||||
url: https://github.com/google/flutter-desktop-embedding
|
||||
|
@ -67,7 +67,7 @@ dependencies:
|
|||
git:
|
||||
url: https://github.com/JGeek00/flutter_split_view
|
||||
ref: master-alt
|
||||
url_launcher: ^6.1.11
|
||||
url_launcher: ^6.2.2
|
||||
contextmenu: ^3.0.0
|
||||
async: ^2.11.0
|
||||
sentry_flutter: ^7.13.2
|
||||
|
@ -75,7 +75,7 @@ dependencies:
|
|||
flutter_reorderable_list: ^1.3.1
|
||||
pie_chart: ^5.4.0
|
||||
segmented_button_slide: ^1.0.4
|
||||
http: ^1.1.0
|
||||
http: ^1.1.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -88,7 +88,7 @@ dev_dependencies:
|
|||
# rules and activating additional ones.
|
||||
flutter_lints: ^3.0.1
|
||||
flutter_launcher_icons: ^0.13.1
|
||||
flutter_native_splash: ^2.3.6
|
||||
flutter_native_splash: ^2.3.8
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue