Added add client to lists

This commit is contained in:
Juan Gilsanz Polo 2022-09-29 23:12:24 +02:00
parent 9f248d18a1
commit 6b51102dd2
8 changed files with 350 additions and 4 deletions

View file

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:adguard_home_manager/screens/connect/fab.dart';
import 'package:adguard_home_manager/screens/home/appbar.dart';
import 'package:adguard_home_manager/screens/connect/appbar.dart';
import 'package:adguard_home_manager/screens/clients/fab.dart';
import 'package:adguard_home_manager/screens/connect/connect.dart';
import 'package:adguard_home_manager/screens/home/home.dart';
import 'package:adguard_home_manager/screens/clients/clients.dart';
@ -40,6 +41,7 @@ List<AppScreen> screensServerConnected = [
name: "clients",
icon: Icons.devices,
body: Clients(),
fab: ClientsFab()
),
const AppScreen(
name: "settings",

View file

@ -87,5 +87,10 @@
"removeClientMessage": "Are you sure you want to remove this client from the list?",
"confirm": "Confirm",
"removingClient": "Removing client...",
"clientNotRemoved": "Client could not be removed from the list"
"clientNotRemoved": "Client could not be removed from the list",
"addClient": "Add client",
"list": "List",
"ipAddress": "IP address",
"ipNotValid": "IP address not valid",
"clientAddedSuccessfully": "Client added to the list successfully"
}

View file

@ -87,5 +87,10 @@
"removeClientMessage": "Estás seguro que deseas eliminar este cliente de la lista?",
"confirm": "Confirmar",
"removingClient": "Eliminando cliente...",
"clientNotRemoved": "El cliente no pudo ser eliminado de la lista"
"clientNotRemoved": "El cliente no pudo ser eliminado de la lista",
"addClient": "Agregar cliente",
"list": "Lista",
"ipAddress": "Dirección IP",
"ipNotValid": "Dirección IP no válida",
"clientAddedSuccessfully": "Cliente añadido a la lista satisfactoriamente"
}

View file

@ -13,6 +13,8 @@ class AppConfigProvider with ChangeNotifier {
int _selectedTheme = 0;
int _selectedClientsTab = 0;
PackageInfo? get getAppInfo {
return _appInfo;
}
@ -47,6 +49,9 @@ class AppConfigProvider with ChangeNotifier {
return _selectedTheme;
}
int get selectedClientsTab {
return _selectedClientsTab;
}
void setDbInstance(Database db) {
_dbInstance = db;
@ -64,6 +69,11 @@ class AppConfigProvider with ChangeNotifier {
_iosDeviceInfo = deviceInfo;
}
void setSelectedClientsTab(int tab) {
_selectedClientsTab = tab;
notifyListeners();
}
Future<bool> setSelectedTheme(int value) async {
final updated = await _updateThemeDb(value);
if (updated == true) {

View file

@ -0,0 +1,198 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class AddClientModal extends StatefulWidget {
final String list;
final void Function(String, String) onConfirm;
const AddClientModal({
Key? key,
required this.list,
required this.onConfirm
}) : super(key: key);
@override
State<AddClientModal> createState() => _AddClientModalState();
}
class _AddClientModalState extends State<AddClientModal> {
String list = '';
TextEditingController ipController = TextEditingController();
String? ipError;
@override
void initState() {
list = widget.list;
super.initState();
}
void validateIp(String value) {
RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$');
if (ipAddress.hasMatch(value) == true) {
setState(() => ipError = null);
}
else {
setState(() => ipError = AppLocalizations.of(context)!.ipNotValid);
}
}
bool checkValidValues() {
if (
(list == 'allowed' ||
list == 'blocked') &&
ipController.text != '' &&
ipError == null
) {
return true;
}
else {
return false;
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Column(
children: [
const Icon(
Icons.add,
size: 26,
),
const SizedBox(height: 20),
Text(
AppLocalizations.of(context)!.addClient,
)
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Material(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
bottomLeft: Radius.circular(15)
),
color: Colors.transparent,
child: InkWell(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
bottomLeft: Radius.circular(15)
),
onTap: () => setState(() => list = 'allowed'),
child: AnimatedContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
bottomLeft: Radius.circular(15)
),
border: Border.all(
color: Theme.of(context).primaryColor
),
color: list == 'allowed'
? Theme.of(context).primaryColor
: Theme.of(context).primaryColor.withOpacity(0.05)
),
child: Text(
AppLocalizations.of(context)!.allowed,
style: TextStyle(
color: list == 'allowed'
? Colors.white
: null
),
),
),
),
),
Material(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(15),
bottomRight: Radius.circular(15)
),
color: Colors.transparent,
child: InkWell(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(15),
bottomRight: Radius.circular(15)
),
onTap: () => setState(() => list = 'blocked'),
child: AnimatedContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(15),
bottomRight: Radius.circular(15)
),
border: Border.all(
color: Theme.of(context).primaryColor
),
color: list == 'blocked'
? Theme.of(context).primaryColor
: Theme.of(context).primaryColor.withOpacity(0.05)
),
child: Text(
AppLocalizations.of(context)!.blocked,
style: TextStyle(
color: list == 'blocked'
? Colors.white
: null
),
),
),
),
)
],
),
const SizedBox(height: 30),
TextFormField(
controller: ipController,
onChanged: validateIp,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.link_rounded),
errorText: ipError,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10)
)
),
labelText: AppLocalizations.of(context)!.ipAddress,
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(AppLocalizations.of(context)!.cancel)
),
TextButton(
onPressed: checkValidValues() == true
? () {
Navigator.pop(context);
widget.onConfirm(list, ipController.text);
}
: null,
child: Text(
AppLocalizations.of(context)!.confirm,
style: TextStyle(
color: checkValidValues() == true
? Theme.of(context).primaryColor
: Colors.grey
),
)
),
],
);
}
}

View file

@ -5,6 +5,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/clients/clients_list.dart';
import 'package:adguard_home_manager/screens/clients/blocked_allowed_list.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/models/server.dart';
import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:adguard_home_manager/models/clients.dart';
@ -16,11 +17,13 @@ class Clients extends StatelessWidget {
@override
Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
return ClientsWidget(
server: serversProvider.selectedServer!,
setLoadingStatus: serversProvider.setClientsLoadStatus,
setClientsData: serversProvider.setClientsData,
setSelectedClientsTab: appConfigProvider.setSelectedClientsTab,
);
}
}
@ -29,19 +32,23 @@ class ClientsWidget extends StatefulWidget {
final Server server;
final void Function(int, bool) setLoadingStatus;
final void Function(ClientsData) setClientsData;
final void Function(int) setSelectedClientsTab;
const ClientsWidget({
Key? key,
required this.server,
required this.setLoadingStatus,
required this.setClientsData,
required this.setSelectedClientsTab
}) : super(key: key);
@override
State<ClientsWidget> createState() => _ClientsWidgetState();
}
class _ClientsWidgetState extends State<ClientsWidget> {
class _ClientsWidgetState extends State<ClientsWidget> with TickerProviderStateMixin {
late TabController tabController;
void fetchClients() async {
widget.setLoadingStatus(0, false);
final result = await getClients(widget.server);
@ -58,6 +65,12 @@ class _ClientsWidgetState extends State<ClientsWidget> {
void initState() {
fetchClients();
super.initState();
tabController = TabController(
initialIndex: 0,
length: 3,
vsync: this,
);
tabController.addListener(() => widget.setSelectedClientsTab(tabController.index));
}
List<AutoClient> generateClientsList(List<AutoClient> clients, List<String> ips) {
@ -103,6 +116,7 @@ class _ClientsWidgetState extends State<ClientsWidget> {
floating: true,
forceElevated: innerBoxIsScrolled,
bottom: TabBar(
controller: tabController,
tabs: [
Tab(
icon: const Icon(Icons.devices),
@ -132,6 +146,7 @@ class _ClientsWidgetState extends State<ClientsWidget> {
)
),
child: TabBarView(
controller: tabController,
children: [
ClientsList(
data: serversProvider.clients.data!.autoClientsData,

View file

@ -0,0 +1,101 @@
// ignore_for_file: use_build_context_synchronously
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/add_client_modal.dart';
import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:adguard_home_manager/models/clients_allowed_blocked.dart';
import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
class ClientsFab extends StatelessWidget {
const ClientsFab({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
void confirmRemoveDomain(String list, String ip) async {
Map<String, List<String>> body = {};
if (list == 'allowed') {
final List<String> clients = [...serversProvider.clients.data!.clientsAllowedBlocked?.allowedClients ?? [], ip];
body = {
"allowed_clients": clients,
"disallowed_clients": serversProvider.clients.data!.clientsAllowedBlocked?.disallowedClients ?? [],
"blocked_hosts": serversProvider.clients.data!.clientsAllowedBlocked?.blockedHosts ?? [],
};
}
else if (list == 'blocked') {
final List<String> clients = [...serversProvider.clients.data!.clientsAllowedBlocked?.disallowedClients ?? [], ip];
body = {
"allowed_clients": serversProvider.clients.data!.clientsAllowedBlocked?.allowedClients ?? [],
"disallowed_clients": clients,
"blocked_hosts": serversProvider.clients.data!.clientsAllowedBlocked?.blockedHosts ?? [],
};
}
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.removingClient);
final result = await requestAllowedBlockedClientsHosts(serversProvider.selectedServer!, body);
processModal.close();
if (result['result'] == 'success') {
serversProvider.setAllowedDisallowedClientsBlockedDomains(
ClientsAllowedBlocked(
allowedClients: body['allowed_clients'] ?? [],
disallowedClients: body['disallowed_clients'] ?? [],
blockedHosts: body['blocked_hosts'] ?? [],
)
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.clientAddedSuccessfully),
backgroundColor: Colors.green,
)
);
}
else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.clientNotRemoved),
backgroundColor: Colors.red,
)
);
}
}
void openAddClient(String list) {
showDialog(
context: context,
builder: (ctx) => AddClientModal(
list: list,
onConfirm: confirmRemoveDomain
)
);
}
if (appConfigProvider.selectedClientsTab == 1) {
return FloatingActionButton(
onPressed: () => openAddClient('allowed'),
child: const Icon(Icons.add),
);
}
else if (appConfigProvider.selectedClientsTab == 2) {
return FloatingActionButton(
onPressed: () => openAddClient('blocked'),
child: const Icon(Icons.add),
);
}
else {
return const SizedBox();
}
}
}

View file

@ -1,6 +1,8 @@
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/app_config_provider.dart';
import 'package:adguard_home_manager/models/app_screen.dart';
class BottomNavBar extends StatelessWidget {
@ -17,6 +19,8 @@ class BottomNavBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final appConfigProvider = Provider.of<AppConfigProvider>(context);
String translatedName(String key) {
switch (key) {
case 'home':
@ -42,7 +46,13 @@ class BottomNavBar extends StatelessWidget {
icon: Icon(screen.icon),
label: translatedName(screen.name)
)).toList(),
onDestinationSelected: onSelect,
onDestinationSelected: (value) {
// Reset clients tab to 0 when changing screen
if (value != 1) {
appConfigProvider.setSelectedClientsTab(0);
}
onSelect(value);
},
);
}
}