mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-05-03 11:54:47 +00:00
Merge branch 'beta'
This commit is contained in:
commit
6ead07e464
37 changed files with 1140 additions and 979 deletions
|
@ -5,7 +5,9 @@ import 'package:adguard_home_manager/constants/enums.dart';
|
|||
final List<HomeTopItems> homeTopItemsDefaultOrder = [
|
||||
HomeTopItems.queriedDomains,
|
||||
HomeTopItems.blockedDomains,
|
||||
HomeTopItems.recurrentClients
|
||||
HomeTopItems.recurrentClients,
|
||||
HomeTopItems.topUpstreams,
|
||||
HomeTopItems.avgUpstreamResponseTime
|
||||
];
|
||||
|
||||
final String homeTopItemsDefaultOrderString = jsonEncode(
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
enum LoadStatus { loading, loaded, error }
|
||||
enum HomeTopItems { queriedDomains, blockedDomains, recurrentClients }
|
||||
enum HomeTopItems { queriedDomains, blockedDomains, recurrentClients, topUpstreams, avgUpstreamResponseTime }
|
|
@ -686,5 +686,7 @@
|
|||
"unsupportedServerVersion": "Unsupported server version",
|
||||
"unsupportedServerVersionMessage": "Your AdGuard Home server version is too old and is not supported by AdGuard Home Manager. You will need to upgrade your AdGuard Home server to a newer version to use this application.",
|
||||
"yourVersion": "Your version: {version}",
|
||||
"minimumRequiredVersion": "Minimum required version: {version}"
|
||||
"minimumRequiredVersion": "Minimum required version: {version}",
|
||||
"topUpstreams": "Top upstreams",
|
||||
"averageUpstreamResponseTime": "Average upstream response time"
|
||||
}
|
|
@ -686,5 +686,7 @@
|
|||
"unsupportedServerVersion": "Versión del servidor no soportada",
|
||||
"unsupportedServerVersionMessage": "La versión de tu servidor AdGuard Home es demasiado antigua y no está soportada por AdGuard Home Manager. Necesitarás actualizar tu servidor AdGuard Home a una versión más actual para utilizar esta aplicación.",
|
||||
"yourVersion": "Tu versión: {version}",
|
||||
"minimumRequiredVersion": "Versión mínima requerida: {version}"
|
||||
"minimumRequiredVersion": "Versión mínima requerida: {version}",
|
||||
"topUpstreams": "DNS de subida más frecuentes",
|
||||
"averageUpstreamResponseTime": "Tiempo promedio de respuesta upstream"
|
||||
}
|
|
@ -176,7 +176,7 @@
|
|||
"generalSettings": "Genel ayarlar",
|
||||
"generalSettingsDescription": "Çeşitli farklı ayarları yönet",
|
||||
"hideZeroValues": "Sıfır değerlerini gizle",
|
||||
"hideZeroValuesDescription": "Ana ekranda, değeri sıfır olan blokları gizler",
|
||||
"hideZeroValuesDescription": "Ana ekranda, değeri sıfır olan blokları gizler.",
|
||||
"webAdminPanel": "Web yönetim paneli",
|
||||
"visitGooglePlay": "Google Play sayfasını ziyaret et",
|
||||
"gitHub": "Kaynak kodlarına GitHub'dan ulaşabilirsiniz",
|
||||
|
@ -192,17 +192,17 @@
|
|||
"enableFiltering": "Filtrelemeyi etkinleştir",
|
||||
"enableSafeBrowsing": "Güvenli gezintiyi etkinleştir",
|
||||
"enableParentalControl": "Ebeveyn kontrolünü etkinleştir",
|
||||
"enableSafeSearch": "Güvenli aramayı etkinleştir",
|
||||
"enableSafeSearch": "Güvenli aramayı aktif et",
|
||||
"blockedServices": "Engellenen hizmetler",
|
||||
"selectBlockedServices": "Engellenen hizmetleri seç",
|
||||
"noBlockedServicesSelected": "Engellenen hizmetler seçilmedi",
|
||||
"services": "Hizmetler",
|
||||
"servicesBlocked": "Hizmetler engellendi",
|
||||
"tagsSelected": "Seçilen etiketler",
|
||||
"upstreamServers": "Üst akış sunucuları",
|
||||
"upstreamServers": "Üst kaynak sunucuları",
|
||||
"serverAddress": "Sunucu adresi",
|
||||
"noUpstreamServers": "Üst akış sunucusu yok.",
|
||||
"willBeUsedGeneralServers": "Genel üst akış sunucuları kullanılacak.",
|
||||
"noUpstreamServers": "Üst kaynak sunucusu yok.",
|
||||
"willBeUsedGeneralServers": "Genel üst kaynak sunucuları kullanılacak.",
|
||||
"added": "Eklenenler",
|
||||
"clientUpdatedSuccessfully": "İstemci başarıyla güncellendi",
|
||||
"clientNotUpdated": "İstemci güncellenemedi",
|
||||
|
@ -283,7 +283,7 @@
|
|||
"clientsNotLoaded": "İstemciler yüklenemedi.",
|
||||
"noAllowedClients": "İzin verilmiş istemci yok",
|
||||
"allowedClientsDescription": "Eğer bu liste girdiler içeriyorsa, AdGuard Home yalnızca bu istemcilerden gelen talepleri kabul edecektir.",
|
||||
"blockedClientsDescription": "Bu liste girdiler içeriyorsa, AdGuard Home bu istemcilerden gelen talepleri reddedecektir. Bu alan, İzin Verilen İstemciler'de girdi varsa görmezden gelinir.",
|
||||
"blockedClientsDescription": "Bu liste girdileri içeriyorsa, AdGuard Home bu istemcilerden gelen talepleri reddedecektir. Bu alan adı, İzin Verilen İstemciler'de girdi varsa görmezden gelinir.",
|
||||
"disallowedDomainsDescription": "AdGuard Home, bu alan adlarına uyan DNS sorgularını reddeder ve bu sorgular sorgu günlüğünde bile görünmez.",
|
||||
"addClientFieldDescription": "CIDR'ler, IP adresi veya ClientID",
|
||||
"clientIdentifier": "İstemci tanımlayıcısı",
|
||||
|
@ -293,15 +293,15 @@
|
|||
"domainNotAdded": "Alan adı eklenemedi",
|
||||
"statusSelected": "Durum seçildi.",
|
||||
"updateLists": "Listeleri güncelle",
|
||||
"checkHostFiltered": "Ana bilgisayarı kontrol et",
|
||||
"checkHostFiltered": "Filtrelemeyi kontrol et",
|
||||
"updatingLists": "Listeler güncelleniyor...",
|
||||
"listsUpdated": "Listeler güncellendi",
|
||||
"listsNotUpdated": "Listeler güncellenemedi",
|
||||
"listsNotLoaded": "Listeler yüklenemedi",
|
||||
"domainNotValid": "Alan adı geçersiz",
|
||||
"check": "Kontrol et",
|
||||
"checkingHost": "Ana bilgisayar kontrol ediliyor",
|
||||
"errorCheckingHost": "Ana bilgisayar kontrol edilemedi",
|
||||
"checkingHost": "Kontrol ediliyor",
|
||||
"errorCheckingHost": "Kontrol etme başarısız",
|
||||
"block": "Engelle",
|
||||
"unblock": "Engeli kaldır",
|
||||
"custom": "Özel",
|
||||
|
@ -336,7 +336,7 @@
|
|||
"updating": "Değerler güncelleniyor...",
|
||||
"blockedServicesUpdated": "Engellenen hizmetler başarıyla güncellendi",
|
||||
"blockedServicesNotUpdated": "Engellenen hizmetler güncellenemedi",
|
||||
"insertDomain": "Durumu kontrol etmek için bir alan adı ekleyin.",
|
||||
"insertDomain": "Filtreleme durumunu kontrol etmek için bir alan adı ekleyin.",
|
||||
"dhcpSettings": "DHCP ayarları",
|
||||
"dhcpSettingsDescription": "DHCP sunucusunu yapılandır",
|
||||
"dhcpSettingsNotLoaded": "DHCP ayarları yüklenemedi",
|
||||
|
@ -394,18 +394,18 @@
|
|||
"noLeases": "Kullanılabilir DHCP kiralaması yok",
|
||||
"dnsRewrites": "DNS yeniden yazımları",
|
||||
"dnsRewritesDescription": "Özel DNS kurallarını yapılandır",
|
||||
"loadingRewriteRules": "Yeniden yazma kuralları yükleniyor...",
|
||||
"rewriteRulesNotLoaded": "DNS yeniden yazma kuralları yüklenemedi.",
|
||||
"loadingRewriteRules": "Yeniden yazım kuralları yükleniyor...",
|
||||
"rewriteRulesNotLoaded": "DNS yeniden yazım kuralları yüklenemedi.",
|
||||
"noRewriteRules": "DNS yeniden yazım kuralları yok",
|
||||
"answer": "Yanıt",
|
||||
"deleteDnsRewrite": "DNS yeniden yazımı sil",
|
||||
"deleteDnsRewriteMessage": "Bu DNS yeniden yazmasını silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
|
||||
"dnsRewriteRuleDeleted": "DNS yeniden yazma kuralı başarıyla silindi",
|
||||
"dnsRewriteRuleNotDeleted": "DNS yeniden yazma kuralı silinemedi",
|
||||
"deleteDnsRewriteMessage": "Bu DNS yeniden yazımını silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
|
||||
"dnsRewriteRuleDeleted": "DNS yeniden yazım kuralı başarıyla silindi",
|
||||
"dnsRewriteRuleNotDeleted": "DNS yeniden yazım kuralı silinemedi",
|
||||
"addDnsRewrite": "DNS yeniden yazımı ekle",
|
||||
"addingRewrite": "Yeniden yazma ekleniyor...",
|
||||
"dnsRewriteRuleAdded": "DNS yeniden yazma kuralı başarıyla eklendi",
|
||||
"dnsRewriteRuleNotAdded": "DNS yeniden yazma kuralı eklenemedi",
|
||||
"addingRewrite": "Yeniden yazım ekleniyor...",
|
||||
"dnsRewriteRuleAdded": "DNS yeniden yazım kuralı başarıyla eklendi",
|
||||
"dnsRewriteRuleNotAdded": "DNS yeniden yazım kuralı eklenemedi",
|
||||
"logsSettings": "Günlük ayarları",
|
||||
"enableLog": "Günlüğü etkinleştir",
|
||||
"clearLogs": "Günlükleri temizle",
|
||||
|
@ -422,33 +422,33 @@
|
|||
"deletingLogs": "Günlükler temizleniyor...",
|
||||
"logsCleared": "Günlükler başarıyla temizlendi",
|
||||
"logsNotCleared": "Günlükler temizlenemedi",
|
||||
"runningHomeAssistant": "Ev Asistanı üzerinde çalıştır",
|
||||
"runningHomeAssistant": "Ev asistanı üzerinde çalıştır",
|
||||
"serverError": "Sunucu hatası",
|
||||
"noItems": "Burada gösterilecek öğe yok",
|
||||
"dnsSettings": "DNS ayarları",
|
||||
"dnsSettingsDescription": "DNS sunucuları ile bağlantıyı yapılandır",
|
||||
"upstreamDns": "Üst akış DNS sunucuları",
|
||||
"upstreamDns": "Üst kaynak DNS sunucuları",
|
||||
"bootstrapDns": "Önyükleme DNS sunucuları",
|
||||
"noUpstreamDns": "Üst akış DNS sunucuları eklenmedi.",
|
||||
"noUpstreamDns": "Üst kaynak DNS sunucuları eklenmedi.",
|
||||
"dnsMode": "DNS modu",
|
||||
"noDnsMode": "DNS modu seçili değil",
|
||||
"loadBalancing": "Yük dengeleme",
|
||||
"parallelRequests": "Paralel istekler",
|
||||
"fastestIpAddress": "En hızlı IP adresi",
|
||||
"loadBalancingDescription": "Her seferinde bir üst akış sunucusuna sorgu yap. AdGuard Home, sunucuyu seçmek için ağırlıklı rastgele algoritmasını kullanır, böylece en hızlı sunucu daha sık kullanılır.",
|
||||
"parallelRequestsDescription": "Tüm üst akış sunucularını aynı anda sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
|
||||
"loadBalancingDescription": "Her seferinde bir üst kaynak sunucusuna sorgu yap. AdGuard Home, sunucuyu seçmek için ağırlıklı rastgele algoritmasını kullanır, böylece en hızlı sunucu daha sık kullanılır.",
|
||||
"parallelRequestsDescription": "Tüm üst kaynak sunucularını aynı anda sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
|
||||
"fastestIpAddressDescription": "Tüm DNS sunucularına sorgu yapın ve tüm yanıtlar arasında en hızlı IP adresini döndürün. Bu, AdGuard Home'un tüm DNS sunucularından yanıtları beklemesi gerektiği için DNS sorgularını yavaşlatır, ancak genel bağlantıyı iyileştirir.",
|
||||
"noBootstrapDns": "Önyükleme DNS sunucuları eklenmedi.",
|
||||
"bootstrapDnsServersInfo": "Önyükleme DNS sunucuları, üst akışlarda belirttiğiniz DoH/DoT çözümleyicilerinin IP adreslerini çözmek için kullanılır.",
|
||||
"bootstrapDnsServersInfo": "Önyükleme DNS sunucuları, üst kaynaklarda belirttiğiniz DoH/DoT çözümleyicilerinin IP adreslerini çözmek için kullanılır.",
|
||||
"privateReverseDnsServers": "Özel ters DNS sunucuları",
|
||||
"privateReverseDnsServersDescription": "AdGuard Home'un yerel PTR sorguları için kullandığı DNS sunucuları. Bu sunucular, özel IP aralıklarındaki adresler için ters DNS kullanarak PTR isteklerini çözmek için kullanılır, örneğin '192.168.12.34' olarak ayarlanmamışsa AdGuard Home, AdGuard Home'un kendi adresleri dışında, işletim sisteminizin varsayılan DNS çözümleyicilerinin adreslerini kullanır.",
|
||||
"reverseDnsDefault": "Varsayılan olarak, AdGuard Home aşağıdaki ters DNS çözümleyicilerini kullanır",
|
||||
"addItem": "Öğe ekle",
|
||||
"noServerAddressesAdded": "Sunucu adresleri eklenmedi.",
|
||||
"usePrivateReverseDnsResolvers": "Özel ters DNS çözümleyicilerini kullan",
|
||||
"usePrivateReverseDnsResolversDescription": "Bu üst akış sunucularını kullanarak yerel olarak sunulan adresler için ters DNS sorguları gerçekleştirin. Devre dışı bırakılırsa, AdGuard Home, DHCP, /etc/hosts vb. kaynaklardan bilinen istemciler dışında tüm PTR isteklerine NXDOMAIN yanıtı verir.",
|
||||
"usePrivateReverseDnsResolversDescription": "Bu üst kaynak sunucularını kullanarak yerel olarak sunulan adresler için ters DNS sorguları gerçekleştirin. Devre dışı bırakılırsa, AdGuard Home, DHCP, /etc/hosts vb. kaynaklardan bilinen istemciler dışında tüm PTR isteklerine NXDOMAIN yanıtı verir.",
|
||||
"enableReverseResolving": "İstemcilerin IP adreslerinin ters çözümlemesini etkinleştir",
|
||||
"enableReverseResolvingDescription": "İstemcilerin IP adreslerini karşılık gelen çözücülere PTR sorguları göndererek IP adreslerini tersine çözümleyerek (yerel istemciler için özel DNS sunucuları, genel IP adresine sahip istemciler için yukarı akış sunucuları) istemcilerin ana bilgisayar adlarını tersine çöz.",
|
||||
"enableReverseResolvingDescription": "İstemcilerin IP adreslerini karşılık gelen çözücülere PTR sorguları göndererek IP adreslerini tersine çözümleyerek (yerel istemciler için özel DNS sunucuları, genel IP adresine sahip istemciler için üst kaynak sunucuları) istemcilerin ana bilgisayar adlarını tersine çöz.",
|
||||
"dnsServerSettings": "AdGuard Home DNS sunucusu ayarları",
|
||||
"limitRequestsSecond": "Saniye başına sınırlama isteği",
|
||||
"valueNotNumber": "Değer bir sayı değil",
|
||||
|
@ -460,13 +460,13 @@
|
|||
"disableResolvingIpv6Description": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırın.",
|
||||
"blockingMode": "Engelleme modu",
|
||||
"defaultMode": "Varsayılan",
|
||||
"defaultDescription": "Reklam engelleme tarzı bir kural tarafından engellendiğinde sıfır IP adresi ile yanıt verin (A için 0.0.0.0; :: AAAA için) /etc/hosts tarzı bir kural tarafından engellendiğinde kuralda belirtilen IP adresi ile yanıt verin",
|
||||
"refusedDescription": "REFUSED kodu ile yanıt verin",
|
||||
"nxdomainDescription": "NXDOMAIN kodu ile yanıt verin",
|
||||
"defaultDescription": "Reklam engelleme tarzı bir kural tarafından engellendiğinde sıfır IP adresi ile yanıt verin. (A için 0.0.0.0; :: AAAA için) /etc/hosts tarzı bir kural tarafından engellendiğinde kuralda belirtilen IP adresi ile yanıt verin.",
|
||||
"refusedDescription": "REFUSED kodu ile yanıt verin.",
|
||||
"nxdomainDescription": "NXDOMAIN kodu ile yanıt verin.",
|
||||
"nullIp": "Boş IP",
|
||||
"nullIpDescription": "Sıfır IP adresi ile yanıt verin (A için 0.0.0.0; :: AAAA için)",
|
||||
"nullIpDescription": "Sıfır IP adresi ile yanıt verin. (A için 0.0.0.0; :: AAAA için)",
|
||||
"customIp": "Özel IP",
|
||||
"customIpDescription": "Manuel olarak ayarlanmış bir IP adresi ile yanıt verin",
|
||||
"customIpDescription": "Manuel olarak ayarlanmış bir IP adresi ile yanıt verin.",
|
||||
"dnsCacheConfig": "DNS önbellek yapılandırması",
|
||||
"cacheSize": "Önbellek boyutu",
|
||||
"inBytes": "Bayt olarak",
|
||||
|
@ -487,7 +487,7 @@
|
|||
"dnsConfigNotSaved": "DNS sunucusu yapılandırması kaydedilemedi",
|
||||
"savingConfig": "Yapılandırma kaydediliyor...",
|
||||
"someValueNotValid": "Bazı değerler geçerli değil",
|
||||
"upstreamDnsDescription": "Üst akış sunucularını ve DNS modunu yapılandır",
|
||||
"upstreamDnsDescription": "Üst kaynak sunucularını ve DNS modunu yapılandır",
|
||||
"bootstrapDnsDescription": "Önyükleme DNS sunucularını yapılandır",
|
||||
"privateReverseDnsDescription": "Özel DNS çözümleyicileri yapılandır ve özel ters DNS çözümlemeyi etkinleştir",
|
||||
"dnsServerSettingsDescription": "Hız limiti, engelleme modu ve daha fazlasını yapılandır",
|
||||
|
@ -551,7 +551,7 @@
|
|||
"deepOrange": "Koyu turuncu",
|
||||
"indigo": "Çivit mavisi",
|
||||
"useThemeColorStatus": "Durum için tema rengini kullan",
|
||||
"useThemeColorStatusDescription": "Yeşil ve kırmızı durum renklerini tema rengi ve gri ile değiştirir",
|
||||
"useThemeColorStatusDescription": "Yeşil ve kırmızı durum renklerini tema rengi ve gri ile değiştirir.",
|
||||
"invalidCertificateChain": "Geçersiz sertifika zinciri",
|
||||
"validCertificateChain": "Geçerli sertifika zinciri",
|
||||
"subject": "Konu",
|
||||
|
@ -624,18 +624,18 @@
|
|||
"unsupprtedVersionMessage": "Sunucu sürümünüz {version} için destek garantisi verilmiyor. Bu uygulamanın bu sunucu sürümüyle çalışmasında bazı sorunlar olabilir. AdGuard Home Yöneticisi, AdGuard Home sunucunun kararlı sürümleriyle çalışacak şekilde tasarlanmıştır. Alfa ve beta sürümleriyle çalışabilir, ancak uyumluluk garanti edilmez ve uygulama bu sürümlerle çalışırken bazı sorunlar yaşayabilir.",
|
||||
"iUnderstand": "Anladım",
|
||||
"appUpdates": "Uygulama güncellemeleri",
|
||||
"usingLatestVersion": "En son sürümü kullanıyorsunuz",
|
||||
"usingLatestVersion": "En son sürümü kullanıyorsunuz :)",
|
||||
"ipLogs": "Günlüklerdeki IP",
|
||||
"ipLogsDescription": "Günlükler listesinde istemci adı yerine IP adresini göster",
|
||||
"application": "Uygulama",
|
||||
"combinedChart": "Birleştirilmiş grafik",
|
||||
"combinedChartDescription": "Tüm grafikleri bir araya getirir",
|
||||
"combinedChartDescription": "Tüm grafikleri bir araya getirir.",
|
||||
"statistics": "İstatistikler",
|
||||
"errorLoadFilters": "Filtreler yüklenirken hata oluştu.",
|
||||
"clientRemovedSuccessfully": "İstemci başarıyla kaldırıldı.",
|
||||
"editRewriteRule": "Yeniden yazma kuralını düzenle",
|
||||
"dnsRewriteRuleUpdated": "DNS yeniden yazma kuralı başarıyla güncellendi",
|
||||
"dnsRewriteRuleNotUpdated": "DNS yeniden yazma kuralı güncellenemedi",
|
||||
"editRewriteRule": "Yeniden yazım kuralını düzenle",
|
||||
"dnsRewriteRuleUpdated": "DNS yeniden yazım kuralı başarıyla güncellendi",
|
||||
"dnsRewriteRuleNotUpdated": "DNS yeniden yazım kuralı güncellenemedi",
|
||||
"updatingRule": "Kural güncelleniyor...",
|
||||
"serverUpdateNeeded": "Sunucu güncellemesi gerekli",
|
||||
"updateYourServer": "Bu özelliği kullanmak için AdGuard Home sunucunuzu {version} veya üzeri bir sürüme güncelleyin.",
|
||||
|
@ -657,7 +657,7 @@
|
|||
"quickFilters": "Hızlı filtreler",
|
||||
"searchDomainInternet": "İnternette alan adı ara",
|
||||
"hideServerAddress": "Sunucu adresini gizle",
|
||||
"hideServerAddressDescription": "Ana ekranda sunucu adresini gizler",
|
||||
"hideServerAddressDescription": "Ana ekranda sunucu adresini gizler.",
|
||||
"topItemsOrder": "Öne çıkan öğeler sıralaması",
|
||||
"topItemsOrderDescription": "Ana ekrandaki öne çıkan öğe listelerini sırala",
|
||||
"topItemsReorderInfo": "Yeniden sıralamak için bir öğeyi basılı tutun ve kaydırın.",
|
||||
|
@ -682,9 +682,11 @@
|
|||
"processingLists": "Listeler işleniyor...",
|
||||
"enableDisableResult": "Sonucu etkinleştir veya devre dışı bırak",
|
||||
"selectedListsEnabledDisabledSuccessfully": "Seçilen tüm listeler başarıyla etkinleştirildi veya devre dışı bırakıldı",
|
||||
"sslWarning": "Kendinden imzalı bir sertifika ile HTTPS bağlantısı kullanıyorsanız, Ayarlar > Gelişmiş ayarlar bölümünde \"SSL sertifikasını kontrol etme\" seçeneğini etkinleştirdiğinizden emin olun.",
|
||||
"sslWarning": "Kendinden imzalı bir sertifika ile HTTPS bağlantısı kullanıyorsanız, Ayarlar > Gelişmiş ayarlar bölümünde \"SSL sertifikasını asla kontrol etme\" seçeneğini etkinleştirdiğinizden emin olun.",
|
||||
"unsupportedServerVersion": "Desteklenmeyen sunucu sürümü",
|
||||
"unsupportedServerVersionMessage": "AdGuard Home sunucu sürümünüz çok eski ve AdGuard Home Manager tarafından desteklenmiyor. Bu uygulamayı kullanmak için AdGuard Home sunucunuzu daha yeni bir sürüme yükseltmeniz gerekecektir.",
|
||||
"yourVersion": "Yüklü sürüm: {version}",
|
||||
"minimumRequiredVersion": "Gerekli minimum sürüm: {version}"
|
||||
"minimumRequiredVersion": "Gerekli minimum sürüm: {version}",
|
||||
"topUpstreams": "Öne çıkan üst kaynaklar",
|
||||
"averageUpstreamResponseTime": "Üst kaynak ortalama yanıt süresi"
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ class DnsStatistics {
|
|||
final List<Map<String, int>> topQueriedDomains;
|
||||
final List<Map<String, int>> topClients;
|
||||
final List<Map<String, int>> topBlockedDomains;
|
||||
final List<Map<String, int>>? topUpstreamResponses;
|
||||
final List<Map<String, double>>? topUpstreamsAvgTime;
|
||||
final List<int> dnsQueries;
|
||||
final List<int> blockedFiltering;
|
||||
final List<int> replacedSafebrowsing;
|
||||
|
@ -25,6 +27,8 @@ class DnsStatistics {
|
|||
required this.topQueriedDomains,
|
||||
required this.topClients,
|
||||
required this.topBlockedDomains,
|
||||
required this.topUpstreamResponses,
|
||||
required this.topUpstreamsAvgTime,
|
||||
required this.dnsQueries,
|
||||
required this.blockedFiltering,
|
||||
required this.replacedSafebrowsing,
|
||||
|
@ -42,6 +46,8 @@ class DnsStatistics {
|
|||
topQueriedDomains: List<Map<String, int>>.from(json["top_queried_domains"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
|
||||
topClients: List<Map<String, int>>.from(json["top_clients"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
|
||||
topBlockedDomains: List<Map<String, int>>.from(json["top_blocked_domains"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
|
||||
topUpstreamResponses: json["top_upstreams_responses"] != null ? List<Map<String, int>>.from(json["top_upstreams_responses"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))) : null,
|
||||
topUpstreamsAvgTime: json["top_upstreams_avg_time"] != null ? List<Map<String, double>>.from(json["top_upstreams_avg_time"].map((x) => Map.from(x).map((k, v) => MapEntry<String, double>(k, v)))) : null,
|
||||
dnsQueries: List<int>.from(json["dns_queries"].map((x) => x)),
|
||||
blockedFiltering: List<int>.from(json["blocked_filtering"].map((x) => x)),
|
||||
replacedSafebrowsing: List<int>.from(json["replaced_safebrowsing"].map((x) => x)),
|
||||
|
@ -59,6 +65,8 @@ class DnsStatistics {
|
|||
"top_queried_domains": List<dynamic>.from(topQueriedDomains.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
|
||||
"top_clients": List<dynamic>.from(topClients.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
|
||||
"top_blocked_domains": List<dynamic>.from(topBlockedDomains.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
|
||||
"top_upstreams_responses": topUpstreamResponses != null ? List<dynamic>.from(topUpstreamResponses!.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))) : null,
|
||||
"top_upstreams_avg_time": topUpstreamsAvgTime != null ? List<dynamic>.from(topUpstreamsAvgTime!.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))) : null,
|
||||
"dns_queries": List<dynamic>.from(dnsQueries.map((x) => x)),
|
||||
"blocked_filtering": List<dynamic>.from(blockedFiltering.map((x) => x)),
|
||||
"replaced_safebrowsing": List<dynamic>.from(replacedSafebrowsing.map((x) => x)),
|
||||
|
|
|
@ -124,12 +124,19 @@ class EncryptionData {
|
|||
}
|
||||
|
||||
|
||||
class EncryptionValidationResult {
|
||||
final bool isObject;
|
||||
final EncryptionValidation? encryptionValidation;
|
||||
final String? message;
|
||||
|
||||
EncyptionValidation encyptionValidationFromJson(String str) => EncyptionValidation.fromJson(json.decode(str));
|
||||
const EncryptionValidationResult({
|
||||
required this.isObject,
|
||||
this.encryptionValidation,
|
||||
this.message
|
||||
});
|
||||
}
|
||||
|
||||
String encyptionValidationToJson(EncyptionValidation data) => json.encode(data.toJson());
|
||||
|
||||
class EncyptionValidation {
|
||||
class EncryptionValidation {
|
||||
final String? subject;
|
||||
final String? issuer;
|
||||
final String? keyType;
|
||||
|
@ -156,7 +163,7 @@ class EncyptionValidation {
|
|||
final String? privateKeyPath;
|
||||
final bool? privateKeySaved;
|
||||
|
||||
EncyptionValidation({
|
||||
EncryptionValidation({
|
||||
this.subject,
|
||||
this.issuer,
|
||||
this.keyType,
|
||||
|
@ -184,7 +191,7 @@ class EncyptionValidation {
|
|||
this.privateKeySaved,
|
||||
});
|
||||
|
||||
factory EncyptionValidation.fromJson(Map<String, dynamic> json) => EncyptionValidation(
|
||||
factory EncryptionValidation.fromJson(Map<String, dynamic> json) => EncryptionValidation(
|
||||
subject: json["subject"],
|
||||
issuer: json["issuer"],
|
||||
keyType: json["key_type"],
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||
class MenuOption {
|
||||
final IconData? icon;
|
||||
final String title;
|
||||
final void Function() action;
|
||||
final void Function(dynamic) action;
|
||||
final bool? disabled;
|
||||
|
||||
const MenuOption({
|
||||
|
|
|
@ -449,7 +449,7 @@ class AppConfigProvider with ChangeNotifier {
|
|||
_showTopItemsChart = dbData['showTopItemsChart'];
|
||||
if (dbData['homeTopItemsOrder'] != null) {
|
||||
try {
|
||||
_homeTopItemsOrder = List<HomeTopItems>.from(
|
||||
final itemsOrder = List<HomeTopItems>.from(
|
||||
List<String>.from(jsonDecode(dbData['homeTopItemsOrder'])).map((e) {
|
||||
switch (e) {
|
||||
case 'queriedDomains':
|
||||
|
@ -461,11 +461,22 @@ class AppConfigProvider with ChangeNotifier {
|
|||
case 'recurrentClients':
|
||||
return HomeTopItems.recurrentClients;
|
||||
|
||||
case 'topUpstreams':
|
||||
return HomeTopItems.topUpstreams;
|
||||
|
||||
case 'avgUpstreamResponseTime':
|
||||
return HomeTopItems.avgUpstreamResponseTime;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}).where((e) => e != null).toList()
|
||||
);
|
||||
final missingItems = homeTopItemsDefaultOrder.where((e) => !itemsOrder.contains(e));
|
||||
_homeTopItemsOrder = [
|
||||
...itemsOrder,
|
||||
...missingItems
|
||||
];
|
||||
} catch (e) {
|
||||
Sentry.captureException(e);
|
||||
_homeTopItemsOrder = homeTopItemsDefaultOrder;
|
||||
|
|
|
@ -237,7 +237,7 @@ class StatusProvider with ChangeNotifier {
|
|||
}
|
||||
|
||||
Future<bool> getServerStatus({
|
||||
bool? withLoadingIndicator,
|
||||
bool? withLoadingIndicator = true,
|
||||
bool? overrideCheckServerVersion
|
||||
}) async {
|
||||
if (withLoadingIndicator == true) {
|
||||
|
|
|
@ -11,7 +11,6 @@ import 'package:adguard_home_manager/screens/clients/client/client_screen_functi
|
|||
import 'package:adguard_home_manager/screens/clients/client/added_client_tile.dart';
|
||||
import 'package:adguard_home_manager/screens/clients/client/remove_client_modal.dart';
|
||||
import 'package:adguard_home_manager/screens/clients/fab.dart';
|
||||
import 'package:adguard_home_manager/screens/clients/options_modal.dart';
|
||||
import 'package:adguard_home_manager/widgets/tab_content_list.dart';
|
||||
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
|
@ -143,16 +142,11 @@ class _AddedListState extends State<AddedList> {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
void openOptionsModal(Client client) {
|
||||
showModal(
|
||||
context: context,
|
||||
builder: (ctx) => OptionsModal(
|
||||
onDelete: () => openDeleteModal(client),
|
||||
onEdit: () => openClientModal(client),
|
||||
)
|
||||
);
|
||||
}
|
||||
final clientsDisplay = clientsProvider.searchTermClients != null && clientsProvider.searchTermClients != ""
|
||||
? widget.data.where(
|
||||
(c) => c.name.toLowerCase().contains(clientsProvider.searchTermClients.toString()) || c.ids.where((id) => id.contains(clientsProvider.searchTermClients.toString())).isNotEmpty
|
||||
).toList()
|
||||
: widget.data;
|
||||
|
||||
return CustomTabContentList(
|
||||
listPadding: widget.splitView == true
|
||||
|
@ -177,12 +171,11 @@ class _AddedListState extends State<AddedList> {
|
|||
],
|
||||
),
|
||||
),
|
||||
itemsCount: widget.data.length,
|
||||
itemsCount: clientsDisplay.length,
|
||||
contentWidget: (index) => AddedClientTile(
|
||||
selectedClient: widget.selectedClient,
|
||||
client: widget.data[index],
|
||||
client: clientsDisplay[index],
|
||||
onTap: widget.onClientSelected,
|
||||
onLongPress: openOptionsModal,
|
||||
onEdit: statusProvider.serverStatus != null
|
||||
? (c) => openClientModal(c)
|
||||
: null,
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:contextmenu/contextmenu.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_modal.dart';
|
||||
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
|
||||
|
@ -16,54 +15,29 @@ class ActiveClientTile extends StatelessWidget {
|
|||
final AutoClient? selectedClient;
|
||||
|
||||
const ActiveClientTile({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.client,
|
||||
required this.onTap,
|
||||
required this.splitView,
|
||||
this.selectedClient
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
void openOptionsModal() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => OptionsModal(
|
||||
options: [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: () {
|
||||
copyToClipboard(
|
||||
value: client.name != ''
|
||||
? client.name!
|
||||
: client.ip,
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard,
|
||||
);
|
||||
},
|
||||
)
|
||||
]
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (splitView == true) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: ContextMenuArea(
|
||||
builder: (context) => [
|
||||
CustomListTile(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
child: OptionsMenu(
|
||||
options: [
|
||||
MenuOption(
|
||||
icon: Icons.copy_rounded,
|
||||
onTap: () {
|
||||
copyToClipboard(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
action: (_) => copyToClipboard(
|
||||
value: client.name != ''
|
||||
? client.name!
|
||||
: client.ip,
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)
|
||||
)
|
||||
],
|
||||
child: Material(
|
||||
|
@ -72,10 +46,6 @@ class ActiveClientTile extends StatelessWidget {
|
|||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
onTap: () => onTap(client),
|
||||
onLongPress: () {
|
||||
Navigator.pop(context);
|
||||
openOptionsModal();
|
||||
},
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
|
@ -128,20 +98,17 @@ class ActiveClientTile extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
else {
|
||||
return ContextMenuArea(
|
||||
builder: (context) => [
|
||||
CustomListTile(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
return OptionsMenu(
|
||||
options: [
|
||||
MenuOption(
|
||||
icon: Icons.copy_rounded,
|
||||
onTap: () {
|
||||
copyToClipboard(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
action: (_) => copyToClipboard(
|
||||
value: client.name != ''
|
||||
? client.name!
|
||||
: client.ip,
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)
|
||||
)
|
||||
],
|
||||
child: CustomListTile(
|
||||
|
@ -158,7 +125,6 @@ class ActiveClientTile extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
onTap: () => onTap(client),
|
||||
onLongPress: openOptionsModal,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:contextmenu/contextmenu.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
|
||||
import 'package:adguard_home_manager/models/clients.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class AddedClientTile extends StatelessWidget {
|
||||
class AddedClientTile extends StatefulWidget {
|
||||
final Client client;
|
||||
final void Function(Client) onTap;
|
||||
final void Function(Client) onLongPress;
|
||||
final void Function(Client)? onEdit;
|
||||
final void Function(Client) onDelete;
|
||||
final Client? selectedClient;
|
||||
|
@ -22,63 +22,50 @@ class AddedClientTile extends StatelessWidget {
|
|||
super.key,
|
||||
required this.client,
|
||||
required this.onTap,
|
||||
required this.onLongPress,
|
||||
this.onEdit,
|
||||
required this.onDelete,
|
||||
this.selectedClient,
|
||||
required this.splitView,
|
||||
});
|
||||
|
||||
@override
|
||||
State<AddedClientTile> createState() => _AddedClientTileState();
|
||||
}
|
||||
|
||||
class _AddedClientTileState extends State<AddedClientTile> {
|
||||
bool _isHover = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
|
||||
if (splitView == true) {
|
||||
if (widget.splitView == true) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
child: ContextMenuArea(
|
||||
builder: (context) => [
|
||||
if (onEdit != null) CustomListTile(
|
||||
title: AppLocalizations.of(context)!.edit,
|
||||
icon: Icons.edit_rounded,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onEdit!(client);
|
||||
}
|
||||
),
|
||||
CustomListTile(
|
||||
title: AppLocalizations.of(context)!.delete,
|
||||
icon: Icons.delete_rounded,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onDelete(client);
|
||||
}
|
||||
),
|
||||
CustomListTile(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
child: OptionsMenu(
|
||||
options: [
|
||||
MenuOption(
|
||||
icon: Icons.copy_rounded,
|
||||
onTap: () {
|
||||
copyToClipboard(
|
||||
value: client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
action: (_) => copyToClipboard(
|
||||
value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
)
|
||||
),
|
||||
],
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
onTap: () => onTap(client),
|
||||
onLongPress: () => onLongPress(client),
|
||||
onTap: () => widget.onTap(widget.client),
|
||||
onHover: (v) => setState(() => _isHover = v),
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
color: client == selectedClient
|
||||
color: widget.client == widget.selectedClient
|
||||
? Theme.of(context).colorScheme.primaryContainer
|
||||
: null
|
||||
),
|
||||
|
@ -94,7 +81,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
|
@ -107,7 +94,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.filter_list_rounded,
|
||||
size: 19,
|
||||
color: client.filteringEnabled == true
|
||||
color: widget.client.filteringEnabled == true
|
||||
? appConfigProvider.useThemeColorForStatus == true
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.green
|
||||
|
@ -119,7 +106,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.vpn_lock_rounded,
|
||||
size: 18,
|
||||
color: client.safebrowsingEnabled == true
|
||||
color: widget.client.safebrowsingEnabled == true
|
||||
? appConfigProvider.useThemeColorForStatus == true
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.green
|
||||
|
@ -131,7 +118,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.block,
|
||||
size: 18,
|
||||
color: client.parentalEnabled == true
|
||||
color: widget.client.parentalEnabled == true
|
||||
? appConfigProvider.useThemeColorForStatus == true
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.green
|
||||
|
@ -143,7 +130,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.search_rounded,
|
||||
size: 19,
|
||||
color: client.safeSearch != null && client.safeSearch!.enabled == true
|
||||
color: widget.client.safeSearch != null && widget.client.safeSearch!.enabled == true
|
||||
? appConfigProvider.useThemeColorForStatus == true
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.green
|
||||
|
@ -159,6 +146,14 @@ class AddedClientTile extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
if (widget.onEdit != null && _isHover == true) ...[
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
onPressed: () => widget.onEdit!(widget.client),
|
||||
icon: const Icon(Icons.file_open_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.seeDetails,
|
||||
)
|
||||
]
|
||||
],
|
||||
)
|
||||
),
|
||||
|
@ -168,37 +163,30 @@ class AddedClientTile extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
else {
|
||||
return ContextMenuArea(
|
||||
builder: (context) => [
|
||||
if (onEdit != null) CustomListTile(
|
||||
return OptionsMenu(
|
||||
options: [
|
||||
if (widget.onEdit != null) MenuOption(
|
||||
title: AppLocalizations.of(context)!.seeDetails,
|
||||
icon: Icons.file_open_rounded,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onEdit!(client);
|
||||
}
|
||||
action: (_) => widget.onEdit!(widget.client)
|
||||
),
|
||||
CustomListTile(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
MenuOption(
|
||||
icon: Icons.copy_rounded,
|
||||
onTap: () {
|
||||
copyToClipboard(
|
||||
value: client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
action: (_) => copyToClipboard(
|
||||
value: widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
successMessage: AppLocalizations.of(context)!.copiedClipboard,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
)
|
||||
),
|
||||
],
|
||||
child: CustomListTile(
|
||||
onLongPress: () => onLongPress(client),
|
||||
onTap: () => onTap(client),
|
||||
title: client.name,
|
||||
onTap: () => widget.onTap(widget.client),
|
||||
title: widget.client.name,
|
||||
subtitleWidget: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
widget.client.ids.toString().replaceAll(RegExp(r'^\[|\]$'), ''),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).listTileTheme.textColor
|
||||
),
|
||||
|
@ -209,7 +197,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.filter_list_rounded,
|
||||
size: 19,
|
||||
color: client.filteringEnabled == true
|
||||
color: widget.client.filteringEnabled == true
|
||||
? appConfigProvider.useThemeColorForStatus == true
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.green
|
||||
|
@ -221,7 +209,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.vpn_lock_rounded,
|
||||
size: 18,
|
||||
color: client.safebrowsingEnabled == true
|
||||
color: widget.client.safebrowsingEnabled == true
|
||||
? appConfigProvider.useThemeColorForStatus == true
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.green
|
||||
|
@ -233,7 +221,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.block,
|
||||
size: 18,
|
||||
color: client.parentalEnabled == true
|
||||
color: widget.client.parentalEnabled == true
|
||||
? appConfigProvider.useThemeColorForStatus == true
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.green
|
||||
|
@ -245,7 +233,7 @@ class AddedClientTile extends StatelessWidget {
|
|||
Icon(
|
||||
Icons.search_rounded,
|
||||
size: 19,
|
||||
color: client.safeSearch != null && client.safeSearch!.enabled == true
|
||||
color: widget.client.safeSearch != null && widget.client.safeSearch!.enabled == true
|
||||
? appConfigProvider.useThemeColorForStatus == true
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.green
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:adguard_home_manager/screens/clients/clients_lists.dart';
|
|||
import 'package:adguard_home_manager/models/clients.dart';
|
||||
|
||||
class Clients extends StatefulWidget {
|
||||
const Clients({Key? key}) : super(key: key);
|
||||
const Clients({super.key});
|
||||
|
||||
@override
|
||||
State<Clients> createState() => _ClientsState();
|
||||
|
@ -20,7 +20,8 @@ class _ClientsState extends State<Clients> with TickerProviderStateMixin {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
return Scaffold(
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth > 1000) {
|
||||
return SplitView.material(
|
||||
|
@ -50,6 +51,7 @@ class _ClientsState extends State<Clients> with TickerProviderStateMixin {
|
|||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,12 +16,12 @@ class ClientsList extends StatelessWidget {
|
|||
final bool splitView;
|
||||
|
||||
const ClientsList({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.data,
|
||||
required this.onClientSelected,
|
||||
this.selectedClient,
|
||||
required this.splitView,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -18,9 +18,9 @@ class ClientsLists extends StatefulWidget {
|
|||
final bool splitView;
|
||||
|
||||
const ClientsLists({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.splitView,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<ClientsLists> createState() => _ClientsListsState();
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile_dialog.dart';
|
||||
|
||||
class OptionsModal extends StatelessWidget {
|
||||
final void Function() onEdit;
|
||||
final void Function() onDelete;
|
||||
|
||||
const OptionsModal({
|
||||
Key? key,
|
||||
required this.onDelete,
|
||||
required this.onEdit,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
|
||||
return AlertDialog(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 0,
|
||||
vertical: 16
|
||||
),
|
||||
title: Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.more_horiz,
|
||||
color: Theme.of(context).listTileTheme.iconColor
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.options,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 24),
|
||||
if (statusProvider.serverStatus != null) CustomListTileDialog(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onEdit();
|
||||
},
|
||||
title: AppLocalizations.of(context)!.edit,
|
||||
icon: Icons.edit,
|
||||
),
|
||||
CustomListTileDialog(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
onDelete();
|
||||
},
|
||||
title: AppLocalizations.of(context)!.delete,
|
||||
icon: Icons.delete,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(AppLocalizations.of(context)!.close)
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,11 +7,12 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|||
|
||||
import 'package:adguard_home_manager/screens/clients/client/remove_client_modal.dart';
|
||||
import 'package:adguard_home_manager/screens/clients/client/client_screen_functions.dart';
|
||||
import 'package:adguard_home_manager/screens/clients/options_modal.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/section_label.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'package:adguard_home_manager/classes/process_modal.dart';
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
@ -151,15 +152,15 @@ class _SearchClientsState extends State<SearchClients> {
|
|||
);
|
||||
}
|
||||
|
||||
void openOptionsModal(Client client) {
|
||||
showModal(
|
||||
context: context,
|
||||
builder: (ctx) => OptionsModal(
|
||||
onDelete: () => openDeleteModal(client),
|
||||
onEdit: () => openClientModal(client),
|
||||
)
|
||||
);
|
||||
}
|
||||
// void openOptionsModal(Client client) {
|
||||
// showModal(
|
||||
// context: context,
|
||||
// builder: (ctx) => OptionsModal(
|
||||
// onDelete: () => openDeleteModal(client),
|
||||
// onEdit: () => openClientModal(client),
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
|
@ -224,12 +225,25 @@ class _SearchClientsState extends State<SearchClients> {
|
|||
primary: false,
|
||||
itemCount: clientsScreen.length,
|
||||
padding: const EdgeInsets.only(bottom: 0),
|
||||
itemBuilder: (context, index) => ListTile(
|
||||
itemBuilder: (context, index) => OptionsMenu(
|
||||
options: [
|
||||
MenuOption(
|
||||
icon: Icons.edit_rounded,
|
||||
title: AppLocalizations.of(context)!.edit,
|
||||
action: (v) => openClientModal(v)
|
||||
),
|
||||
MenuOption(
|
||||
icon: Icons.delete_rounded,
|
||||
title: AppLocalizations.of(context)!.delete,
|
||||
action: (v) => openDeleteModal(v)
|
||||
),
|
||||
],
|
||||
value: clientsScreen[index],
|
||||
child: ListTile(
|
||||
contentPadding: index == 0
|
||||
? const EdgeInsets.only(left: 20, right: 20, bottom: 15)
|
||||
: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
|
||||
isThreeLine: true,
|
||||
onLongPress: () => openOptionsModal(clientsScreen[index]),
|
||||
onTap: statusProvider.serverStatus != null
|
||||
? () => openClientModal(clientsScreen[index])
|
||||
: null,
|
||||
|
@ -301,6 +315,7 @@ class _SearchClientsState extends State<SearchClients> {
|
|||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
|
|
|
@ -172,7 +172,7 @@ class _FiltersListState extends State<FiltersList> {
|
|||
loadStatus: widget.loadStatus,
|
||||
onRefresh: () async {
|
||||
final result = await filteringProvider.fetchFilters();
|
||||
if (result == false) {
|
||||
if (result == false && mounted) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.errorLoadFilters,
|
||||
|
|
|
@ -7,10 +7,10 @@ import 'package:contextmenu/contextmenu.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
import 'package:adguard_home_manager/screens/filters/add_button.dart';
|
||||
import 'package:adguard_home_manager/screens/filters/list_options_menu.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_modal.dart';
|
||||
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/functions/desktop_mode.dart';
|
||||
|
@ -28,11 +28,11 @@ class FiltersTripleColumn extends StatelessWidget {
|
|||
final List<Widget> actions;
|
||||
|
||||
const FiltersTripleColumn({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.onRemoveCustomRule,
|
||||
required this.onOpenDetailsModal,
|
||||
required this.actions,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -282,22 +282,18 @@ class FiltersTripleColumn extends StatelessWidget {
|
|||
}
|
||||
),
|
||||
],
|
||||
child: CustomListTile(
|
||||
onLongPress: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) => OptionsModal(
|
||||
child: OptionsMenu(
|
||||
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,
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
],
|
||||
child: CustomListTile(
|
||||
title: filteringProvider.filtering!.userRules[index],
|
||||
subtitleWidget: generateSubtitle(filteringProvider.filtering!.userRules[index]),
|
||||
trailing: IconButton(
|
||||
|
@ -308,6 +304,7 @@ class FiltersTripleColumn extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
@ -8,7 +8,7 @@ import 'package:provider/provider.dart';
|
|||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_modal.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
import 'package:adguard_home_manager/screens/filters/selection/selection_screen.dart';
|
||||
|
||||
import 'package:adguard_home_manager/functions/open_url.dart';
|
||||
|
@ -144,9 +144,7 @@ class ListOptionsMenu extends StatelessWidget {
|
|||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onLongPress: Platform.isAndroid || Platform.isIOS ? () => showDialog(
|
||||
context: context,
|
||||
builder: (context) => OptionsModal(
|
||||
child: OptionsMenu(
|
||||
options: [
|
||||
MenuOption(
|
||||
title: list.enabled == true
|
||||
|
@ -155,12 +153,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
|
||||
)
|
||||
|
@ -168,17 +166,19 @@ 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: openSelectionMode
|
||||
),
|
||||
]
|
||||
action: (_) => Future.delayed(
|
||||
const Duration(milliseconds: 0),
|
||||
() => openSelectionMode()
|
||||
)
|
||||
) : null,
|
||||
),
|
||||
],
|
||||
child: child
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -8,10 +8,10 @@ import 'package:provider/provider.dart';
|
|||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/screens/home/server_status.dart';
|
||||
import 'package:adguard_home_manager/screens/home/top_items/top_items_lists.dart';
|
||||
import 'package:adguard_home_manager/screens/home/combined_chart.dart';
|
||||
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/top_items/top_items.dart';
|
||||
import 'package:adguard_home_manager/screens/home/chart.dart';
|
||||
|
||||
import 'package:adguard_home_manager/functions/number_format.dart';
|
||||
|
@ -21,7 +21,7 @@ import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
|||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
const Home({Key? key}) : super(key: key);
|
||||
const Home({super.key});
|
||||
|
||||
@override
|
||||
State<Home> createState() => _HomeState();
|
||||
|
@ -32,8 +32,11 @@ class _HomeState extends State<Home> {
|
|||
late bool isVisible;
|
||||
|
||||
@override
|
||||
initState(){
|
||||
Provider.of<StatusProvider>(context, listen: false).getServerStatus();
|
||||
initState() {
|
||||
final statusProvider = Provider.of<StatusProvider>(context, listen: false);
|
||||
statusProvider.getServerStatus(
|
||||
withLoadingIndicator: statusProvider.serverStatus != null ? false : true
|
||||
);
|
||||
|
||||
super.initState();
|
||||
|
||||
|
@ -234,74 +237,3 @@ class _HomeState extends State<Home> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TopItemsLists extends StatelessWidget {
|
||||
final List<HomeTopItems> order;
|
||||
|
||||
const TopItemsLists({
|
||||
Key? key,
|
||||
required this.order,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
|
||||
List<Widget> bottom = [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Divider(
|
||||
thickness: 1,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
];
|
||||
|
||||
return Column(
|
||||
children: order.asMap().entries.map((item) {
|
||||
switch (item.value) {
|
||||
case HomeTopItems.queriedDomains:
|
||||
return Column(
|
||||
children: [
|
||||
TopItems(
|
||||
label: AppLocalizations.of(context)!.topQueriedDomains,
|
||||
data: statusProvider.serverStatus!.stats.topQueriedDomains,
|
||||
type: 'topQueriedDomains',
|
||||
),
|
||||
if (item.key < order.length - 1) ...bottom
|
||||
],
|
||||
);
|
||||
|
||||
case HomeTopItems.blockedDomains:
|
||||
return Column(
|
||||
children: [
|
||||
TopItems(
|
||||
label: AppLocalizations.of(context)!.topBlockedDomains,
|
||||
data: statusProvider.serverStatus!.stats.topBlockedDomains,
|
||||
type: 'topBlockedDomains',
|
||||
),
|
||||
if (item.key < order.length - 1) ...bottom
|
||||
],
|
||||
);
|
||||
|
||||
case HomeTopItems.recurrentClients:
|
||||
return Column(
|
||||
children: [
|
||||
TopItems(
|
||||
label: AppLocalizations.of(context)!.topClients,
|
||||
data: statusProvider.serverStatus!.stats.topClients,
|
||||
type: 'topClients',
|
||||
clients: true,
|
||||
),
|
||||
if (item.key < order.length - 1) ...bottom
|
||||
],
|
||||
);
|
||||
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,30 +2,35 @@ 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/domain_options.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
|
||||
import 'package:adguard_home_manager/models/applied_filters.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
|
||||
class RowItem extends StatefulWidget {
|
||||
final String type;
|
||||
final HomeTopItems type;
|
||||
final Color chartColor;
|
||||
final String domain;
|
||||
final String number;
|
||||
final bool clients;
|
||||
final bool showColor;
|
||||
final String? unit;
|
||||
final List<MenuOption> options;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
|
||||
const RowItem({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.chartColor,
|
||||
required this.domain,
|
||||
required this.number,
|
||||
required this.clients,
|
||||
required this.showColor,
|
||||
}) : super(key: key);
|
||||
required this.options,
|
||||
this.onTapEntry,
|
||||
this.unit,
|
||||
});
|
||||
|
||||
@override
|
||||
State<RowItem> createState() => _RowItemState();
|
||||
|
@ -77,8 +82,6 @@ class _RowItemState extends State<RowItem> with TickerProviderStateMixin {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
final logsProvider = Provider.of<LogsProvider>(context);
|
||||
|
||||
String? name;
|
||||
if (widget.clients == true) {
|
||||
|
@ -91,36 +94,10 @@ class _RowItemState extends State<RowItem> with TickerProviderStateMixin {
|
|||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: DomainOptions(
|
||||
item: widget.domain,
|
||||
isClient: widget.type == 'topClients',
|
||||
isBlocked: widget.type == 'topBlockedDomains',
|
||||
onTap: () {
|
||||
if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') {
|
||||
logsProvider.setSearchText(widget.domain);
|
||||
logsProvider.setSelectedClients(null);
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: 'all',
|
||||
searchText: widget.domain,
|
||||
clients: null
|
||||
)
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
}
|
||||
else if (widget.type == 'topClients') {
|
||||
logsProvider.setSearchText(null);
|
||||
logsProvider.setSelectedClients([widget.domain]);
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: 'all',
|
||||
searchText: null,
|
||||
clients: [widget.domain]
|
||||
)
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
}
|
||||
},
|
||||
child: OptionsMenu(
|
||||
value: widget.domain,
|
||||
options: widget.options,
|
||||
onTap: widget.onTapEntry,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
|
@ -195,10 +172,10 @@ class OthersRowItem extends StatefulWidget {
|
|||
final bool showColor;
|
||||
|
||||
const OthersRowItem({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.items,
|
||||
required this.showColor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<OthersRowItem> createState() => _OthersRowItemState();
|
||||
|
|
|
@ -7,26 +7,35 @@ import 'package:provider/provider.dart';
|
|||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/screens/home/top_items/row_item.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_pie_chart.dart';
|
||||
import 'package:adguard_home_manager/screens/top_items/top_items_modal.dart';
|
||||
import 'package:adguard_home_manager/screens/top_items/top_items.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_pie_chart.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class TopItems extends StatefulWidget {
|
||||
final String type;
|
||||
final HomeTopItems type;
|
||||
final String label;
|
||||
final List<Map<String, dynamic>> data;
|
||||
final bool? clients;
|
||||
final bool withChart;
|
||||
final bool withProgressBar;
|
||||
final String Function(dynamic) buildValue;
|
||||
final List<MenuOption> menuOptions;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
|
||||
const TopItems({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.label,
|
||||
required this.data,
|
||||
this.clients
|
||||
}) : super(key: key);
|
||||
required this.withChart,
|
||||
required this.withProgressBar,
|
||||
required this.buildValue,
|
||||
required this.menuOptions,
|
||||
this.onTapEntry,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TopItems> createState() => _TopItemsState();
|
||||
|
@ -52,25 +61,9 @@ class _TopItemsState extends State<TopItems> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
|
||||
List<Map<String, dynamic>> generateData() {
|
||||
switch (widget.type) {
|
||||
case 'topQueriedDomains':
|
||||
return statusProvider.serverStatus!.stats.topQueriedDomains;
|
||||
|
||||
case 'topBlockedDomains':
|
||||
return statusProvider.serverStatus!.stats.topBlockedDomains;
|
||||
|
||||
case 'topClients':
|
||||
return statusProvider.serverStatus!.stats.topClients;
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
final withChart = widget.type != HomeTopItems.avgUpstreamResponseTime;
|
||||
|
||||
Map<String, double> chartData() {
|
||||
Map<String, double> values = {};
|
||||
|
@ -110,10 +103,12 @@ class _TopItemsState extends State<TopItems> {
|
|||
child: Column(
|
||||
children: [
|
||||
if (widget.data.isEmpty) noItems,
|
||||
if (widget.data.isNotEmpty && width > 700) Row(
|
||||
if (widget.data.isNotEmpty && width > 700) Padding(
|
||||
padding: EdgeInsets.only(bottom: withChart == false ? 16 : 0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
if (withChart == true) Expanded(
|
||||
flex: 1,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
|
@ -132,14 +127,30 @@ class _TopItemsState extends State<TopItems> {
|
|||
flex: 2,
|
||||
child: Column(
|
||||
children: [
|
||||
ItemsList(
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
bottom: 16
|
||||
),
|
||||
child: Text(
|
||||
widget.label,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500
|
||||
),
|
||||
),
|
||||
),
|
||||
_ItemsList(
|
||||
colors: colors,
|
||||
data: widget.data,
|
||||
clients: widget.clients,
|
||||
clients: widget.type == HomeTopItems.recurrentClients,
|
||||
type: widget.type,
|
||||
showChart: _showChart
|
||||
showChart: withChart == true ? _showChart : false,
|
||||
buildValue: widget.buildValue,
|
||||
menuOptions: widget.menuOptions,
|
||||
onTapEntry: widget.onTapEntry,
|
||||
),
|
||||
OthersRowItem(
|
||||
if (withChart == true) OthersRowItem(
|
||||
items: widget.data,
|
||||
showColor: true,
|
||||
)
|
||||
|
@ -148,7 +159,12 @@ class _TopItemsState extends State<TopItems> {
|
|||
)
|
||||
],
|
||||
),
|
||||
if (widget.data.isNotEmpty && width <= 700) ...[
|
||||
),
|
||||
if (widget.data.isNotEmpty && width <= 700) Builder(
|
||||
builder: (context) {
|
||||
if (widget.withChart == true) {
|
||||
return Column(
|
||||
children: [
|
||||
ExpansionPanelList(
|
||||
expandedHeaderPadding: const EdgeInsets.all(0),
|
||||
elevation: 0,
|
||||
|
@ -157,20 +173,23 @@ class _TopItemsState extends State<TopItems> {
|
|||
children: [
|
||||
ExpansionPanel(
|
||||
headerBuilder: (context, isExpanded) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: width <= 700
|
||||
? MainAxisAlignment.spaceBetween
|
||||
: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
Flexible(
|
||||
child: Text(
|
||||
widget.label,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -195,20 +214,72 @@ class _TopItemsState extends State<TopItems> {
|
|||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: ItemsList(
|
||||
child: _ItemsList(
|
||||
colors: colors,
|
||||
data: widget.data,
|
||||
clients: widget.clients,
|
||||
clients: widget.type == HomeTopItems.recurrentClients,
|
||||
type: widget.type,
|
||||
showChart: _showChart
|
||||
showChart: _showChart,
|
||||
buildValue: widget.buildValue,
|
||||
menuOptions: widget.menuOptions,
|
||||
onTapEntry: widget.onTapEntry,
|
||||
),
|
||||
),
|
||||
OthersRowItem(
|
||||
if (widget.withChart == true) OthersRowItem(
|
||||
items: widget.data,
|
||||
showColor: _showChart,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18),
|
||||
child: Row(
|
||||
mainAxisAlignment: width <= 700
|
||||
? MainAxisAlignment.spaceBetween
|
||||
: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
widget.label,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: _ItemsList(
|
||||
colors: colors,
|
||||
data: widget.data,
|
||||
clients: widget.type == HomeTopItems.recurrentClients,
|
||||
type: widget.type,
|
||||
showChart: false,
|
||||
buildValue: widget.buildValue,
|
||||
menuOptions: widget.menuOptions,
|
||||
onTapEntry: widget.onTapEntry,
|
||||
),
|
||||
),
|
||||
if (widget.withChart == true) OthersRowItem(
|
||||
items: widget.data,
|
||||
showColor: false,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
if (widget.data.length > 5) ...[
|
||||
Padding(
|
||||
|
@ -225,8 +296,12 @@ class _TopItemsState extends State<TopItems> {
|
|||
builder: (context) => TopItemsModal(
|
||||
type: widget.type,
|
||||
title: widget.label,
|
||||
isClient: widget.clients,
|
||||
data: generateData(),
|
||||
isClient: widget.type == HomeTopItems.recurrentClients,
|
||||
data: widget.data,
|
||||
withProgressBar: widget.withProgressBar,
|
||||
buildValue: widget.buildValue,
|
||||
options: widget.menuOptions,
|
||||
onTapEntry: widget.onTapEntry,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -236,8 +311,12 @@ class _TopItemsState extends State<TopItems> {
|
|||
builder: (context) => TopItemsScreen(
|
||||
type: widget.type,
|
||||
title: widget.label,
|
||||
isClient: widget.clients,
|
||||
data: generateData(),
|
||||
isClient: widget.type == HomeTopItems.recurrentClients,
|
||||
data: widget.data,
|
||||
withProgressBar: widget.withProgressBar,
|
||||
buildValue: widget.buildValue,
|
||||
menuOptions: widget.menuOptions,
|
||||
onTapEntry: widget.onTapEntry,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -266,21 +345,26 @@ class _TopItemsState extends State<TopItems> {
|
|||
}
|
||||
}
|
||||
|
||||
class ItemsList extends StatelessWidget {
|
||||
class _ItemsList extends StatelessWidget {
|
||||
final List<Color> colors;
|
||||
final List<Map<String, dynamic>> data;
|
||||
final bool? clients;
|
||||
final String type;
|
||||
final HomeTopItems type;
|
||||
final bool showChart;
|
||||
final String Function(dynamic) buildValue;
|
||||
final List<MenuOption> menuOptions;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
|
||||
const ItemsList({
|
||||
Key? key,
|
||||
const _ItemsList({
|
||||
required this.colors,
|
||||
required this.data,
|
||||
required this.clients,
|
||||
required this.type,
|
||||
required this.showChart,
|
||||
}) : super(key: key);
|
||||
required this.buildValue,
|
||||
required this.menuOptions,
|
||||
this.onTapEntry,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -290,10 +374,12 @@ class ItemsList extends StatelessWidget {
|
|||
).asMap().entries.map((e) => RowItem(
|
||||
clients: clients ?? false,
|
||||
domain: e.value.keys.toList()[0],
|
||||
number: e.value.values.toList()[0].toString(),
|
||||
number: buildValue(e.value.values.toList()[0]),
|
||||
type: type,
|
||||
chartColor: colors[e.key],
|
||||
showColor: showChart,
|
||||
options: menuOptions,
|
||||
onTapEntry: onTapEntry,
|
||||
)).toList()
|
||||
);
|
||||
}
|
||||
|
|
239
lib/screens/home/top_items/top_items_lists.dart
Normal file
239
lib/screens/home/top_items/top_items_lists.dart
Normal file
|
@ -0,0 +1,239 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/screens/home/top_items/top_items.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';
|
||||
import 'package:adguard_home_manager/models/applied_filters.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
||||
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
|
||||
class TopItemsLists extends StatelessWidget {
|
||||
final List<HomeTopItems> order;
|
||||
|
||||
const TopItemsLists({
|
||||
super.key,
|
||||
required this.order,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
final logsProvider = Provider.of<LogsProvider>(context);
|
||||
|
||||
List<Widget> bottom = [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Divider(
|
||||
thickness: 1,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
];
|
||||
|
||||
void filterDomainLogs({required String value}) {
|
||||
logsProvider.setSearchText(value);
|
||||
logsProvider.setSelectedClients(null);
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: 'all',
|
||||
searchText: value,
|
||||
clients: null
|
||||
)
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
}
|
||||
|
||||
void filterClientLogs({required String value}) {
|
||||
logsProvider.setSearchText(null);
|
||||
logsProvider.setSelectedClients([value]);
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: 'all',
|
||||
searchText: null,
|
||||
clients: [value]
|
||||
)
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
}
|
||||
|
||||
void blockUnblock({required String domain, required String newStatus}) async {
|
||||
final ProcessModal processModal = ProcessModal();
|
||||
processModal.open(AppLocalizations.of(context)!.savingUserFilters);
|
||||
|
||||
final rules = await statusProvider.blockUnblockDomain(
|
||||
domain: domain,
|
||||
newStatus: newStatus
|
||||
);
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (!context.mounted) return;
|
||||
if (rules == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.userFilteringRulesUpdated,
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.userFilteringRulesNotUpdated,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void copyValueClipboard(value) {
|
||||
copyToClipboard(value: value, successMessage: AppLocalizations.of(context)!.copiedClipboard);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: order.asMap().entries.map((item) {
|
||||
switch (item.value) {
|
||||
case HomeTopItems.queriedDomains:
|
||||
return Column(
|
||||
children: [
|
||||
TopItems(
|
||||
label: AppLocalizations.of(context)!.topQueriedDomains,
|
||||
type: HomeTopItems.queriedDomains,
|
||||
data: statusProvider.serverStatus?.stats.topQueriedDomains ?? [],
|
||||
withChart: true,
|
||||
withProgressBar: true,
|
||||
buildValue: (v) => v.toString(),
|
||||
menuOptions: [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.blockDomain,
|
||||
icon: Icons.block_rounded,
|
||||
action: (v) => blockUnblock(domain: v.toString(), newStatus: 'block')
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
),
|
||||
],
|
||||
onTapEntry: (v) => filterDomainLogs(value: v.toString()),
|
||||
),
|
||||
if (item.key < order.length - 1) ...bottom
|
||||
],
|
||||
);
|
||||
|
||||
case HomeTopItems.blockedDomains:
|
||||
return Column(
|
||||
children: [
|
||||
TopItems(
|
||||
label: AppLocalizations.of(context)!.topBlockedDomains,
|
||||
type: HomeTopItems.blockedDomains,
|
||||
data: statusProvider.serverStatus?.stats.topBlockedDomains ?? [],
|
||||
withChart: true,
|
||||
withProgressBar: true,
|
||||
buildValue: (v) => v.toString(),
|
||||
menuOptions: [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.unblockDomain,
|
||||
icon: Icons.check_rounded,
|
||||
action: (v) => blockUnblock(domain: v, newStatus: 'unblock')
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
)
|
||||
],
|
||||
onTapEntry: (v) => filterDomainLogs(value: v),
|
||||
),
|
||||
if (item.key < order.length - 1) ...bottom
|
||||
],
|
||||
);
|
||||
|
||||
case HomeTopItems.recurrentClients:
|
||||
return Column(
|
||||
children: [
|
||||
TopItems(
|
||||
label: AppLocalizations.of(context)!.topClients,
|
||||
type: HomeTopItems.recurrentClients,
|
||||
data: statusProvider.serverStatus?.stats.topClients ?? [],
|
||||
withChart: true,
|
||||
withProgressBar: true,
|
||||
buildValue: (v) => v.toString(),
|
||||
menuOptions: [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
)
|
||||
],
|
||||
onTapEntry: (v) => filterClientLogs(value: v),
|
||||
),
|
||||
if (item.key < order.length - 1) ...bottom
|
||||
],
|
||||
);
|
||||
|
||||
case HomeTopItems.topUpstreams:
|
||||
return statusProvider.serverStatus!.stats.topUpstreamResponses != null
|
||||
? Column(
|
||||
children: [
|
||||
TopItems(
|
||||
label: AppLocalizations.of(context)!.topUpstreams,
|
||||
type: HomeTopItems.topUpstreams,
|
||||
data: statusProvider.serverStatus?.stats.topUpstreamResponses ?? [],
|
||||
withChart: true,
|
||||
withProgressBar: true,
|
||||
buildValue: (v) => v.toString(),
|
||||
menuOptions: [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
)
|
||||
],
|
||||
),
|
||||
if (item.key < order.length - 1) ...bottom
|
||||
],
|
||||
)
|
||||
: const SizedBox();
|
||||
|
||||
case HomeTopItems.avgUpstreamResponseTime:
|
||||
return statusProvider.serverStatus!.stats.topUpstreamsAvgTime != null
|
||||
? Column(
|
||||
children: [
|
||||
TopItems(
|
||||
label: AppLocalizations.of(context)!.averageUpstreamResponseTime,
|
||||
type: HomeTopItems.avgUpstreamResponseTime,
|
||||
data: statusProvider.serverStatus?.stats.topUpstreamsAvgTime ?? [],
|
||||
withChart: false,
|
||||
withProgressBar: false,
|
||||
buildValue: (v) => "${doubleFormat(v*1000, Platform.localeName)} ms",
|
||||
menuOptions: [
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: copyValueClipboard
|
||||
)
|
||||
],
|
||||
),
|
||||
if (item.key < order.length - 1) ...bottom
|
||||
],
|
||||
)
|
||||
: const SizedBox();
|
||||
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,9 +2,15 @@
|
|||
|
||||
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/domain_options.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
import 'package:adguard_home_manager/classes/process_modal.dart';
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/functions/copy_clipboard.dart';
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
import 'package:adguard_home_manager/functions/get_filtered_status.dart';
|
||||
import 'package:adguard_home_manager/models/logs.dart';
|
||||
|
@ -20,7 +26,7 @@ class LogTile extends StatelessWidget {
|
|||
final bool twoColumns;
|
||||
|
||||
const LogTile({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.log,
|
||||
required this.length,
|
||||
required this.index,
|
||||
|
@ -28,11 +34,12 @@ class LogTile extends StatelessWidget {
|
|||
required this.onLogTap,
|
||||
this.useAlwaysNormalTile,
|
||||
required this.twoColumns,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
|
||||
Widget logStatusWidget({
|
||||
required IconData icon,
|
||||
|
@ -83,16 +90,63 @@ class LogTile extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
void blockUnblock({required String domain, required String newStatus}) async {
|
||||
final ProcessModal processModal = ProcessModal();
|
||||
processModal.open(AppLocalizations.of(context)!.savingUserFilters);
|
||||
|
||||
final rules = await statusProvider.blockUnblockDomain(
|
||||
domain: domain,
|
||||
newStatus: newStatus
|
||||
);
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (!context.mounted) return;
|
||||
if (rules == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.userFilteringRulesUpdated,
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.userFilteringRulesNotUpdated,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final domainBlocked = isDomainBlocked(log.reason);
|
||||
|
||||
if (twoColumns && !(useAlwaysNormalTile == true)) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
child: DomainOptions(
|
||||
onTap: () => onLogTap(log),
|
||||
child: OptionsMenu(
|
||||
onTap: (_) => onLogTap(log),
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
item: log.question.name,
|
||||
isBlocked: isDomainBlocked(log.reason),
|
||||
options: [
|
||||
if (log.question.name != null) MenuOption(
|
||||
title: domainBlocked == true
|
||||
? AppLocalizations.of(context)!.unblockDomain
|
||||
: AppLocalizations.of(context)!.blockDomain,
|
||||
icon: domainBlocked == true
|
||||
? Icons.check_rounded
|
||||
: Icons.block_rounded,
|
||||
action: (_) => blockUnblock(
|
||||
domain: log.question.name!,
|
||||
newStatus: domainBlocked == true ? 'unblock' : 'block'
|
||||
)
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: (v) => copyToClipboard(value: v, successMessage: AppLocalizations.of(context)!.copiedClipboard)
|
||||
)
|
||||
],
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
|
@ -250,10 +304,27 @@ class LogTile extends StatelessWidget {
|
|||
else {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: DomainOptions(
|
||||
onTap: () => onLogTap(log),
|
||||
item: log.question.name,
|
||||
isBlocked: isDomainBlocked(log.reason),
|
||||
child: OptionsMenu(
|
||||
onTap: (_) => onLogTap(log),
|
||||
options: [
|
||||
if (log.question.name != null) MenuOption(
|
||||
title: domainBlocked == true
|
||||
? AppLocalizations.of(context)!.unblockDomain
|
||||
: AppLocalizations.of(context)!.blockDomain,
|
||||
icon: domainBlocked == true
|
||||
? Icons.check_rounded
|
||||
: Icons.block_rounded,
|
||||
action: (_) => blockUnblock(
|
||||
domain: log.question.name!,
|
||||
newStatus: domainBlocked == true ? 'unblock' : 'block'
|
||||
)
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
action: (v) => copyToClipboard(value: v, successMessage: AppLocalizations.of(context)!.copiedClipboard)
|
||||
)
|
||||
],
|
||||
child: Container(
|
||||
width: double.maxFinite,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:adguard_home_manager/models/logs.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:adguard_home_manager/screens/logs/logs_list.dart';
|
||||
import 'package:adguard_home_manager/screens/logs/details/log_details_screen.dart';
|
||||
|
||||
import 'package:adguard_home_manager/models/logs.dart';
|
||||
|
||||
class Logs extends StatefulWidget {
|
||||
const Logs({Key? key}) : super(key: key);
|
||||
|
||||
|
|
|
@ -72,7 +72,8 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
|
|||
String? validDataError;
|
||||
int certKeyValidApi = 0;
|
||||
|
||||
EncyptionValidation? certKeyValid;
|
||||
EncryptionValidation? certKeyValid;
|
||||
String? encryptionResultMessage;
|
||||
|
||||
bool formEdited = false;
|
||||
|
||||
|
@ -140,20 +141,27 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
|
|||
|
||||
if (!mounted) return;
|
||||
if (result.successful == true) {
|
||||
final data = result.content as EncyptionValidation;
|
||||
final data = result.content as EncryptionValidationResult;
|
||||
if (data.isObject == true) {
|
||||
final object = data.encryptionValidation!;
|
||||
setState(() {
|
||||
if (data.warningValidation != null && data.warningValidation != '') {
|
||||
if (object.warningValidation != null && object.warningValidation != '') {
|
||||
certKeyValidApi = 2;
|
||||
validDataError = data.warningValidation;
|
||||
validDataError = object.warningValidation;
|
||||
}
|
||||
else {
|
||||
certKeyValidApi = 1;
|
||||
validDataError = null;
|
||||
}
|
||||
certKeyValid = data;
|
||||
certKeyValid = object;
|
||||
});
|
||||
}
|
||||
else {
|
||||
setState(() {
|
||||
encryptionResultMessage = data.message;
|
||||
certKeyValidApi = 2;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,11 +261,13 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
|
|||
centerTitle: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: certKeyValidApi == 2 && validDataError != null
|
||||
onPressed: certKeyValidApi == 2 && (validDataError != null || encryptionResultMessage != null)
|
||||
? () => {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => EncryptionErrorModal(error: validDataError!)
|
||||
builder: (context) => EncryptionErrorModal(
|
||||
error: validDataError ?? encryptionResultMessage ?? AppLocalizations.of(context)!.unknownError
|
||||
)
|
||||
)
|
||||
} : null,
|
||||
icon: generateStatus(context, appConfigProvider, localValidationValid, certKeyValidApi, formEdited),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:adguard_home_manager/functions/desktop_mode.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -9,15 +8,16 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|||
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
|
||||
import 'package:adguard_home_manager/functions/desktop_mode.dart';
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class ItemData {
|
||||
class _ItemData {
|
||||
final HomeTopItems title;
|
||||
final Key key;
|
||||
|
||||
const ItemData({
|
||||
const _ItemData({
|
||||
required this.title,
|
||||
required this.key
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ enum DraggingMode {
|
|||
}
|
||||
|
||||
class ReorderableTopItemsHome extends StatefulWidget {
|
||||
const ReorderableTopItemsHome({Key? key}) : super(key: key);
|
||||
const ReorderableTopItemsHome({super.key});
|
||||
|
||||
@override
|
||||
State<ReorderableTopItemsHome> createState() => _ReorderableTopItemsHomeState();
|
||||
|
@ -38,10 +38,10 @@ class ReorderableTopItemsHome extends StatefulWidget {
|
|||
class _ReorderableTopItemsHomeState extends State<ReorderableTopItemsHome> {
|
||||
List<HomeTopItems> homeTopItemsList = [];
|
||||
List<HomeTopItems> persistHomeTopItemsList = [];
|
||||
List<ItemData> renderItems = [];
|
||||
List<_ItemData> renderItems = [];
|
||||
|
||||
int _indexOfKey(Key key) {
|
||||
return renderItems.indexWhere((ItemData d) => d.key == key);
|
||||
return renderItems.indexWhere((_ItemData d) => d.key == key);
|
||||
}
|
||||
|
||||
bool _reorderCallback(Key item, Key newPosition) {
|
||||
|
@ -79,7 +79,7 @@ class _ReorderableTopItemsHomeState extends State<ReorderableTopItemsHome> {
|
|||
homeTopItemsList = appConfigProvider.homeTopItemsOrder;
|
||||
persistHomeTopItemsList = appConfigProvider.homeTopItemsOrder;
|
||||
renderItems = appConfigProvider.homeTopItemsOrder.asMap().entries.map(
|
||||
(e) => ItemData(
|
||||
(e) => _ItemData(
|
||||
key: ValueKey(e.key),
|
||||
title: e.value,
|
||||
)
|
||||
|
@ -117,16 +117,31 @@ class _ReorderableTopItemsHomeState extends State<ReorderableTopItemsHome> {
|
|||
padding: const EdgeInsets.all(16)
|
||||
);
|
||||
|
||||
case HomeTopItems.topUpstreams:
|
||||
return CustomListTile(
|
||||
title: AppLocalizations.of(context)!.topUpstreams,
|
||||
icon: Icons.upload_file_rounded,
|
||||
padding: const EdgeInsets.all(16)
|
||||
);
|
||||
|
||||
case HomeTopItems.avgUpstreamResponseTime:
|
||||
return CustomListTile(
|
||||
title: AppLocalizations.of(context)!.averageUpstreamResponseTime,
|
||||
icon: Icons.timer_rounded,
|
||||
padding: const EdgeInsets.all(16)
|
||||
);
|
||||
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> onWillPopScope() async {
|
||||
Future<bool> onWillPopScope(bool popInvoked) async {
|
||||
if (!listEquals(appConfigProvider.homeTopItemsOrder, persistHomeTopItemsList)) {
|
||||
showDialog(
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (dialogContext) => AlertDialog(
|
||||
useRootNavigator: false,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.discardChanges),
|
||||
content: Text(AppLocalizations.of(context)!.discardChangesDescription),
|
||||
actions: [
|
||||
|
@ -135,14 +150,14 @@ class _ReorderableTopItemsHomeState extends State<ReorderableTopItemsHome> {
|
|||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(dialogContext);
|
||||
Navigator.pop(context);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.confirm)
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(dialogContext),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(AppLocalizations.of(context)!.cancel)
|
||||
),
|
||||
],
|
||||
|
@ -175,8 +190,9 @@ class _ReorderableTopItemsHomeState extends State<ReorderableTopItemsHome> {
|
|||
}
|
||||
}
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: onWillPopScope,
|
||||
return PopScope(
|
||||
canPop: listEquals(appConfigProvider.homeTopItemsOrder, persistHomeTopItemsList),
|
||||
onPopInvoked: onWillPopScope,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.topItemsOrder),
|
||||
|
@ -219,7 +235,7 @@ class _ReorderableTopItemsHomeState extends State<ReorderableTopItemsHome> {
|
|||
child: ListView.builder(
|
||||
itemBuilder: (context, index) => reorderable_list_library.ReorderableItem(
|
||||
key: renderItems[index].key,
|
||||
childBuilder: (context, state) => Item(
|
||||
childBuilder: (context, state) => _Item(
|
||||
tileWidget: tile(renderItems[index].title),
|
||||
isFirst: index == 0,
|
||||
isLast: index == renderItems.length - 1,
|
||||
|
@ -237,19 +253,18 @@ class _ReorderableTopItemsHomeState extends State<ReorderableTopItemsHome> {
|
|||
}
|
||||
}
|
||||
|
||||
class Item extends StatelessWidget {
|
||||
class _Item extends StatelessWidget {
|
||||
final Widget tileWidget;
|
||||
final bool isFirst;
|
||||
final bool isLast;
|
||||
final reorderable_list_library.ReorderableItemState state;
|
||||
|
||||
const Item({
|
||||
Key? key,
|
||||
const _Item({
|
||||
required this.tileWidget,
|
||||
required this.isFirst,
|
||||
required this.isLast,
|
||||
required this.state,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -7,29 +7,37 @@ import 'package:percent_indicator/percent_indicator.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/domain_options.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
|
||||
import 'package:adguard_home_manager/models/applied_filters.dart';
|
||||
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/functions/number_format.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class TopItemsScreen extends StatefulWidget {
|
||||
final String type;
|
||||
final HomeTopItems type;
|
||||
final String title;
|
||||
final bool? isClient;
|
||||
final List<Map<String, dynamic>> data;
|
||||
final bool withProgressBar;
|
||||
final String Function(dynamic) buildValue;
|
||||
final List<MenuOption> menuOptions;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
|
||||
const TopItemsScreen({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.title,
|
||||
this.isClient,
|
||||
required this.data,
|
||||
}) : super(key: key);
|
||||
required this.withProgressBar,
|
||||
required this.buildValue,
|
||||
required this.menuOptions,
|
||||
this.onTapEntry,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TopItemsScreen> createState() => _TopItemsScreenState();
|
||||
|
@ -58,11 +66,10 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
|||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
final logsProvider = Provider.of<LogsProvider>(context);
|
||||
|
||||
int total = 0;
|
||||
double total = 0;
|
||||
for (var element in data) {
|
||||
total = total + int.parse(element.values.toList()[0].toString());
|
||||
total = total + double.parse(element.values.toList()[0].toString());
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
|
@ -152,42 +159,19 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
return DomainOptions(
|
||||
item: screenData[index].keys.toList()[0],
|
||||
isBlocked: widget.type == 'topBlockedDomains',
|
||||
isClient: widget.type == 'topClients',
|
||||
onTap: () {
|
||||
if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') {
|
||||
logsProvider.setSearchText(screenData[index].keys.toList()[0]);
|
||||
logsProvider.setSelectedClients(null);
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: 'all',
|
||||
searchText: screenData[index].keys.toList()[0],
|
||||
clients: null
|
||||
)
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
return OptionsMenu(
|
||||
value: screenData[index].keys.toList()[0],
|
||||
options: widget.menuOptions,
|
||||
onTap: widget.onTapEntry != null
|
||||
? (v) {
|
||||
widget.onTapEntry!(v);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
else if (widget.type == 'topClients') {
|
||||
logsProvider.setSearchText(null);
|
||||
logsProvider.setSelectedClients([screenData[index].keys.toList()[0]]);
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: 'all',
|
||||
searchText: null,
|
||||
clients: [screenData[index].keys.toList()[0]]
|
||||
)
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
: null,
|
||||
child: CustomListTile(
|
||||
title: screenData[index].keys.toList()[0],
|
||||
trailing: Text(
|
||||
screenData[index].values.toList()[0].toString(),
|
||||
widget.buildValue(screenData[index].values.toList()[0]),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
|
@ -205,7 +189,7 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
|||
),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
Row(
|
||||
if (widget.withProgressBar == true) Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 50,
|
||||
|
|
|
@ -7,28 +7,35 @@ import 'package:percent_indicator/percent_indicator.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/domain_options.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
|
||||
import 'package:adguard_home_manager/models/applied_filters.dart';
|
||||
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
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';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class TopItemsModal extends StatefulWidget {
|
||||
final String type;
|
||||
final HomeTopItems type;
|
||||
final String title;
|
||||
final bool? isClient;
|
||||
final List<Map<String, dynamic>> data;
|
||||
final bool withProgressBar;
|
||||
final String Function(dynamic) buildValue;
|
||||
final List<MenuOption> options;
|
||||
final void Function(dynamic)? onTapEntry;
|
||||
|
||||
const TopItemsModal({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.title,
|
||||
this.isClient,
|
||||
required this.data,
|
||||
}) : super(key: key);
|
||||
required this.withProgressBar,
|
||||
required this.buildValue,
|
||||
required this.options,
|
||||
this.onTapEntry,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TopItemsModal> createState() => _TopItemsModalState();
|
||||
|
@ -56,12 +63,10 @@ class _TopItemsModalState extends State<TopItemsModal> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
final logsProvider = Provider.of<LogsProvider>(context);
|
||||
|
||||
int total = 0;
|
||||
double total = 0;
|
||||
for (var element in data) {
|
||||
total = total + int.parse(element.values.toList()[0].toString());
|
||||
total = total + double.parse(element.values.toList()[0].toString());
|
||||
}
|
||||
|
||||
return Dialog(
|
||||
|
@ -128,42 +133,19 @@ class _TopItemsModalState extends State<TopItemsModal> {
|
|||
}
|
||||
}
|
||||
|
||||
return DomainOptions(
|
||||
isBlocked: widget.type == 'topBlockedDomains',
|
||||
isClient: widget.type == 'topClients',
|
||||
item: screenData[index].keys.toList()[0],
|
||||
onTap: () {
|
||||
if (widget.type == 'topQueriedDomains' || widget.type == 'topBlockedDomains') {
|
||||
logsProvider.setSearchText(screenData[index].keys.toList()[0]);
|
||||
logsProvider.setSelectedClients(null);
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: 'all',
|
||||
searchText: screenData[index].keys.toList()[0],
|
||||
clients: null
|
||||
)
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
return OptionsMenu(
|
||||
options: widget.options,
|
||||
value: screenData[index].keys.toList()[0],
|
||||
onTap: widget.onTapEntry != null
|
||||
? (v) {
|
||||
widget.onTapEntry!(v);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
else if (widget.type == 'topClients') {
|
||||
logsProvider.setSearchText(null);
|
||||
logsProvider.setSelectedClients([screenData[index].keys.toList()[0]]);
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: 'all',
|
||||
searchText: null,
|
||||
clients: [screenData[index].keys.toList()[0]]
|
||||
)
|
||||
);
|
||||
appConfigProvider.setSelectedScreen(2);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
: null,
|
||||
child: CustomListTile(
|
||||
title: screenData[index].keys.toList()[0],
|
||||
trailing: Text(
|
||||
screenData[index].values.toList()[0].toString(),
|
||||
widget.buildValue(screenData[index].values.toList()[0]),
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||
),
|
||||
|
@ -181,7 +163,7 @@ class _TopItemsModalState extends State<TopItemsModal> {
|
|||
),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
Row(
|
||||
if (widget.withProgressBar == true) Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 50,
|
||||
|
|
|
@ -698,9 +698,18 @@ class ApiClientV2 {
|
|||
try {
|
||||
return ApiResponse(
|
||||
successful: result.successful,
|
||||
content: result.body != null
|
||||
? EncyptionValidation.fromJson(jsonDecode(result.body!))
|
||||
: null
|
||||
content: result.body != null ? EncryptionValidationResult(
|
||||
isObject: false,
|
||||
encryptionValidation: EncryptionValidation.fromJson(jsonDecode(result.body!))
|
||||
) : null
|
||||
);
|
||||
} on FormatException {
|
||||
return ApiResponse(
|
||||
successful: result.successful,
|
||||
content: result.body != null ? EncryptionValidationResult(
|
||||
isObject: false,
|
||||
message: result.body
|
||||
) : null
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
Sentry.captureException(e, stackTrace: stackTrace);
|
||||
|
|
|
@ -6,11 +6,11 @@ class CustomListTileDialog extends StatelessWidget {
|
|||
final void Function()? onTap;
|
||||
|
||||
const CustomListTileDialog({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.title,
|
||||
this.icon,
|
||||
this.onTap
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:contextmenu/contextmenu.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/options_modal.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/classes/process_modal.dart';
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
|
||||
class DomainOptions extends StatelessWidget {
|
||||
final bool isBlocked;
|
||||
final bool? isClient;
|
||||
final String? item;
|
||||
final Widget child;
|
||||
final void Function() onTap;
|
||||
final BorderRadius? borderRadius;
|
||||
|
||||
const DomainOptions({
|
||||
Key? key,
|
||||
required this.isBlocked,
|
||||
this.isClient,
|
||||
required this.item,
|
||||
required this.child,
|
||||
required this.onTap,
|
||||
this.borderRadius
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
|
||||
void blockUnblock(String domain, String newStatus) async {
|
||||
final ProcessModal processModal = ProcessModal();
|
||||
processModal.open(AppLocalizations.of(context)!.savingUserFilters);
|
||||
|
||||
final rules = await statusProvider.blockUnblockDomain(
|
||||
domain: domain,
|
||||
newStatus: newStatus
|
||||
);
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (rules == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.userFilteringRulesUpdated,
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.userFilteringRulesNotUpdated,
|
||||
color: Colors.red
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void copyDomainClipboard(String domain) async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: domain)
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.domainCopiedClipboard),
|
||||
backgroundColor: Colors.green,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
List<MenuOption> generateOptions() {
|
||||
return [
|
||||
if (isClient != true && isBlocked == true) MenuOption(
|
||||
title: AppLocalizations.of(context)!.unblock,
|
||||
icon: Icons.check,
|
||||
action: () => blockUnblock(item!, 'unblock')
|
||||
),
|
||||
if (isClient != true && isBlocked == false) MenuOption(
|
||||
title: AppLocalizations.of(context)!.block,
|
||||
icon: Icons.block,
|
||||
action: () => blockUnblock(item!, 'block')
|
||||
),
|
||||
MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy,
|
||||
action: () => copyDomainClipboard(item!)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
void openOptionsModal() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => OptionsModal(
|
||||
options: generateOptions(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (item != null) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
borderRadius: borderRadius,
|
||||
child: ContextMenuArea(
|
||||
builder: (context) => generateOptions().map((opt) => CustomListTile(
|
||||
title: opt.title,
|
||||
icon: opt.icon,
|
||||
onTap: () {
|
||||
opt.action();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)).toList(),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
onLongPress: openOptionsModal,
|
||||
borderRadius: borderRadius,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,75 @@
|
|||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:contextmenu/contextmenu.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile_dialog.dart';
|
||||
import 'package:adguard_home_manager/widgets/custom_list_tile.dart';
|
||||
|
||||
class OptionsModal extends StatelessWidget {
|
||||
import 'package:adguard_home_manager/models/menu_option.dart';
|
||||
|
||||
class OptionsMenu extends StatelessWidget {
|
||||
final Widget child;
|
||||
final List<MenuOption> options;
|
||||
final dynamic value;
|
||||
final BorderRadius? borderRadius;
|
||||
final void Function(dynamic)? onTap;
|
||||
|
||||
const OptionsModal({
|
||||
const OptionsMenu({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.options,
|
||||
this.value,
|
||||
this.borderRadius,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
void openOptionsModal() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => _OptionsModal(
|
||||
options: options,
|
||||
value: value
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: ContextMenuArea(
|
||||
builder: (context) => options.map((opt) => CustomListTile(
|
||||
title: opt.title,
|
||||
icon: opt.icon,
|
||||
onTap: () {
|
||||
opt.action(value);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
)).toList(),
|
||||
child: InkWell(
|
||||
onTap: onTap != null
|
||||
? () => onTap!(value)
|
||||
: null,
|
||||
onLongPress: (Platform.isAndroid || Platform.isIOS)
|
||||
? () => openOptionsModal()
|
||||
: null,
|
||||
borderRadius: borderRadius,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OptionsModal extends StatelessWidget {
|
||||
final List<MenuOption> options;
|
||||
final dynamic value;
|
||||
|
||||
const _OptionsModal({
|
||||
required this.options,
|
||||
this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -43,7 +103,7 @@ class OptionsModal extends StatelessWidget {
|
|||
icon: opt.icon,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
opt.action();
|
||||
opt.action(value);
|
||||
},
|
||||
)).toList()
|
||||
),
|
34
pubspec.lock
34
pubspec.lock
|
@ -17,6 +17,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
ansicolor:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ansicolor
|
||||
sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -133,10 +141,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419"
|
||||
sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.0"
|
||||
version: "9.1.1"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -197,10 +205,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: fl_chart
|
||||
sha256: "6b9eb2b3017241d05c482c01f668dd05cc909ec9a0114fdd49acd958ff2432fa"
|
||||
sha256: "5a74434cc83bf64346efb562f1a06eefaf1bcb530dc3d96a104f631a1eff8d79"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.64.0"
|
||||
version: "0.65.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -263,10 +271,10 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_native_splash
|
||||
sha256: d93394f22f73e810bda59e11ebe83329c5511d6460b6b7509c4e1f3c92d6d625
|
||||
sha256: c4d899312b36e7454bedfd0a4740275837b99e532d81c8477579d8183db1de6c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.5"
|
||||
version: "2.3.6"
|
||||
flutter_reorderable_list:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -426,10 +434,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
|
||||
sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "5.0.1"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -514,18 +522,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: sentry
|
||||
sha256: e8040183ea1a0323999bce69786ed8429b1b89fbe5815a504d5bb7493a6464cc
|
||||
sha256: e7ded42974bac5f69e4ca4ddc57d30499dd79381838f24b7e8fd9aa4139e7b79
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.13.1"
|
||||
version: "7.13.2"
|
||||
sentry_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sentry_flutter
|
||||
sha256: "843e317e605e5860e30ae431e9b8724c54f1a8567c9ad495e04595926bf22376"
|
||||
sha256: d6f55ec7a1f681784165021f749007712a72ff57eadf91e963331b6ae326f089
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.13.1"
|
||||
version: "7.13.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -813,5 +821,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.2.0-194.0.dev <4.0.0"
|
||||
dart: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.13.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.11.3+108
|
||||
version: 2.12.0-beta.2+110
|
||||
|
||||
environment:
|
||||
sdk: '>=2.18.1 <3.0.0'
|
||||
|
@ -41,14 +41,14 @@ dependencies:
|
|||
intl: any
|
||||
provider: ^6.1.1
|
||||
sqflite: ^2.3.0
|
||||
package_info_plus: ^4.2.0
|
||||
package_info_plus: ^5.0.1
|
||||
flutter_displaymode: ^0.6.0
|
||||
dynamic_color: ^1.6.8
|
||||
animations: ^2.0.8
|
||||
device_info_plus: ^9.1.0
|
||||
device_info_plus: ^9.1.1
|
||||
uuid: ^4.2.1
|
||||
expandable: ^5.0.1
|
||||
fl_chart: ^0.64.0
|
||||
fl_chart: ^0.65.0
|
||||
flutter_web_browser: ^0.17.1
|
||||
flutter_svg: ^2.0.9
|
||||
percent_indicator: ^4.2.3
|
||||
|
@ -70,7 +70,7 @@ dependencies:
|
|||
url_launcher: ^6.1.11
|
||||
contextmenu: ^3.0.0
|
||||
async: ^2.11.0
|
||||
sentry_flutter: ^7.13.1
|
||||
sentry_flutter: ^7.13.2
|
||||
flutter_dotenv: ^5.1.0
|
||||
flutter_reorderable_list: ^1.3.1
|
||||
pie_chart: ^5.4.0
|
||||
|
@ -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.2.10+1
|
||||
flutter_native_splash: ^2.3.6
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
|
Loading…
Add table
Reference in a new issue