From 83ea5891876f4d690c579ab361cc8d981dec97a5 Mon Sep 17 00:00:00 2001 From: Juan Gilsanz Polo Date: Fri, 8 Dec 2023 20:43:45 +0100 Subject: [PATCH] Support AGH without DHCP server --- lib/l10n/app_en.arb | 4 +- lib/l10n/app_es.arb | 4 +- lib/models/dhcp.dart | 4 +- lib/providers/dhcp_provider.dart | 4 +- lib/screens/settings/dhcp/dhcp.dart | 418 +++++++----------- .../settings/dhcp/dhcp_main_button.dart | 76 ++++ .../settings/dhcp/dhcp_not_available.dart | 47 ++ lib/services/api_client.dart | 7 +- lib/services/http_requests.dart | 1 + 9 files changed, 291 insertions(+), 274 deletions(-) create mode 100644 lib/screens/settings/dhcp/dhcp_main_button.dart create mode 100644 lib/screens/settings/dhcp/dhcp_not_available.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index b9ba629..fb875cc 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -688,5 +688,7 @@ "yourVersion": "Your version: {version}", "minimumRequiredVersion": "Minimum required version: {version}", "topUpstreams": "Top upstreams", - "averageUpstreamResponseTime": "Average upstream response time" + "averageUpstreamResponseTime": "Average upstream response time", + "dhcpNotAvailable": "The DHCP server is not available.", + "osServerInstalledIncompatible": "The OS where the server is installed is not compatible with this feature." } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 0e1566c..cf328dc 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -688,5 +688,7 @@ "yourVersion": "Tu versión: {version}", "minimumRequiredVersion": "Versión mínima requerida: {version}", "topUpstreams": "DNS de subida más frecuentes", - "averageUpstreamResponseTime": "Tiempo promedio de respuesta upstream" + "averageUpstreamResponseTime": "Tiempo promedio de respuesta upstream", + "dhcpNotAvailable": "El servidor DHCP no está disponible.", + "osServerInstalledIncompatible": "El SO donde el servidor está instalado no es compatible con esta característica." } \ No newline at end of file diff --git a/lib/models/dhcp.dart b/lib/models/dhcp.dart index 21b460b..9e7f7ca 100644 --- a/lib/models/dhcp.dart +++ b/lib/models/dhcp.dart @@ -1,9 +1,11 @@ import 'dart:convert'; class DhcpModel { + bool dhcpAvailable; List networkInterfaces; - DhcpStatus dhcpStatus; + DhcpStatus? dhcpStatus; DhcpModel({ + required this.dhcpAvailable, required this.networkInterfaces, required this.dhcpStatus, }); diff --git a/lib/providers/dhcp_provider.dart b/lib/providers/dhcp_provider.dart index 637f870..4fbd66b 100644 --- a/lib/providers/dhcp_provider.dart +++ b/lib/providers/dhcp_provider.dart @@ -69,7 +69,7 @@ class DhcpProvider with ChangeNotifier { if (result.successful == true) { DhcpModel data = dhcp!; - data.dhcpStatus.staticLeases = data.dhcpStatus.staticLeases.where((l) => l.mac != lease.mac).toList(); + data.dhcpStatus!.staticLeases = data.dhcpStatus!.staticLeases.where((l) => l.mac != lease.mac).toList(); setDhcpData(data); return true; } @@ -90,7 +90,7 @@ class DhcpProvider with ChangeNotifier { if (result.successful == true) { DhcpModel data = dhcp!; - data.dhcpStatus.staticLeases.add(lease); + data.dhcpStatus!.staticLeases.add(lease); setDhcpData(data); return result; } diff --git a/lib/screens/settings/dhcp/dhcp.dart b/lib/screens/settings/dhcp/dhcp.dart index 88f1dad..fff464e 100644 --- a/lib/screens/settings/dhcp/dhcp.dart +++ b/lib/screens/settings/dhcp/dhcp.dart @@ -7,8 +7,10 @@ import 'package:flutter_split_view/flutter_split_view.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:adguard_home_manager/widgets/section_label.dart'; +import 'package:adguard_home_manager/screens/settings/dhcp/dhcp_not_available.dart'; import 'package:adguard_home_manager/widgets/confirm_action_modal.dart'; +import 'package:adguard_home_manager/screens/settings/dhcp/dhcp_main_button.dart'; +import 'package:adguard_home_manager/widgets/section_label.dart'; import 'package:adguard_home_manager/screens/settings/dhcp/dhcp_leases.dart'; import 'package:adguard_home_manager/screens/settings/dhcp/select_interface_modal.dart'; @@ -22,7 +24,7 @@ import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart'; class DhcpScreen extends StatefulWidget { - const DhcpScreen({Key? key}) : super(key: key); + const DhcpScreen({super.key}); @override State createState() => _DhcpScreenState(); @@ -55,24 +57,25 @@ class _DhcpScreenState extends State { void loadDhcpStatus() async { final result = await Provider.of(context, listen: false).loadDhcpStatus(); - if (mounted && result == true) { - final dhcpProvider = Provider.of(context, listen: false); - if (dhcpProvider.dhcp != null) { - setState(() { - if (dhcpProvider.dhcp!.dhcpStatus.interfaceName != null && dhcpProvider.dhcp!.dhcpStatus.interfaceName != '') { - try {selectedInterface = dhcpProvider.dhcp!.networkInterfaces.firstWhere((iface) => iface.name == dhcpProvider.dhcp!.dhcpStatus.interfaceName);} catch (_) {} - enabled = dhcpProvider.dhcp!.dhcpStatus.enabled; - if (dhcpProvider.dhcp!.dhcpStatus.v4 != null) { - ipv4StartRangeController.text = dhcpProvider.dhcp!.dhcpStatus.v4!.rangeStart; - ipv4EndRangeController.text = dhcpProvider.dhcp!.dhcpStatus.v4!.rangeEnd ?? ''; - ipv4SubnetMaskController.text = dhcpProvider.dhcp!.dhcpStatus.v4!.subnetMask ?? ''; - ipv4GatewayController.text = dhcpProvider.dhcp!.dhcpStatus.v4!.gatewayIp ?? ''; - ipv4LeaseTimeController.text = dhcpProvider.dhcp!.dhcpStatus.v4!.leaseDuration.toString(); - } - } - }); + if (!mounted || result == false) return; + + final dhcpProvider = Provider.of(context, listen: false); + if (dhcpProvider.dhcp == null) return; + + setState(() { + if (dhcpProvider.dhcp!.dhcpStatus!.interfaceName != null && dhcpProvider.dhcp!.dhcpStatus!.interfaceName != '') { + try {selectedInterface = dhcpProvider.dhcp!.networkInterfaces.firstWhere((iface) => iface.name == dhcpProvider.dhcp!.dhcpStatus!.interfaceName);} catch (_) {} + enabled = dhcpProvider.dhcp!.dhcpStatus!.enabled; + if (dhcpProvider.dhcp!.dhcpStatus!.v4 != null) { + ipv4StartRangeController.text = dhcpProvider.dhcp!.dhcpStatus!.v4!.rangeStart; + ipv4EndRangeController.text = dhcpProvider.dhcp!.dhcpStatus!.v4!.rangeEnd ?? ''; + ipv4SubnetMaskController.text = dhcpProvider.dhcp!.dhcpStatus!.v4!.subnetMask ?? ''; + ipv4GatewayController.text = dhcpProvider.dhcp!.dhcpStatus!.v4!.gatewayIp ?? ''; + ipv4LeaseTimeController.text = dhcpProvider.dhcp!.dhcpStatus!.v4!.leaseDuration.toString(); + } } - } + }); + checkDataValid(); } @@ -266,8 +269,8 @@ class _DhcpScreenState extends State { if (result.successful == true) { DhcpModel data = dhcpProvider.dhcp!; - data.dhcpStatus.staticLeases = []; - data.dhcpStatus.leases = []; + data.dhcpStatus!.staticLeases = []; + data.dhcpStatus!.leases = []; dhcpProvider.setDhcpData(data); showSnacbkar( @@ -350,6 +353,14 @@ class _DhcpScreenState extends State { }); } + if ( + dhcpProvider.loadStatus == LoadStatus.loaded && + dhcpProvider.dhcp != null && + dhcpProvider.dhcp!.dhcpAvailable == false + ) { + return const DhcpNotAvailable(); + } + return Scaffold( appBar: AppBar( title: Text(AppLocalizations.of(context)!.dhcpSettings), @@ -429,60 +440,10 @@ class _DhcpScreenState extends State { return SingleChildScrollView( child: Wrap( children: [ - Padding( - padding: const EdgeInsets.only( - top: 10, - left: 16, - right: 16 - ), - child: Material( - color: Theme.of(context).colorScheme.primary.withOpacity(0.1), - borderRadius: BorderRadius.circular(28), - child: InkWell( - onTap: selectedInterface != null - ? () => setState(() => enabled = !enabled) - : null, - borderRadius: BorderRadius.circular(28), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 12 - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - AppLocalizations.of(context)!.enableDhcpServer, - style: TextStyle( - fontSize: 16, - color: Theme.of(context).colorScheme.onSurface - ), - ), - if (selectedInterface != null) ...[ - Text( - selectedInterface!.name, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).listTileTheme.textColor - ), - ) - ] - ], - ), - Switch( - value: enabled, - onChanged: selectedInterface != null - ? (value) => setState(() => enabled = value) - : null, - ), - ], - ), - ), - ), - ), + DhcpMainButton( + selectedInterface: selectedInterface, + enabled: enabled, + setEnabled: (v) => setState(() => enabled = v) ), if (selectedInterface!.ipv4Addresses.isNotEmpty) ...[ SectionLabel( @@ -491,125 +452,47 @@ class _DhcpScreenState extends State { top: 24, left: 16, right: 16, bottom: 8 ) ), - FractionallySizedBox( - widthFactor: width > 900 ? 0.5 : 1, - child: Padding( - padding: width > 900 - ? const EdgeInsets.only(top: 12, bottom: 12, left: 16, right: 8) - : const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: TextFormField( - controller: ipv4StartRangeController, - onChanged: (value) => validateIpV4(value, 'ipv4StartRangeError', AppLocalizations.of(context)!.ipNotValid), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.skip_previous_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: ipv4StartRangeError, - labelText: AppLocalizations.of(context)!.startOfRange, - ), - keyboardType: TextInputType.number, - ), - ), + _DhcpField( + icon: Icons.skip_previous_rounded, + label: AppLocalizations.of(context)!.startOfRange, + controller: ipv4StartRangeController, + onChanged: (value) => validateIpV4(value, 'ipv4StartRangeError', AppLocalizations.of(context)!.ipNotValid), + error: ipv4StartRangeError ), - FractionallySizedBox( - widthFactor: width > 900 ? 0.5 : 1, - child: Padding( - padding: width > 900 - ? const EdgeInsets.only(top: 12, bottom: 12, left: 8, right: 16) - : const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: TextFormField( - controller: ipv4EndRangeController, - onChanged: (value) => validateIpV4(value, 'ipv4EndRangeError', AppLocalizations.of(context)!.ipNotValid), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.skip_next_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: ipv4EndRangeError, - labelText: AppLocalizations.of(context)!.endOfRange, - ), - keyboardType: TextInputType.number, - ), - ), + _DhcpField( + icon: Icons.skip_next_rounded, + label: AppLocalizations.of(context)!.endOfRange, + controller: ipv4EndRangeController, + onChanged: (value) => validateIpV4(value, 'ipv4EndRangeError', AppLocalizations.of(context)!.ipNotValid), + error: ipv4EndRangeError ), - FractionallySizedBox( - widthFactor: width > 900 ? 0.5 : 1, - child: Padding( - padding: width > 900 - ? const EdgeInsets.only(top: 12, bottom: 12, left: 16, right: 8) - : const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: TextFormField( - controller: ipv4SubnetMaskController, - onChanged: (value) => validateIpV4(value, 'ipv4SubnetMaskError', AppLocalizations.of(context)!.subnetMaskNotValid), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.hub_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: ipv4SubnetMaskError, - labelText: AppLocalizations.of(context)!.subnetMask, - ), - keyboardType: TextInputType.number, - ), - ), + _DhcpField( + icon: Icons.hub_rounded, + label: AppLocalizations.of(context)!.subnetMask, + controller: ipv4SubnetMaskController, + onChanged: (value) => validateIpV4(value, 'ipv4SubnetMaskError', AppLocalizations.of(context)!.subnetMaskNotValid), + error: ipv4SubnetMaskError ), - FractionallySizedBox( - widthFactor: width > 900 ? 0.5 : 1, - child: Padding( - padding: width > 900 - ? const EdgeInsets.only(top: 12, bottom: 12, left: 8, right: 16) - : const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: TextFormField( - controller: ipv4GatewayController, - onChanged: (value) => validateIpV4(value, 'ipv4GatewayError', AppLocalizations.of(context)!.gatewayNotValid), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.router_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: ipv4GatewayError, - labelText: AppLocalizations.of(context)!.gateway, - ), - keyboardType: TextInputType.number, - ), - ), + _DhcpField( + icon: Icons.router_rounded, + label: AppLocalizations.of(context)!.gateway, + controller: ipv4GatewayController, + onChanged: (value) => validateIpV4(value, 'ipv4GatewayError', AppLocalizations.of(context)!.gatewayNotValid), + error: ipv4GatewayError ), - FractionallySizedBox( - widthFactor: 1, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: TextFormField( - controller: ipv4LeaseTimeController, - onChanged: (value) { - if (int.tryParse(value).runtimeType == int) { - setState(() => ipv4LeaseTimeError = null); - } - else { - setState(() => ipv4LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid); - } - }, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.timer), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: ipv4LeaseTimeError, - labelText: AppLocalizations.of(context)!.leaseTime, - ), - keyboardType: TextInputType.number, - ), - ), + _DhcpField( + icon: Icons.timer, + label: AppLocalizations.of(context)!.leaseTime, + controller: ipv4LeaseTimeController, + onChanged: (value) { + if (int.tryParse(value).runtimeType == int) { + setState(() => ipv4LeaseTimeError = null); + } + else { + setState(() => ipv4LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid); + } + }, + error: ipv4LeaseTimeError ), ], if (selectedInterface!.ipv6Addresses.isNotEmpty) ...[ @@ -617,80 +500,34 @@ class _DhcpScreenState extends State { label: AppLocalizations.of(context)!.ipv6settings, padding: const EdgeInsets.all(16) ), - FractionallySizedBox( - widthFactor: width > 900 ? 0.5 : 1, - child: Padding( - padding: width > 900 - ? const EdgeInsets.only(top: 8, bottom: 12, left: 16, right: 8) - : const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: TextFormField( - controller: ipv6StartRangeController, - onChanged: (value) => validateIpV4(value, 'ipv6StartRangeError', AppLocalizations.of(context)!.ipNotValid), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.skip_next_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: ipv6StartRangeError, - labelText: AppLocalizations.of(context)!.startOfRange, - ), - keyboardType: TextInputType.number, - ), - ), + _DhcpField( + icon: Icons.skip_next_rounded, + label: AppLocalizations.of(context)!.startOfRange, + controller: ipv6StartRangeController, + onChanged: (value) => validateIpV6(value, 'ipv6StartRangeError', AppLocalizations.of(context)!.ipNotValid), + error: ipv6StartRangeError ), - FractionallySizedBox( - widthFactor: width > 900 ? 0.5 : 1, - child: Padding( - padding: width > 900 - ? const EdgeInsets.only(top: 8, bottom: 12, left: 8, right: 16) - : const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: TextFormField( - controller: ipv6EndRangeController, - onChanged: (value) => validateIpV4(value, 'ipv6EndRangeError', AppLocalizations.of(context)!.ipNotValid), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.skip_previous_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: ipv6EndRangeError, - labelText: AppLocalizations.of(context)!.endOfRange, - ), - keyboardType: TextInputType.number, - ), - ), - ), - FractionallySizedBox( - widthFactor: 1, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: TextFormField( - controller: ipv6LeaseTimeController, - onChanged: (value) { - if (int.tryParse(value).runtimeType == int) { - setState(() => ipv6LeaseTimeError = null); - } - else { - setState(() => ipv6LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid); - } - }, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.timer), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: ipv6LeaseTimeError, - labelText: AppLocalizations.of(context)!.leaseTime, - ), - keyboardType: TextInputType.number, - ), - ), + _DhcpField( + icon: Icons.skip_previous_rounded, + label: AppLocalizations.of(context)!.endOfRange, + controller: ipv6EndRangeController, + onChanged: (value) => validateIpV6(value, 'ipv6EndRangeError', AppLocalizations.of(context)!.ipNotValid), + error: ipv6EndRangeError ), + _DhcpField( + icon: Icons.timer, + label: AppLocalizations.of(context)!.leaseTime, + controller: ipv6LeaseTimeController, + onChanged: (value) { + if (int.tryParse(value).runtimeType == int) { + setState(() => ipv6LeaseTimeError = null); + } + else { + setState(() => ipv6LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid); + } + }, + error: ipv6LeaseTimeError + ) ], const SizedBox(height: 20), SectionLabel( @@ -704,7 +541,7 @@ class _DhcpScreenState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (context) => DhcpLeases( - items: dhcpProvider.dhcp!.dhcpStatus.leases, + items: dhcpProvider.dhcp!.dhcpStatus!.leases, staticLeases: false, ) ) @@ -739,7 +576,7 @@ class _DhcpScreenState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (context) => DhcpLeases( - items: dhcpProvider.dhcp!.dhcpStatus.staticLeases, + items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases, staticLeases: true, ) ) @@ -775,7 +612,7 @@ class _DhcpScreenState extends State { if (!(Platform.isAndroid || Platform.isIOS)) { SplitView.of(context).push( DhcpLeases( - items: dhcpProvider.dhcp!.dhcpStatus.leases, + items: dhcpProvider.dhcp!.dhcpStatus!.leases, staticLeases: false, ) ); @@ -784,7 +621,7 @@ class _DhcpScreenState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (context) => DhcpLeases( - items: dhcpProvider.dhcp!.dhcpStatus.leases, + items: dhcpProvider.dhcp!.dhcpStatus!.leases, staticLeases: false, ) ) @@ -804,7 +641,7 @@ class _DhcpScreenState extends State { if (!(Platform.isAndroid || Platform.isIOS)) { SplitView.of(context).push( DhcpLeases( - items: dhcpProvider.dhcp!.dhcpStatus.staticLeases, + items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases, staticLeases: true, ) ); @@ -813,7 +650,7 @@ class _DhcpScreenState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (context) => DhcpLeases( - items: dhcpProvider.dhcp!.dhcpStatus.staticLeases, + items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases, staticLeases: true, ) ) @@ -899,4 +736,49 @@ class _DhcpScreenState extends State { ) ); } +} + +class _DhcpField extends StatelessWidget { + final IconData icon; + final String label; + final TextEditingController controller; + final void Function(String) onChanged; + final String? error; + + const _DhcpField({ + required this.icon, + required this.label, + required this.controller, + required this.onChanged, + required this.error, + }); + + @override + Widget build(BuildContext context) { + final width = MediaQuery.of(context).size.width; + + return FractionallySizedBox( + widthFactor: width > 900 ? 0.5 : 1, + child: Padding( + padding: width > 900 + ? const EdgeInsets.only(top: 12, bottom: 12, left: 16, right: 8) + : const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: TextFormField( + controller: controller, + onChanged: onChanged, + decoration: InputDecoration( + prefixIcon: Icon(icon), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + errorText: error, + labelText: label, + ), + keyboardType: TextInputType.number, + ), + ), + ); + } } \ No newline at end of file diff --git a/lib/screens/settings/dhcp/dhcp_main_button.dart b/lib/screens/settings/dhcp/dhcp_main_button.dart new file mode 100644 index 0000000..3e46a14 --- /dev/null +++ b/lib/screens/settings/dhcp/dhcp_main_button.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import 'package:adguard_home_manager/models/dhcp.dart'; + +class DhcpMainButton extends StatelessWidget { + final NetworkInterface? selectedInterface; + final bool enabled; + final void Function(bool) setEnabled; + + const DhcpMainButton({ + super.key, + required this.selectedInterface, + required this.enabled, + required this.setEnabled, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only( + top: 10, + left: 16, + right: 16 + ), + child: Material( + color: Theme.of(context).colorScheme.primary.withOpacity(0.1), + borderRadius: BorderRadius.circular(28), + child: InkWell( + onTap: selectedInterface != null + ? () => setEnabled(!enabled) + : null, + borderRadius: BorderRadius.circular(28), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 12 + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.of(context)!.enableDhcpServer, + style: TextStyle( + fontSize: 16, + color: Theme.of(context).colorScheme.onSurface + ), + ), + if (selectedInterface != null) ...[ + Text( + selectedInterface!.name, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).listTileTheme.textColor + ), + ) + ] + ], + ), + Switch( + value: enabled, + onChanged: selectedInterface != null + ? (value) => setEnabled(value) + : null, + ), + ], + ), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/settings/dhcp/dhcp_not_available.dart b/lib/screens/settings/dhcp/dhcp_not_available.dart new file mode 100644 index 0000000..ceb3f6b --- /dev/null +++ b/lib/screens/settings/dhcp/dhcp_not_available.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import 'package:adguard_home_manager/functions/desktop_mode.dart'; + +class DhcpNotAvailable extends StatelessWidget { + const DhcpNotAvailable({super.key}); + + @override + Widget build(BuildContext context) { + final width = MediaQuery.of(context).size.width; + + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.dhcpSettings), + centerTitle: false, + surfaceTintColor: isDesktop(width) ? Colors.transparent : null, + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + AppLocalizations.of(context)!.dhcpNotAvailable, + textAlign: TextAlign.center, + style: const TextStyle( + fontWeight: FontWeight.w400, + fontSize: 24 + ), + ), + const SizedBox(height: 20), + Text( + AppLocalizations.of(context)!.osServerInstalledIncompatible, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 16, + color: Theme.of(context).colorScheme.onSurfaceVariant + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/services/api_client.dart b/lib/services/api_client.dart index 636cc34..f626f75 100644 --- a/lib/services/api_client.dart +++ b/lib/services/api_client.dart @@ -482,8 +482,13 @@ class ApiClientV2 { return ApiResponse( successful: true, content: DhcpModel( + dhcpAvailable: jsonDecode(results[1].body!)['message'] != null + ? false + : true, networkInterfaces: interfaces, - dhcpStatus: DhcpStatus.fromJson(jsonDecode(results[1].body!)) + dhcpStatus: jsonDecode(results[1].body!)['message'] != null + ? null + : DhcpStatus.fromJson(jsonDecode(results[1].body!)) ) ); } catch (e, stackTrace) { diff --git a/lib/services/http_requests.dart b/lib/services/http_requests.dart index 2d001f2..37a2cc8 100644 --- a/lib/services/http_requests.dart +++ b/lib/services/http_requests.dart @@ -1352,6 +1352,7 @@ class ApiClient { return { 'result': 'success', 'data': DhcpModel( + dhcpAvailable: true, networkInterfaces: interfaces, dhcpStatus: DhcpStatus.fromJson(jsonDecode(result[1]['body'])) )