mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-05-14 14:02:48 +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/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 bool dialog;
|
||||
|
||||
|
@ -22,109 +32,64 @@ class ClientsModal extends StatelessWidget {
|
|||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
State<ClientsModal> createState() => _ClientsModalState();
|
||||
}
|
||||
|
||||
class _ModalContent extends StatelessWidget {
|
||||
final List<String> selectedClients;
|
||||
final void Function(List<String>) onClientsSelected;
|
||||
class _ClientsModalState extends State<ClientsModal> {
|
||||
List<_ClientLog> _filteredClients = [];
|
||||
final _searchController = TextEditingController();
|
||||
|
||||
const _ModalContent({
|
||||
required this.selectedClients,
|
||||
required this.onClientsSelected,
|
||||
});
|
||||
@override
|
||||
void initState() {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
final clientsProvider = Provider.of<ClientsProvider>(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,
|
||||
children: [
|
||||
Padding(
|
||||
|
@ -147,6 +112,11 @@ class _ModalContent extends StatelessWidget {
|
|||
Flexible(
|
||||
child: ListView(
|
||||
children: [
|
||||
_SearchField(
|
||||
controller: _searchController,
|
||||
onClear: () => setState(() => _searchController.text = ""),
|
||||
onSearch: onSearch
|
||||
),
|
||||
Card(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Padding(
|
||||
|
@ -186,21 +156,21 @@ class _ModalContent extends StatelessWidget {
|
|||
ListView.builder(
|
||||
primary: false,
|
||||
shrinkWrap: true,
|
||||
itemCount: clientsProvider.clients!.autoClients.length,
|
||||
itemCount: _filteredClients.length,
|
||||
itemBuilder: (context, index) => _ListItem(
|
||||
label: clientsProvider.clients!.autoClients[index].ip,
|
||||
checkboxActive: selectedClients.contains(clientsProvider.clients!.autoClients[index].ip),
|
||||
label: _filteredClients[index].ip,
|
||||
checkboxActive: logsProvider.selectedClients.contains(_filteredClients[index].ip),
|
||||
onChanged: (isSelected) {
|
||||
if (isSelected == true) {
|
||||
onClientsSelected([
|
||||
...selectedClients,
|
||||
clientsProvider.clients!.autoClients[index].ip
|
||||
logsProvider.setSelectedClients([
|
||||
...logsProvider.selectedClients,
|
||||
_filteredClients[index].ip
|
||||
]);
|
||||
}
|
||||
else {
|
||||
onClientsSelected(
|
||||
selectedClients.where(
|
||||
(item) => item != clientsProvider.clients!.autoClients[index].ip
|
||||
logsProvider.setSelectedClients(
|
||||
logsProvider.selectedClients.where(
|
||||
(item) => item != _filteredClients[index].ip
|
||||
).toList()
|
||||
);
|
||||
}
|
||||
|
@ -212,6 +182,128 @@ class _ModalContent extends StatelessWidget {
|
|||
),
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: DraggableScrollableSheet(
|
||||
return DraggableScrollableSheet(
|
||||
initialChildSize: initialChildSize ?? 0.6,
|
||||
minChildSize: minChildSize ?? 0.3,
|
||||
maxChildSize: maxChildSize ?? 1,
|
||||
|
@ -80,7 +78,6 @@ class ListBottomSheet extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue