Format code

This commit is contained in:
Ian VanSchooten 2024-08-28 14:46:03 -04:00
parent b598aa338f
commit 35fbf86c8c
20 changed files with 377 additions and 370 deletions

View File

@ -28,7 +28,8 @@ class SiteItem extends StatelessWidget {
Widget _buildContent(BuildContext context) { Widget _buildContent(BuildContext context) {
final border = BorderSide(color: Utils.configSectionBorder(context)); final border = BorderSide(color: Utils.configSectionBorder(context));
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg'; final dnIcon =
Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
return SpecialButton( return SpecialButton(
decoration: decoration:
@ -39,9 +40,9 @@ class SiteItem extends StatelessWidget {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
site.managed ? site.managed
Padding(padding: EdgeInsets.only(right: 10), child: SvgPicture.asset(dnIcon, width: 12)) : ? Padding(padding: EdgeInsets.only(right: 10), child: SvgPicture.asset(dnIcon, width: 12))
Container(), : Container(),
Expanded(child: Text(site.name, style: TextStyle(fontWeight: FontWeight.bold))), Expanded(child: Text(site.name, style: TextStyle(fontWeight: FontWeight.bold))),
Padding(padding: EdgeInsets.only(right: 10)), Padding(padding: EdgeInsets.only(right: 10)),
Icon(CupertinoIcons.forward, color: CupertinoColors.placeholderText.resolveFrom(context), size: 18) Icon(CupertinoIcons.forward, color: CupertinoColors.placeholderText.resolveFrom(context), size: 18)

View File

@ -52,7 +52,9 @@ class ConfigPageItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
label != null ? Container(width: labelWidth, child: label) : Container(), label != null ? Container(width: labelWidth, child: label) : Container(),
Expanded(child: Container(child: content, padding: EdgeInsets.only(right: 10))), Expanded(child: Container(child: content, padding: EdgeInsets.only(right: 10))),
this.disabled ? Container() : Icon(CupertinoIcons.forward, color: CupertinoColors.placeholderText.resolveFrom(context), size: 18) this.disabled
? Container()
: Icon(CupertinoIcons.forward, color: CupertinoColors.placeholderText.resolveFrom(context), size: 18)
], ],
)), )),
); );

View File

@ -110,11 +110,9 @@ class _AppState extends State<App> {
if (uri.path == EnrollmentScreen.routeName) { if (uri.path == EnrollmentScreen.routeName) {
// TODO: maybe implement this as a dialog instead of a page, you can stack multiple enrollment screens which is annoying in dev // TODO: maybe implement this as a dialog instead of a page, you can stack multiple enrollment screens which is annoying in dev
return platformPageRoute( return platformPageRoute(
context: context, context: context,
builder: (context) => EnrollmentScreen( builder: (context) =>
code: EnrollmentScreen.parseCode(settings.name!), EnrollmentScreen(code: EnrollmentScreen.parseCode(settings.name!), stream: this.dnEnrolled),
stream: this.dnEnrolled
),
); );
} }

View File

@ -206,8 +206,7 @@ class Site {
"unsafeRoutes": unsafeRoutes, "unsafeRoutes": unsafeRoutes,
"managed": json['managed'] ?? false, "managed": json['managed'] ?? false,
"rawConfig": json['rawConfig'], "rawConfig": json['rawConfig'],
"lastManagedUpdate": json["lastManagedUpdate"] == null ? "lastManagedUpdate": json["lastManagedUpdate"] == null ? null : DateTime.parse(json["lastManagedUpdate"]),
null : DateTime.parse(json["lastManagedUpdate"]),
}; };
} }

View File

@ -91,37 +91,38 @@ class _EnrollmentScreenState extends State<EnrollmentScreen> {
} else { } else {
// No code, show the error // No code, show the error
child = Padding( child = Padding(
child: Center(child: Text( child: Center(
child: Text(
'No valid enrollment code was found.\n\nContact your administrator to obtain a new enrollment code.', 'No valid enrollment code was found.\n\nContact your administrator to obtain a new enrollment code.',
textAlign: TextAlign.center, textAlign: TextAlign.center,
)), )),
padding: EdgeInsets.only(top: 20) padding: EdgeInsets.only(top: 20));
);
} }
} else if (this.error != null) { } else if (this.error != null) {
// Error while enrolling, display it // Error while enrolling, display it
child = Center(child: Column( child = Center(
child: Column(
children: [ children: [
Padding( Padding(
child: SelectableText('There was an issue while attempting to enroll this device. Contact your administrator to obtain a new enrollment code.'), child: SelectableText(
padding: EdgeInsets.symmetric(vertical: 20) 'There was an issue while attempting to enroll this device. Contact your administrator to obtain a new enrollment code.'),
), padding: EdgeInsets.symmetric(vertical: 20)),
Padding(child: SelectableText.rich(TextSpan(children: [ Padding(
TextSpan(text: 'If the problem persists, please let us know at '), child: SelectableText.rich(TextSpan(children: [
TextSpan( TextSpan(text: 'If the problem persists, please let us know at '),
text: 'support@defined.net', TextSpan(
style: bodyTextStyle.apply(color: colorScheme.primary), text: 'support@defined.net',
recognizer: TapGestureRecognizer() style: bodyTextStyle.apply(color: colorScheme.primary),
..onTap = () async { recognizer: TapGestureRecognizer()
if (await canLaunchUrl(contactUri)) { ..onTap = () async {
print(await launchUrl(contactUri)); if (await canLaunchUrl(contactUri)) {
} print(await launchUrl(contactUri));
}, }
), },
TextSpan(text: ' and provide the following error:'), ),
])), padding: EdgeInsets.only(bottom: 10)), TextSpan(text: ' and provide the following error:'),
])),
padding: EdgeInsets.only(bottom: 10)),
Container( Container(
child: Padding(child: SelectableText(this.error!), padding: EdgeInsets.all(10)), child: Padding(child: SelectableText(this.error!), padding: EdgeInsets.all(10)),
color: Theme.of(context).colorScheme.errorContainer, color: Theme.of(context).colorScheme.errorContainer,
@ -130,47 +131,43 @@ class _EnrollmentScreenState extends State<EnrollmentScreen> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
)); ));
} else if (this.enrolled) { } else if (this.enrolled) {
// Enrollment complete! // Enrollment complete!
child = Padding( child = Padding(
child: Center(child: Text( child: Center(
child: Text(
'Enrollment complete! 🎉', 'Enrollment complete! 🎉',
textAlign: TextAlign.center, textAlign: TextAlign.center,
)), )),
padding: EdgeInsets.only(top: 20) padding: EdgeInsets.only(top: 20));
);
} else { } else {
// Have a code and actively enrolling // Have a code and actively enrolling
alignment = Alignment.center; alignment = Alignment.center;
child = Center(child: Column( child = Center(
children: [ child: Column(children: [
Padding(child: Text('Contacting DN for enrollment'), padding: EdgeInsets.only(bottom: 25)), Padding(child: Text('Contacting DN for enrollment'), padding: EdgeInsets.only(bottom: 25)),
PlatformCircularProgressIndicator(cupertino: (_, __) { PlatformCircularProgressIndicator(cupertino: (_, __) {
return CupertinoProgressIndicatorData(radius: 50); return CupertinoProgressIndicatorData(radius: 50);
}) })
] ]));
));
} }
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg'; final dnIcon =
Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
return SimplePage( return SimplePage(
title: Text('Enroll with Managed Nebula', style: TextStyle(fontWeight: FontWeight.bold)), title: Text('Enroll with Managed Nebula', style: TextStyle(fontWeight: FontWeight.bold)),
child: Padding(child: child, padding: EdgeInsets.symmetric(horizontal: 10)), child: Padding(child: child, padding: EdgeInsets.symmetric(horizontal: 10)),
alignment: alignment alignment: alignment);
);
} }
Widget _codeEntry() { Widget _codeEntry() {
return Column(children: [ return Column(children: [
Padding( Padding(
padding: EdgeInsets.only(top: 20), padding: EdgeInsets.only(top: 20),
child: PlatformTextField( child: PlatformTextField(
hintText: 'defined.net enrollment code or link', hintText: 'defined.net enrollment code or link',
controller: enrollInput, controller: enrollInput,
) )),
),
PlatformTextButton( PlatformTextButton(
child: Text('Submit'), child: Text('Submit'),
onPressed: () { onPressed: () {

View File

@ -75,10 +75,11 @@ class _HostInfoScreenState extends State<HostInfoScreen> {
labelWidth: 150, labelWidth: 150,
content: Text(hostInfo.cert!.details.name), content: Text(hostInfo.cert!.details.name),
onPressed: () => Utils.openPage( onPressed: () => Utils.openPage(
context, (context) => CertificateDetailsScreen( context,
certInfo: CertificateInfo(cert: hostInfo.cert!), (context) => CertificateDetailsScreen(
supportsQRScanning: widget.supportsQRScanning, certInfo: CertificateInfo(cert: hostInfo.cert!),
))) supportsQRScanning: widget.supportsQRScanning,
)))
: Container(), : Container(),
]); ]);
} }

View File

@ -128,9 +128,9 @@ class _MainScreenState extends State<MainScreen> {
// Determine whether the device supports QR scanning. For example, some // Determine whether the device supports QR scanning. For example, some
// Chromebooks do not have camera support. // Chromebooks do not have camera support.
if (Platform.isAndroid) { if (Platform.isAndroid) {
platform.invokeMethod("android.deviceHasCamera").then( platform
(hasCamera) => setState(() => supportsQRScanning = hasCamera) .invokeMethod("android.deviceHasCamera")
); .then((hasCamera) => setState(() => supportsQRScanning = hasCamera));
} else { } else {
supportsQRScanning = true; supportsQRScanning = true;
} }
@ -143,9 +143,11 @@ class _MainScreenState extends State<MainScreen> {
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
icon: Icon(Icons.add, size: 28.0), icon: Icon(Icons.add, size: 28.0),
onPressed: () => Utils.openPage(context, (context) { onPressed: () => Utils.openPage(context, (context) {
return SiteConfigScreen(onSave: (_) { return SiteConfigScreen(
_loadSites(); onSave: (_) {
}, supportsQRScanning: supportsQRScanning); _loadSites();
},
supportsQRScanning: supportsQRScanning);
}), }),
), ),
refreshController: refreshController, refreshController: refreshController,
@ -210,9 +212,9 @@ class _MainScreenState extends State<MainScreen> {
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return SiteDetailScreen( return SiteDetailScreen(
site: site, site: site,
onChanged: () => _loadSites(), onChanged: () => _loadSites(),
supportsQRScanning: supportsQRScanning, supportsQRScanning: supportsQRScanning,
); );
}); });
})); }));
@ -289,16 +291,15 @@ class _MainScreenState extends State<MainScreen> {
); );
} }
Widget _debugClearKeys() { Widget _debugClearKeys() {
return CupertinoButton( return CupertinoButton(
child: Text("Clear Keys"), child: Text("Clear Keys"),
onPressed: () async { onPressed: () async {
await platform.invokeMethod("debug.clearKeys", null); await platform.invokeMethod("debug.clearKeys", null);
}, },
); );
} }
_loadSites() async { _loadSites() async {
//TODO: This can throw, we need to show an error dialog //TODO: This can throw, we need to show an error dialog
Map<String, dynamic> rawSites = jsonDecode(await platform.invokeMethod('listSites')); Map<String, dynamic> rawSites = jsonDecode(await platform.invokeMethod('listSites'));
@ -322,7 +323,6 @@ class _MainScreenState extends State<MainScreen> {
} }
}); });
sites!.add(site); sites!.add(site);
} catch (err) { } catch (err) {
//TODO: handle error //TODO: handle error

View File

@ -86,13 +86,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
)), )),
)); ));
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg'; final dnIcon =
Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
items.add(ConfigSection(children: [ items.add(ConfigSection(children: [
ConfigPageItem( ConfigPageItem(
label: Text('Enroll with Managed Nebula'), label: Text('Enroll with Managed Nebula'),
labelWidth: 200, labelWidth: 200,
onPressed: () => Utils.openPage(context, (context) => EnrollmentScreen(stream: widget.stream, allowCodeEntry: true)) onPressed: () =>
) Utils.openPage(context, (context) => EnrollmentScreen(stream: widget.stream, allowCodeEntry: true)))
])); ]));
items.add(ConfigSection(children: [ items.add(ConfigSection(children: [

View File

@ -79,11 +79,12 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg'; final dnIcon =
Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
final title = Row(children: [ final title = Row(children: [
site.managed ? site.managed
Padding(padding: EdgeInsets.only(right: 10), child: SvgPicture.asset(dnIcon, width: 12)) : ? Padding(padding: EdgeInsets.only(right: 10), child: SvgPicture.asset(dnIcon, width: 12))
Container(), : Container(),
Expanded(child: Text(site.name, style: TextStyle(fontWeight: FontWeight.bold))) Expanded(child: Text(site.name, style: TextStyle(fontWeight: FontWeight.bold)))
]); ]);
@ -192,16 +193,16 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
Utils.openPage( Utils.openPage(
context, context,
(context) => SiteTunnelsScreen( (context) => SiteTunnelsScreen(
pending: false, pending: false,
tunnels: activeHosts!, tunnels: activeHosts!,
site: site, site: site,
onChanged: (hosts) { onChanged: (hosts) {
setState(() { setState(() {
activeHosts = hosts; activeHosts = hosts;
}); });
}, },
supportsQRScanning: widget.supportsQRScanning, supportsQRScanning: widget.supportsQRScanning,
)); ));
}, },
label: Text("Active"), label: Text("Active"),
content: Container(alignment: Alignment.centerRight, child: active)), content: Container(alignment: Alignment.centerRight, child: active)),
@ -212,16 +213,16 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
Utils.openPage( Utils.openPage(
context, context,
(context) => SiteTunnelsScreen( (context) => SiteTunnelsScreen(
pending: true, pending: true,
tunnels: pendingHosts!, tunnels: pendingHosts!,
site: site, site: site,
onChanged: (hosts) { onChanged: (hosts) {
setState(() { setState(() {
pendingHosts = hosts; pendingHosts = hosts;
}); });
}, },
supportsQRScanning: widget.supportsQRScanning, supportsQRScanning: widget.supportsQRScanning,
)); ));
}, },
label: Text("Pending"), label: Text("Pending"),
content: Container(alignment: Alignment.centerRight, child: pending)) content: Container(alignment: Alignment.centerRight, child: pending))
@ -237,11 +238,11 @@ class _SiteDetailScreenState extends State<SiteDetailScreen> {
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return SiteConfigScreen( return SiteConfigScreen(
site: widget.site, site: widget.site,
onSave: (site) async { onSave: (site) async {
changed = true; changed = true;
setState(() {}); setState(() {});
}, },
supportsQRScanning: widget.supportsQRScanning, supportsQRScanning: widget.supportsQRScanning,
); );
}); });

View File

@ -40,11 +40,12 @@ class _SiteLogsScreenState extends State<SiteLogsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg'; final dnIcon =
Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg';
final title = Row(children: [ final title = Row(children: [
widget.site.managed ? widget.site.managed
Padding(padding: EdgeInsets.only(right: 10), child: SvgPicture.asset(dnIcon, width: 12)) : ? Padding(padding: EdgeInsets.only(right: 10), child: SvgPicture.asset(dnIcon, width: 12))
Container(), : Container(),
Expanded(child: Text(widget.site.name, style: TextStyle(fontWeight: FontWeight.bold))) Expanded(child: Text(widget.site.name, style: TextStyle(fontWeight: FontWeight.bold)))
]); ]);
@ -83,29 +84,26 @@ class _SiteLogsScreenState extends State<SiteLogsScreen> {
border: Border(top: borderSide), border: Border(top: borderSide),
), ),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Expanded( Expanded(child: Builder(builder: (BuildContext context) {
child: Builder( return PlatformIconButton(
builder: (BuildContext context) { padding: padding,
return PlatformIconButton( icon: Icon(context.platformIcons.share, size: 30),
padding: padding, onPressed: () {
icon: Icon(context.platformIcons.share, size: 30), Share.shareFile(context,
onPressed: () { title: '${widget.site.name} logs',
Share.shareFile(context, filePath: widget.site.logFile,
title: '${widget.site.name} logs', filename: '${widget.site.name}.log');
filePath: widget.site.logFile, },
filename: '${widget.site.name}.log'); );
}, })),
);
}
)),
Expanded( Expanded(
child: PlatformIconButton( child: PlatformIconButton(
padding: padding, padding: padding,
icon: Icon(context.platformIcons.downArrow, size: 30), icon: Icon(context.platformIcons.downArrow, size: 30),
onPressed: () async { onPressed: () async {
controller.animateTo(controller.position.maxScrollExtent, controller.animateTo(controller.position.maxScrollExtent,
duration: const Duration(milliseconds: 500), curve: Curves.linearToEaseOut); duration: const Duration(milliseconds: 500), curve: Curves.linearToEaseOut);
}, },
)), )),
])); ]));
} }

View File

@ -17,8 +17,7 @@ class SiteTunnelsScreen extends StatefulWidget {
required this.pending, required this.pending,
required this.onChanged, required this.onChanged,
required this.supportsQRScanning, required this.supportsQRScanning,
}) }) : super(key: key);
: super(key: key);
final Site site; final Site site;
final List<HostInfo> tunnels; final List<HostInfo> tunnels;
@ -67,17 +66,17 @@ class _SiteTunnelsScreenState extends State<SiteTunnelsScreen> {
children.add(ConfigPageItem( children.add(ConfigPageItem(
onPressed: () => Utils.openPage( onPressed: () => Utils.openPage(
context, context,
(context) => HostInfoScreen( (context) => HostInfoScreen(
isLighthouse: isLh, isLighthouse: isLh,
hostInfo: hostInfo, hostInfo: hostInfo,
pending: widget.pending, pending: widget.pending,
site: widget.site, site: widget.site,
onChanged: () { onChanged: () {
_listHostmap(); _listHostmap();
}, },
supportsQRScanning: widget.supportsQRScanning, supportsQRScanning: widget.supportsQRScanning,
), ),
), ),
label: Row(children: <Widget>[Padding(child: icon, padding: EdgeInsets.only(right: 10)), Text(hostInfo.vpnIp)]), label: Row(children: <Widget>[Padding(child: icon, padding: EdgeInsets.only(right: 10)), Text(hostInfo.vpnIp)]),
labelWidth: ipWidth, labelWidth: ipWidth,

View File

@ -98,12 +98,10 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
content: Text('Share Public Key'), content: Text('Share Public Key'),
onPressed: () async { onPressed: () async {
await Share.share(context, await Share.share(context,
title: 'Please sign and return a certificate', title: 'Please sign and return a certificate', text: pubKey, filename: 'device.pub');
text: pubKey,
filename: 'device.pub');
}, },
); );
}, },
), ),
]) ])
]; ];
@ -269,12 +267,12 @@ class _AddCertificateScreenState extends State<AddCertificateScreen> {
// We have a cert, pop the details screen where they can hit save // We have a cert, pop the details screen where they can hit save
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return CertificateDetailsScreen( return CertificateDetailsScreen(
certInfo: tryCertInfo, certInfo: tryCertInfo,
onSave: () { onSave: () {
Navigator.pop(context); Navigator.pop(context);
widget.onSave!(CertificateResult(certInfo: tryCertInfo, key: keyController.text)); widget.onSave!(CertificateResult(certInfo: tryCertInfo, key: keyController.text));
}, },
supportsQRScanning: widget.supportsQRScanning, supportsQRScanning: widget.supportsQRScanning,
); );
}); });
} }

View File

@ -86,62 +86,62 @@ class _AdvancedScreenState extends State<AdvancedScreen> {
label: Text("Lighthouse interval"), label: Text("Lighthouse interval"),
labelWidth: 200, labelWidth: 200,
//TODO: Auto select on focus? //TODO: Auto select on focus?
content: widget.site.managed ? content: widget.site.managed
Text(settings.lhDuration.toString() + " seconds", textAlign: TextAlign.right) : ? Text(settings.lhDuration.toString() + " seconds", textAlign: TextAlign.right)
PlatformTextFormField( : PlatformTextFormField(
initialValue: settings.lhDuration.toString(), initialValue: settings.lhDuration.toString(),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
suffix: Text("seconds"), suffix: Text("seconds"),
textAlign: TextAlign.right, textAlign: TextAlign.right,
maxLength: 5, maxLength: 5,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onSaved: (val) { onSaved: (val) {
setState(() { setState(() {
if (val != null) { if (val != null) {
settings.lhDuration = int.parse(val); settings.lhDuration = int.parse(val);
} }
}); });
}, },
)), )),
ConfigItem( ConfigItem(
label: Text("Listen port"), label: Text("Listen port"),
labelWidth: 150, labelWidth: 150,
//TODO: Auto select on focus? //TODO: Auto select on focus?
content: widget.site.managed ? content: widget.site.managed
Text(settings.port.toString(), textAlign: TextAlign.right) : ? Text(settings.port.toString(), textAlign: TextAlign.right)
PlatformTextFormField( : PlatformTextFormField(
initialValue: settings.port.toString(), initialValue: settings.port.toString(),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
textAlign: TextAlign.right, textAlign: TextAlign.right,
maxLength: 5, maxLength: 5,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onSaved: (val) { onSaved: (val) {
setState(() { setState(() {
if (val != null) { if (val != null) {
settings.port = int.parse(val); settings.port = int.parse(val);
} }
}); });
}, },
)), )),
ConfigItem( ConfigItem(
label: Text("MTU"), label: Text("MTU"),
labelWidth: 150, labelWidth: 150,
content: widget.site.managed ? content: widget.site.managed
Text(settings.mtu.toString(), textAlign: TextAlign.right) : ? Text(settings.mtu.toString(), textAlign: TextAlign.right)
PlatformTextFormField( : PlatformTextFormField(
initialValue: settings.mtu.toString(), initialValue: settings.mtu.toString(),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
textAlign: TextAlign.right, textAlign: TextAlign.right,
maxLength: 5, maxLength: 5,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onSaved: (val) { onSaved: (val) {
setState(() { setState(() {
if (val != null) { if (val != null) {
settings.mtu = int.parse(val); settings.mtu = int.parse(val);
} }
}); });
}, },
)), )),
ConfigPageItem( ConfigPageItem(
disabled: widget.site.managed, disabled: widget.site.managed,
label: Text('Cipher'), label: Text('Cipher'),
@ -184,12 +184,14 @@ class _AdvancedScreenState extends State<AdvancedScreen> {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return UnsafeRoutesScreen( return UnsafeRoutesScreen(
unsafeRoutes: settings.unsafeRoutes, unsafeRoutes: settings.unsafeRoutes,
onSave: widget.site.managed ? null : (routes) { onSave: widget.site.managed
setState(() { ? null
settings.unsafeRoutes = routes; : (routes) {
changed = true; setState(() {
}); settings.unsafeRoutes = routes;
}); changed = true;
});
});
}); });
}, },
) )

View File

@ -64,18 +64,18 @@ class _CAListScreenState extends State<CAListScreen> {
} }
return FormPage( return FormPage(
title: 'Certificate Authorities', title: 'Certificate Authorities',
changed: changed, changed: changed,
onSave: () { onSave: () {
if (widget.onSave != null) { if (widget.onSave != null) {
Navigator.pop(context); Navigator.pop(context);
widget.onSave!(cas.values.map((ca) { widget.onSave!(cas.values.map((ca) {
return ca; return ca;
}).toList()); }).toList());
} }
}, },
child: Column(children: items)); child: Column(children: items));
} }
List<Widget> _buildCAs() { List<Widget> _buildCAs() {
List<Widget> items = []; List<Widget> items = [];
@ -85,14 +85,16 @@ class _CAListScreenState extends State<CAListScreen> {
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return CertificateDetailsScreen( return CertificateDetailsScreen(
certInfo: ca, certInfo: ca,
onDelete: widget.onSave == null ? null : () { onDelete: widget.onSave == null
setState(() { ? null
changed = true; : () {
cas.remove(key); setState(() {
}); changed = true;
}, cas.remove(key);
supportsQRScanning: widget.supportsQRScanning, });
},
supportsQRScanning: widget.supportsQRScanning,
); );
}); });
}, },

View File

@ -170,19 +170,19 @@ class _CertificateDetailsScreenState extends State<CertificateDetailsScreen> {
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return AddCertificateScreen( return AddCertificateScreen(
onReplace: (result) { onReplace: (result) {
setState(() { setState(() {
changed = true; changed = true;
certResult = result; certResult = result;
certInfo = result.certInfo; certInfo = result.certInfo;
}); });
// Slam the page back to the top // Slam the page back to the top
controller.animateTo(0, controller.animateTo(0,
duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut); duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut);
}, },
pubKey: widget.pubKey!, pubKey: widget.pubKey!,
privKey: widget.privKey!, privKey: widget.privKey!,
supportsQRScanning: widget.supportsQRScanning, supportsQRScanning: widget.supportsQRScanning,
); );
}); });
}))); })));

View File

@ -19,17 +19,13 @@ class RenderedConfigScreen extends StatelessWidget {
title: Text('Rendered Site Config'), title: Text('Rendered Site Config'),
scrollable: SimpleScrollable.both, scrollable: SimpleScrollable.both,
trailingActions: <Widget>[ trailingActions: <Widget>[
Builder( Builder(builder: (BuildContext context) {
builder: (BuildContext context) { return PlatformIconButton(
return PlatformIconButton( padding: EdgeInsets.zero,
padding: EdgeInsets.zero, icon: Icon(context.platformIcons.share, size: 28.0),
icon: Icon(context.platformIcons.share, size: 28.0), onPressed: () => Share.share(context, title: '$name.yaml', text: config, filename: '$name.yaml'),
onPressed: () => );
Share.share(context, }),
title: '$name.yaml', text: config, filename: '$name.yaml'),
);
}
),
], ],
child: Container( child: Container(
padding: EdgeInsets.all(5), padding: EdgeInsets.all(5),

View File

@ -139,17 +139,17 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
lastUpdate = formatter.format(site.lastManagedUpdate!.toLocal()); lastUpdate = formatter.format(site.lastManagedUpdate!.toLocal());
} }
return site.managed ? ConfigSection( return site.managed
label: "MANAGED CONFIG", ? ConfigSection(label: "MANAGED CONFIG", children: <Widget>[
children: <Widget>[ ConfigItem(
ConfigItem( label: Text("Last Update"),
label: Text("Last Update"), content:
content: Wrap(alignment: WrapAlignment.end, crossAxisAlignment: WrapCrossAlignment.center, children: <Widget>[ Wrap(alignment: WrapAlignment.end, crossAxisAlignment: WrapCrossAlignment.center, children: <Widget>[
Text(lastUpdate), Text(lastUpdate),
]), ]),
) )
] ])
) : Container(); : Container();
} }
Widget _keys() { Widget _keys() {
@ -183,30 +183,32 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
if (site.certInfo != null) { if (site.certInfo != null) {
return CertificateDetailsScreen( return CertificateDetailsScreen(
certInfo: site.certInfo!, certInfo: site.certInfo!,
pubKey: pubKey, pubKey: pubKey,
privKey: privKey, privKey: privKey,
onReplace: site.managed ? null : (result) { onReplace: site.managed
setState(() { ? null
changed = true; : (result) {
site.certInfo = result.certInfo; setState(() {
site.key = result.key; changed = true;
}); site.certInfo = result.certInfo;
}, site.key = result.key;
supportsQRScanning: widget.supportsQRScanning, });
},
supportsQRScanning: widget.supportsQRScanning,
); );
} }
return AddCertificateScreen( return AddCertificateScreen(
pubKey: pubKey!, pubKey: pubKey!,
privKey: privKey!, privKey: privKey!,
onSave: (result) { onSave: (result) {
setState(() { setState(() {
changed = true; changed = true;
site.certInfo = result.certInfo; site.certInfo = result.certInfo;
site.key = result.key; site.key = result.key;
}); });
}, },
supportsQRScanning: widget.supportsQRScanning, supportsQRScanning: widget.supportsQRScanning,
); );
}); });
@ -226,14 +228,16 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
onPressed: () { onPressed: () {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return CAListScreen( return CAListScreen(
cas: site.ca, cas: site.ca,
onSave: site.managed ? null : (ca) { onSave: site.managed
setState(() { ? null
changed = true; : (ca) {
site.ca = ca; setState(() {
}); changed = true;
}, site.ca = ca;
supportsQRScanning: widget.supportsQRScanning, });
},
supportsQRScanning: widget.supportsQRScanning,
); );
}); });
}) })
@ -261,12 +265,14 @@ class _SiteConfigScreenState extends State<SiteConfigScreen> {
Utils.openPage(context, (context) { Utils.openPage(context, (context) {
return StaticHostsScreen( return StaticHostsScreen(
hostmap: site.staticHostmap, hostmap: site.staticHostmap,
onSave: site.managed ? null : (map) { onSave: site.managed
setState(() { ? null
changed = true; : (map) {
site.staticHostmap = map; setState(() {
}); changed = true;
}); site.staticHostmap = map;
});
});
}); });
}, },
), ),

View File

@ -66,7 +66,11 @@ class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FormPage( return FormPage(
title: widget.onDelete == null ? widget.onSave == null ? 'View Static Host' : 'New Static Host' : 'Edit Static Host', title: widget.onDelete == null
? widget.onSave == null
? 'View Static Host'
: 'New Static Host'
: 'Edit Static Host',
changed: changed, changed: changed,
onSave: _onSave, onSave: _onSave,
child: Column(children: [ child: Column(children: [
@ -74,20 +78,20 @@ class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
ConfigItem( ConfigItem(
label: Text('Nebula IP'), label: Text('Nebula IP'),
labelWidth: 200, labelWidth: 200,
content: widget.onSave == null ? content: widget.onSave == null
Text(_nebulaIp, textAlign: TextAlign.end) : ? Text(_nebulaIp, textAlign: TextAlign.end)
IPFormField( : IPFormField(
help: "Required", help: "Required",
initialValue: _nebulaIp, initialValue: _nebulaIp,
ipOnly: true, ipOnly: true,
textAlign: TextAlign.end, textAlign: TextAlign.end,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
onSaved: (v) { onSaved: (v) {
if (v != null) { if (v != null) {
_nebulaIp = v; _nebulaIp = v;
} }
})), })),
ConfigItem( ConfigItem(
label: Text('Lighthouse'), label: Text('Lighthouse'),
labelWidth: 200, labelWidth: 200,
@ -96,12 +100,14 @@ class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
child: Switch.adaptive( child: Switch.adaptive(
value: _lighthouse, value: _lighthouse,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: widget.onSave == null ? null : (v) { onChanged: widget.onSave == null
setState(() { ? null
changed = true; : (v) {
_lighthouse = v; setState(() {
}); changed = true;
})), _lighthouse = v;
});
})),
), ),
]), ]),
ConfigSection( ConfigSection(
@ -128,8 +134,7 @@ class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
_onSave() { _onSave() {
Navigator.pop(context); Navigator.pop(context);
if (widget.onSave != null) { if (widget.onSave != null) {
var map = Hostmap( var map = Hostmap(nebulaIp: _nebulaIp, destinations: [], lighthouse: _lighthouse);
nebulaIp: _nebulaIp, destinations: [], lighthouse: _lighthouse);
_destinations.forEach((_, dest) { _destinations.forEach((_, dest) {
map.destinations.add(dest.destination); map.destinations.add(dest.destination);
@ -147,30 +152,32 @@ class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
key: key, key: key,
label: Align( label: Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: widget.onSave == null ? Container() : PlatformIconButton( child: widget.onSave == null
padding: EdgeInsets.zero, ? Container()
icon: Icon(Icons.remove_circle, color: CupertinoColors.systemRed.resolveFrom(context)), : PlatformIconButton(
onPressed: () => setState(() { padding: EdgeInsets.zero,
_removeDestination(key); icon: Icon(Icons.remove_circle, color: CupertinoColors.systemRed.resolveFrom(context)),
_dismissKeyboard(); onPressed: () => setState(() {
}))), _removeDestination(key);
_dismissKeyboard();
}))),
labelWidth: 70, labelWidth: 70,
content: Row(children: <Widget>[ content: Row(children: <Widget>[
Expanded( Expanded(
child: widget.onSave == null ? child: widget.onSave == null
Text(dest.destination.toString(), textAlign: TextAlign.end) : ? Text(dest.destination.toString(), textAlign: TextAlign.end)
IPAndPortFormField( : IPAndPortFormField(
ipHelp: 'public ip or name', ipHelp: 'public ip or name',
ipTextAlign: TextAlign.end, ipTextAlign: TextAlign.end,
enableIPV6: true, enableIPV6: true,
noBorder: true, noBorder: true,
initialValue: dest.destination, initialValue: dest.destination,
onSaved: (v) { onSaved: (v) {
if (v != null) { if (v != null) {
dest.destination = v; dest.destination = v;
} }
}, },
)), )),
]), ]),
)); ));
}); });
@ -178,8 +185,7 @@ class _StaticHostmapScreenState extends State<StaticHostmapScreen> {
if (widget.onSave != null) { if (widget.onSave != null) {
items.add(ConfigButtonItem( items.add(ConfigButtonItem(
content: Text('Add another'), content: Text('Add another'),
onPressed: () => onPressed: () => setState(() {
setState(() {
_addDestination(); _addDestination();
_dismissKeyboard(); _dismissKeyboard();
}))); })));

View File

@ -70,8 +70,7 @@ class _StaticHostsScreenState extends State<StaticHostsScreen> {
if (widget.onSave != null) { if (widget.onSave != null) {
Map<String, StaticHost> map = {}; Map<String, StaticHost> map = {};
_hostmap.forEach((_, host) { _hostmap.forEach((_, host) {
map[host.nebulaIp] = StaticHost( map[host.nebulaIp] = StaticHost(destinations: host.destinations, lighthouse: host.lighthouse);
destinations: host.destinations, lighthouse: host.lighthouse);
}); });
widget.onSave!(map); widget.onSave!(map);
@ -98,20 +97,24 @@ class _StaticHostsScreenState extends State<StaticHostsScreen> {
nebulaIp: host.nebulaIp, nebulaIp: host.nebulaIp,
destinations: host.destinations, destinations: host.destinations,
lighthouse: host.lighthouse, lighthouse: host.lighthouse,
onSave: widget.onSave == null ? null :(map) { onSave: widget.onSave == null
setState(() { ? null
changed = true; : (map) {
host.nebulaIp = map.nebulaIp; setState(() {
host.destinations = map.destinations; changed = true;
host.lighthouse = map.lighthouse; host.nebulaIp = map.nebulaIp;
}); host.destinations = map.destinations;
}, host.lighthouse = map.lighthouse;
onDelete: widget.onSave == null ? null : () { });
setState(() { },
changed = true; onDelete: widget.onSave == null
_hostmap.remove(key); ? null
}); : () {
}); setState(() {
changed = true;
_hostmap.remove(key);
});
});
}); });
}, },
)); ));

View File

@ -41,9 +41,7 @@ class Share {
/// - filePath: Path to the file to share /// - filePath: Path to the file to share
/// - filename: An optional filename to override the existing file /// - filename: An optional filename to override the existing file
static Future<bool> shareFile(BuildContext context, static Future<bool> shareFile(BuildContext context,
{required String title, {required String title, required String filePath, String? filename}) async {
required String filePath,
String? filename}) async {
assert(title.isNotEmpty); assert(title.isNotEmpty);
assert(filePath.isNotEmpty); assert(filePath.isNotEmpty);
@ -54,8 +52,7 @@ class Share {
// and then delete it // and then delete it
final xFile = sp.XFile(filePath, name: filename); final xFile = sp.XFile(filePath, name: filename);
final result = await sp.Share.shareXFiles([xFile], final result = await sp.Share.shareXFiles([xFile],
subject: title, subject: title, sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size);
return result.status == sp.ShareResultStatus.success; return result.status == sp.ShareResultStatus.success;
} }
} }