mobile_nebula/lib/screens/HostInfoScreen.dart

204 lines
7.1 KiB
Dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:mobile_nebula/components/SimplePage.dart';
import 'package:mobile_nebula/components/config/ConfigCheckboxItem.dart';
import 'package:mobile_nebula/components/config/ConfigItem.dart';
import 'package:mobile_nebula/components/config/ConfigPageItem.dart';
import 'package:mobile_nebula/components/config/ConfigSection.dart';
import 'package:mobile_nebula/models/Certificate.dart';
import 'package:mobile_nebula/models/HostInfo.dart';
import 'package:mobile_nebula/models/Site.dart';
import 'package:mobile_nebula/screens/siteConfig/CertificateDetailsScreen.dart';
import 'package:mobile_nebula/services/utils.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
class HostInfoScreen extends StatefulWidget {
const HostInfoScreen({
Key? key,
required this.hostInfo,
required this.isLighthouse,
required this.pending,
this.onChanged,
required this.site,
required this.supportsQRScanning,
}) : super(key: key);
final bool isLighthouse;
final bool pending;
final HostInfo hostInfo;
final Function? onChanged;
final Site site;
final bool supportsQRScanning;
@override
_HostInfoScreenState createState() => _HostInfoScreenState();
}
//TODO: have a config option to refresh hostmaps on a cadence (applies to 3 screens so far)
class _HostInfoScreenState extends State<HostInfoScreen> {
late HostInfo hostInfo;
RefreshController refreshController = RefreshController(initialRefresh: false);
@override
void initState() {
_setHostInfo(widget.hostInfo);
super.initState();
}
@override
Widget build(BuildContext context) {
final title = widget.pending ? 'Pending' : 'Active';
return SimplePage(
title: Text('$title Host Info'),
refreshController: refreshController,
onRefresh: () async {
await _getHostInfo();
refreshController.refreshCompleted();
},
leadingAction: Utils.leadingBackWidget(context, onPressed: () {
Navigator.pop(context);
}),
child: Column(
children: [_buildMain(), _buildDetails(), _buildRemotes(), !widget.pending ? _buildClose() : Container()]));
}
Widget _buildMain() {
return ConfigSection(children: [
ConfigItem(label: Text('VPN IP'), labelWidth: 150, content: SelectableText(hostInfo.vpnIp)),
hostInfo.cert != null
? ConfigPageItem(
label: Text('Certificate'),
labelWidth: 150,
content: Text(hostInfo.cert!.details.name),
onPressed: () => Utils.openPage(
context,
(context) => CertificateDetailsScreen(
certInfo: CertificateInfo(cert: hostInfo.cert!),
supportsQRScanning: widget.supportsQRScanning,
)))
: Container(),
]);
}
Widget _buildDetails() {
return ConfigSection(children: <Widget>[
ConfigItem(
label: Text('Lighthouse'), labelWidth: 150, content: SelectableText(widget.isLighthouse ? 'Yes' : 'No')),
ConfigItem(label: Text('Local Index'), labelWidth: 150, content: SelectableText('${hostInfo.localIndex}')),
ConfigItem(label: Text('Remote Index'), labelWidth: 150, content: SelectableText('${hostInfo.remoteIndex}')),
ConfigItem(
label: Text('Message Counter'), labelWidth: 150, content: SelectableText('${hostInfo.messageCounter}')),
]);
}
Widget _buildRemotes() {
if (hostInfo.remoteAddresses.length == 0) {
return ConfigSection(
label: 'REMOTES', children: [ConfigItem(content: Text('No remote addresses yet'), labelWidth: 0)]);
}
return widget.pending ? _buildStaticRemotes() : _buildEditRemotes();
}
Widget _buildEditRemotes() {
List<Widget> items = [];
final currentRemote = hostInfo.currentRemote.toString();
final double ipWidth =
Utils.textSize("000.000.000.000:000000", CupertinoTheme.of(context).textTheme.textStyle).width;
hostInfo.remoteAddresses.forEach((remoteObj) {
String remote = remoteObj.toString();
items.add(ConfigCheckboxItem(
key: Key(remote),
label: Text(remote), //TODO: need to do something to adjust the font size in the event we have an ipv6 address
labelWidth: ipWidth,
checked: currentRemote == remote,
onChanged: () async {
if (remote == currentRemote) {
return;
}
try {
final h = await widget.site.setRemoteForTunnel(hostInfo.vpnIp, remote);
if (h != null) {
_setHostInfo(h);
}
} catch (err) {
Utils.popError(context, 'Error while changing the remote', err.toString());
}
},
));
});
return ConfigSection(label: items.length > 0 ? 'Tap to change the active address' : null, children: items);
}
Widget _buildStaticRemotes() {
List<Widget> items = [];
final currentRemote = hostInfo.currentRemote.toString();
final double ipWidth =
Utils.textSize("000.000.000.000:000000", CupertinoTheme.of(context).textTheme.textStyle).width;
hostInfo.remoteAddresses.forEach((remoteObj) {
String remote = remoteObj.toString();
items.add(ConfigCheckboxItem(
key: Key(remote),
label: Text(remote), //TODO: need to do something to adjust the font size in the event we have an ipv6 address
labelWidth: ipWidth,
checked: currentRemote == remote,
));
});
return ConfigSection(label: items.length > 0 ? 'REMOTES' : null, children: items);
}
Widget _buildClose() {
return Padding(
padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10),
child: SizedBox(
width: double.infinity,
child: PlatformElevatedButton(
child: Text('Close Tunnel'),
color: CupertinoColors.systemRed.resolveFrom(context),
onPressed: () => Utils.confirmDelete(context, 'Close Tunnel?', () async {
try {
await widget.site.closeTunnel(hostInfo.vpnIp);
if (widget.onChanged != null) {
widget.onChanged!();
}
Navigator.pop(context);
} catch (err) {
Utils.popError(context, 'Error while trying to close the tunnel', err.toString());
}
}, deleteLabel: 'Close'))));
}
_getHostInfo() async {
try {
final h = await widget.site.getHostInfo(hostInfo.vpnIp, widget.pending);
if (h == null) {
return Utils.popError(context, '', 'The tunnel for this host no longer exists');
}
_setHostInfo(h);
} catch (err) {
Utils.popError(context, 'Failed to refresh host info', err.toString());
}
}
_setHostInfo(HostInfo h) {
h.remoteAddresses.sort((a, b) {
final diff = a.ip.compareTo(b.ip);
return diff == 0 ? a.port - b.port : diff;
});
setState(() {
hostInfo = h;
});
}
}