mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-05-14 22:12:53 +00:00
Added search clients filter
This commit is contained in:
parent
2585826a29
commit
5f448f0af0
2 changed files with 316 additions and 227 deletions
|
@ -11,7 +11,17 @@ import 'package:adguard_home_manager/providers/status_provider.dart';
|
||||||
import 'package:adguard_home_manager/providers/clients_provider.dart';
|
import 'package:adguard_home_manager/providers/clients_provider.dart';
|
||||||
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
import 'package:adguard_home_manager/providers/logs_provider.dart';
|
||||||
|
|
||||||
class ClientsModal extends StatelessWidget {
|
class _ClientLog {
|
||||||
|
final String ip;
|
||||||
|
final String? name;
|
||||||
|
|
||||||
|
const _ClientLog({
|
||||||
|
required this.ip,
|
||||||
|
required this.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientsModal extends StatefulWidget {
|
||||||
final List<String>? value;
|
final List<String>? value;
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
|
@ -22,109 +32,64 @@ class ClientsModal extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
State<ClientsModal> createState() => _ClientsModalState();
|
||||||
final clientsProvider = Provider.of<ClientsProvider>(context);
|
|
||||||
final logsProvider = Provider.of<LogsProvider>(context);
|
|
||||||
|
|
||||||
if (dialog == true) {
|
|
||||||
return Dialog(
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
maxWidth: 500
|
|
||||||
),
|
|
||||||
child: _ModalContent(
|
|
||||||
selectedClients: logsProvider.selectedClients,
|
|
||||||
onClientsSelected: (v) => logsProvider.setSelectedClients(v),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return ListBottomSheet(
|
|
||||||
icon: Icons.smartphone_rounded,
|
|
||||||
title: AppLocalizations.of(context)!.clients,
|
|
||||||
children: [
|
|
||||||
Card(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.info_rounded,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
Flexible(
|
|
||||||
child: Text(AppLocalizations.of(context)!.selectClientsFiltersInfo)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
CustomCheckboxListTile(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 24,
|
|
||||||
top: 8,
|
|
||||||
right: 12,
|
|
||||||
bottom: 8
|
|
||||||
),
|
|
||||||
value: logsProvider.selectedClients.length == clientsProvider.clients!.autoClients.length,
|
|
||||||
onChanged: (v) {
|
|
||||||
if (v == true) {
|
|
||||||
logsProvider.setSelectedClients(clientsProvider.clients!.autoClients.map((e) => e.ip).toList());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logsProvider.setSelectedClients([]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: AppLocalizations.of(context)!.selectAll
|
|
||||||
),
|
|
||||||
ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
primary: false,
|
|
||||||
itemCount: clientsProvider.clients!.autoClients.length,
|
|
||||||
itemBuilder: (context, index) => _ListItem(
|
|
||||||
label: clientsProvider.clients!.autoClients[index].ip,
|
|
||||||
checkboxActive: logsProvider.selectedClients.contains(clientsProvider.clients!.autoClients[index].ip),
|
|
||||||
onChanged: (isSelected) {
|
|
||||||
if (isSelected == true) {
|
|
||||||
logsProvider.setSelectedClients([
|
|
||||||
...logsProvider.selectedClients,
|
|
||||||
clientsProvider.clients!.autoClients[index].ip
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logsProvider.setSelectedClients(
|
|
||||||
logsProvider.selectedClients.where(
|
|
||||||
(item) => item != clientsProvider.clients!.autoClients[index].ip
|
|
||||||
).toList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ModalContent extends StatelessWidget {
|
class _ClientsModalState extends State<ClientsModal> {
|
||||||
final List<String> selectedClients;
|
List<_ClientLog> _filteredClients = [];
|
||||||
final void Function(List<String>) onClientsSelected;
|
final _searchController = TextEditingController();
|
||||||
|
|
||||||
const _ModalContent({
|
@override
|
||||||
required this.selectedClients,
|
void initState() {
|
||||||
required this.onClientsSelected,
|
final clientsProvider = Provider.of<ClientsProvider>(context, listen: false);
|
||||||
});
|
final statusProvider = Provider.of<StatusProvider>(context, listen: false);
|
||||||
|
_filteredClients = clientsProvider.clients!.autoClients.map((e) {
|
||||||
|
String? name;
|
||||||
|
try {
|
||||||
|
name = statusProvider.serverStatus!.clients.firstWhere((c) => c.ids.contains(e.ip)).name;
|
||||||
|
} catch (e) {
|
||||||
|
// ---- //
|
||||||
|
}
|
||||||
|
return _ClientLog(
|
||||||
|
ip: e.ip,
|
||||||
|
name: name
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final clientsProvider = Provider.of<ClientsProvider>(context);
|
final clientsProvider = Provider.of<ClientsProvider>(context);
|
||||||
final logsProvider = Provider.of<LogsProvider>(context);
|
final logsProvider = Provider.of<LogsProvider>(context);
|
||||||
|
final statusProvider = Provider.of<StatusProvider>(context);
|
||||||
|
|
||||||
return Column(
|
void onSearch(String value) {
|
||||||
|
final filtered = clientsProvider.clients!.autoClients.map((e) {
|
||||||
|
String? name;
|
||||||
|
try {
|
||||||
|
name = statusProvider.serverStatus!.clients.firstWhere((c) => c.ids.contains(e.ip)).name;
|
||||||
|
} catch (e) {
|
||||||
|
// ---- //
|
||||||
|
}
|
||||||
|
return _ClientLog(
|
||||||
|
ip: e.ip,
|
||||||
|
name: name
|
||||||
|
);
|
||||||
|
}).where(
|
||||||
|
(c) => c.ip.contains(value.toLowerCase()) || (c.name != null && c.name!.toLowerCase().contains(value.toLowerCase()))
|
||||||
|
).toList();
|
||||||
|
setState(() => _filteredClients = filtered);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.dialog == true) {
|
||||||
|
return Dialog(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 500
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -147,6 +112,11 @@ class _ModalContent extends StatelessWidget {
|
||||||
Flexible(
|
Flexible(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
|
_SearchField(
|
||||||
|
controller: _searchController,
|
||||||
|
onClear: () => setState(() => _searchController.text = ""),
|
||||||
|
onSearch: onSearch
|
||||||
|
),
|
||||||
Card(
|
Card(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -186,21 +156,21 @@ class _ModalContent extends StatelessWidget {
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
primary: false,
|
primary: false,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: clientsProvider.clients!.autoClients.length,
|
itemCount: _filteredClients.length,
|
||||||
itemBuilder: (context, index) => _ListItem(
|
itemBuilder: (context, index) => _ListItem(
|
||||||
label: clientsProvider.clients!.autoClients[index].ip,
|
label: _filteredClients[index].ip,
|
||||||
checkboxActive: selectedClients.contains(clientsProvider.clients!.autoClients[index].ip),
|
checkboxActive: logsProvider.selectedClients.contains(_filteredClients[index].ip),
|
||||||
onChanged: (isSelected) {
|
onChanged: (isSelected) {
|
||||||
if (isSelected == true) {
|
if (isSelected == true) {
|
||||||
onClientsSelected([
|
logsProvider.setSelectedClients([
|
||||||
...selectedClients,
|
...logsProvider.selectedClients,
|
||||||
clientsProvider.clients!.autoClients[index].ip
|
_filteredClients[index].ip
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
onClientsSelected(
|
logsProvider.setSelectedClients(
|
||||||
selectedClients.where(
|
logsProvider.selectedClients.where(
|
||||||
(item) => item != clientsProvider.clients!.autoClients[index].ip
|
(item) => item != _filteredClients[index].ip
|
||||||
).toList()
|
).toList()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -212,6 +182,128 @@ class _ModalContent extends StatelessWidget {
|
||||||
),
|
),
|
||||||
if (Platform.isIOS) const SizedBox(height: 16)
|
if (Platform.isIOS) const SizedBox(height: 16)
|
||||||
],
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Padding(
|
||||||
|
padding: MediaQuery.of(context).viewInsets,
|
||||||
|
child: ListBottomSheet(
|
||||||
|
icon: Icons.smartphone_rounded,
|
||||||
|
title: AppLocalizations.of(context)!.clients,
|
||||||
|
children: [
|
||||||
|
_SearchField(
|
||||||
|
controller: _searchController,
|
||||||
|
onClear: () => setState(() => _searchController.text = ""),
|
||||||
|
onSearch: onSearch
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.info_rounded,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Flexible(
|
||||||
|
child: Text(AppLocalizations.of(context)!.selectClientsFiltersInfo)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CustomCheckboxListTile(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 24,
|
||||||
|
top: 8,
|
||||||
|
right: 12,
|
||||||
|
bottom: 8
|
||||||
|
),
|
||||||
|
value: logsProvider.selectedClients.length == clientsProvider.clients!.autoClients.length,
|
||||||
|
onChanged: (v) {
|
||||||
|
if (v == true) {
|
||||||
|
logsProvider.setSelectedClients(clientsProvider.clients!.autoClients.map((e) => e.ip).toList());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logsProvider.setSelectedClients([]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: AppLocalizations.of(context)!.selectAll
|
||||||
|
),
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
primary: false,
|
||||||
|
itemCount: _filteredClients.length,
|
||||||
|
itemBuilder: (context, index) => _ListItem(
|
||||||
|
label: _filteredClients[index].ip,
|
||||||
|
checkboxActive: logsProvider.selectedClients.contains(_filteredClients[index].ip),
|
||||||
|
onChanged: (isSelected) {
|
||||||
|
if (isSelected == true) {
|
||||||
|
logsProvider.setSelectedClients([
|
||||||
|
...logsProvider.selectedClients,
|
||||||
|
_filteredClients[index].ip
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logsProvider.setSelectedClients(
|
||||||
|
logsProvider.selectedClients.where(
|
||||||
|
(item) => item != _filteredClients[index].ip
|
||||||
|
).toList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SearchField extends StatelessWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
final void Function(String) onSearch;
|
||||||
|
final void Function() onClear;
|
||||||
|
|
||||||
|
const _SearchField({
|
||||||
|
required this.controller,
|
||||||
|
required this.onClear,
|
||||||
|
required this.onSearch,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: controller,
|
||||||
|
onChanged: onSearch,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: AppLocalizations.of(context)!.search,
|
||||||
|
prefixIcon: const Icon(Icons.search_rounded),
|
||||||
|
border: InputBorder.none,
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.grey.withOpacity(0.2),
|
||||||
|
suffixIcon: controller.text != ""
|
||||||
|
? IconButton(
|
||||||
|
onPressed: onClear,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.close_rounded,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
tooltip: AppLocalizations.of(context)!.clearSearch,
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,7 @@ class ListBottomSheet extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return DraggableScrollableSheet(
|
||||||
onTap: () => Navigator.of(context).pop(),
|
|
||||||
child: DraggableScrollableSheet(
|
|
||||||
initialChildSize: initialChildSize ?? 0.6,
|
initialChildSize: initialChildSize ?? 0.6,
|
||||||
minChildSize: minChildSize ?? 0.3,
|
minChildSize: minChildSize ?? 0.3,
|
||||||
maxChildSize: maxChildSize ?? 1,
|
maxChildSize: maxChildSize ?? 1,
|
||||||
|
@ -80,7 +78,6 @@ class ListBottomSheet extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue