mobile_nebula/lib/screens/HostInfoScreen.dart

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

204 lines
7.1 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/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);
2020-07-27 20:43:58 +00:00
final bool isLighthouse;
final bool pending;
final HostInfo hostInfo;
final Function? onChanged;
2020-07-27 20:43:58 +00:00
final Site site;
final bool supportsQRScanning;
2020-07-27 20:43:58 +00:00
@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;
2020-07-27 20:43:58 +00:00
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'),
2020-07-27 20:43:58 +00:00
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)),
2020-07-27 20:43:58 +00:00
hostInfo.cert != null
? ConfigPageItem(
label: Text('Certificate'),
labelWidth: 150,
content: Text(hostInfo.cert!.details.name),
2020-07-27 20:43:58 +00:00
onPressed: () => Utils.openPage(
context,
(context) => CertificateDetailsScreen(
certInfo: CertificateInfo(cert: hostInfo.cert!),
supportsQRScanning: widget.supportsQRScanning,
)))
2020-07-27 20:43:58 +00:00
: Container(),
]);
}
Widget _buildDetails() {
return ConfigSection(children: <Widget>[
ConfigItem(
2022-07-27 16:38:02 +00:00
label: Text('Lighthouse'), labelWidth: 150, content: SelectableText(widget.isLighthouse ? 'Yes' : 'No')),
ConfigItem(label: Text('Local Index'), labelWidth: 150, content: SelectableText('${hostInfo.localIndex}')),
2022-07-27 16:38:02 +00:00
ConfigItem(label: Text('Remote Index'), labelWidth: 150, content: SelectableText('${hostInfo.remoteIndex}')),
2020-07-27 20:43:58 +00:00
ConfigItem(
2022-07-27 16:38:02 +00:00
label: Text('Message Counter'), labelWidth: 150, content: SelectableText('${hostInfo.messageCounter}')),
2020-07-27 20:43:58 +00:00
]);
}
Widget _buildRemotes() {
if (hostInfo.remoteAddresses.length == 0) {
2021-04-23 21:23:06 +00:00
return ConfigSection(
label: 'REMOTES', children: [ConfigItem(content: Text('No remote addresses yet'), labelWidth: 0)]);
2020-07-27 20:43:58 +00:00
}
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),
2021-04-23 21:23:06 +00:00
label: Text(remote), //TODO: need to do something to adjust the font size in the event we have an ipv6 address
2020-07-27 20:43:58 +00:00
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());
2020-07-27 20:43:58 +00:00
}
},
));
});
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),
2021-04-23 21:23:06 +00:00
label: Text(remote), //TODO: need to do something to adjust the font size in the event we have an ipv6 address
2020-07-27 20:43:58 +00:00
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,
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(
2020-07-27 20:43:58 +00:00
child: Text('Close Tunnel'),
onPressed: () => Utils.confirmDelete(context, 'Close Tunnel?', () async {
try {
await widget.site.closeTunnel(hostInfo.vpnIp);
if (widget.onChanged != null) {
widget.onChanged!();
2020-07-27 20:43:58 +00:00
}
Navigator.pop(context);
} catch (err) {
Utils.popError(context, 'Error while trying to close the tunnel', err.toString());
2020-07-27 20:43:58 +00:00
}
}, 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());
2020-07-27 20:43:58 +00:00
}
}
_setHostInfo(HostInfo h) {
h.remoteAddresses.sort((a, b) {
2021-04-23 21:23:06 +00:00
final diff = a.ip.compareTo(b.ip);
2020-07-27 20:43:58 +00:00
return diff == 0 ? a.port - b.port : diff;
});
setState(() {
hostInfo = h;
});
}
}