mobile_nebula/lib/screens/siteConfig/StaticHostmapScreen.dart

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

224 lines
7.2 KiB
Dart
Raw Normal View History

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';
Add DangerButton component (#219) This pulls out a component that we can use for "dangerous" operations like deleting, and styles it in one place. It also starts to move us slowly towards Material 3, with the rounded corners on these buttons in Android. Android: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="425" alt="Android Studio 2025-01-15 14 16 36" src="https://github.com/user-attachments/assets/4823e551-6a40-48dd-9bc1-3004699b90ea" />|<img width="417" alt="Android Studio 2025-01-15 14 16 47" src="https://github.com/user-attachments/assets/df5461fd-586e-47bb-99b9-0212e63f0454" />|<img width="413" alt="Android Studio 2025-01-15 14 15 59" src="https://github.com/user-attachments/assets/d88a6225-b71a-4886-8387-e35811a3a6ec" />|<img width="418" alt="Android Studio 2025-01-15 14 16 15" src="https://github.com/user-attachments/assets/d4f23b1c-7003-4a00-b865-4a123d8fe3e9" />| iOS: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="437" alt="Simulator 2025-01-15 15 56 26" src="https://github.com/user-attachments/assets/87c4eed3-6d07-4858-8ad8-d8c011538154" />|<img width="445" alt="Simulator 2025-01-15 15 56 36" src="https://github.com/user-attachments/assets/9dc5b174-7bc7-48ec-a3c0-61633168c31a" />|<img width="439" alt="Simulator 2025-01-15 16 05 23" src="https://github.com/user-attachments/assets/31dc9ab6-8a3c-49c7-892d-627f16e2a8cd" />|<img width="444" alt="Simulator 2025-01-15 16 05 37" src="https://github.com/user-attachments/assets/979280d6-e1f4-4d57-a86a-10bb4def729a" />|
2025-01-16 13:16:23 +00:00
import 'package:mobile_nebula/components/DangerButton.dart';
2020-07-27 20:43:58 +00:00
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({required this.focusNode, required this.destination});
2020-07-27 20:43:58 +00:00
}
class StaticHostmapScreen extends StatefulWidget {
StaticHostmapScreen({
Key? key,
this.nebulaIp = '',
destinations,
this.lighthouse = false,
this.onDelete,
required this.onSave,
}) : this.destinations = destinations ?? [],
super(key: key);
2020-07-27 20:43:58 +00:00
final List<IPAndPort> destinations;
final String nebulaIp;
final bool lighthouse;
final ValueChanged<Hostmap>? onSave;
final Function? onDelete;
2020-07-27 20:43:58 +00:00
@override
_StaticHostmapScreenState createState() => _StaticHostmapScreenState();
}
class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
late Map<Key, _IPAndPort> _destinations;
late String _nebulaIp;
late bool _lighthouse;
2020-07-27 20:43:58 +00:00
bool changed = false;
@override
void initState() {
_nebulaIp = widget.nebulaIp;
_lighthouse = widget.lighthouse;
_destinations = {};
widget.destinations.forEach((dest) {
2020-07-27 20:43:58 +00:00
_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
? widget.onSave == null
? 'View Static Host'
: 'New Static Host'
: 'Edit Static Host',
2020-07-27 20:43:58 +00:00
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: widget.onSave == null
? Text(_nebulaIp, textAlign: TextAlign.end)
: IPFormField(
help: "Required",
initialValue: _nebulaIp,
ipOnly: true,
textAlign: TextAlign.end,
crossAxisAlignment: CrossAxisAlignment.end,
textInputAction: TextInputAction.next,
onSaved: (v) {
if (v != null) {
_nebulaIp = v;
}
})),
2020-07-27 20:43:58 +00:00
ConfigItem(
label: Text('Lighthouse'),
labelWidth: 200,
content: Container(
alignment: Alignment.centerRight,
child: Switch.adaptive(
value: _lighthouse,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: widget.onSave == null
? null
: (v) {
setState(() {
changed = true;
_lighthouse = v;
});
})),
2020-07-27 20:43:58 +00:00
),
]),
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,
Add DangerButton component (#219) This pulls out a component that we can use for "dangerous" operations like deleting, and styles it in one place. It also starts to move us slowly towards Material 3, with the rounded corners on these buttons in Android. Android: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="425" alt="Android Studio 2025-01-15 14 16 36" src="https://github.com/user-attachments/assets/4823e551-6a40-48dd-9bc1-3004699b90ea" />|<img width="417" alt="Android Studio 2025-01-15 14 16 47" src="https://github.com/user-attachments/assets/df5461fd-586e-47bb-99b9-0212e63f0454" />|<img width="413" alt="Android Studio 2025-01-15 14 15 59" src="https://github.com/user-attachments/assets/d88a6225-b71a-4886-8387-e35811a3a6ec" />|<img width="418" alt="Android Studio 2025-01-15 14 16 15" src="https://github.com/user-attachments/assets/d4f23b1c-7003-4a00-b865-4a123d8fe3e9" />| iOS: |Before Light|Before Dark|After Light|After Dark| |---|---|---|---| |<img width="437" alt="Simulator 2025-01-15 15 56 26" src="https://github.com/user-attachments/assets/87c4eed3-6d07-4858-8ad8-d8c011538154" />|<img width="445" alt="Simulator 2025-01-15 15 56 36" src="https://github.com/user-attachments/assets/9dc5b174-7bc7-48ec-a3c0-61633168c31a" />|<img width="439" alt="Simulator 2025-01-15 16 05 23" src="https://github.com/user-attachments/assets/31dc9ab6-8a3c-49c7-892d-627f16e2a8cd" />|<img width="444" alt="Simulator 2025-01-15 16 05 37" src="https://github.com/user-attachments/assets/979280d6-e1f4-4d57-a86a-10bb4def729a" />|
2025-01-16 13:16:23 +00:00
child: DangerButton(
child: Text('Delete'),
onPressed: () => Utils.confirmDelete(context, 'Delete host map?', () {
Navigator.of(context).pop();
widget.onDelete!();
}))))
2020-07-27 20:43:58 +00:00
: Container()
]));
}
_onSave() {
Navigator.pop(context);
if (widget.onSave != null) {
var map = Hostmap(nebulaIp: _nebulaIp, destinations: [], lighthouse: _lighthouse);
2020-07-27 20:43:58 +00:00
_destinations.forEach((_, dest) {
map.destinations.add(dest.destination);
});
2020-07-27 20:43:58 +00:00
widget.onSave!(map);
}
2020-07-27 20:43:58 +00:00
}
List<Widget> _buildHosts() {
List<Widget> items = [];
_destinations.forEach((key, dest) {
items.add(ConfigItem(
key: key,
label: Align(
alignment: Alignment.centerLeft,
child: widget.onSave == null
? Container()
: PlatformIconButton(
padding: EdgeInsets.zero,
icon: Icon(Icons.remove_circle, color: CupertinoColors.systemRed.resolveFrom(context)),
onPressed: () => setState(() {
_removeDestination(key);
_dismissKeyboard();
}))),
2020-07-27 20:43:58 +00:00
labelWidth: 70,
content: Row(children: <Widget>[
Expanded(
child: widget.onSave == null
? Text(dest.destination.toString(), textAlign: TextAlign.end)
: IPAndPortFormField(
ipHelp: 'public ip or name',
ipTextAlign: TextAlign.end,
enableIPV6: true,
noBorder: true,
initialValue: dest.destination,
onSaved: (v) {
if (v != null) {
dest.destination = v;
}
},
)),
2020-07-27 20:43:58 +00:00
]),
));
});
if (widget.onSave != null) {
items.add(ConfigButtonItem(
content: Text('Add another'),
onPressed: () => setState(() {
_addDestination();
_dismissKeyboard();
})));
}
2020-07-27 20:43:58 +00:00
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();
}
}