import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:mobile_nebula/components/FormPage.dart'; import 'package:mobile_nebula/components/IPAndPortFormField.dart'; import 'package:mobile_nebula/components/IPFormField.dart'; import 'package:mobile_nebula/components/config/ConfigButtonItem.dart'; import 'package:mobile_nebula/components/config/ConfigItem.dart'; import 'package:mobile_nebula/components/config/ConfigSection.dart'; import 'package:mobile_nebula/models/Hostmap.dart'; import 'package:mobile_nebula/models/IPAndPort.dart'; import 'package:mobile_nebula/services/utils.dart'; class _IPAndPort { final FocusNode focusNode; IPAndPort destination; _IPAndPort({this.focusNode, this.destination}); } class StaticHostmapScreen extends StatefulWidget { const StaticHostmapScreen( {Key key, this.nebulaIp, this.destinations, this.lighthouse = false, this.onDelete, @required this.onSave}) : super(key: key); final List destinations; final String nebulaIp; final bool lighthouse; final ValueChanged onSave; final Function onDelete; @override _StaticHostmapScreenState createState() => _StaticHostmapScreenState(); } class _StaticHostmapScreenState extends State { Map _destinations = {}; String _nebulaIp; bool _lighthouse; bool changed = false; @override void initState() { _nebulaIp = widget.nebulaIp; _lighthouse = widget.lighthouse; widget.destinations?.forEach((dest) { _destinations[UniqueKey()] = _IPAndPort(focusNode: FocusNode(), destination: dest); }); if (_destinations.length == 0) { _addDestination(); } // _addDestination() above sets us to changed, set it back to false since we are at the default state changed = false; super.initState(); } @override Widget build(BuildContext context) { return FormPage( title: widget.onDelete == null ? 'New Static Host' : 'Edit Static Host', changed: changed, onSave: _onSave, child: Column(children: [ ConfigSection(label: 'Maps a nebula ip address to multiple real world addresses', children: [ ConfigItem( label: Text('Nebula IP'), labelWidth: 200, content: IPFormField( help: "Required", initialValue: _nebulaIp, ipOnly: true, textAlign: TextAlign.end, crossAxisAlignment: CrossAxisAlignment.end, textInputAction: TextInputAction.next, onSaved: (v) { _nebulaIp = v; })), ConfigItem( label: Text('Lighthouse'), labelWidth: 200, content: Container( alignment: Alignment.centerRight, child: Switch.adaptive( value: _lighthouse, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, onChanged: (v) { setState(() { changed = true; _lighthouse = v; }); })), ), ]), ConfigSection( label: 'List of public ips or dns names where for this host', children: _buildHosts(), ), widget.onDelete != null ? Padding( padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10), child: SizedBox( width: double.infinity, child: PlatformElevatedButton( child: Text('Delete'), color: CupertinoColors.systemRed.resolveFrom(context), onPressed: () => Utils.confirmDelete(context, 'Delete host map?', () { Navigator.of(context).pop(); widget.onDelete(); }), ))) : Container() ])); } _onSave() { Navigator.pop(context); if (widget.onSave != null) { var map = Hostmap(nebulaIp: _nebulaIp, destinations: [], lighthouse: _lighthouse); _destinations.forEach((_, dest) { map.destinations.add(dest.destination); }); widget.onSave(map); } } List _buildHosts() { List items = []; _destinations.forEach((key, dest) { items.add(ConfigItem( key: key, label: Align( alignment: Alignment.centerLeft, child: PlatformIconButton( padding: EdgeInsets.zero, icon: Icon(Icons.remove_circle, color: CupertinoColors.systemRed.resolveFrom(context)), onPressed: () => setState(() { _removeDestination(key); _dismissKeyboard(); }))), labelWidth: 70, content: Row(children: [ Expanded( child: IPAndPortFormField( ipHelp: 'public ip or name', ipTextAlign: TextAlign.end, enableIPV6: true, noBorder: true, initialValue: dest.destination, onSaved: (v) { dest.destination = v; }, )), ]), )); }); items.add(ConfigButtonItem( content: Text('Add another'), onPressed: () => setState(() { _addDestination(); _dismissKeyboard(); }))); return items; } _addDestination() { changed = true; _destinations[UniqueKey()] = _IPAndPort(focusNode: FocusNode(), destination: IPAndPort()); // We can't onChanged here because it causes rendering issues on first build due to ensuring there is a single destination } _removeDestination(Key key) { changed = true; _destinations.remove(key); } _dismissKeyboard() { FocusScopeNode currentFocus = FocusScope.of(context); if (!currentFocus.hasPrimaryFocus) { currentFocus.unfocus(); } } @override void dispose() { _destinations.forEach((key, dest) { dest.focusNode.dispose(); }); super.dispose(); } }