mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-06-28 20:09:51 +00:00
Merge branch 'beta'
This commit is contained in:
commit
ecc9cf1073
19 changed files with 926 additions and 522 deletions
|
@ -779,5 +779,17 @@
|
|||
"enablePlainDnsDescription": "Plain DNS is enabled by default. You can disable it to force all devices to use encrypted DNS. To do this, you must enable at least one encrypted DNS protocol.",
|
||||
"date": "Date",
|
||||
"loadingChangelog": "Loading changelog...",
|
||||
"invalidIpOrUrl": "Invalid IP address or URL"
|
||||
"invalidIpOrUrl": "Invalid IP address or URL",
|
||||
"addPersistentClient": "Add as a persistent client",
|
||||
"blockThisClientOnly": "Block for this client only",
|
||||
"unblockThisClientOnly": "Unblock for this client only",
|
||||
"domainBlockedThisClient": "{domain} blocked for this client",
|
||||
"domainUnblockedThisClient": "{domain} unblocked for this client",
|
||||
"disallowThisClient": "Disallow this client",
|
||||
"allowThisClient": "Allow this client",
|
||||
"clientAllowedSuccessfully": "Client allowed successfully",
|
||||
"clientDisallowedSuccessfully": "Client disallowed successfully",
|
||||
"changesNotSaved": "Changes could not be saved",
|
||||
"allowingClient": "Allowing client...",
|
||||
"disallowingClient": "Disallowing client..."
|
||||
}
|
|
@ -779,5 +779,17 @@
|
|||
"enablePlainDnsDescription": "El DNS simple (sin cifrado) está activado de forma predeterminada. Puedes desactivarlo para obligar a todos los dispositivos a utilizar DNS cifrado. Para ello, debes habilitar al menos un protocolo DNS cifrado.",
|
||||
"date": "Fecha",
|
||||
"loadingChangelog": "Cargando registro de cambios...",
|
||||
"invalidIpOrUrl": "Dirección IP o URL no válida"
|
||||
"invalidIpOrUrl": "Dirección IP o URL no válida",
|
||||
"addPersistentClient": "Añadir como cliente persistente",
|
||||
"blockThisClientOnly": "Bloquear sólo para este cliente",
|
||||
"unblockThisClientOnly": "Desbloquear sólo para este cliente",
|
||||
"domainBlockedThisClient": "{domain} bloqueado para este cliente",
|
||||
"domainUnblockedThisClient": "{domain} desbloqueado para este cliente",
|
||||
"disallowThisClient": "No permitir este cliente",
|
||||
"allowThisClient": "Permitir este cliente",
|
||||
"clientAllowedSuccessfully": "Cliente permitido correctamente",
|
||||
"clientDisallowedSuccessfully": "Cliente no permitido correctamente",
|
||||
"changesNotSaved": "Los cambios no han podido ser guardados",
|
||||
"allowingClient": "Permitiendo cliente...",
|
||||
"disallowingClient": "No permitiendo cliente..."
|
||||
}
|
|
@ -75,7 +75,7 @@
|
|||
"userNotEmpty": "Kullanıcı adı boş bırakılamaz",
|
||||
"passwordNotEmpty": "Şifre boş bırakılamaz",
|
||||
"examplePath": "Örnek: /adguard",
|
||||
"helperPath": "Ters proxy kullanıyorsanız",
|
||||
"helperPath": "Eğer ters proxy kullanıyorsanız",
|
||||
"aboutApp": "Uygulama hakkında",
|
||||
"appVersion": "Uygulama sürümü",
|
||||
"createdBy": "Geliştirici",
|
||||
|
@ -103,7 +103,7 @@
|
|||
"logsCopiedClipboard": "Günlükler panoya kopyalandı",
|
||||
"advancedSettings": "Gelişmiş ayarlar",
|
||||
"dontCheckCertificate": "SSL sertifikasını kontrol etme",
|
||||
"dontCheckCertificateDescription": "Sunucunun SSL sertifikası doğrulamasını geçersiz kılar",
|
||||
"dontCheckCertificateDescription": "Sunucunun SSL sertifikası doğrulamasını geçersiz kılar.",
|
||||
"advancedSetupDescription": "Gelişmiş seçenekleri yönet",
|
||||
"settingsUpdatedSuccessfully": "Ayarlar başarıyla güncellendi.",
|
||||
"cannotUpdateSettings": "Ayarlar güncellenemiyor.",
|
||||
|
@ -131,11 +131,11 @@
|
|||
"rewrite": "Yeniden Yaz",
|
||||
"status": "Durum",
|
||||
"result": "Sonuç",
|
||||
"time": "Zaman",
|
||||
"time": "Saat",
|
||||
"blocklist": "Engelleme Listesi",
|
||||
"request": "İstek",
|
||||
"domain": "Alan adı",
|
||||
"type": "Tip",
|
||||
"type": "Tür",
|
||||
"clas": "Sınıf",
|
||||
"response": "Yanıt",
|
||||
"dnsServer": "DNS sunucusu",
|
||||
|
@ -204,7 +204,7 @@
|
|||
"noUpstreamServers": "Üst sunucu yok.",
|
||||
"willBeUsedGeneralServers": "Genel üst sunucular kullanılacak.",
|
||||
"added": "Eklenenler",
|
||||
"clientUpdatedSuccessfully": "İstemci başarıyla güncellendi",
|
||||
"clientUpdatedSuccessfully": "İstemci ayarları başarıyla güncellendi",
|
||||
"clientNotUpdated": "İstemci güncellenemedi",
|
||||
"clientDeletedSuccessfully": "İstemci başarıyla kaldırıldı",
|
||||
"clientNotDeleted": "İstemci silinemedi",
|
||||
|
@ -308,8 +308,8 @@
|
|||
"addImportant": "Başına $important ekle",
|
||||
"howCreateRules": "Özel kurallar nasıl oluşturulur?",
|
||||
"examples": "Örnekler",
|
||||
"example1": "example.org (ornek.org) ve tüm alt alan adlarına erişimi engeller.",
|
||||
"example2": "example.org (ornek.org) ve tüm alt alan adlarına erişimi engellemeyi kaldırır.",
|
||||
"example1": "example.org ve tüm alt alan adlarına erişimi engeller.",
|
||||
"example2": "example.org ve tüm alt alan adlarına erişimi engellemeyi kaldırır.",
|
||||
"example3": "Yorum ekler.",
|
||||
"example4": "Belirtilen düzenli ifadeye uyan alan adlarına erişimi engeller.",
|
||||
"moreInformation": "Daha fazla bilgi",
|
||||
|
@ -418,7 +418,7 @@
|
|||
"logSettingsNotLoaded": "Günlük ayarları yüklenemedi.",
|
||||
"updatingSettings": "Ayarlar güncelleniyor...",
|
||||
"logsConfigUpdated": "Günlük ayarları başarıyla güncellendi",
|
||||
"logsConfigNotUpdated": "Günlük ayarları başarıyla güncellendi",
|
||||
"logsConfigNotUpdated": "Günlük ayarları güncellenemedi",
|
||||
"deletingLogs": "Günlükler temizleniyor...",
|
||||
"logsCleared": "Günlükler başarıyla temizlendi",
|
||||
"logsNotCleared": "Günlükler temizlenemedi",
|
||||
|
@ -436,7 +436,7 @@
|
|||
"parallelRequests": "Paralel istekler",
|
||||
"fastestIpAddress": "En hızlı IP adresi",
|
||||
"loadBalancingDescription": "Her seferinde bir üst sunucuya sorgu yapar. 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 sunucuları aynı anda sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanır.",
|
||||
"parallelRequestsDescription": "Tüm üst sunucuları aynı anda sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanılır.",
|
||||
"fastestIpAddressDescription": "Tüm DNS sunucularına sorgu yapın ve tüm yanıtlar arasında en hızlı IP adresini döndürür. 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 kaynaklarda belirttiğiniz DoH/DoT çözümleyicilerinin IP adreslerini çözmek için kullanılır.",
|
||||
|
@ -469,7 +469,7 @@
|
|||
"customIpDescription": "Manuel olarak ayarlanmış bir IP adresi ile yanıt verir.",
|
||||
"dnsCacheConfig": "DNS önbellek yapılandırması",
|
||||
"cacheSize": "Önbellek boyutu",
|
||||
"inBytes": "Alınacak önbelleğin boyutunu ayarla (bayt olarak)",
|
||||
"inBytes": "Alınacak önbelleğin boyutunu ayarla (Bayt olarak)",
|
||||
"overrideMinimumTtl": "Minimum kullanım süresini geçersiz kıl",
|
||||
"overrideMinimumTtlDescription": "DNS yanıtlarını önbelleğe alırken üst sunucudan alınan minimum kullanım süresi değerini (TTL) saniye olarak ayarlayın.",
|
||||
"overrideMaximumTtl": "Maksimum kullanım süresini geçersiz kıl",
|
||||
|
@ -494,7 +494,7 @@
|
|||
"dnsCacheConfigDescription": "Sunucunun DNS önbelleğini nasıl yöneteceğini yapılandır",
|
||||
"comment": "Yorum",
|
||||
"address": "Adres",
|
||||
"commentsDescription": "Yorumlar her zaman # işareti ile başlar. Onu eklemenize gerek yok, otomatik olarak eklenir.",
|
||||
"commentsDescription": "Yorumlar her zaman # işareti ile başlar. Eklemenize gerek yok, otomatik olarak eklenecektir.",
|
||||
"encryptionSettings": "Şifreleme ayarları",
|
||||
"encryptionSettingsDescription": "Şifreleme (HTTPS/QUIC/TLS) desteği",
|
||||
"loadingEncryptionSettings": "Şifreleme ayarları yükleniyor...",
|
||||
|
@ -560,8 +560,8 @@
|
|||
"validPrivateKey": "Geçerli özel anahtar",
|
||||
"expirationDate": "Son kullanma tarihi",
|
||||
"keysNotMatch": "Geçersiz bir sertifika veya anahtar: tls: özel anahtar genel anahtarla eşleşmiyor.",
|
||||
"timeLogs": "Günlüklerde işlem süresi göster",
|
||||
"timeLogsDescription": "Günlükler listesinde zaman yerine işlem süresini göster.",
|
||||
"timeLogs": "Günlüklerde işlem süresini göster",
|
||||
"timeLogsDescription": "Günlükler listesinde zaman yerine işlem süresini gösterir.",
|
||||
"hostNames": "Ana bilgisayar adları",
|
||||
"keyType": "Anahtar türü",
|
||||
"updateAvailable": "Güncelleme mevcut",
|
||||
|
@ -626,7 +626,7 @@
|
|||
"appUpdates": "Uygulama güncellemeleri",
|
||||
"usingLatestVersion": "En son sürümü kullanıyorsunuz",
|
||||
"ipLogs": "Günlüklerde IP adresini göster",
|
||||
"ipLogsDescription": "Günlükler listesinde istemci adı yerine IP adresini göster.",
|
||||
"ipLogsDescription": "Günlükler listesinde istemci adı yerine IP adresini gösterir.",
|
||||
"application": "Uygulama",
|
||||
"combinedChart": "Birleştirilmiş grafik",
|
||||
"combinedChartDescription": "Tüm grafikleri bir araya getirir.",
|
||||
|
@ -688,7 +688,7 @@
|
|||
"yourVersion": "Yüklü sürüm: {version}",
|
||||
"minimumRequiredVersion": "Gerekli minimum sürüm: {version}",
|
||||
"topUpstreams": "Öne çıkan DNS sunucuları",
|
||||
"averageUpstreamResponseTime": "DNS sunucusu ortalama işlem süresi" ,
|
||||
"averageUpstreamResponseTime": "DNS sunucuları işlem süresi" ,
|
||||
"dhcpNotAvailable": "DHCP sunucusu kullanılamıyor.",
|
||||
"osServerInstalledIncompatible": "AdGuard Home, işletim sisteminizde DHCP sunucusu çalıştıramıyor.",
|
||||
"resetSettings": "Ayarları sıfırla",
|
||||
|
@ -721,7 +721,7 @@
|
|||
"unblockingClient": "İstemci engeli kaldırılıyor...",
|
||||
"upstreamDnsCacheConfiguration": "DNS önbellek yapılandırması",
|
||||
"enableDnsCachingClient": "Bu istemci için DNS önbelleğe almayı etkinleştir",
|
||||
"dnsCacheSize": "DNS önbellek boyutu (bayt cinsinden)",
|
||||
"dnsCacheSize": "DNS önbellek boyutu (Bayt cinsinden)",
|
||||
"nameInvalid": "Ad gereklidir",
|
||||
"oneIdentifierRequired": "En az bir tanımlayıcı gereklidir",
|
||||
"dnsCacheNumber": "DNS önbellek boyutu bir rakam içermelidir",
|
||||
|
@ -754,7 +754,9 @@
|
|||
"statisticsSettingsDescription": "İstatistikler için veri toplamayı yapılandır",
|
||||
"loadingStatisticsSettings": "İstatistik ayarları yükleniyor...",
|
||||
"statisticsSettingsLoadError": "İstatistik ayarları yüklenirken bir hata oluştu.",
|
||||
"customTimeInHours": "Özel zaman (saat olarak)",
|
||||
"statisticsConfigUpdated": "İstatistik ayarları başarıyla güncellendi",
|
||||
"statisticsConfigNotUpdated": "İstatistik ayarları güncellenemedi",
|
||||
"customTimeInHours": "Özel zaman (Saat olarak)",
|
||||
"invalidTime": "Geçersiz zaman",
|
||||
"removeDomain": "Alan adını kaldır",
|
||||
"addDomain": "Alan adı ekle",
|
||||
|
@ -774,5 +776,8 @@
|
|||
"showHide": "Göster/gizle",
|
||||
"noElementsReorderMessage": "Burada yeniden sıralamak için göster/gizle sekmesindeki bazı öğeleri etkinleştirin.",
|
||||
"enablePlainDns": "Düz DNS'i etkinleştir",
|
||||
"enablePlainDnsDescription": "Düz DNS varsayılan olarak etkindir. Tüm aygıtları şifrelenmiş DNS kullanmaya zorlamak için bunu devre dışı bırakabilirsiniz. Bunu yapmak için en az bir şifrelenmiş DNS protokolünü etkinleştirmeniz gerekir."
|
||||
"enablePlainDnsDescription": "Düz DNS varsayılan olarak etkindir. Tüm aygıtları şifrelenmiş DNS kullanmaya zorlamak için bunu devre dışı bırakabilirsiniz. Bunu yapmak için en az bir şifrelenmiş DNS protokolünü etkinleştirmeniz gerekir.",
|
||||
"date": "Tarih",
|
||||
"loadingChangelog": "Değişiklikler yükleniyor...",
|
||||
"invalidIpOrUrl": "Geçersiz IP adresi veya URL"
|
||||
}
|
|
@ -10,6 +10,16 @@ import 'package:adguard_home_manager/models/safe_search.dart';
|
|||
import 'package:adguard_home_manager/providers/clients_provider.dart';
|
||||
import 'package:adguard_home_manager/models/clients.dart';
|
||||
|
||||
class ClientInitialData {
|
||||
final String name;
|
||||
final String ip;
|
||||
|
||||
const ClientInitialData({
|
||||
required this.name,
|
||||
required this.ip,
|
||||
});
|
||||
}
|
||||
|
||||
class ControllerListItem {
|
||||
final String id;
|
||||
final TextEditingController controller;
|
||||
|
@ -25,13 +35,15 @@ class ClientScreen extends StatefulWidget {
|
|||
final void Function(Client) onConfirm;
|
||||
final void Function(Client)? onDelete;
|
||||
final bool fullScreen;
|
||||
final ClientInitialData? initialData;
|
||||
|
||||
const ClientScreen({
|
||||
super.key,
|
||||
this.client,
|
||||
required this.onConfirm,
|
||||
this.onDelete,
|
||||
required this.fullScreen
|
||||
required this.fullScreen,
|
||||
this.initialData,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -146,6 +158,13 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
_blockedServicesSchedule = widget.client!.blockedServicesSchedule!;
|
||||
}
|
||||
}
|
||||
if (widget.initialData != null) {
|
||||
nameController.text = widget.initialData!.name;
|
||||
identifiersControllers[0] = ControllerListItem(
|
||||
id: uuid.v4(),
|
||||
controller: TextEditingController(text: widget.initialData!.ip)
|
||||
);
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -216,9 +235,16 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
|
||||
|
||||
if (widget.fullScreen == true) {
|
||||
return Dialog.fullscreen(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
return Material(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
sliver: SliverAppBar.large(
|
||||
pinned: true,
|
||||
floating: true,
|
||||
centerTitle: false,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
leading: IconButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
icon: const Icon(Icons.close)
|
||||
|
@ -229,10 +255,19 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
: AppLocalizations.of(context)!.addClient
|
||||
),
|
||||
actions: actions(),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
controller: _scrollController,
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Builder(
|
||||
builder: (context) => CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
SliverList.list(
|
||||
children: [
|
||||
if (!_nameValid || !_identifiersValid || !_dnsCacheValid) _Errors(
|
||||
nameValid: _nameValid,
|
||||
|
@ -279,8 +314,11 @@ class _ClientScreenState extends State<ClientScreen> {
|
|||
setBlockedServicesSchedule: (v) => setState(() => _blockedServicesSchedule = v),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ void openClientFormModal({
|
|||
Client? client,
|
||||
required void Function(Client) onConfirm,
|
||||
void Function(Client)? onDelete,
|
||||
ClientInitialData? initialData,
|
||||
}) {
|
||||
showGeneralDialog(
|
||||
context: context,
|
||||
|
@ -105,6 +106,7 @@ void openClientFormModal({
|
|||
client: client,
|
||||
onConfirm: onConfirm,
|
||||
onDelete: onDelete,
|
||||
initialData: initialData,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -131,10 +131,38 @@ class _LogsListClientState extends State<LogsListClient> {
|
|||
setState(() => previousIp = widget.ip);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.name != null && widget.name != '' ? widget.name! : widget.ip),
|
||||
centerTitle: true,
|
||||
return Material(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
sliver: SliverAppBar.large(
|
||||
pinned: true,
|
||||
floating: true,
|
||||
centerTitle: false,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
title: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
AppLocalizations.of(context)!.client,
|
||||
style: const TextStyle(
|
||||
fontSize: 24
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
widget.name != null && widget.name != '' ? widget.name! : widget.ip,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.secondary
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
surfaceTintColor: isDesktop(MediaQuery.of(context).size.width)
|
||||
? Colors.transparent
|
||||
: null,
|
||||
|
@ -148,13 +176,23 @@ class _LogsListClientState extends State<LogsListClient> {
|
|||
const SizedBox(width: 8)
|
||||
]
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
switch (loadStatus) {
|
||||
case 0:
|
||||
return SizedBox(
|
||||
builder: (context) => RefreshIndicator(
|
||||
onRefresh: fetchLogs,
|
||||
displacement: 95,
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
if (loadStatus == 0) SliverFillRemaining(
|
||||
child: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
@ -172,15 +210,9 @@ class _LogsListClientState extends State<LogsListClient> {
|
|||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
case 1:
|
||||
if (logsData!.data.isNotEmpty) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: fetchLogs,
|
||||
child: ListView.builder(
|
||||
controller: scrollController,
|
||||
padding: const EdgeInsets.only(top: 0),
|
||||
)
|
||||
),
|
||||
if (loadStatus == 1 && logsData!.data.isNotEmpty) SliverList.builder(
|
||||
itemCount: isLoadingMore == true
|
||||
? logsData!.data.length+1
|
||||
: logsData!.data.length,
|
||||
|
@ -225,10 +257,8 @@ class _LogsListClientState extends State<LogsListClient> {
|
|||
}
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Center(
|
||||
if (loadStatus == 1 && logsData!.data.isEmpty) SliverFillRemaining(
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.noLogsDisplay,
|
||||
textAlign: TextAlign.center,
|
||||
|
@ -237,11 +267,10 @@ class _LogsListClientState extends State<LogsListClient> {
|
|||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
case 2:
|
||||
return SizedBox(
|
||||
),
|
||||
),
|
||||
if (loadStatus == 2) SliverFillRemaining(
|
||||
child: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
@ -263,14 +292,14 @@ class _LogsListClientState extends State<LogsListClient> {
|
|||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ import 'package:adguard_home_manager/classes/process_modal.dart';
|
|||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class ClientsFab extends StatelessWidget {
|
||||
const ClientsFab({Key? key}) : super(key: key);
|
||||
const ClientsFab({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -85,8 +85,16 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
|
|||
|
||||
if (widget.fullScreen == true) {
|
||||
return Dialog.fullscreen(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
child: Material(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
sliver: SliverAppBar.large(
|
||||
pinned: true,
|
||||
floating: true,
|
||||
centerTitle: false,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
leading: CloseButton(onPressed: () => Navigator.pop(context)),
|
||||
title: Text(AppLocalizations.of(context)!.blockedServices),
|
||||
actions: [
|
||||
|
@ -99,24 +107,108 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
|
|||
),
|
||||
const SizedBox(width: 10)
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
body: SafeArea(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
final result = await filteringProvider.loadBlockedServices();
|
||||
if (result == false) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.blockedServicesListNotLoaded,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
},
|
||||
child: _Content(
|
||||
values: values,
|
||||
updateValues: updateValues,
|
||||
top: false,
|
||||
bottom: true,
|
||||
child: Builder(
|
||||
builder: (context) => CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
if (filteringProvider.blockedServicesLoadStatus == LoadStatus.loading) Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
width: double.maxFinite,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const CircularProgressIndicator(),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.loadingBlockedServicesList,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (filteringProvider.blockedServicesLoadStatus == LoadStatus.loaded) SliverList.builder(
|
||||
itemCount: filteringProvider.blockedServices!.services.length,
|
||||
itemBuilder: (context, index) => Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => updateValues(
|
||||
values.contains(filteringProvider.blockedServices!.services[index].id),
|
||||
filteringProvider.blockedServices!.services[index]
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
right: 12,
|
||||
left: 24
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
filteringProvider.blockedServices!.services[index].name,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: values.contains(filteringProvider.blockedServices!.services[index].id),
|
||||
onChanged: (value) => updateValues(
|
||||
value!,
|
||||
filteringProvider.blockedServices!.services[index]
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5)
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
if (filteringProvider.blockedServicesLoadStatus == LoadStatus.error) Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
width: double.maxFinite,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.error,
|
||||
color: Colors.red,
|
||||
size: 50,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.blockedServicesListNotLoaded,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -162,32 +254,8 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
|
|||
),
|
||||
),
|
||||
Expanded(
|
||||
child: _Content(
|
||||
values: values,
|
||||
updateValues: updateValues,
|
||||
)
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _Content extends StatelessWidget {
|
||||
final List<String> values;
|
||||
final void Function(bool value, BlockedService item) updateValues;
|
||||
|
||||
const _Content({
|
||||
required this.values,
|
||||
required this.updateValues,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final filteringProvider = Provider.of<FilteringProvider>(context);
|
||||
|
||||
child: Builder(
|
||||
builder: (ctx) {
|
||||
switch (filteringProvider.blockedServicesLoadStatus) {
|
||||
case LoadStatus.loading:
|
||||
return Container(
|
||||
|
@ -284,8 +352,17 @@ class _Content extends StatelessWidget {
|
|||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void openBlockedServicesModal({
|
||||
required BuildContext context,
|
||||
|
|
|
@ -55,8 +55,16 @@ class _AddCustomRuleState extends State<AddCustomRule> {
|
|||
Widget build(BuildContext context) {
|
||||
if (widget.fullScreen == true) {
|
||||
return Dialog.fullscreen(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
child: Material(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
sliver: SliverAppBar.large(
|
||||
pinned: true,
|
||||
floating: true,
|
||||
centerTitle: false,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
leading: CloseButton(onPressed: () => Navigator.pop(context)),
|
||||
title: Text(AppLocalizations.of(context)!.addCustomRule),
|
||||
actions: [
|
||||
|
@ -77,9 +85,19 @@ class _AddCustomRuleState extends State<AddCustomRule> {
|
|||
),
|
||||
const SizedBox(width: 10)
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
top: false,
|
||||
bottom: true,
|
||||
child: Builder(
|
||||
builder: (context) => CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
SliverList.list(
|
||||
children: [
|
||||
_CustomRuleEditor(
|
||||
domainController: _domainController,
|
||||
|
@ -91,8 +109,12 @@ class _AddCustomRuleState extends State<AddCustomRule> {
|
|||
validateDomain: validateDomain
|
||||
)
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -37,8 +37,16 @@ class _EditCustomRulesState extends State<EditCustomRules> {
|
|||
Widget build(BuildContext context) {
|
||||
if (widget.fullScreen == true) {
|
||||
return Dialog.fullscreen(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
child: Material(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) => [
|
||||
SliverOverlapAbsorber(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
sliver: SliverAppBar.large(
|
||||
pinned: true,
|
||||
floating: true,
|
||||
centerTitle: false,
|
||||
forceElevated: innerBoxIsScrolled,
|
||||
leading: CloseButton(onPressed: () => Navigator.pop(context)),
|
||||
title: Text(AppLocalizations.of(context)!.editCustomRules),
|
||||
actions: [
|
||||
|
@ -52,14 +60,28 @@ class _EditCustomRulesState extends State<EditCustomRules> {
|
|||
),
|
||||
const SizedBox(width: 10)
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
body: SafeArea(
|
||||
child: ListView(
|
||||
top: false,
|
||||
bottom: true,
|
||||
child: Builder(
|
||||
builder: (context) => CustomScrollView(
|
||||
slivers: [
|
||||
SliverOverlapInjector(
|
||||
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
),
|
||||
SliverList.list(
|
||||
children: [
|
||||
_CustomRulesRawEditor(fieldController: _fieldController)
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,12 +4,17 @@ 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/clients/client/client_screen_functions.dart';
|
||||
import 'package:adguard_home_manager/screens/clients/client/client_screen.dart';
|
||||
import 'package:adguard_home_manager/widgets/options_menu.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||
import 'package:adguard_home_manager/providers/filtering_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/clients.dart';
|
||||
import 'package:adguard_home_manager/providers/clients_provider.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';
|
||||
|
@ -40,6 +45,8 @@ class LogTile extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
final statusProvider = Provider.of<StatusProvider>(context);
|
||||
final clientsProvider = Provider.of<ClientsProvider>(context);
|
||||
final filteringProvider = Provider.of<FilteringProvider>(context);
|
||||
|
||||
Widget logStatusWidget({
|
||||
required IconData icon,
|
||||
|
@ -118,6 +125,114 @@ class LogTile extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
void confirmAddClient(Client client) async {
|
||||
ProcessModal processModal = ProcessModal();
|
||||
processModal.open(AppLocalizations.of(context)!.addingClient);
|
||||
|
||||
final result = await clientsProvider.addClient(client);
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (result == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.clientAddedSuccessfully,
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.clientNotAdded,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void blockUnblockRuleClient() async {
|
||||
ProcessModal processModal = ProcessModal();
|
||||
processModal.open(AppLocalizations.of(context)!.addingRule);
|
||||
|
||||
final rule = isDomainBlocked(log.reason) == true
|
||||
? "@@||${log.question.name}^\$client='${log.client}'"
|
||||
: "||${log.question.name}^\$client='${log.client}'";
|
||||
|
||||
final result = await filteringProvider.addCustomRule(rule);
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (!context.mounted) return;
|
||||
if (result == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: isDomainBlocked(log.reason) == true
|
||||
? AppLocalizations.of(context)!.domainUnblockedThisClient(log.question.name!)
|
||||
: AppLocalizations.of(context)!.domainBlockedThisClient(log.question.name!),
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.ruleNotAdded,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void allowDisallowClient() async {
|
||||
ProcessModal processModal = ProcessModal();
|
||||
processModal.open(
|
||||
log.clientInfo!.disallowed == true
|
||||
? AppLocalizations.of(context)!.allowingClient
|
||||
: AppLocalizations.of(context)!.disallowingClient
|
||||
);
|
||||
|
||||
final result = await clientsProvider.addClientList(
|
||||
log.client,
|
||||
log.clientInfo!.disallowed == true
|
||||
? AccessSettingsList.allowed
|
||||
: AccessSettingsList.disallowed
|
||||
);
|
||||
|
||||
processModal.close();
|
||||
|
||||
if (!context.mounted) return;
|
||||
if (result.successful == true) {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.clientAddedSuccessfully,
|
||||
color: Colors.green
|
||||
);
|
||||
}
|
||||
else if (result.successful == false && result.content == 'client_another_list') {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.clientAnotherList,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
appConfigProvider: appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.changesNotSaved,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void openAddClient() {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 0),
|
||||
() => openClientFormModal(
|
||||
context: context,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
onConfirm: confirmAddClient,
|
||||
initialData: ClientInitialData(name: "Client ${log.client}", ip: log.client)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final domainBlocked = isDomainBlocked(log.reason);
|
||||
|
||||
if (twoColumns && !(useAlwaysNormalTile == true)) {
|
||||
|
@ -141,6 +256,29 @@ class LogTile extends StatelessWidget {
|
|||
newStatus: domainBlocked == true ? 'unblock' : 'block'
|
||||
)
|
||||
),
|
||||
if (filteringProvider.filtering != null) MenuOption(
|
||||
title: domainBlocked == true
|
||||
? AppLocalizations.of(context)!.unblockThisClientOnly
|
||||
: AppLocalizations.of(context)!.blockThisClientOnly,
|
||||
icon: domainBlocked == true
|
||||
? Icons.check_rounded
|
||||
: Icons.block_rounded,
|
||||
action: blockUnblockRuleClient
|
||||
),
|
||||
if (log.clientInfo?.name == "") MenuOption(
|
||||
title: AppLocalizations.of(context)!.addPersistentClient,
|
||||
icon: Icons.add_rounded,
|
||||
action: openAddClient
|
||||
),
|
||||
MenuOption(
|
||||
title: log.clientInfo!.disallowed == true
|
||||
? AppLocalizations.of(context)!.allowThisClient
|
||||
: AppLocalizations.of(context)!.disallowThisClient,
|
||||
icon: log.clientInfo!.disallowed == true
|
||||
? Icons.check_rounded
|
||||
: Icons.block_rounded,
|
||||
action: allowDisallowClient
|
||||
),
|
||||
if (log.question.name != null) MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
|
@ -319,6 +457,29 @@ class LogTile extends StatelessWidget {
|
|||
newStatus: domainBlocked == true ? 'unblock' : 'block'
|
||||
)
|
||||
),
|
||||
if (filteringProvider.filtering != null) MenuOption(
|
||||
title: domainBlocked == true
|
||||
? AppLocalizations.of(context)!.unblockThisClientOnly
|
||||
: AppLocalizations.of(context)!.blockThisClientOnly,
|
||||
icon: domainBlocked == true
|
||||
? Icons.check_rounded
|
||||
: Icons.block_rounded,
|
||||
action: blockUnblockRuleClient
|
||||
),
|
||||
if (log.clientInfo?.name == "") MenuOption(
|
||||
title: AppLocalizations.of(context)!.addPersistentClient,
|
||||
icon: Icons.add_rounded,
|
||||
action: openAddClient
|
||||
),
|
||||
MenuOption(
|
||||
title: log.clientInfo!.disallowed == true
|
||||
? AppLocalizations.of(context)!.allowThisClient
|
||||
: AppLocalizations.of(context)!.disallowThisClient,
|
||||
icon: log.clientInfo!.disallowed == true
|
||||
? Icons.check_rounded
|
||||
: Icons.block_rounded,
|
||||
action: allowDisallowClient
|
||||
),
|
||||
if (log.question.name != null) MenuOption(
|
||||
title: AppLocalizations.of(context)!.copyClipboard,
|
||||
icon: Icons.copy_rounded,
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.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';
|
||||
import 'package:adguard_home_manager/providers/filtering_provider.dart';
|
||||
|
||||
class Logs extends StatefulWidget {
|
||||
const Logs({Key? key}) : super(key: key);
|
||||
const Logs({super.key});
|
||||
|
||||
@override
|
||||
State<Logs> createState() => _LogsState();
|
||||
|
@ -17,6 +19,12 @@ class Logs extends StatefulWidget {
|
|||
class _LogsState extends State<Logs> {
|
||||
Log? _selectedLog;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
Provider.of<FilteringProvider>(context, listen: false).fetchFilters();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
|
|
|
@ -8,11 +8,14 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|||
|
||||
import 'package:adguard_home_manager/screens/logs/filters/logs_filters_modal.dart';
|
||||
|
||||
import 'package:adguard_home_manager/config/globals.dart';
|
||||
import 'package:adguard_home_manager/constants/enums.dart';
|
||||
import 'package:adguard_home_manager/functions/desktop_mode.dart';
|
||||
import 'package:adguard_home_manager/models/applied_filters.dart';
|
||||
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
||||
|
||||
final GlobalKey _searchButtonKey = GlobalKey();
|
||||
|
||||
class LogsListAppBar extends StatelessWidget {
|
||||
final bool innerBoxIsScrolled;
|
||||
final bool showDivider;
|
||||
|
@ -52,6 +55,25 @@ class LogsListAppBar extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
void showSearchDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => _Search(
|
||||
searchButtonRenderBox: _searchButtonKey.currentContext?.findRenderObject() as RenderBox?,
|
||||
onSearch: (v) {
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus,
|
||||
searchText: v != "" ? v : null,
|
||||
clients: logsProvider.appliedFilters.clients
|
||||
)
|
||||
);
|
||||
logsProvider.filterLogs();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final Map<String, String> translatedString = {
|
||||
"all": AppLocalizations.of(context)!.all,
|
||||
"filtered": AppLocalizations.of(context)!.filtered,
|
||||
|
@ -77,22 +99,8 @@ class LogsListAppBar extends StatelessWidget {
|
|||
tooltip: AppLocalizations.of(context)!.refresh,
|
||||
),
|
||||
if (logsProvider.loadStatus == LoadStatus.loaded) IconButton(
|
||||
onPressed: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) => _Search(
|
||||
hasTopBar: MediaQuery.of(context).viewPadding.top > 0,
|
||||
onSearch: (v) {
|
||||
logsProvider.setAppliedFilters(
|
||||
AppliedFiters(
|
||||
selectedResultStatus: logsProvider.appliedFilters.selectedResultStatus,
|
||||
searchText: v != "" ? v : null,
|
||||
clients: logsProvider.appliedFilters.clients
|
||||
)
|
||||
);
|
||||
logsProvider.filterLogs();
|
||||
},
|
||||
),
|
||||
),
|
||||
key: _searchButtonKey,
|
||||
onPressed: showSearchDialog,
|
||||
icon: const Icon(Icons.search_rounded),
|
||||
tooltip: AppLocalizations.of(context)!.search,
|
||||
),
|
||||
|
@ -235,11 +243,11 @@ class LogsListAppBar extends StatelessWidget {
|
|||
|
||||
class _Search extends StatefulWidget {
|
||||
final void Function(String) onSearch;
|
||||
final bool hasTopBar;
|
||||
final RenderBox? searchButtonRenderBox;
|
||||
|
||||
const _Search({
|
||||
required this.onSearch,
|
||||
required this.hasTopBar,
|
||||
required this.searchButtonRenderBox,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -261,21 +269,26 @@ class _SearchState extends State<_Search> {
|
|||
Widget build(BuildContext context) {
|
||||
final logsProvider = Provider.of<LogsProvider>(context);
|
||||
|
||||
final position = widget.searchButtonRenderBox?.localToGlobal(Offset.zero);
|
||||
final topPadding = MediaQuery.of(globalNavigatorKey.currentContext!).viewPadding.top;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final double width = constraints.maxWidth - 32 > 500 ? 500 : constraints.maxWidth - 32;
|
||||
return Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
GestureDetector(
|
||||
Positioned(
|
||||
top: position != null ? position.dy - topPadding : topPadding,
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
child: GestureDetector(
|
||||
onTap: () => {},
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 500),
|
||||
child: Container(
|
||||
margin: widget.hasTopBar
|
||||
? const EdgeInsets.symmetric(horizontal: 16)
|
||||
: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(16)
|
||||
|
@ -320,8 +333,11 @@ class _SearchState extends State<_Search> {
|
|||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -165,7 +165,7 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
|||
processModal.open(AppLocalizations.of(context)!.savingConfig);
|
||||
|
||||
final result = await dnsProvider.saveDnsServerConfig({
|
||||
"ratelimit": int.parse(_limitRequestsController.text),
|
||||
"ratelimit": int.tryParse(_limitRequestsController.text),
|
||||
"edns_cs_enabled": _enableEdns,
|
||||
"edns_cs_use_custom": _useCustomIpEdns,
|
||||
"edns_cs_custom_ip": _customIpEdnsController.text,
|
||||
|
|
|
@ -47,6 +47,7 @@ class _UpdateScreenState extends State<UpdateScreen> {
|
|||
|
||||
void processChangelog() async {
|
||||
final serversProvider = Provider.of<ServersProvider>(context, listen: false);
|
||||
if (serversProvider.updateAvailable.data?.changelog == null) return;
|
||||
final markdownResult = await compute(md.markdownToHtml, serversProvider.updateAvailable.data!.changelog!);
|
||||
final htmlParsedResult = await compute(html.parse, markdownResult);
|
||||
setState(() => _htmlChangelog = htmlParsedResult.outerHtml);
|
||||
|
@ -84,7 +85,7 @@ class _UpdateScreenState extends State<UpdateScreen> {
|
|||
|
||||
processModal.close();
|
||||
|
||||
if (!mounted) return;
|
||||
if (!context.mounted) return;
|
||||
if (result.successful == true) {
|
||||
serversProvider.recheckPeriodServerUpdated();
|
||||
showSnacbkar(
|
||||
|
|
|
@ -76,6 +76,7 @@ class _OptionsModal extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
||||
scrollable: true,
|
||||
title: Column(
|
||||
children: [
|
||||
Icon(
|
||||
|
@ -94,10 +95,9 @@ class _OptionsModal extends StatelessWidget {
|
|||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 400
|
||||
maxWidth: 500
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Wrap(
|
||||
child: Column(
|
||||
children: options(value).map((opt) => CustomListTileDialog(
|
||||
title: opt.title,
|
||||
icon: opt.icon,
|
||||
|
@ -108,7 +108,6 @@ class _OptionsModal extends StatelessWidget {
|
|||
)).toList()
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
|
|
|
@ -6,13 +6,13 @@ PODS:
|
|||
- FlutterMacOS (1.0.0)
|
||||
- package_info_plus (0.0.1):
|
||||
- FlutterMacOS
|
||||
- Sentry/HybridSDK (8.20.0):
|
||||
- SentryPrivate (= 8.20.0)
|
||||
- Sentry/HybridSDK (8.21.0):
|
||||
- SentryPrivate (= 8.21.0)
|
||||
- sentry_flutter (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- Sentry/HybridSDK (= 8.20.0)
|
||||
- SentryPrivate (8.20.0)
|
||||
- Sentry/HybridSDK (= 8.21.0)
|
||||
- SentryPrivate (8.21.0)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
|
@ -84,9 +84,9 @@ SPEC CHECKSUMS:
|
|||
dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
|
||||
Sentry: a8d7b373b9f9868442b02a0c425192f693103cbf
|
||||
sentry_flutter: 03e7660857a8cdb236e71456a7e8447b65c8a788
|
||||
SentryPrivate: 006b24af16828441f70e2ab6adf241bd0a8ad130
|
||||
Sentry: ebc12276bd17613a114ab359074096b6b3725203
|
||||
sentry_flutter: dff1df05dc39c83d04f9330b36360fc374574c5e
|
||||
SentryPrivate: d651efb234cf385ec9a1cdd3eff94b5e78a0e0fe
|
||||
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||
sqlite3: 73b7fc691fdc43277614250e04d183740cb15078
|
||||
|
|
24
pubspec.lock
24
pubspec.lock
|
@ -157,10 +157,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: dynamic_color
|
||||
sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b
|
||||
sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.9"
|
||||
version: "1.7.0"
|
||||
equatable:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -311,10 +311,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: a64c5323ac83ed2b7940d2b6288d160aa1753ff271ba9d9b2a86770414aa3eab
|
||||
sha256: cb44f7831b23a6bdd0f501718b0d2e8045cbc625a15f668af37ddb80314821db
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.20+1"
|
||||
version: "0.6.21"
|
||||
flutter_native_splash:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -458,10 +458,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: markdown
|
||||
sha256: "1b134d9f8ff2da15cb298efe6cd8b7d2a78958c1b00384ebcbdf13fe340a6c90"
|
||||
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.1"
|
||||
version: "7.2.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -618,18 +618,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: sentry
|
||||
sha256: d2ee9c850d876d285f22e2e662f400ec2438df9939fe4acd5d780df9841794ce
|
||||
sha256: a524a87d096799b775530176c8c082afe7aa1f10cc31ba078fecdd74e9afc923
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.16.1"
|
||||
version: "7.17.0"
|
||||
sentry_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sentry_flutter
|
||||
sha256: "5b428c189c825f16fb14e9166529043f06b965d5b59bfc3a1415e39c082398c0"
|
||||
sha256: e0f8367f8f7c74dba9f7521f71700bce6c6ee065cf342f065d4fce411b84fc7b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.16.1"
|
||||
version: "7.17.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -951,10 +951,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
version: "5.3.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -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.16.4+134
|
||||
version: 2.17.0+136
|
||||
|
||||
environment:
|
||||
sdk: '>=2.18.1 <3.0.0'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue