2020-07-27 20:43:58 +00:00
|
|
|
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<IPAndPort> destinations;
|
|
|
|
final String nebulaIp;
|
|
|
|
final bool lighthouse;
|
|
|
|
final ValueChanged<Hostmap> onSave;
|
|
|
|
final Function onDelete;
|
|
|
|
|
|
|
|
@override
|
|
|
|
_StaticHostmapScreenState createState() => _StaticHostmapScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
|
|
|
|
Map<Key, _IPAndPort> _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();
|
|
|
|
}
|
|
|
|
|
2021-04-23 21:23:06 +00:00
|
|
|
// _addDestination() above sets us to changed, set it back to false since we are at the default state
|
|
|
|
changed = false;
|
2020-07-27 20:43:58 +00:00
|
|
|
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: <Widget>[
|
|
|
|
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,
|
2022-07-27 16:38:02 +00:00
|
|
|
child: PlatformElevatedButton(
|
2020-07-27 20:43:58 +00:00
|
|
|
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<Widget> _buildHosts() {
|
|
|
|
List<Widget> 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: <Widget>[
|
|
|
|
Expanded(
|
|
|
|
child: IPAndPortFormField(
|
|
|
|
ipHelp: 'public ip or name',
|
|
|
|
ipTextAlign: TextAlign.end,
|
2021-04-23 21:23:06 +00:00
|
|
|
enableIPV6: true,
|
2020-07-27 20:43:58 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|