mirror of
https://github.com/DefinedNet/mobile_nebula.git
synced 2025-01-30 17:07:02 +00:00
ad45cc1d78
TODO: - [x] Address Android, which this probably breaks. Previously the back button was taking up all the room in the title bar, this fixes it so that we can see titles again. It also truncates site names so they stay to one line. |Before|After| |---|---| |![image](https://github.com/user-attachments/assets/3e07a50d-fb40-40da-87f8-4d623019b26d)|![Simulator 2025-01-23 16 32 34](https://github.com/user-attachments/assets/ea668973-e67d-4fc5-8731-578e5f3fdd27)| |Before|After| |---|---| |![image](https://github.com/user-attachments/assets/d95e1a9d-f431-42aa-a9f2-357b20c37abb)|![Simulator 2025-01-23 16 11 15](https://github.com/user-attachments/assets/ff3f664b-1983-4514-a492-cf585153e294)| |Before|After| |---|---| |![image](https://github.com/user-attachments/assets/0ea3aa0d-340a-44db-8a0a-e0c8032c2450)|![image](https://github.com/user-attachments/assets/fb7e26c5-5c67-4dd7-808c-d471ca1e913e)| |Before|After| |---|---| |![image](https://github.com/user-attachments/assets/bffec7e3-561d-4a43-ab8a-3bd1cc95003c)|![Simulator 2025-01-23 16 13 23](https://github.com/user-attachments/assets/288c1f7f-4d79-4b59-b693-0cbcdd2024db)| A few other "After" screenshots: |Logs|DN enrollment| |---|---| |![Simulator 2025-01-23 16 30 48](https://github.com/user-attachments/assets/4698939e-c4ad-4929-bd0b-1b72fc21c439)|![image](https://github.com/user-attachments/assets/4c738c41-af3c-4465-9907-76fce34ecdd9)|
141 lines
4 KiB
Dart
141 lines
4 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:mobile_nebula/components/SimplePage.dart';
|
|
import 'package:mobile_nebula/components/config/ConfigPageItem.dart';
|
|
import 'package:mobile_nebula/components/config/ConfigSection.dart';
|
|
import 'package:mobile_nebula/models/HostInfo.dart';
|
|
import 'package:mobile_nebula/models/Site.dart';
|
|
import 'package:mobile_nebula/screens/HostInfoScreen.dart';
|
|
import 'package:mobile_nebula/services/utils.dart';
|
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
|
|
|
class SiteTunnelsScreen extends StatefulWidget {
|
|
const SiteTunnelsScreen({
|
|
Key? key,
|
|
required this.site,
|
|
required this.tunnels,
|
|
required this.pending,
|
|
required this.onChanged,
|
|
required this.supportsQRScanning,
|
|
}) : super(key: key);
|
|
|
|
final Site site;
|
|
final List<HostInfo> tunnels;
|
|
final bool pending;
|
|
final Function(List<HostInfo>)? onChanged;
|
|
|
|
final bool supportsQRScanning;
|
|
|
|
@override
|
|
_SiteTunnelsScreenState createState() => _SiteTunnelsScreenState();
|
|
}
|
|
|
|
class _SiteTunnelsScreenState extends State<SiteTunnelsScreen> {
|
|
late Site site;
|
|
late List<HostInfo> tunnels;
|
|
RefreshController refreshController = RefreshController(initialRefresh: false);
|
|
|
|
@override
|
|
void initState() {
|
|
site = widget.site;
|
|
tunnels = widget.tunnels;
|
|
_sortTunnels();
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
refreshController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final double ipWidth = Utils.textSize("000.000.000.000", CupertinoTheme.of(context).textTheme.textStyle).width + 32;
|
|
|
|
List<Widget> children = [];
|
|
tunnels.forEach((hostInfo) {
|
|
Widget icon;
|
|
|
|
final isLh = site.staticHostmap[hostInfo.vpnIp]?.lighthouse ?? false;
|
|
if (isLh) {
|
|
icon = Icon(Icons.lightbulb_outline, color: CupertinoColors.placeholderText.resolveFrom(context));
|
|
} else {
|
|
icon = Icon(Icons.computer, color: CupertinoColors.placeholderText.resolveFrom(context));
|
|
}
|
|
|
|
children.add(ConfigPageItem(
|
|
onPressed: () => Utils.openPage(
|
|
context,
|
|
(context) => HostInfoScreen(
|
|
isLighthouse: isLh,
|
|
hostInfo: hostInfo,
|
|
pending: widget.pending,
|
|
site: widget.site,
|
|
onChanged: () {
|
|
_listHostmap();
|
|
},
|
|
supportsQRScanning: widget.supportsQRScanning,
|
|
),
|
|
),
|
|
label: Row(children: <Widget>[Padding(child: icon, padding: EdgeInsets.only(right: 10)), Text(hostInfo.vpnIp)]),
|
|
labelWidth: ipWidth,
|
|
content: Container(alignment: Alignment.centerRight, child: Text(hostInfo.cert?.details.name ?? "")),
|
|
));
|
|
});
|
|
|
|
Widget child;
|
|
if (children.length == 0) {
|
|
child = Center(child: Padding(child: Text('No tunnels to show'), padding: EdgeInsets.only(top: 30)));
|
|
} else {
|
|
child = ConfigSection(children: children);
|
|
}
|
|
|
|
final title = widget.pending ? 'Pending' : 'Active';
|
|
|
|
return SimplePage(
|
|
title: Text('$title Tunnels'),
|
|
refreshController: refreshController,
|
|
onRefresh: () async {
|
|
await _listHostmap();
|
|
refreshController.refreshCompleted();
|
|
},
|
|
child: child);
|
|
}
|
|
|
|
_sortTunnels() {
|
|
tunnels.sort((a, b) {
|
|
final aLh = _isLighthouse(a.vpnIp), bLh = _isLighthouse(b.vpnIp);
|
|
|
|
if (aLh && !bLh) {
|
|
return -1;
|
|
} else if (!aLh && bLh) {
|
|
return 1;
|
|
}
|
|
|
|
return Utils.ip2int(a.vpnIp) - Utils.ip2int(b.vpnIp);
|
|
});
|
|
}
|
|
|
|
bool _isLighthouse(String vpnIp) {
|
|
return site.staticHostmap[vpnIp]?.lighthouse ?? false;
|
|
}
|
|
|
|
_listHostmap() async {
|
|
try {
|
|
if (widget.pending) {
|
|
tunnels = await site.listPendingHostmap();
|
|
} else {
|
|
tunnels = await site.listHostmap();
|
|
}
|
|
|
|
_sortTunnels();
|
|
if (widget.onChanged != null) {
|
|
widget.onChanged!(tunnels);
|
|
}
|
|
setState(() {});
|
|
} catch (err) {
|
|
Utils.popError(context, 'Error while fetching hostmap', err.toString());
|
|
}
|
|
}
|
|
}
|