adguard-home-manager/lib/widgets/add_server_modal.dart

688 lines
22 KiB
Dart
Raw Normal View History

2022-09-26 18:49:41 +02:00
// ignore_for_file: use_build_context_synchronously
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
2022-09-26 18:49:41 +02:00
import 'package:uuid/uuid.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
2022-09-30 00:24:56 +02:00
import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/functions/encode_base64.dart';
2022-09-26 18:49:41 +02:00
import 'package:adguard_home_manager/services/http_requests.dart';
2022-11-01 19:41:41 +01:00
import 'package:adguard_home_manager/models/app_log.dart';
2022-09-26 18:49:41 +02:00
import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/models/server.dart';
2023-01-25 19:55:34 +01:00
enum ConnectionType { http, https}
class AddServerModal extends StatefulWidget {
2022-09-26 22:43:30 +02:00
final Server? server;
const AddServerModal({
Key? key,
this.server,
}) : super(key: key);
@override
State<AddServerModal> createState() => _AddServerModalState();
}
class _AddServerModalState extends State<AddServerModal> {
2022-09-26 18:49:41 +02:00
final uuid = const Uuid();
final TextEditingController nameController = TextEditingController();
2022-09-26 18:49:41 +02:00
String? nameError;
2023-01-25 19:55:34 +01:00
ConnectionType connectionType = ConnectionType.http;
final TextEditingController ipDomainController = TextEditingController();
String? ipDomainError;
final TextEditingController pathController = TextEditingController();
String? pathError;
final TextEditingController portController = TextEditingController();
String? portError;
final TextEditingController userController = TextEditingController();
2022-09-28 01:25:04 +02:00
String? userError;
final TextEditingController passwordController = TextEditingController();
2022-09-28 01:25:04 +02:00
String? passwordError;
bool defaultServer = false;
bool homeAssistant = false;
2022-09-26 18:49:41 +02:00
bool allDataValid = false;
bool isConnecting = false;
Widget sectionLabel(String label) {
return Padding(
padding: const EdgeInsets.symmetric(
2022-11-04 01:22:57 +01:00
horizontal: 24,
vertical: 24
),
child: Text(
label,
2022-10-21 12:41:04 +02:00
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
2022-10-21 12:41:04 +02:00
color: Theme.of(context).primaryColor
),
),
);
}
Widget textField({
required String label,
required TextEditingController controller,
String? error,
required IconData icon,
2022-09-26 18:49:41 +02:00
TextInputType? keyboardType,
Function(String)? onChanged,
2022-09-28 01:25:04 +02:00
bool? obscureText,
String? hintText,
String? helperText
}) {
return Padding(
2022-11-04 01:22:57 +01:00
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextFormField(
controller: controller,
2022-09-26 18:49:41 +02:00
onChanged: onChanged,
2022-09-28 01:25:04 +02:00
obscureText: obscureText ?? false,
decoration: InputDecoration(
prefixIcon: Icon(icon),
errorText: error,
2022-09-28 01:25:04 +02:00
hintText: hintText,
helperText: helperText,
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10)
)
),
labelText: label,
),
keyboardType: keyboardType,
),
);
}
2022-09-26 18:49:41 +02:00
void checkDataValid() {
if (
nameController.text != '' &&
ipDomainController.text != '' &&
ipDomainError == null &&
pathError == null &&
portError == null &&
userController.text != '' &&
passwordController.text != ''
) {
setState(() {
allDataValid = true;
});
}
else {
setState(() {
allDataValid = false;
});
}
}
void validatePort(String? value) {
if (value != null && value != '') {
if (int.tryParse(value) != null && int.parse(value) <= 65535) {
setState(() {
portError = null;
});
}
else {
setState(() {
portError = AppLocalizations.of(context)!.invalidPort;
});
}
}
else {
setState(() {
portError = null;
});
}
checkDataValid();
}
void validateSubroute(String? value) {
if (value != null && value != '') {
RegExp subrouteRegexp = RegExp(r'^\/\b([A-Za-z0-9_\-~/]*)[^\/|\.|\:]$');
if (subrouteRegexp.hasMatch(value) == true) {
setState(() {
pathError = null;
});
}
else {
setState(() {
pathError = AppLocalizations.of(context)!.invalidPath;
});
}
}
else {
setState(() {
pathError = null;
});
}
checkDataValid();
}
void validateAddress(String? value) {
if (value != null && value != '') {
RegExp ipAddress = RegExp(r'^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$');
RegExp domain = RegExp(r'^([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+$');
2022-09-26 18:49:41 +02:00
if (ipAddress.hasMatch(value) == true || domain.hasMatch(value) == true) {
setState(() {
ipDomainError = null;
});
}
else {
setState(() {
ipDomainError = AppLocalizations.of(context)!.invalidIpDomain;
});
}
}
else {
setState(() {
ipDomainError = AppLocalizations.of(context)!.ipDomainNotEmpty;
});
}
checkDataValid();
}
2022-09-28 01:25:04 +02:00
void validateUser(String? value) {
if (value != null && value != '') {
setState(() {
userError = null;
});
}
else {
setState(() {
userError = AppLocalizations.of(context)!.userNotEmpty;
});
}
checkDataValid();
}
void validatePassword(String? value) {
if (value != null && value != '') {
setState(() {
passwordError = null;
});
}
else {
setState(() {
passwordError = AppLocalizations.of(context)!.passwordNotEmpty;
});
}
checkDataValid();
}
2022-09-26 22:43:30 +02:00
@override
void initState() {
if (widget.server != null) {
nameController.text = widget.server!.name;
2023-01-25 19:55:34 +01:00
connectionType = widget.server!.connectionMethod == 'https' ? ConnectionType.https : ConnectionType.http;
2022-09-26 22:43:30 +02:00
ipDomainController.text = widget.server!.domain;
pathController.text = widget.server!.path ?? '';
portController.text = widget.server!.port != null ? widget.server!.port.toString() : "";
userController.text = widget.server!.user;
passwordController.text = widget.server!.password;
defaultServer = widget.server!.defaultServer;
2022-10-17 01:21:49 +02:00
homeAssistant = widget.server!.runningOnHa;
2022-09-26 22:43:30 +02:00
}
checkDataValid();
super.initState();
}
@override
Widget build(BuildContext context) {
2022-09-26 18:49:41 +02:00
final serversProvider = Provider.of<ServersProvider>(context, listen: false);
2022-09-30 00:24:56 +02:00
final appConfigProvider = Provider.of<AppConfigProvider>(context, listen: false);
2022-09-26 18:49:41 +02:00
final mediaQuery = MediaQuery.of(context);
void connect() async {
Server serverObj = Server(
2022-09-26 18:49:41 +02:00
id: uuid.v4(),
name: nameController.text,
2023-01-25 19:55:34 +01:00
connectionMethod: connectionType.name,
2022-09-26 18:49:41 +02:00
domain: ipDomainController.text,
port: portController.text != '' ? int.parse(portController.text) : null,
2022-09-26 18:49:41 +02:00
user: userController.text,
password: passwordController.text,
defaultServer: defaultServer,
authToken: homeAssistant == true
? encodeBase64UserPass(userController.text, passwordController.text)
: '',
runningOnHa: homeAssistant
2022-09-26 18:49:41 +02:00
);
2022-09-26 22:43:30 +02:00
setState(() => isConnecting = true);
final result = homeAssistant == true
? await loginHA(serverObj)
: await login(serverObj);
2022-09-26 22:43:30 +02:00
setState(() => isConnecting = false);
2022-09-26 18:49:41 +02:00
if (result['result'] == 'success') {
serverObj.authToken = encodeBase64UserPass(serverObj.user, serverObj.password);
2022-09-26 18:49:41 +02:00
final serverCreated = await serversProvider.createServer(serverObj);
2022-11-01 19:41:41 +01:00
if (serverCreated == null) {
2022-09-27 14:29:36 +02:00
serversProvider.setServerStatusLoad(0);
2022-09-27 12:43:25 +02:00
final serverStatus = await getServerStatus(serverObj);
if (serverStatus['result'] == 'success') {
2022-09-27 14:29:36 +02:00
serversProvider.setServerStatusData(serverStatus['data']);
serversProvider.setServerStatusLoad(1);
}
else {
2022-10-03 22:41:19 +02:00
appConfigProvider.addLog(serverStatus['log']);
2022-09-27 14:29:36 +02:00
serversProvider.setServerStatusLoad(2);
}
2022-09-26 18:49:41 +02:00
Navigator.pop(context);
}
else {
2022-11-01 19:41:41 +01:00
appConfigProvider.addLog(
AppLog(
type: 'save_connection_db',
dateTime: DateTime.now(),
message: serverCreated.toString()
)
);
2022-09-26 18:49:41 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.connectionNotCreated),
backgroundColor: Colors.red,
)
);
}
}
2022-09-30 00:24:56 +02:00
else if (result['result'] == 'invalid_username_password') {
2022-09-30 01:23:28 +02:00
appConfigProvider.addLog(result['log']);
2022-09-26 18:49:41 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.invalidUsernamePassword),
backgroundColor: Colors.red,
)
);
}
2022-09-30 00:24:56 +02:00
else if (result['result'] == 'many_attempts') {
2022-09-30 01:23:28 +02:00
appConfigProvider.addLog(result['log']);
2022-09-26 18:49:41 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.tooManyAttempts),
backgroundColor: Colors.red,
)
);
}
2022-09-30 00:24:56 +02:00
else if (result['result'] == 'no_connection') {
2022-09-30 01:23:28 +02:00
appConfigProvider.addLog(result['log']);
2022-09-26 18:49:41 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.cantReachServer),
backgroundColor: Colors.red,
)
);
}
2022-09-30 00:24:56 +02:00
else if (result['result'] == 'ssl_error') {
2022-09-30 01:23:28 +02:00
appConfigProvider.addLog(result['log']);
2022-09-26 18:49:41 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.sslError),
backgroundColor: Colors.red,
)
);
}
else if (result['result'] == 'server_error') {
appConfigProvider.addLog(result['log']);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.serverError),
backgroundColor: Colors.red,
)
);
}
2022-09-26 18:49:41 +02:00
else {
2022-09-30 01:23:28 +02:00
appConfigProvider.addLog(result['log']);
2022-09-26 18:49:41 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.unknownError),
backgroundColor: Colors.red,
)
);
}
}
2022-09-26 22:43:30 +02:00
void edit() async {
final Server serverObj = Server(
id: widget.server!.id,
name: nameController.text,
2023-01-25 19:55:34 +01:00
connectionMethod: connectionType.name,
2022-09-26 22:43:30 +02:00
domain: ipDomainController.text,
port: portController.text != '' ? int.parse(portController.text) : null,
2022-09-26 22:43:30 +02:00
user: userController.text,
password: passwordController.text,
defaultServer: defaultServer,
authToken: homeAssistant == true
? encodeBase64UserPass(userController.text, passwordController.text)
: '',
runningOnHa: homeAssistant
2022-09-26 22:43:30 +02:00
);
final result = homeAssistant == true
? await loginHA(serverObj)
: await login(serverObj);
2022-09-26 22:43:30 +02:00
if (result['result'] == 'success') {
serverObj.authToken = encodeBase64UserPass(serverObj.user, serverObj.password);
2022-09-26 22:43:30 +02:00
final serverSaved = await serversProvider.editServer(serverObj);
2022-11-01 19:41:41 +01:00
if (serverSaved == null) {
2022-09-26 22:43:30 +02:00
Navigator.pop(context);
}
else {
2022-11-01 19:41:41 +01:00
appConfigProvider.addLog(
AppLog(
type: 'edit_connection_db',
dateTime: DateTime.now(),
message: serverSaved.toString()
)
);
2022-09-26 22:43:30 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
2022-11-01 19:41:41 +01:00
content: Text(AppLocalizations.of(context)!.connectionNotCreated),
2022-09-26 22:43:30 +02:00
backgroundColor: Colors.red,
)
);
}
}
2022-09-30 01:23:28 +02:00
else if (result['result'] == 'invalid_username_password') {
appConfigProvider.addLog(result['log']);
2022-09-26 22:43:30 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.invalidUsernamePassword),
backgroundColor: Colors.red,
)
);
}
2022-09-30 01:23:28 +02:00
else if (result['result'] == 'many_attempts') {
appConfigProvider.addLog(result['log']);
2022-09-26 22:43:30 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.tooManyAttempts),
backgroundColor: Colors.red,
)
);
}
2022-09-30 01:23:28 +02:00
else if (result['result'] == 'no_connection') {
appConfigProvider.addLog(result['log']);
2022-09-26 22:43:30 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.cantReachServer),
backgroundColor: Colors.red,
)
);
}
2022-09-30 01:23:28 +02:00
else if (result['result'] == 'ssl_error') {
appConfigProvider.addLog(result['log']);
2022-09-26 22:43:30 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.sslError),
backgroundColor: Colors.red,
)
);
}
else if (result['result'] == 'server_error') {
appConfigProvider.addLog(result['log']);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.serverError),
backgroundColor: Colors.red,
)
);
}
2022-09-26 22:43:30 +02:00
else {
2022-09-30 01:23:28 +02:00
appConfigProvider.addLog(result['log']);
2022-09-26 22:43:30 +02:00
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalizations.of(context)!.unknownError),
backgroundColor: Colors.red,
)
);
}
}
2022-11-05 02:35:35 +01:00
return Stack(
children: [
Scaffold(
2022-11-05 02:35:35 +01:00
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.createConnection),
actions: [
Padding(
padding: const EdgeInsets.only(right: 10),
child: IconButton(
2022-09-26 22:43:30 +02:00
tooltip: widget.server == null
? AppLocalizations.of(context)!.connect
: AppLocalizations.of(context)!.save,
2022-09-26 18:49:41 +02:00
onPressed: allDataValid == true
2022-09-26 22:43:30 +02:00
? widget.server == null
? () => connect()
: () => edit()
2022-09-26 18:49:41 +02:00
: null,
2022-09-26 22:43:30 +02:00
icon: Icon(
widget.server == null
? Icons.login_rounded
: Icons.save_rounded
)
),
),
],
toolbarHeight: 70,
),
body: ListView(
children: [
2022-09-30 02:41:03 +02:00
Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
margin: const EdgeInsets.only(
2022-11-04 01:22:57 +01:00
top: 24,
left: 24,
right: 24
2022-09-30 02:41:03 +02:00
),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor.withOpacity(0.05),
borderRadius: BorderRadius.circular(30),
border: Border.all(
color: Theme.of(context).primaryColor
)
),
child: Text(
2023-01-25 19:55:34 +01:00
"${connectionType.name}://${ipDomainController.text}${portController.text != '' ? ':${portController.text}' : ""}${pathController.text}",
2022-09-30 02:41:03 +02:00
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.w500
),
),
),
sectionLabel(AppLocalizations.of(context)!.general),
textField(
label: AppLocalizations.of(context)!.name,
controller: nameController,
2022-09-26 18:49:41 +02:00
icon: Icons.badge_rounded,
error: nameError,
2022-10-17 01:39:21 +02:00
onChanged: (value) {
if (value != '') {
setState(() => nameError = null);
}
else {
setState(() => nameError = AppLocalizations.of(context)!.nameNotEmpty);
}
checkDataValid();
}
),
sectionLabel(AppLocalizations.of(context)!.connection),
2022-11-04 22:56:00 +01:00
Padding(
2023-01-25 19:55:34 +01:00
padding: const EdgeInsets.symmetric(horizontal: 24),
child: SegmentedButton<ConnectionType>(
segments: const [
ButtonSegment(
value: ConnectionType.http,
label: Text("HTTP")
),
ButtonSegment(
value: ConnectionType.https,
label: Text("HTTPS")
),
],
selected: <ConnectionType>{connectionType},
onSelectionChanged: (value) => setState(() => connectionType = value.first),
2022-11-04 22:56:00 +01:00
),
),
2023-01-04 14:32:58 +01:00
const SizedBox(height: 30),
textField(
label: AppLocalizations.of(context)!.ipDomain,
controller: ipDomainController,
icon: Icons.link_rounded,
error: ipDomainError,
2022-09-26 18:49:41 +02:00
keyboardType: TextInputType.url,
onChanged: validateAddress
),
const SizedBox(height: 20),
textField(
label: AppLocalizations.of(context)!.path,
controller: pathController,
icon: Icons.route_rounded,
2022-09-26 18:49:41 +02:00
error: pathError,
2022-09-28 01:25:04 +02:00
onChanged: validateSubroute,
hintText: AppLocalizations.of(context)!.examplePath,
helperText: AppLocalizations.of(context)!.helperPath,
),
const SizedBox(height: 20),
textField(
label: AppLocalizations.of(context)!.port,
controller: portController,
icon: Icons.numbers_rounded,
error: portError,
2022-09-26 18:49:41 +02:00
keyboardType: TextInputType.number,
onChanged: validatePort
),
sectionLabel(AppLocalizations.of(context)!.authentication),
textField(
label: AppLocalizations.of(context)!.username,
controller: userController,
icon: Icons.person_rounded,
2022-09-28 01:25:04 +02:00
onChanged: validateUser,
error: userError
),
const SizedBox(height: 20),
textField(
label: AppLocalizations.of(context)!.password,
controller: passwordController,
icon: Icons.lock_rounded,
2022-09-26 18:49:41 +02:00
keyboardType: TextInputType.visiblePassword,
2022-09-28 01:25:04 +02:00
onChanged: validatePassword,
error: passwordError,
obscureText: true
),
sectionLabel(AppLocalizations.of(context)!.other),
Material(
color: Colors.transparent,
child: InkWell(
2022-09-26 22:43:30 +02:00
onTap: widget.server == null
? () => setState(() => defaultServer = !defaultServer)
: null,
child: Padding(
2022-11-04 01:22:57 +01:00
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppLocalizations.of(context)!.defaultServer,
style: const TextStyle(
fontSize: 15,
),
),
Switch(
value: defaultServer,
2022-09-26 22:43:30 +02:00
onChanged: widget.server == null
? (value) => setState(() => defaultServer = value)
: null,
activeColor: Theme.of(context).primaryColor,
)
],
),
),
),
),
const SizedBox(height: 20),
Material(
color: Colors.transparent,
child: InkWell(
2022-10-17 01:21:49 +02:00
onTap: () => setState(() => homeAssistant = !homeAssistant),
child: Padding(
2022-11-04 01:22:57 +01:00
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppLocalizations.of(context)!.runningHomeAssistant,
style: const TextStyle(
fontSize: 15,
),
),
Switch(
value: homeAssistant,
2022-10-17 01:21:49 +02:00
onChanged: (value) => setState(() => homeAssistant = value),
activeColor: Theme.of(context).primaryColor,
)
],
),
),
),
),
const SizedBox(height: 20),
],
),
2022-09-26 18:49:41 +02:00
),
AnimatedOpacity(
opacity: isConnecting == true ? 1 : 0,
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
child: IgnorePointer(
ignoring: isConnecting == true ? false : true,
child: Scaffold(
backgroundColor: Colors.transparent,
body: Container(
width: mediaQuery.size.width,
height: mediaQuery.size.height,
color: const Color.fromRGBO(0, 0, 0, 0.7),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(
color: Colors.white,
),
const SizedBox(height: 30),
Text(
AppLocalizations.of(context)!.connecting,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 26
),
)
],
),
),
),
),
)
],
);
}
}