mobile_nebula/lib/screens/SiteTunnelsScreen.dart
Ian VanSchooten ad45cc1d78
Fix screen titles on iOS (#230)
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)|
2025-01-24 07:51:37 -05:00

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());
}
}
}