Merge branch 'beta'
0
ios/.gitignore
vendored
Normal file → Executable file
0
ios/Flutter/AppFrameworkInfo.plist
Normal file → Executable file
0
ios/Flutter/Debug.xcconfig
Normal file → Executable file
0
ios/Flutter/Release.xcconfig
Normal file → Executable file
0
ios/Podfile
Normal file → Executable file
0
ios/Podfile.lock
Normal file → Executable file
41
ios/Runner.xcodeproj/project.pbxproj
Normal file → Executable file
|
@ -68,7 +68,6 @@
|
|||
65533F0C0783FDE34AE79B0A /* Pods-Runner.release.xcconfig */,
|
||||
69C2CC4A6DE17506FC5C0F13 /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
@ -156,6 +155,7 @@
|
|||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1430;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
|
@ -325,6 +325,7 @@
|
|||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
|
@ -335,6 +336,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -343,7 +345,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
@ -361,15 +363,22 @@
|
|||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 38Z3B9TJTR;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "AdGuard Home";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguardHomeManager;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
|
@ -397,6 +406,7 @@
|
|||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
|
@ -407,6 +417,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -421,7 +432,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
|
@ -452,6 +463,7 @@
|
|||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
|
@ -462,6 +474,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -470,7 +483,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
@ -490,16 +503,23 @@
|
|||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 38Z3B9TJTR;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "AdGuard Home";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguardHomeManager;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -513,15 +533,22 @@
|
|||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 38Z3B9TJTR;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "AdGuard Home";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguardHomeManager;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
|
0
ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file → Executable file
0
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Normal file → Executable file
0
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
Normal file → Executable file
0
ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
Normal file → Executable file
0
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Normal file → Executable file
0
ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Normal file → Executable file
0
ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
Normal file → Executable file
0
ios/Runner/AppDelegate.swift
Normal file → Executable file
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file → Executable file
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
Normal file → Executable file
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
Normal file → Executable file
Before Width: | Height: | Size: 526 B After Width: | Height: | Size: 526 B |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 1,005 B After Width: | Height: | Size: 1,005 B |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
Normal file → Executable file
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
Normal file → Executable file
Before Width: | Height: | Size: 773 B After Width: | Height: | Size: 773 B |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
Normal file → Executable file
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
Normal file → Executable file
Before Width: | Height: | Size: 1,005 B After Width: | Height: | Size: 1,005 B |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
Normal file → Executable file
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
Normal file → Executable file
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
Normal file → Executable file
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
Normal file → Executable file
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
Normal file → Executable file
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
Normal file → Executable file
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
0
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
Normal file → Executable file
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
0
ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
vendored
Normal file → Executable file
0
ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
vendored
Normal file → Executable file
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
0
ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png
vendored
Normal file → Executable file
Before Width: | Height: | Size: 70 B After Width: | Height: | Size: 70 B |
0
ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
vendored
Normal file → Executable file
0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
vendored
Normal file → Executable file
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
vendored
Normal file → Executable file
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
vendored
Normal file → Executable file
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png
vendored
Normal file → Executable file
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png
vendored
Normal file → Executable file
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png
vendored
Normal file → Executable file
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
0
ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
vendored
Normal file → Executable file
0
ios/Runner/Base.lproj/LaunchScreen.storyboard
Normal file → Executable file
0
ios/Runner/Base.lproj/Main.storyboard
Normal file → Executable file
16
ios/Runner/Info.plist
Normal file → Executable file
|
@ -2,10 +2,12 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>AdGuard Home Manager</string>
|
||||
<string>AdGuard Home</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
@ -13,7 +15,7 @@
|
|||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>adguard_home_manager</string>
|
||||
<string>adguardHomeManager</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
|
@ -24,10 +26,14 @@
|
|||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
|
@ -43,11 +49,5 @@
|
|||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
0
ios/Runner/Runner-Bridging-Header.h
Normal file → Executable file
|
@ -713,5 +713,17 @@
|
|||
"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"
|
||||
"invalidValue": "Invalid value",
|
||||
"noDataChart": "There's no data to display this chart.",
|
||||
"noData": "No data",
|
||||
"unblockClient": "Unblock client",
|
||||
"blockingClient": "Blocking client...",
|
||||
"unblockingClient": "Unblocking client...",
|
||||
"upstreamDnsCacheConfiguration": "Configuración de la caché DNS upstream",
|
||||
"enableDnsCachingClient": "Enable DNS caching for this client",
|
||||
"dnsCacheSize": "DNS cache size",
|
||||
"nameInvalid": "Name is required",
|
||||
"oneIdentifierRequired": "At least one identifier is required",
|
||||
"dnsCacheNumber": "DNS cache size must be a number",
|
||||
"errors": "Errors"
|
||||
}
|
|
@ -713,5 +713,17 @@
|
|||
"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"
|
||||
"invalidValue": "Valor no válido",
|
||||
"noDataChart": "No hay datos para mostrar este gráfico.",
|
||||
"noData": "No hay datos",
|
||||
"unblockClient": "Desbloquear cliente",
|
||||
"blockingClient": "Bloqueando cliente...",
|
||||
"unblockingClient": "Desbloqueando cliente...",
|
||||
"upstreamDnsCacheConfiguration": "Configuración de la caché DNS upstream",
|
||||
"enableDnsCachingClient": "Habilitar caché de DNS para este cliente",
|
||||
"dnsCacheSize": "Tamaño de caché de DNS",
|
||||
"nameInvalid": "Se requiere un nombre",
|
||||
"oneIdentifierRequired": "Se require al menos un identificador",
|
||||
"dnsCacheNumber": "El tamaño de caché de DNS debe ser un número",
|
||||
"errors": "Errores"
|
||||
}
|
|
@ -89,6 +89,8 @@ class Client {
|
|||
final SafeSearch? safeSearch;
|
||||
final bool? ignoreQuerylog;
|
||||
final bool? ignoreStatistics;
|
||||
final bool? upstreamsCacheEnabled;
|
||||
final int? upstreamsCacheSize;
|
||||
|
||||
Client({
|
||||
required this.name,
|
||||
|
@ -104,6 +106,8 @@ class Client {
|
|||
required this.safeSearch,
|
||||
required this.ignoreQuerylog,
|
||||
required this.ignoreStatistics,
|
||||
required this.upstreamsCacheEnabled,
|
||||
required this.upstreamsCacheSize,
|
||||
});
|
||||
|
||||
factory Client.fromJson(Map<String, dynamic> json) => Client(
|
||||
|
@ -121,7 +125,9 @@ class Client {
|
|||
? SafeSearch.fromJson(json["safe_search"])
|
||||
: null,
|
||||
ignoreQuerylog: json["ignore_querylog"],
|
||||
ignoreStatistics: json["ignore_statistics"]
|
||||
ignoreStatistics: json["ignore_statistics"],
|
||||
upstreamsCacheEnabled: json["upstreams_cache_enabled"],
|
||||
upstreamsCacheSize: json["upstreams_cache_size"]
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
@ -138,5 +144,7 @@ class Client {
|
|||
"use_global_settings": useGlobalSettings,
|
||||
"ignore_querylog": ignoreQuerylog,
|
||||
"ignore_statistics": ignoreStatistics,
|
||||
"upstreams_cache_enabled": upstreamsCacheEnabled,
|
||||
"upstreams_cache_size": upstreamsCacheSize
|
||||
};
|
||||
}
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||
class MenuOption {
|
||||
final IconData? icon;
|
||||
final String title;
|
||||
final void Function(dynamic) action;
|
||||
final void Function() action;
|
||||
final bool? disabled;
|
||||
|
||||
const MenuOption({
|
||||
|
|
|
@ -190,6 +190,16 @@ class ClientsProvider with ChangeNotifier {
|
|||
"blocked_hosts": clients!.clientsAllowedBlocked?.blockedHosts ?? [],
|
||||
};
|
||||
|
||||
if (body['allowed_clients']!.contains(item)) {
|
||||
body['allowed_clients'] = body['allowed_clients']!.where((e) => e != item).toList();
|
||||
}
|
||||
else if (body['disallowed_clients']!.contains(item)) {
|
||||
body['disallowed_clients'] = body['disallowed_clients']!.where((e) => e != item).toList();
|
||||
}
|
||||
else if (body['blocked_hosts']!.contains(item)) {
|
||||
body['blocked_hosts'] = body['blocked_hosts']!.where((e) => e != item).toList();
|
||||
}
|
||||
|
||||
if (type == AccessSettingsList.allowed) {
|
||||
body['allowed_clients']!.add(item);
|
||||
}
|
||||
|
@ -223,6 +233,18 @@ class ClientsProvider with ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
AccessSettingsList? checkClientList(String client) {
|
||||
if (_clients!.clientsAllowedBlocked!.allowedClients.contains(client)) {
|
||||
return AccessSettingsList.allowed;
|
||||
}
|
||||
else if (_clients!.clientsAllowedBlocked!.disallowedClients.contains(client)) {
|
||||
return AccessSettingsList.disallowed;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse> removeClientList(String client, AccessSettingsList type) async {
|
||||
Map<String, List<String>> body = {
|
||||
"allowed_clients": clients!.clientsAllowedBlocked?.allowedClients ?? [],
|
||||
|
|
|
@ -114,9 +114,15 @@ class LogsProvider with ChangeNotifier {
|
|||
_offset = value;
|
||||
}
|
||||
|
||||
void setSelectedResultStatus(String value) {
|
||||
void setSelectedResultStatus({
|
||||
required String value,
|
||||
bool? refetch
|
||||
}) {
|
||||
_selectedResultStatus = value;
|
||||
notifyListeners();
|
||||
if (refetch = true) {
|
||||
filterLogs();
|
||||
}
|
||||
}
|
||||
|
||||
void setSearchText(String? value) {
|
||||
|
|
|
@ -28,11 +28,11 @@ class ActiveClientTile extends StatelessWidget {
|
|||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: OptionsMenu(
|
||||
options: [
|
||||
options: (_) => [
|
||||
MenuOption(
|
||||
icon: Icons.copy_rounded,
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
action: (_) => copyToClipboard(
|
||||
action: () => copyToClipboard(
|
||||
value: client.name != ''
|
||||
? client.name!
|
||||
: client.ip,
|
||||
|
@ -99,11 +99,11 @@ class ActiveClientTile extends StatelessWidget {
|
|||
}
|
||||
else {
|
||||
return OptionsMenu(
|
||||
options: [
|
||||
options: (_) => [
|
||||
MenuOption(
|
||||
icon: Icons.copy_rounded,
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
action: (_) => copyToClipboard(
|
||||
action: () => copyToClipboard(
|
||||
value: client.name != ''
|
||||
? client.name!
|
||||
: client.ip,
|
||||
|
|
|
@ -46,11 +46,11 @@ class _AddedClientTileState extends State<AddedClientTile> {
|
|||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
child: OptionsMenu(
|
||||
options: [
|
||||
options: (_) => [
|
||||
MenuOption(
|
||||
icon: Icons.copy_rounded,
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
action: (_) => copyToClipboard(
|
||||
action: () => copyToClipboard(
|
||||
value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard,
|
||||
)
|
||||
|
@ -164,16 +164,16 @@ class _AddedClientTileState extends State<AddedClientTile> {
|
|||
}
|
||||
else {
|
||||
return OptionsMenu(
|
||||
options: [
|
||||
options: (_) => [
|
||||
if (widget.onEdit != null) MenuOption(
|
||||
title: AppLocalizations.of(context)!.seeDetails,
|
||||
icon: Icons.file_open_rounded,
|
||||
action: (_) => widget.onEdit!(widget.client)
|
||||
action: () => widget.onEdit!(widget.client)
|
||||
),
|
||||
MenuOption(
|
||||
icon: Icons.copy_rounded,
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
action: (_) => copyToClipboard(
|
||||
action: () => copyToClipboard(
|
||||
value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard,
|
||||
)
|
||||
|
|
|
@ -22,7 +22,7 @@ class BlockedServicesSection extends StatelessWidget {
|
|||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
|
@ -31,8 +31,8 @@ class BlockedServicesSection extends StatelessWidget {
|
|||
borderRadius: BorderRadius.circular(28),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 5
|
||||
horizontal: 16,
|
||||
vertical: 6
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
@ -56,7 +56,7 @@ class BlockedServicesSection extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(height: 12),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
|
@ -69,7 +69,7 @@ class BlockedServicesSection extends StatelessWidget {
|
|||
: null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 8, horizontal: 24
|
||||
vertical: 8, horizontal: 32
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
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';
|
||||
|
||||
|
@ -12,6 +9,7 @@ import 'package:adguard_home_manager/screens/clients/client/settings_tile.dart';
|
|||
import 'package:adguard_home_manager/screens/clients/client/tags_section.dart';
|
||||
import 'package:adguard_home_manager/screens/clients/client/upstream_servers_section.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/custom_switch_list_tile.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
import 'package:adguard_home_manager/widgets/section_label.dart';
|
||||
|
||||
|
@ -22,7 +20,6 @@ class ClientForm extends StatelessWidget {
|
|||
final bool isFullScreen;
|
||||
final Client? client;
|
||||
final TextEditingController nameController;
|
||||
final void Function(bool) updateValidValues;
|
||||
final List<ControllerListItem> identifiersControllers;
|
||||
final List<String> selectedTags;
|
||||
final bool useGlobalSettingsFiltering;
|
||||
|
@ -50,13 +47,17 @@ class ClientForm extends StatelessWidget {
|
|||
final void Function(bool) updateIgnoreClientQueryLog;
|
||||
final bool ignoreClientStatistics;
|
||||
final void Function(bool) updateIgnoreClientStatistics;
|
||||
final bool enableDnsCache;
|
||||
final void Function(bool) updateEnableDnsCache;
|
||||
final TextEditingController dnsCacheField;
|
||||
final String? dnsCacheError;
|
||||
final void Function(String?) updateDnsCacheError;
|
||||
|
||||
const ClientForm({
|
||||
super.key,
|
||||
required this.isFullScreen,
|
||||
required this.client,
|
||||
required this.nameController,
|
||||
required this.updateValidValues,
|
||||
required this.identifiersControllers,
|
||||
required this.selectedTags,
|
||||
required this.useGlobalSettingsFiltering,
|
||||
|
@ -84,26 +85,24 @@ class ClientForm extends StatelessWidget {
|
|||
required this.ignoreClientStatistics,
|
||||
required this.updateIgnoreClientQueryLog,
|
||||
required this.updateIgnoreClientStatistics,
|
||||
required this.enableDnsCache,
|
||||
required this.updateEnableDnsCache,
|
||||
required this.dnsCacheField,
|
||||
required this.dnsCacheError,
|
||||
required this.updateDnsCacheError,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.only(top: 0),
|
||||
return Column(
|
||||
children: [
|
||||
if (isFullScreen == true) const SizedBox(height: 24),
|
||||
if (isFullScreen == false) const SizedBox(height: 6),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: TextFormField(
|
||||
enabled: client != null ? false : true,
|
||||
controller: nameController,
|
||||
onChanged: (_) => updateValidValues(
|
||||
checkValidValues(
|
||||
identifiersControllers: identifiersControllers,
|
||||
nameController: nameController
|
||||
)
|
||||
),
|
||||
onChanged: (_) => {},
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.badge_rounded),
|
||||
border: const OutlineInputBorder(
|
||||
|
@ -117,7 +116,7 @@ class ClientForm extends StatelessWidget {
|
|||
),
|
||||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.tags,
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
),
|
||||
TagsSection(
|
||||
selectedTags: selectedTags,
|
||||
|
@ -127,28 +126,17 @@ class ClientForm extends StatelessWidget {
|
|||
identifiersControllers: identifiersControllers,
|
||||
onUpdateIdentifiersControllers: (c) {
|
||||
updateIdentifiersControllers(c);
|
||||
updateValidValues(
|
||||
checkValidValues(
|
||||
nameController: nameController,
|
||||
identifiersControllers: identifiersControllers
|
||||
)
|
||||
);
|
||||
},
|
||||
onCheckValidValues: () => updateValidValues(
|
||||
checkValidValues(
|
||||
identifiersControllers: identifiersControllers,
|
||||
nameController: nameController
|
||||
)
|
||||
),
|
||||
onCheckValidValues: () => {}
|
||||
),
|
||||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.settings,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 24, right: 24, top: 12, bottom: 24
|
||||
left: 16, right: 16, top: 12, bottom: 24
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
|
@ -157,8 +145,8 @@ class ClientForm extends StatelessWidget {
|
|||
borderRadius: BorderRadius.circular(28),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 5
|
||||
horizontal: 16,
|
||||
vertical: 6
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
@ -182,7 +170,7 @@ class ClientForm extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const SizedBox(height: 8),
|
||||
SettingsTile(
|
||||
label: AppLocalizations.of(context)!.enableFiltering,
|
||||
value: enableFiltering,
|
||||
|
@ -204,7 +192,7 @@ class ClientForm extends StatelessWidget {
|
|||
CustomListTile(
|
||||
title: AppLocalizations.of(context)!.safeSearch,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 42,
|
||||
horizontal: 34,
|
||||
vertical: 16
|
||||
),
|
||||
trailing: Padding(
|
||||
|
@ -228,15 +216,15 @@ class ClientForm extends StatelessWidget {
|
|||
),
|
||||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.queryLogsAndStatistics,
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
),
|
||||
CustomSwitchListTile(
|
||||
title: AppLocalizations.of(context)!.ignoreClientQueryLog,
|
||||
value: ignoreClientQueryLog,
|
||||
onChanged: updateIgnoreClientQueryLog,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 4
|
||||
horizontal: 16,
|
||||
vertical: 6
|
||||
),
|
||||
),
|
||||
CustomSwitchListTile(
|
||||
|
@ -244,13 +232,13 @@ class ClientForm extends StatelessWidget {
|
|||
value: ignoreClientStatistics,
|
||||
onChanged: updateIgnoreClientStatistics,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
vertical: 4
|
||||
horizontal: 16,
|
||||
vertical: 6
|
||||
),
|
||||
),
|
||||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.blockedServices,
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
),
|
||||
BlockedServicesSection(
|
||||
useGlobalSettingsServices: useGlobalSettingsServices,
|
||||
|
@ -260,15 +248,40 @@ class ClientForm extends StatelessWidget {
|
|||
),
|
||||
UpstreamServersSection(
|
||||
upstreamServers: upstreamServers,
|
||||
onCheckValidValues: () => updateValidValues(
|
||||
checkValidValues(
|
||||
identifiersControllers: identifiersControllers,
|
||||
nameController: nameController
|
||||
)
|
||||
),
|
||||
onCheckValidValues: () => {},
|
||||
onUpdateUpstreamServers: updateUpstreamServers
|
||||
),
|
||||
SizedBox(height: Platform.isIOS ? 48 : 24)
|
||||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.upstreamDnsCacheConfiguration,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
),
|
||||
CustomSwitchListTile(
|
||||
title: AppLocalizations.of(context)!.enableDnsCachingClient,
|
||||
value: enableDnsCache,
|
||||
onChanged: updateEnableDnsCache,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 6
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
child: TextFormField(
|
||||
controller: dnsCacheField,
|
||||
onChanged: (v) => updateDnsCacheError(!validateNumber(v) ? AppLocalizations.of(context)!.invalidValue : null),
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.storage_rounded),
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10)
|
||||
)
|
||||
),
|
||||
labelText: AppLocalizations.of(context)!.dnsCacheSize,
|
||||
errorText: dnsCacheError
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ class ClientScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ClientScreenState extends State<ClientScreen> {
|
||||
final _scrollController = ScrollController();
|
||||
|
||||
final Uuid uuid = const Uuid();
|
||||
|
||||
bool validValues = false;
|
||||
|
@ -76,6 +78,15 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
bool _ignoreClientQueryLog = false;
|
||||
bool _ignoreClientStatistics = false;
|
||||
|
||||
bool _enableDnsCache = false;
|
||||
final _dnsCacheField = TextEditingController();
|
||||
String? _dnsCacheError;
|
||||
|
||||
// VALIDATIONS
|
||||
bool _nameValid = true;
|
||||
bool _identifiersValid = true;
|
||||
bool _dnsCacheValid = true;
|
||||
|
||||
void enableDisableGlobalSettingsFiltering() {
|
||||
if (useGlobalSettingsFiltering == true) {
|
||||
setState(() {
|
||||
|
@ -125,6 +136,10 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
)).toList();
|
||||
_ignoreClientQueryLog = widget.client!.ignoreQuerylog ?? false;
|
||||
_ignoreClientStatistics = widget.client!.ignoreStatistics ?? false;
|
||||
_enableDnsCache = widget.client!.upstreamsCacheEnabled ?? false;
|
||||
_dnsCacheField.text = widget.client!.upstreamsCacheSize != null
|
||||
? widget.client!.upstreamsCacheSize.toString()
|
||||
: "";
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
@ -147,20 +162,37 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
upstreams: List<String>.from(upstreamServers.map((e) => e.controller.text)),
|
||||
tags: selectedTags,
|
||||
ignoreQuerylog: _ignoreClientQueryLog,
|
||||
ignoreStatistics: _ignoreClientStatistics
|
||||
ignoreStatistics: _ignoreClientStatistics,
|
||||
upstreamsCacheEnabled: _enableDnsCache,
|
||||
upstreamsCacheSize: _dnsCacheField.text != ""
|
||||
? int.parse(_dnsCacheField.text)
|
||||
: null
|
||||
);
|
||||
widget.onConfirm(client);
|
||||
}
|
||||
|
||||
void validateValues() {
|
||||
_nameValid = nameController.text != '';
|
||||
_identifiersValid = identifiersControllers.isNotEmpty && identifiersControllers[0].controller.text != '';
|
||||
_dnsCacheValid = (_dnsCacheField.text == "" || _dnsCacheField.text != "" && RegExp(r'^\d+$').hasMatch(_dnsCacheField.text));
|
||||
if (_nameValid && _identifiersValid && _dnsCacheValid) {
|
||||
createClient();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
else {
|
||||
_scrollController.animateTo(
|
||||
0,
|
||||
curve: Curves.easeOut,
|
||||
duration: const Duration(milliseconds: 500)
|
||||
);
|
||||
setState(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> actions() {
|
||||
return [
|
||||
IconButton(
|
||||
onPressed: validValues == true
|
||||
? () {
|
||||
createClient();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
: null,
|
||||
onPressed: validateValues,
|
||||
icon: const Icon(Icons.save_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.save,
|
||||
),
|
||||
|
@ -193,11 +225,18 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
actions: actions(),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: ClientForm(
|
||||
child: ListView(
|
||||
controller: _scrollController,
|
||||
children: [
|
||||
if (!_nameValid || !_identifiersValid || !_dnsCacheValid) _Errors(
|
||||
nameValid: _nameValid,
|
||||
identifiersValid: _identifiersValid,
|
||||
dnsCacheValid: _dnsCacheValid
|
||||
),
|
||||
ClientForm(
|
||||
isFullScreen: true,
|
||||
client: widget.client,
|
||||
nameController: nameController,
|
||||
updateValidValues: (v) => setState(() => validValues = v),
|
||||
identifiersControllers: identifiersControllers,
|
||||
selectedTags: selectedTags,
|
||||
useGlobalSettingsFiltering: useGlobalSettingsFiltering,
|
||||
|
@ -225,6 +264,13 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
ignoreClientStatistics: _ignoreClientStatistics,
|
||||
updateIgnoreClientQueryLog: (v) => setState(() => _ignoreClientQueryLog = v),
|
||||
updateIgnoreClientStatistics: (v) => setState(() => _ignoreClientStatistics = v),
|
||||
enableDnsCache: _enableDnsCache,
|
||||
updateEnableDnsCache: (v) => setState(() => _enableDnsCache = v),
|
||||
dnsCacheField: _dnsCacheField,
|
||||
dnsCacheError: _dnsCacheError,
|
||||
updateDnsCacheError: (v) => setState(() => _dnsCacheError = v)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -264,11 +310,18 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
),
|
||||
),
|
||||
Flexible(
|
||||
child: ClientForm(
|
||||
child: ListView(
|
||||
controller: _scrollController,
|
||||
children: [
|
||||
if (!_nameValid || !_identifiersValid || !_dnsCacheValid) _Errors(
|
||||
nameValid: _nameValid,
|
||||
identifiersValid: _identifiersValid,
|
||||
dnsCacheValid: _dnsCacheValid
|
||||
),
|
||||
ClientForm(
|
||||
isFullScreen: false,
|
||||
client: widget.client,
|
||||
nameController: nameController,
|
||||
updateValidValues: (v) => setState(() => validValues = v),
|
||||
identifiersControllers: identifiersControllers,
|
||||
selectedTags: selectedTags,
|
||||
useGlobalSettingsFiltering: useGlobalSettingsFiltering,
|
||||
|
@ -296,6 +349,13 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
ignoreClientStatistics: _ignoreClientStatistics,
|
||||
updateIgnoreClientQueryLog: (v) => setState(() => _ignoreClientQueryLog = v),
|
||||
updateIgnoreClientStatistics: (v) => setState(() => _ignoreClientStatistics = v),
|
||||
enableDnsCache: _enableDnsCache,
|
||||
updateEnableDnsCache: (v) => setState(() => _enableDnsCache = v),
|
||||
dnsCacheField: _dnsCacheField,
|
||||
dnsCacheError: _dnsCacheError,
|
||||
updateDnsCacheError: (v) => setState(() => _dnsCacheError = v)
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
|
@ -306,3 +366,56 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
class _Errors extends StatelessWidget {
|
||||
final bool nameValid;
|
||||
final bool identifiersValid;
|
||||
final bool dnsCacheValid;
|
||||
|
||||
const _Errors({
|
||||
required this.nameValid,
|
||||
required this.identifiersValid,
|
||||
required this.dnsCacheValid,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 0,
|
||||
color: Colors.red.withOpacity(0.2),
|
||||
margin: const EdgeInsets.all(16),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalizations.of(context)!.errors,
|
||||
style: const TextStyle(
|
||||
fontSize: 18
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (!nameValid) Text(
|
||||
"● ${AppLocalizations.of(context)!.nameInvalid}",
|
||||
style: const TextStyle(
|
||||
fontSize: 14
|
||||
),
|
||||
),
|
||||
if (!identifiersValid) Text(
|
||||
"● ${AppLocalizations.of(context)!.oneIdentifierRequired}",
|
||||
style: const TextStyle(
|
||||
fontSize: 14
|
||||
),
|
||||
),
|
||||
if (!dnsCacheValid) Text(
|
||||
"● ${AppLocalizations.of(context)!.dnsCacheNumber}",
|
||||
style: const TextStyle(
|
||||
fontSize: 14
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -74,22 +74,6 @@ void openSafeSearchModal({
|
|||
);
|
||||
}
|
||||
|
||||
bool checkValidValues({
|
||||
required TextEditingController nameController,
|
||||
required List<ControllerListItem> identifiersControllers
|
||||
}) {
|
||||
if (
|
||||
nameController.text != '' &&
|
||||
identifiersControllers.isNotEmpty &&
|
||||
identifiersControllers[0].controller.text != ''
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void openClientFormModal({
|
||||
required BuildContext context,
|
||||
required double width,
|
||||
|
@ -124,3 +108,9 @@ void openClientFormModal({
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool validateNumber(String value) {
|
||||
if (value == "") return true;
|
||||
final regexp = RegExp(r'^\d+$');
|
||||
return regexp.hasMatch(value);
|
||||
}
|
|
@ -11,11 +11,11 @@ class IdentifiersSection extends StatefulWidget {
|
|||
final void Function() onCheckValidValues;
|
||||
|
||||
const IdentifiersSection({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.identifiersControllers,
|
||||
required this.onUpdateIdentifiersControllers,
|
||||
required this.onCheckValidValues
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<IdentifiersSection> createState() => _IdentifiersSectionState();
|
||||
|
@ -34,11 +34,11 @@ class _IdentifiersSectionState extends State<IdentifiersSection> {
|
|||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.identifiers,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 24, right: 24, top: 24, bottom: 12
|
||||
left: 16, right: 16, top: 24, bottom: 12
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 20),
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
child: IconButton(
|
||||
onPressed: () => widget.onUpdateIdentifiersControllers([
|
||||
...widget.identifiersControllers,
|
||||
|
@ -54,7 +54,7 @@ class _IdentifiersSectionState extends State<IdentifiersSection> {
|
|||
),
|
||||
if (widget.identifiersControllers.isNotEmpty) ...widget.identifiersControllers.map((controller) => Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12, bottom: 12, left: 24, right: 20
|
||||
top: 12, bottom: 12, left: 16, right: 10
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
@ -75,9 +75,9 @@ class _IdentifiersSectionState extends State<IdentifiersSection> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
const SizedBox(width: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 25),
|
||||
padding: const EdgeInsets.only(bottom: 24),
|
||||
child: IconButton(
|
||||
onPressed: () => widget.onUpdateIdentifiersControllers(
|
||||
widget.identifiersControllers.where((e) => e.id != controller.id).toList()
|
||||
|
@ -87,7 +87,7 @@ class _IdentifiersSectionState extends State<IdentifiersSection> {
|
|||
)
|
||||
],
|
||||
),
|
||||
)).toList(),
|
||||
)),
|
||||
if (widget.identifiersControllers.isEmpty) Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text(
|
||||
|
|
|
@ -7,12 +7,12 @@ class SettingsTile extends StatelessWidget {
|
|||
final bool useGlobalSettingsFiltering;
|
||||
|
||||
const SettingsTile({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.value,
|
||||
this.onChange,
|
||||
required this.useGlobalSettingsFiltering
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -23,10 +23,7 @@ class SettingsTile extends StatelessWidget {
|
|||
? value != null ? () => onChange!(!value!) : null
|
||||
: null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 42,
|
||||
vertical: 5
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 6),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
|
|
@ -7,10 +7,10 @@ class TagsSection extends StatelessWidget {
|
|||
final void Function(List<String>) onTagsSelected;
|
||||
|
||||
const TagsSection({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.selectedTags,
|
||||
required this.onTagsSelected
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -24,7 +24,7 @@ class TagsSection extends StatelessWidget {
|
|||
) ,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 0, horizontal: 24
|
||||
vertical: 0, horizontal: 16
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
|
@ -11,11 +11,11 @@ class UpstreamServersSection extends StatefulWidget {
|
|||
final void Function(List<ControllerListItem>) onUpdateUpstreamServers;
|
||||
|
||||
const UpstreamServersSection({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.upstreamServers,
|
||||
required this.onCheckValidValues,
|
||||
required this.onUpdateUpstreamServers
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<UpstreamServersSection> createState() => _UpstreamServersSectionState();
|
||||
|
@ -33,10 +33,10 @@ class _UpstreamServersSectionState extends State<UpstreamServersSection> {
|
|||
children: [
|
||||
SectionLabel(
|
||||
label: AppLocalizations.of(context)!.upstreamServers,
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 20),
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: IconButton(
|
||||
onPressed: () => setState(() => widget.upstreamServers.add(
|
||||
ControllerListItem(
|
||||
|
@ -50,7 +50,7 @@ class _UpstreamServersSectionState extends State<UpstreamServersSection> {
|
|||
],
|
||||
),
|
||||
if (widget.upstreamServers.isNotEmpty) ...widget.upstreamServers.map((controller) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
padding: const EdgeInsets.only(left: 16, right: 12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Row(
|
||||
|
@ -71,7 +71,7 @@ class _UpstreamServersSectionState extends State<UpstreamServersSection> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
const SizedBox(width: 12),
|
||||
IconButton(
|
||||
onPressed: () => widget.onUpdateUpstreamServers(
|
||||
widget.upstreamServers.where((e) => e.id != controller.id).toList()
|
||||
|
@ -81,7 +81,7 @@ class _UpstreamServersSectionState extends State<UpstreamServersSection> {
|
|||
],
|
||||
),
|
||||
),
|
||||
)).toList(),
|
||||
)),
|
||||
if (widget.upstreamServers.isEmpty) Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
|
|
|
@ -226,16 +226,16 @@ class _SearchClientsState extends State<SearchClients> {
|
|||
itemCount: clientsScreen.length,
|
||||
padding: const EdgeInsets.only(bottom: 0),
|
||||
itemBuilder: (context, index) => OptionsMenu(
|
||||
options: [
|
||||
options: (v) => [
|
||||
MenuOption(
|
||||
icon: Icons.edit_rounded,
|
||||
title: AppLocalizations.of(context)!.edit,
|
||||
action: (v) => openClientModal(v)
|
||||
action: () => openClientModal(v)
|
||||
),
|
||||
MenuOption(
|
||||
icon: Icons.delete_rounded,
|
||||
title: AppLocalizations.of(context)!.delete,
|
||||
action: (v) => openDeleteModal(v)
|
||||
action: () => openDeleteModal(v)
|
||||
),
|
||||
],
|
||||
value: clientsScreen[index],
|
||||
|
|
|
@ -283,11 +283,11 @@ class FiltersTripleColumn extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
child: OptionsMenu(
|
||||
options: [
|
||||
options: (_) => [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: (_) => copyToClipboard(
|
||||
action: () => copyToClipboard(
|
||||
value: filteringProvider.filtering!.userRules[index],
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard,
|
||||
)
|
||||
|
|
|
@ -146,7 +146,7 @@ class ListOptionsMenu extends StatelessWidget {
|
|||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
child: OptionsMenu(
|
||||
options: [
|
||||
options: (_) => [
|
||||
MenuOption(
|
||||
title: list.enabled == true
|
||||
? AppLocalizations.of(context)!.disable
|
||||
|
@ -154,12 +154,12 @@ class ListOptionsMenu extends StatelessWidget {
|
|||
icon: list.enabled == true
|
||||
? Icons.gpp_bad_rounded
|
||||
: Icons.verified_user_rounded,
|
||||
action: (_) => enableDisable()
|
||||
action: () => enableDisable()
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyListUrl,
|
||||
icon: Icons.copy_rounded,
|
||||
action: (_) => copyToClipboard(
|
||||
action: () => copyToClipboard(
|
||||
value: list.url,
|
||||
successMessage: AppLocalizations.of(context)!.listUrlCopied
|
||||
)
|
||||
|
@ -167,12 +167,12 @@ class ListOptionsMenu extends StatelessWidget {
|
|||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.openListUrl,
|
||||
icon: Icons.open_in_browser_rounded,
|
||||
action: (_) => openUrl(list.url)
|
||||
action: () => openUrl(list.url)
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.selectionMode,
|
||||
icon: Icons.check_rounded,
|
||||
action: (_) => Future.delayed(
|
||||
action: () => Future.delayed(
|
||||
const Duration(milliseconds: 0),
|
||||
() => openSelectionMode()
|
||||
)
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/line_chart.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class HomeChart extends StatelessWidget {
|
||||
class HomeChart extends StatefulWidget {
|
||||
final List<int> data;
|
||||
final String label;
|
||||
final String primaryValue;
|
||||
final String secondaryValue;
|
||||
final Color color;
|
||||
final int hoursInterval;
|
||||
final void Function() onTapTitle;
|
||||
final bool isDesktop;
|
||||
|
||||
const HomeChart({
|
||||
super.key,
|
||||
|
@ -20,20 +23,29 @@ class HomeChart extends StatelessWidget {
|
|||
required this.primaryValue,
|
||||
required this.secondaryValue,
|
||||
required this.color,
|
||||
required this.hoursInterval
|
||||
required this.hoursInterval,
|
||||
required this.onTapTitle,
|
||||
required this.isDesktop,
|
||||
});
|
||||
|
||||
@override
|
||||
State<HomeChart> createState() => _HomeChartState();
|
||||
}
|
||||
|
||||
class _HomeChartState extends State<HomeChart> {
|
||||
bool _isHover = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
|
||||
final bool isEmpty = data.every((i) => i == 0);
|
||||
final bool isEmpty = widget.data.every((i) => i == 0);
|
||||
|
||||
if (!(appConfigProvider.hideZeroValues == true && isEmpty == true)) {
|
||||
List<DateTime> dateTimes = [];
|
||||
DateTime currentDate = DateTime.now().subtract(Duration(hours: hoursInterval*data.length+1));
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
currentDate = currentDate.add(Duration(hours: hoursInterval));
|
||||
DateTime currentDate = DateTime.now().subtract(Duration(hours: widget.hoursInterval*widget.data.length+1));
|
||||
for (var i = 0; i < widget.data.length; i++) {
|
||||
currentDate = currentDate.add(Duration(hours: widget.hoursInterval));
|
||||
dateTimes.add(currentDate);
|
||||
}
|
||||
|
||||
|
@ -49,56 +61,83 @@ class HomeChart extends StatelessWidget {
|
|||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: isEmpty
|
||||
? CrossAxisAlignment.center
|
||||
: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (_) => setState(() => _isHover = true),
|
||||
onExit: (_) => setState(() => _isHover = false),
|
||||
child: GestureDetector(
|
||||
onTapDown: (_) => setState(() => _isHover = true),
|
||||
onTapUp: (_) => setState(() => _isHover = false),
|
||||
onTap: !isEmpty ? () => widget.onTapTitle () : null,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
widget.label,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
color: _isHover && !isEmpty
|
||||
? Theme.of(context).colorScheme.onSurfaceVariant
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
!isEmpty
|
||||
? Column(
|
||||
if (!isEmpty) ...[
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.chevron_right_rounded,
|
||||
size: 20,
|
||||
color: _isHover && !isEmpty
|
||||
? Theme.of(context).colorScheme.onSurfaceVariant
|
||||
: Theme.of(context).colorScheme.onSurface,
|
||||
)
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isEmpty) Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
primaryValue,
|
||||
widget.primaryValue,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
color: widget.color,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500
|
||||
),
|
||||
),
|
||||
Text(
|
||||
secondaryValue,
|
||||
widget.secondaryValue,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: color
|
||||
color: widget.color
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
),
|
||||
if (isEmpty && !widget.isDesktop) Column(
|
||||
children: [
|
||||
Text(
|
||||
primaryValue,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500
|
||||
Icon(
|
||||
Icons.show_chart_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
"($secondaryValue)",
|
||||
AppLocalizations.of(context)!.noData,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: color
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
],
|
||||
|
@ -110,13 +149,35 @@ class HomeChart extends StatelessWidget {
|
|||
width: double.maxFinite,
|
||||
height: 150,
|
||||
child: CustomLineChart(
|
||||
data: data,
|
||||
color: color,
|
||||
data: widget.data,
|
||||
color: widget.color,
|
||||
dates: dateTimes,
|
||||
daysInterval: hoursInterval == 24,
|
||||
daysInterval: widget.hoursInterval == 24,
|
||||
context: context,
|
||||
)
|
||||
),
|
||||
if (isEmpty && widget.isDesktop) SizedBox(
|
||||
height: 150,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.show_chart_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
size: 30,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.noDataChart,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -14,6 +14,8 @@ import 'package:adguard_home_manager/screens/home/appbar.dart';
|
|||
import 'package:adguard_home_manager/screens/home/fab.dart';
|
||||
import 'package:adguard_home_manager/screens/home/chart.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/clients_provider.dart';
|
||||
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
||||
import 'package:adguard_home_manager/functions/number_format.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
|
@ -38,6 +40,9 @@ class _HomeState extends State<Home> {
|
|||
withLoadingIndicator: statusProvider.serverStatus != null ? false : true
|
||||
);
|
||||
|
||||
final clientsProvider = Provider.of<ClientsProvider>(context, listen: false);
|
||||
clientsProvider.fetchClients(updateLoading: false);
|
||||
|
||||
super.initState();
|
||||
|
||||
isVisible = true;
|
||||
|
@ -61,6 +66,7 @@ class _HomeState extends State<Home> {
|
|||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
final logsProvider = Provider.of<LogsProvider>(context);
|
||||
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
|
||||
|
@ -143,6 +149,15 @@ class _HomeState extends State<Home> {
|
|||
secondaryValue: "${doubleFormat(statusProvider.serverStatus!.stats.avgProcessingTime*1000, Platform.localeName)} ms",
|
||||
color: Colors.blue,
|
||||
hoursInterval: statusProvider.serverStatus!.stats.timeUnits == "days" ? 24 : 1,
|
||||
onTapTitle: () {
|
||||
logsProvider.setSelectedResultStatus(
|
||||
value: "all",
|
||||
refetch: true
|
||||
);
|
||||
logsProvider.filterLogs();
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
},
|
||||
isDesktop: width > 700,
|
||||
),
|
||||
),
|
||||
FractionallySizedBox(
|
||||
|
@ -154,6 +169,14 @@ class _HomeState extends State<Home> {
|
|||
secondaryValue: "${statusProvider.serverStatus!.stats.numDnsQueries > 0 ? doubleFormat((statusProvider.serverStatus!.stats.numBlockedFiltering/statusProvider.serverStatus!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
|
||||
color: Colors.red,
|
||||
hoursInterval: statusProvider.serverStatus!.stats.timeUnits == "days" ? 24 : 1,
|
||||
onTapTitle: () {
|
||||
logsProvider.setSelectedResultStatus(
|
||||
value: "blocked",
|
||||
refetch: true
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
},
|
||||
isDesktop: width > 700,
|
||||
),
|
||||
),
|
||||
FractionallySizedBox(
|
||||
|
@ -165,6 +188,14 @@ class _HomeState extends State<Home> {
|
|||
secondaryValue: "${statusProvider.serverStatus!.stats.numDnsQueries > 0 ? doubleFormat((statusProvider.serverStatus!.stats.numReplacedSafebrowsing/statusProvider.serverStatus!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
|
||||
color: Colors.green,
|
||||
hoursInterval: statusProvider.serverStatus!.stats.timeUnits == "days" ? 24 : 1,
|
||||
onTapTitle: () {
|
||||
logsProvider.setSelectedResultStatus(
|
||||
value: "blocked_safebrowsing",
|
||||
refetch: true
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
},
|
||||
isDesktop: width > 700,
|
||||
),
|
||||
),
|
||||
FractionallySizedBox(
|
||||
|
@ -176,6 +207,15 @@ class _HomeState extends State<Home> {
|
|||
secondaryValue: "${statusProvider.serverStatus!.stats.numDnsQueries > 0 ? doubleFormat((statusProvider.serverStatus!.stats.numReplacedParental/statusProvider.serverStatus!.stats.numDnsQueries)*100, Platform.localeName) : 0}%",
|
||||
color: Colors.orange,
|
||||
hoursInterval: statusProvider.serverStatus!.stats.timeUnits == "days" ? 24 : 1,
|
||||
onTapTitle: () {
|
||||
logsProvider.setSelectedResultStatus(
|
||||
value: "blocked_parental",
|
||||
refetch: true
|
||||
);
|
||||
logsProvider.filterLogs();
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
},
|
||||
isDesktop: width > 700,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -16,7 +16,7 @@ class RowItem extends StatefulWidget {
|
|||
final bool clients;
|
||||
final bool showColor;
|
||||
final String? unit;
|
||||
final List<MenuOption> options;
|
||||
final List<MenuOption> Function(dynamic) options;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
|
||||
const RowItem({
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|||
|
||||
import 'package:adguard_home_manager/screens/home/top_items/top_items_section.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/clients_provider.dart';
|
||||
import 'package:adguard_home_manager/functions/number_format.dart';
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/classes/process_modal.dart';
|
||||
|
@ -30,6 +31,7 @@ class TopItemsLists extends StatelessWidget {
|
|||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
final logsProvider = Provider.of<LogsProvider>(context);
|
||||
final clientsProvider = Provider.of<ClientsProvider>(context);
|
||||
|
||||
List<Widget> bottom = [
|
||||
Padding(
|
||||
|
@ -96,10 +98,53 @@ class TopItemsLists extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
void copyValueClipboard(value) {
|
||||
void copyValueClipboard(dynamic value) {
|
||||
copyToClipboard(value: value, successMessage: AppLocalizations.of(context)!.copiedClipboard);
|
||||
}
|
||||
|
||||
void blockUnblockClient(dynamic client) async {
|
||||
final currentList = clientsProvider.checkClientList(client);
|
||||
final newList = currentList == AccessSettingsList.allowed || currentList == null
|
||||
? AccessSettingsList.disallowed
|
||||
: AccessSettingsList.allowed;
|
||||
|
||||
ProcessModal processModal = ProcessModal();
|
||||
processModal.open(
|
||||
currentList == AccessSettingsList.allowed || currentList == null
|
||||
? AppLocalizations.of(context)!.blockingClient
|
||||
: AppLocalizations.of(context)!.unblockingClient
|
||||
);
|
||||
|
||||
final result = await clientsProvider.addClientList(client, newList);
|
||||
if (!context.mounted) return;
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (result.successful == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.clientAddedSuccessfully,
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else if (result.successful == false && result.content == 'client_another_list') {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.clientAnotherList,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: newList == AccessSettingsList.allowed || newList == AccessSettingsList.disallowed
|
||||
? AppLocalizations.of(context)!.clientNotRemoved
|
||||
: AppLocalizations.of(context)!.domainNotAdded,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: order.asMap().entries.map((item) {
|
||||
switch (item.value) {
|
||||
|
@ -113,16 +158,16 @@ class TopItemsLists extends StatelessWidget {
|
|||
withChart: true,
|
||||
withProgressBar: true,
|
||||
buildValue: (v) => v.toString(),
|
||||
menuOptions: [
|
||||
menuOptions: (v) => [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.blockDomain,
|
||||
icon: Icons.block_rounded,
|
||||
action: (v) => blockUnblock(domain: v.toString(), newStatus: 'block')
|
||||
action: () => blockUnblock(domain: v.toString(), newStatus: 'block')
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
action: () => copyValueClipboard(v)
|
||||
),
|
||||
],
|
||||
onTapEntry: (v) => filterDomainLogs(value: v.toString()),
|
||||
|
@ -141,16 +186,16 @@ class TopItemsLists extends StatelessWidget {
|
|||
withChart: true,
|
||||
withProgressBar: true,
|
||||
buildValue: (v) => v.toString(),
|
||||
menuOptions: [
|
||||
menuOptions: (v) => [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.unblockDomain,
|
||||
icon: Icons.check_rounded,
|
||||
action: (v) => blockUnblock(domain: v, newStatus: 'unblock')
|
||||
action: () => blockUnblock(domain: v, newStatus: 'unblock')
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
action: () => copyValueClipboard(v)
|
||||
)
|
||||
],
|
||||
onTapEntry: (v) => filterDomainLogs(value: v),
|
||||
|
@ -169,12 +214,21 @@ class TopItemsLists extends StatelessWidget {
|
|||
withChart: true,
|
||||
withProgressBar: true,
|
||||
buildValue: (v) => v.toString(),
|
||||
menuOptions: [
|
||||
menuOptions: (v) => [
|
||||
if (clientsProvider.clients?.clientsAllowedBlocked != null) MenuOption(
|
||||
title: clientsProvider.checkClientList(v) == AccessSettingsList.allowed || clientsProvider.checkClientList(v) == null
|
||||
? AppLocalizations.of(context)!.blockClient
|
||||
: AppLocalizations.of(context)!.unblockClient,
|
||||
icon: clientsProvider.checkClientList(v) == AccessSettingsList.allowed || clientsProvider.checkClientList(v) == null
|
||||
? Icons.block_rounded
|
||||
: Icons.check_rounded,
|
||||
action: () => blockUnblockClient(v)
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
)
|
||||
action: () => copyValueClipboard(v)
|
||||
),
|
||||
],
|
||||
onTapEntry: (v) => filterClientLogs(value: v),
|
||||
),
|
||||
|
@ -193,11 +247,11 @@ class TopItemsLists extends StatelessWidget {
|
|||
withChart: true,
|
||||
withProgressBar: true,
|
||||
buildValue: (v) => v.toString(),
|
||||
menuOptions: [
|
||||
menuOptions: (v) => [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
action: () => copyValueClipboard(v)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -217,11 +271,11 @@ class TopItemsLists extends StatelessWidget {
|
|||
withChart: false,
|
||||
withProgressBar: false,
|
||||
buildValue: (v) => "${doubleFormat(v*1000, Platform.localeName)} ms",
|
||||
menuOptions: [
|
||||
menuOptions: (v) => [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
action: () => copyValueClipboard(v)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
@ -24,7 +24,7 @@ class TopItemsScreen extends StatefulWidget {
|
|||
final List<Map<String, dynamic>> data;
|
||||
final bool withProgressBar;
|
||||
final String Function(dynamic) buildValue;
|
||||
final List<MenuOption> options;
|
||||
final List<MenuOption> Function(dynamic) options;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
final bool isFullscreen;
|
||||
|
||||
|
@ -263,7 +263,7 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
|||
class _Content extends StatelessWidget {
|
||||
final List<Map<String, dynamic>> screenData;
|
||||
final bool? isClient;
|
||||
final List<MenuOption> options;
|
||||
final List<MenuOption> Function(dynamic) options;
|
||||
final bool withProgressBar;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
final String Function(dynamic) buildValue;
|
||||
|
|
|
@ -21,7 +21,7 @@ class TopItemsSection extends StatefulWidget {
|
|||
final bool withChart;
|
||||
final bool withProgressBar;
|
||||
final String Function(dynamic) buildValue;
|
||||
final List<MenuOption> menuOptions;
|
||||
final List<MenuOption> Function(dynamic) menuOptions;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
|
||||
const TopItemsSection({
|
||||
|
@ -350,7 +350,7 @@ class _ItemsList extends StatelessWidget {
|
|||
final HomeTopItems type;
|
||||
final bool showChart;
|
||||
final String Function(dynamic) buildValue;
|
||||
final List<MenuOption> menuOptions;
|
||||
final List<MenuOption> Function(dynamic) menuOptions;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
|
||||
const _ItemsList({
|
||||
|
|
|
@ -34,7 +34,7 @@ class _FilterStatusModalState extends State<FilterStatusModal> {
|
|||
final logsProvider = Provider.of<LogsProvider>(context);
|
||||
|
||||
void apply() async {
|
||||
logsProvider.setSelectedResultStatus(selectedResultStatus);
|
||||
logsProvider.setSelectedResultStatus(value: selectedResultStatus);
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
|
|
@ -262,13 +262,13 @@ class _FiltersList extends StatelessWidget {
|
|||
FilterChip(
|
||||
selected: logsProvider.selectedResultStatus == "all",
|
||||
label: Text(AppLocalizations.of(context)!.all),
|
||||
onSelected: (_) => logsProvider.setSelectedResultStatus("all")
|
||||
onSelected: (_) => logsProvider.setSelectedResultStatus(value: "all")
|
||||
),
|
||||
FilterChip(
|
||||
selected: logsProvider.selectedResultStatus == "processed" ||
|
||||
logsProvider.selectedResultStatus == "whitelisted",
|
||||
label: Text(AppLocalizations.of(context)!.allowed),
|
||||
onSelected: (_) => logsProvider.setSelectedResultStatus("processed")
|
||||
onSelected: (_) => logsProvider.setSelectedResultStatus(value: "processed")
|
||||
),
|
||||
FilterChip(
|
||||
selected: logsProvider.selectedResultStatus == "blocked" ||
|
||||
|
@ -276,7 +276,7 @@ class _FiltersList extends StatelessWidget {
|
|||
logsProvider.selectedResultStatus == "blocked_parental" ||
|
||||
logsProvider.selectedResultStatus == "safe_search",
|
||||
label: Text(AppLocalizations.of(context)!.blocked),
|
||||
onSelected: (_) => logsProvider.setSelectedResultStatus("blocked")
|
||||
onSelected: (_) => logsProvider.setSelectedResultStatus(value: "blocked")
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -128,7 +128,7 @@ class LogTile extends StatelessWidget {
|
|||
child: OptionsMenu(
|
||||
onTap: (_) => onLogTap(log),
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
options: [
|
||||
options: (_) => [
|
||||
if (log.question.name != null) MenuOption(
|
||||
title: domainBlocked == true
|
||||
? AppLocalizations.of(context)!.unblockDomain
|
||||
|
@ -136,15 +136,15 @@ class LogTile extends StatelessWidget {
|
|||
icon: domainBlocked == true
|
||||
? Icons.check_rounded
|
||||
: Icons.block_rounded,
|
||||
action: (_) => blockUnblock(
|
||||
action: () => blockUnblock(
|
||||
domain: log.question.name!,
|
||||
newStatus: domainBlocked == true ? 'unblock' : 'block'
|
||||
)
|
||||
),
|
||||
MenuOption(
|
||||
if (log.question.name != null) MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: (v) => copyToClipboard(value: v, successMessage: AppLocalizations.of(context)!.copiedClipboard)
|
||||
action: () => copyToClipboard(value: log.question.name!, successMessage: AppLocalizations.of(context)!.copiedClipboard)
|
||||
)
|
||||
],
|
||||
child: Container(
|
||||
|
@ -306,7 +306,7 @@ class LogTile extends StatelessWidget {
|
|||
color: Colors.transparent,
|
||||
child: OptionsMenu(
|
||||
onTap: (_) => onLogTap(log),
|
||||
options: [
|
||||
options: (_) => [
|
||||
if (log.question.name != null) MenuOption(
|
||||
title: domainBlocked == true
|
||||
? AppLocalizations.of(context)!.unblockDomain
|
||||
|
@ -314,7 +314,7 @@ class LogTile extends StatelessWidget {
|
|||
icon: domainBlocked == true
|
||||
? Icons.check_rounded
|
||||
: Icons.block_rounded,
|
||||
action: (_) => blockUnblock(
|
||||
action: () => blockUnblock(
|
||||
domain: log.question.name!,
|
||||
newStatus: domainBlocked == true ? 'unblock' : 'block'
|
||||
)
|
||||
|
@ -322,7 +322,7 @@ class LogTile extends StatelessWidget {
|
|||
if (log.question.name != null) MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: (_) => copyToClipboard(
|
||||
action: () => copyToClipboard(
|
||||
value: log.question.name!,
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard
|
||||
)
|
||||
|
|
|
@ -22,11 +22,11 @@ class LogsListWidget extends StatefulWidget {
|
|||
final void Function(Log) onLogSelected;
|
||||
|
||||
const LogsListWidget({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.twoColumns,
|
||||
required this.selectedLog,
|
||||
required this.onLogSelected,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<LogsListWidget> createState() => _LogsListWidgetState();
|
||||
|
|
|
@ -256,7 +256,7 @@ class LogsListAppBar extends StatelessWidget {
|
|||
clients: logsProvider.appliedFilters.clients
|
||||
)
|
||||
);
|
||||
logsProvider.setSelectedResultStatus('all');
|
||||
logsProvider.setSelectedResultStatus(value: 'all');
|
||||
logsProvider.fetchLogs(
|
||||
inOffset: 0,
|
||||
responseStatus: 'all'
|
||||
|
|
|
@ -156,7 +156,7 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
"blocking_mode": blockingMode,
|
||||
"blocking_ipv4": ipv4controller.text,
|
||||
"blocking_ipv6": ipv6controller.text,
|
||||
"blocked_response_ttl": int.parse(_ttlController.text)
|
||||
"blocked_response_ttl": int.tryParse(_ttlController.text)
|
||||
});
|
||||
|
||||
processModal.close();
|
||||
|
|
|
@ -39,10 +39,17 @@ class ApiClientV2 {
|
|||
Future<ApiResponse> getServerVersion() async {
|
||||
final result = await HttpRequestClient.get(urlPath: '/status', server: server);
|
||||
if (result.successful == true) {
|
||||
try {
|
||||
return ApiResponse(
|
||||
successful: true,
|
||||
content: jsonDecode(result.body!)['version']
|
||||
);
|
||||
} on FormatException {
|
||||
return const ApiResponse(successful: false);
|
||||
} catch (e, stackTrace) {
|
||||
Sentry.captureException(e, stackTrace: stackTrace);
|
||||
return const ApiResponse(successful: false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return const ApiResponse(successful: false);
|
||||
|
|
|
@ -11,7 +11,7 @@ import 'package:adguard_home_manager/models/menu_option.dart';
|
|||
|
||||
class OptionsMenu extends StatelessWidget {
|
||||
final Widget child;
|
||||
final List<MenuOption> options;
|
||||
final List<MenuOption> Function(dynamic) options;
|
||||
final dynamic value;
|
||||
final BorderRadius? borderRadius;
|
||||
final void Function(dynamic)? onTap;
|
||||
|
@ -40,11 +40,11 @@ class OptionsMenu extends StatelessWidget {
|
|||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: ContextMenuArea(
|
||||
builder: (context) => options.map((opt) => CustomListTile(
|
||||
builder: (context) => options(value).map((opt) => CustomListTile(
|
||||
title: opt.title,
|
||||
icon: opt.icon,
|
||||
onTap: () {
|
||||
opt.action(value);
|
||||
opt.action();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)).toList(),
|
||||
|
@ -64,7 +64,7 @@ class OptionsMenu extends StatelessWidget {
|
|||
}
|
||||
|
||||
class _OptionsModal extends StatelessWidget {
|
||||
final List<MenuOption> options;
|
||||
final List<MenuOption> Function(dynamic) options;
|
||||
final dynamic value;
|
||||
|
||||
const _OptionsModal({
|
||||
|
@ -98,12 +98,12 @@ class _OptionsModal extends StatelessWidget {
|
|||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Wrap(
|
||||
children: options.map((opt) => CustomListTileDialog(
|
||||
children: options(value).map((opt) => CustomListTileDialog(
|
||||
title: opt.title,
|
||||
icon: opt.icon,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
opt.action(value);
|
||||
opt.action();
|
||||
},
|
||||
)).toList()
|
||||
),
|
||||
|
|
|
@ -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.13.0+117
|
||||
version: 2.14.0+119
|
||||
|
||||
environment:
|
||||
sdk: '>=2.18.1 <3.0.0'
|
||||
|
|