diff --git a/lib/components/CIDRField.dart b/lib/components/CIDRField.dart index 6aae1f9..24a7dc5 100644 --- a/lib/components/CIDRField.dart +++ b/lib/components/CIDRField.dart @@ -56,42 +56,42 @@ class _CIDRFieldState extends State { return Container( child: Row(children: [ - Expanded( - child: Padding( - padding: EdgeInsets.fromLTRB(6, 6, 2, 6), - child: IPField( - help: widget.ipHelp, - ipOnly: true, - textPadding: EdgeInsets.all(0), - textInputAction: TextInputAction.next, - textAlign: TextAlign.end, - focusNode: widget.focusNode, - nextFocusNode: bitsFocus, - onChanged: (val) { - cidr.ip = val; - widget.onChanged(cidr); - }, - controller: widget.ipController, - ))), - Text("/"), - Container( - width: Utils.textSize("bits", textStyle).width + 12, - padding: EdgeInsets.fromLTRB(2, 6, 6, 6), - child: SpecialTextField( - keyboardType: TextInputType.number, - focusNode: bitsFocus, - nextFocusNode: widget.nextFocusNode, - controller: widget.bitsController, + Expanded( + child: Padding( + padding: EdgeInsets.fromLTRB(6, 6, 2, 6), + child: IPField( + help: widget.ipHelp, + ipOnly: true, + textPadding: EdgeInsets.all(0), + textInputAction: TextInputAction.next, + textAlign: TextAlign.end, + focusNode: widget.focusNode, + nextFocusNode: bitsFocus, onChanged: (val) { - cidr.bits = int.tryParse(val ?? ""); + cidr.ip = val; widget.onChanged(cidr); }, - maxLength: 2, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - textInputAction: widget.textInputAction ?? TextInputAction.done, - placeholder: 'bits', - )) - ])); + controller: widget.ipController, + ))), + Text("/"), + Container( + width: Utils.textSize("bits", textStyle).width + 12, + padding: EdgeInsets.fromLTRB(2, 6, 6, 6), + child: SpecialTextField( + keyboardType: TextInputType.number, + focusNode: bitsFocus, + nextFocusNode: widget.nextFocusNode, + controller: widget.bitsController, + onChanged: (val) { + cidr.bits = int.tryParse(val ?? ""); + widget.onChanged(cidr); + }, + maxLength: 2, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + textInputAction: widget.textInputAction ?? TextInputAction.done, + placeholder: 'bits', + )) + ])); } @override diff --git a/lib/components/FormPage.dart b/lib/components/FormPage.dart index 97117ef..3634f72 100644 --- a/lib/components/FormPage.dart +++ b/lib/components/FormPage.dart @@ -8,7 +8,13 @@ import 'package:mobile_nebula/services/utils.dart'; /// SimplePage with a form and built in validation and confirmation to discard changes if any are made class FormPage extends StatefulWidget { const FormPage( - {Key key, this.title, @required this.child, @required this.onSave, @required this.changed, this.hideSave = false, this.scrollController}) + {Key key, + this.title, + @required this.child, + @required this.onSave, + @required this.changed, + this.hideSave = false, + this.scrollController}) : super(key: key); final String title; diff --git a/lib/components/IPField.dart b/lib/components/IPField.dart index 9153526..995cbf5 100644 --- a/lib/components/IPField.dart +++ b/lib/components/IPField.dart @@ -48,9 +48,7 @@ class IPField extends StatelessWidget { onChanged: onChanged, maxLength: ipOnly ? 15 : null, maxLengthEnforced: ipOnly ? true : false, - inputFormatters: ipOnly - ? [IPTextInputFormatter()] - : [FilteringTextInputFormatter.allow(RegExp(r'[^\s]+'))], + inputFormatters: ipOnly ? [IPTextInputFormatter()] : [FilteringTextInputFormatter.allow(RegExp(r'[^\s]+'))], textInputAction: this.textInputAction, placeholder: help, )); @@ -68,16 +66,17 @@ class IPTextInputFormatter extends TextInputFormatter { return whitelistedPattern .allMatches(substring) .map((Match match) => match.group(0)) - .join().replaceAll(RegExp(r','), '.'); + .join() + .replaceAll(RegExp(r','), '.'); }, ); } } TextEditingValue _selectionAwareTextManipulation( - TextEditingValue value, - String substringManipulation(String substring), - ) { + TextEditingValue value, + String substringManipulation(String substring), +) { final int selectionStartIndex = value.selection.start; final int selectionEndIndex = value.selection.end; String manipulatedText; @@ -85,15 +84,9 @@ TextEditingValue _selectionAwareTextManipulation( if (selectionStartIndex < 0 || selectionEndIndex < 0) { manipulatedText = substringManipulation(value.text); } else { - final String beforeSelection = substringManipulation( - value.text.substring(0, selectionStartIndex) - ); - final String inSelection = substringManipulation( - value.text.substring(selectionStartIndex, selectionEndIndex) - ); - final String afterSelection = substringManipulation( - value.text.substring(selectionEndIndex) - ); + final String beforeSelection = substringManipulation(value.text.substring(0, selectionStartIndex)); + final String inSelection = substringManipulation(value.text.substring(selectionStartIndex, selectionEndIndex)); + final String afterSelection = substringManipulation(value.text.substring(selectionEndIndex)); manipulatedText = beforeSelection + inSelection + afterSelection; if (value.selection.baseOffset > value.selection.extentOffset) { manipulatedSelection = value.selection.copyWith( @@ -110,8 +103,6 @@ TextEditingValue _selectionAwareTextManipulation( return TextEditingValue( text: manipulatedText, selection: manipulatedSelection ?? const TextSelection.collapsed(offset: -1), - composing: manipulatedText == value.text - ? value.composing - : TextRange.empty, + composing: manipulatedText == value.text ? value.composing : TextRange.empty, ); } diff --git a/lib/components/SimplePage.dart b/lib/components/SimplePage.dart index 3006257..074a5e1 100644 --- a/lib/components/SimplePage.dart +++ b/lib/components/SimplePage.dart @@ -45,14 +45,16 @@ class SimplePage extends StatelessWidget { final VoidCallback onLoading; final RefreshController refreshController; - @override Widget build(BuildContext context) { Widget realChild = child; var addScrollbar = this.scrollbar; if (scrollable == SimpleScrollable.vertical || scrollable == SimpleScrollable.both) { - realChild = SingleChildScrollView(scrollDirection: Axis.vertical, child: realChild, controller: refreshController == null ? scrollController : null); + realChild = SingleChildScrollView( + scrollDirection: Axis.vertical, + child: realChild, + controller: refreshController == null ? scrollController : null); addScrollbar = true; } @@ -67,15 +69,15 @@ class SimplePage extends StatelessWidget { footerTriggerDistance: -100, maxUnderScrollExtent: 100, child: SmartRefresher( - scrollController: scrollController, - onRefresh: onRefresh, - onLoading: onLoading, - controller: refreshController, - child: realChild, - enablePullUp: onLoading != null, - enablePullDown: onRefresh != null, - footer: ClassicFooter(loadStyle: LoadStyle.ShowWhenLoading), - )); + scrollController: scrollController, + onRefresh: onRefresh, + onLoading: onLoading, + controller: refreshController, + child: realChild, + enablePullUp: onLoading != null, + enablePullDown: onRefresh != null, + footer: ClassicFooter(loadStyle: LoadStyle.ShowWhenLoading), + )); addScrollbar = true; } diff --git a/lib/components/SpecialButton.dart b/lib/components/SpecialButton.dart index cd04346..381e9f4 100644 --- a/lib/components/SpecialButton.dart +++ b/lib/components/SpecialButton.dart @@ -5,7 +5,8 @@ import 'package:flutter/material.dart'; // This is a button that pushes the bare minimum onto you, it doesn't even respect button themes - unless you tell it to class SpecialButton extends StatefulWidget { - const SpecialButton({Key key, this.child, this.color, this.onPressed, this.useButtonTheme = false, this.decoration}) : super(key: key); + const SpecialButton({Key key, this.child, this.color, this.onPressed, this.useButtonTheme = false, this.decoration}) + : super(key: key); final Widget child; final Color color; @@ -33,12 +34,12 @@ class _SpecialButtonState extends State with SingleTickerProvider return Material( textStyle: textStyle, child: Ink( - decoration: widget.decoration, - color: widget.color, - child: InkWell( - child: widget.child, - onTap: widget.onPressed, - ))); + decoration: widget.decoration, + color: widget.color, + child: InkWell( + child: widget.child, + onTap: widget.onPressed, + ))); } Widget _buildGeneric() { @@ -48,22 +49,21 @@ class _SpecialButtonState extends State with SingleTickerProvider } return Container( - decoration: widget.decoration, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTapDown: _handleTapDown, - onTapUp: _handleTapUp, - onTapCancel: _handleTapCancel, - onTap: widget.onPressed, - child: Semantics( - button: true, - child: FadeTransition( - opacity: _opacityAnimation, - child: DefaultTextStyle(style: textStyle, child: Container(child: widget.child, color: widget.color)), + decoration: widget.decoration, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTapDown: _handleTapDown, + onTapUp: _handleTapUp, + onTapCancel: _handleTapCancel, + onTap: widget.onPressed, + child: Semantics( + button: true, + child: FadeTransition( + opacity: _opacityAnimation, + child: DefaultTextStyle(style: textStyle, child: Container(child: widget.child, color: widget.color)), + ), ), - ), - ) - ); + )); } // Eyeballed values. Feel free to tweak. diff --git a/lib/components/SpecialSelectableText.dart b/lib/components/SpecialSelectableText.dart index 365b4a6..ce86555 100644 --- a/lib/components/SpecialSelectableText.dart +++ b/lib/components/SpecialSelectableText.dart @@ -23,15 +23,15 @@ import 'package:flutter/gestures.dart'; const int iOSHorizontalOffset = -2; class _TextSpanEditingController extends TextEditingController { - _TextSpanEditingController({@required TextSpan textSpan}): - assert(textSpan != null), + _TextSpanEditingController({@required TextSpan textSpan}) + : assert(textSpan != null), _textSpan = textSpan, super(text: textSpan.toPlainText()); final TextSpan _textSpan; @override - TextSpan buildTextSpan({TextStyle style ,bool withComposing}) { + TextSpan buildTextSpan({TextStyle style, bool withComposing}) { // This does not care about composing. return TextSpan( style: style, @@ -49,7 +49,7 @@ class _TextSpanEditingController extends TextEditingController { class _SpecialSelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestureDetectorBuilder { _SpecialSelectableTextSelectionGestureDetectorBuilder({ @required _SpecialSelectableTextState state, - }) : _state = state, + }) : _state = state, super(delegate: state); final _SpecialSelectableTextState _state; @@ -109,8 +109,7 @@ class _SpecialSelectableTextSelectionGestureDetectorBuilder extends TextSelectio break; } } - if (_state.widget.onTap != null) - _state.widget.onTap(); + if (_state.widget.onTap != null) _state.widget.onTap(); } @override @@ -202,40 +201,40 @@ class SpecialSelectableText extends StatefulWidget { /// must not be null. If specified, the [maxLines] argument must be greater /// than zero. const SpecialSelectableText( - this.data, { - Key key, - this.focusNode, - this.style, - this.strutStyle, - this.textAlign, - this.textDirection, - this.textScaleFactor, - this.showCursor = false, - this.autofocus = false, - ToolbarOptions toolbarOptions, - this.minLines, - this.maxLines, - this.cursorWidth = 2.0, - this.cursorRadius, - this.cursorColor, - this.dragStartBehavior = DragStartBehavior.start, - this.enableInteractiveSelection = true, - this.onTap, - this.scrollPhysics, - this.textHeightBehavior, - this.textWidthBasis, - }) : assert(showCursor != null), + this.data, { + Key key, + this.focusNode, + this.style, + this.strutStyle, + this.textAlign, + this.textDirection, + this.textScaleFactor, + this.showCursor = false, + this.autofocus = false, + ToolbarOptions toolbarOptions, + this.minLines, + this.maxLines, + this.cursorWidth = 2.0, + this.cursorRadius, + this.cursorColor, + this.dragStartBehavior = DragStartBehavior.start, + this.enableInteractiveSelection = true, + this.onTap, + this.scrollPhysics, + this.textHeightBehavior, + this.textWidthBasis, + }) : assert(showCursor != null), assert(autofocus != null), assert(dragStartBehavior != null), assert(maxLines == null || maxLines > 0), assert(minLines == null || minLines > 0), assert( - (maxLines == null) || (minLines == null) || (maxLines >= minLines), - 'minLines can\'t be greater than maxLines', + (maxLines == null) || (minLines == null) || (maxLines >= minLines), + 'minLines can\'t be greater than maxLines', ), assert( - data != null, - 'A non-null String must be provided to a SpecialSelectableText widget.', + data != null, + 'A non-null String must be provided to a SpecialSelectableText widget.', ), textSpan = null, toolbarOptions = toolbarOptions ?? @@ -252,40 +251,40 @@ class SpecialSelectableText extends StatefulWidget { /// /// The [autofocus] and [dragStartBehavior] arguments must not be null. const SpecialSelectableText.rich( - this.textSpan, { - Key key, - this.focusNode, - this.style, - this.strutStyle, - this.textAlign, - this.textDirection, - this.textScaleFactor, - this.showCursor = false, - this.autofocus = false, - ToolbarOptions toolbarOptions, - this.minLines, - this.maxLines, - this.cursorWidth = 2.0, - this.cursorRadius, - this.cursorColor, - this.dragStartBehavior = DragStartBehavior.start, - this.enableInteractiveSelection = true, - this.onTap, - this.scrollPhysics, - this.textHeightBehavior, - this.textWidthBasis, - }) : assert(showCursor != null), + this.textSpan, { + Key key, + this.focusNode, + this.style, + this.strutStyle, + this.textAlign, + this.textDirection, + this.textScaleFactor, + this.showCursor = false, + this.autofocus = false, + ToolbarOptions toolbarOptions, + this.minLines, + this.maxLines, + this.cursorWidth = 2.0, + this.cursorRadius, + this.cursorColor, + this.dragStartBehavior = DragStartBehavior.start, + this.enableInteractiveSelection = true, + this.onTap, + this.scrollPhysics, + this.textHeightBehavior, + this.textWidthBasis, + }) : assert(showCursor != null), assert(autofocus != null), assert(dragStartBehavior != null), assert(maxLines == null || maxLines > 0), assert(minLines == null || minLines > 0), assert( - (maxLines == null) || (minLines == null) || (maxLines >= minLines), - 'minLines can\'t be greater than maxLines', + (maxLines == null) || (minLines == null) || (maxLines >= minLines), + 'minLines can\'t be greater than maxLines', ), assert( - textSpan != null, - 'A non-null TextSpan must be provided to a SpecialSelectableText.rich widget.', + textSpan != null, + 'A non-null TextSpan must be provided to a SpecialSelectableText.rich widget.', ), data = null, toolbarOptions = toolbarOptions ?? @@ -434,13 +433,17 @@ class SpecialSelectableText extends StatefulWidget { properties.add(DoubleProperty('cursorWidth', cursorWidth, defaultValue: 2.0)); properties.add(DiagnosticsProperty('cursorRadius', cursorRadius, defaultValue: null)); properties.add(DiagnosticsProperty('cursorColor', cursorColor, defaultValue: null)); - properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled')); + properties.add( + FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled')); properties.add(DiagnosticsProperty('scrollPhysics', scrollPhysics, defaultValue: null)); - properties.add(DiagnosticsProperty('textHeightBehavior', textHeightBehavior, defaultValue: null)); + properties + .add(DiagnosticsProperty('textHeightBehavior', textHeightBehavior, defaultValue: null)); } } -class _SpecialSelectableTextState extends State with AutomaticKeepAliveClientMixin implements TextSelectionGestureDetectorBuilderDelegate { +class _SpecialSelectableTextState extends State + with AutomaticKeepAliveClientMixin + implements TextSelectionGestureDetectorBuilderDelegate { EditableTextState get _editableText => editableTextKey.currentState; _TextSpanEditingController _controller; @@ -468,18 +471,14 @@ class _SpecialSelectableTextState extends State with Auto void initState() { super.initState(); _selectionGestureDetectorBuilder = _SpecialSelectableTextSelectionGestureDetectorBuilder(state: this); - _controller = _TextSpanEditingController( - textSpan: widget.textSpan ?? TextSpan(text: widget.data) - ); + _controller = _TextSpanEditingController(textSpan: widget.textSpan ?? TextSpan(text: widget.data)); } @override void didUpdateWidget(SpecialSelectableText oldWidget) { super.didUpdateWidget(oldWidget); if (widget.data != oldWidget.data || widget.textSpan != oldWidget.textSpan) { - _controller = _TextSpanEditingController( - textSpan: widget.textSpan ?? TextSpan(text: widget.data) - ); + _controller = _TextSpanEditingController(textSpan: widget.textSpan ?? TextSpan(text: widget.data)); } if (_effectiveFocusNode.hasFocus && _controller.selection.isCollapsed) { _showSelectionHandles = false; @@ -525,20 +524,15 @@ class _SpecialSelectableTextState extends State with Auto bool _shouldShowSelectionHandles(SelectionChangedCause cause) { // When the text field is activated by something that doesn't trigger the // selection overlay, we shouldn't show the handles either. - if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) - return false; + if (!_selectionGestureDetectorBuilder.shouldShowSelectionToolbar) return false; - if (_controller.selection.isCollapsed) - return false; + if (_controller.selection.isCollapsed) return false; - if (cause == SelectionChangedCause.keyboard) - return false; + if (cause == SelectionChangedCause.keyboard) return false; - if (cause == SelectionChangedCause.longPress) - return true; + if (cause == SelectionChangedCause.longPress) return true; - if (_controller.text.isNotEmpty) - return true; + if (_controller.text.isNotEmpty) return true; return false; } @@ -555,9 +549,10 @@ class _SpecialSelectableTextState extends State with Auto assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasDirectionality(context)); assert( - !(widget.style != null && widget.style.inherit == false && - (widget.style.fontSize == null || widget.style.textBaseline == null)), - 'inherit false style must supply fontSize and textBaseline', + !(widget.style != null && + widget.style.inherit == false && + (widget.style.fontSize == null || widget.style.textBaseline == null)), + 'inherit false style must supply fontSize and textBaseline', ); final ThemeData themeData = Theme.of(context); @@ -596,8 +591,7 @@ class _SpecialSelectableTextState extends State with Auto final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); TextStyle effectiveTextStyle = widget.style; - if (widget.style == null || widget.style.inherit) - effectiveTextStyle = defaultTextStyle.style.merge(widget.style); + if (widget.style == null || widget.style.inherit) effectiveTextStyle = defaultTextStyle.style.merge(widget.style); if (MediaQuery.boldTextOverride(context)) effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold)); final Widget child = RepaintBoundary( @@ -648,13 +642,12 @@ class _SpecialSelectableTextState extends State with Auto _effectiveFocusNode.requestFocus(); }, child: _selectionGestureDetectorBuilder.buildGestureDetector( - behavior: HitTestBehavior.translucent, - child: RawKeyboardListener( - focusNode: _keyFocusNode, - onKey: _onKey, - child: child, - ) - ), + behavior: HitTestBehavior.translucent, + child: RawKeyboardListener( + focusNode: _keyFocusNode, + onKey: _onKey, + child: child, + )), ); } diff --git a/lib/models/Site.dart b/lib/models/Site.dart index c3f54b7..3af3067 100644 --- a/lib/models/Site.dart +++ b/lib/models/Site.dart @@ -216,7 +216,6 @@ class Site { }); return hosts; - } on PlatformException catch (err) { //TODO: fix this message throw err.details ?? err.message ?? err.toString(); @@ -239,7 +238,6 @@ class Site { }); return hosts; - } on PlatformException catch (err) { throw err.details ?? err.message ?? err.toString(); } catch (err) { @@ -251,7 +249,6 @@ class Site { try { var res = await Future.wait([this.listHostmap(), this.listPendingHostmap()]); return {"active": res[0], "pending": res[1]}; - } on PlatformException catch (err) { throw err.details ?? err.message ?? err.toString(); } catch (err) { @@ -265,14 +262,14 @@ class Site { Future getHostInfo(String vpnIp, bool pending) async { try { - var ret = await platform.invokeMethod("active.getHostInfo", {"id": id, "vpnIp": vpnIp, "pending": pending}); + var ret = await platform + .invokeMethod("active.getHostInfo", {"id": id, "vpnIp": vpnIp, "pending": pending}); final h = jsonDecode(ret); if (h == null) { return null; } return HostInfo.fromJson(h); - } on PlatformException catch (err) { throw err.details ?? err.message ?? err.toString(); } catch (err) { @@ -282,14 +279,14 @@ class Site { Future setRemoteForTunnel(String vpnIp, String addr) async { try { - var ret = await platform.invokeMethod("active.setRemoteForTunnel", {"id": id, "vpnIp": vpnIp, "addr": addr}); + var ret = await platform + .invokeMethod("active.setRemoteForTunnel", {"id": id, "vpnIp": vpnIp, "addr": addr}); final h = jsonDecode(ret); if (h == null) { return null; } return HostInfo.fromJson(h); - } on PlatformException catch (err) { throw err.details ?? err.message ?? err.toString(); } catch (err) { @@ -300,7 +297,6 @@ class Site { Future closeTunnel(String vpnIp) async { try { return await platform.invokeMethod("active.closeTunnel", {"id": id, "vpnIp": vpnIp}); - } on PlatformException catch (err) { throw err.details ?? err.message ?? err.toString(); } catch (err) { diff --git a/lib/models/UnsafeRoute.dart b/lib/models/UnsafeRoute.dart index bb53166..684fed8 100644 --- a/lib/models/UnsafeRoute.dart +++ b/lib/models/UnsafeRoute.dart @@ -15,4 +15,4 @@ class UnsafeRoute { 'via': via, }; } -} \ No newline at end of file +} diff --git a/lib/screens/AboutScreen.dart b/lib/screens/AboutScreen.dart index 8a8433d..9b18878 100644 --- a/lib/screens/AboutScreen.dart +++ b/lib/screens/AboutScreen.dart @@ -48,18 +48,32 @@ class _AboutScreenState extends State { title: 'About', child: Column(children: [ ConfigSection(children: [ - ConfigItem(label: Text('App version'), labelWidth: 150, content: _buildText('${packageInfo.version}-${packageInfo.buildNumber} (sha: $gitSha)')), - ConfigItem(label: Text('Nebula version'), labelWidth: 150, content: _buildText('$nebulaVersion ($goVersion)')), - ConfigItem(label: Text('Flutter version'), labelWidth: 150, content: _buildText(flutterVersion['frameworkVersion'])), - ConfigItem(label: Text('Dart version'), labelWidth: 150, content: _buildText(flutterVersion['dartSdkVersion'])), + ConfigItem( + label: Text('App version'), + labelWidth: 150, + content: _buildText('${packageInfo.version}-${packageInfo.buildNumber} (sha: $gitSha)')), + ConfigItem( + label: Text('Nebula version'), labelWidth: 150, content: _buildText('$nebulaVersion ($goVersion)')), + ConfigItem( + label: Text('Flutter version'), labelWidth: 150, content: _buildText(flutterVersion['frameworkVersion'])), + ConfigItem( + label: Text('Dart version'), labelWidth: 150, content: _buildText(flutterVersion['dartSdkVersion'])), ]), ConfigSection(children: [ //TODO: wire up these other pages // ConfigPageItem(label: Text('Changelog'), labelWidth: 300, onPressed: () => Utils.launchUrl('https://defined.net/mobile/changelog', context)), - ConfigPageItem(label: Text('Privacy policy'), labelWidth: 300, onPressed: () => Utils.launchUrl('https://defined.net/privacy-policy', context)), + ConfigPageItem( + label: Text('Privacy policy'), + labelWidth: 300, + onPressed: () => Utils.launchUrl('https://defined.net/privacy-policy', context)), // ConfigPageItem(label: Text('Licenses'), labelWidth: 300, onPressed: () => Utils.launchUrl('https://defined.net/mobile/license', context)), ]), - Padding(padding: EdgeInsets.only(top: 20), child: Text('Copyright © 2020 Defined Networking, Inc', textAlign: TextAlign.center,)), + Padding( + padding: EdgeInsets.only(top: 20), + child: Text( + 'Copyright © 2020 Defined Networking, Inc', + textAlign: TextAlign.center, + )), ]), ); } @@ -67,4 +81,4 @@ class _AboutScreenState extends State { _buildText(String str) { return Align(alignment: AlignmentDirectional.centerEnd, child: SpecialSelectableText(str)); } -} \ No newline at end of file +} diff --git a/lib/screens/HostInfoScreen.dart b/lib/screens/HostInfoScreen.dart index 696fa34..a139675 100644 --- a/lib/screens/HostInfoScreen.dart +++ b/lib/screens/HostInfoScreen.dart @@ -161,16 +161,16 @@ class _HostInfoScreenState extends State { 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); - } - }, deleteLabel: 'Close')))); + 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); + } + }, deleteLabel: 'Close')))); } _getHostInfo() async { diff --git a/lib/screens/MainScreen.dart b/lib/screens/MainScreen.dart index 1761c6b..38276f6 100644 --- a/lib/screens/MainScreen.dart +++ b/lib/screens/MainScreen.dart @@ -87,18 +87,19 @@ class _MainScreenState extends State { Widget _buildNoSites() { return Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 8.0), - child: Text('Welcome to Nebula!', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)), + padding: const EdgeInsets.all(8.0), + child: Center( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 8.0), + child: Text('Welcome to Nebula!', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)), + ), + Text('You don\'t have any site configurations installed yet. Hit the plus button above to get started.', + textAlign: TextAlign.center), + ], ), - Text('You don\'t have any site configurations installed yet. Hit the plus button above to get started.', - textAlign: TextAlign.center), - ], - ), - )); + )); } Widget _buildSites() { @@ -146,10 +147,7 @@ class _MainScreenState extends State { } // The theme here is to remove the hardcoded canvas border reordering forces on us - return Theme( - data: Theme.of(context).copyWith(canvasColor: Colors.transparent), - child: child - ); + return Theme(data: Theme.of(context).copyWith(canvasColor: Colors.transparent), child: child); } Widget _debugSave() { @@ -173,15 +171,16 @@ mUOcsdFcCZiXrj7ryQIG1+WfqA46w71A/lV4nAc= -----END NEBULA CERTIFICATE-----'''; var s = Site( - name: "DEBUG TEST", - id: uuid.v4(), - staticHostmap: { - "10.1.0.1": StaticHost(lighthouse: true, destinations: [IPAndPort(ip: '10.1.1.53', port: 4242), IPAndPort(ip: '1::1', port: 4242)]) - }, - ca: [CertificateInfo.debug(rawCert: ca)], - certInfo: CertificateInfo.debug(rawCert: cert), - unsafeRoutes: [UnsafeRoute(route: '10.3.3.3/32', via: '10.1.0.1')] - ); + name: "DEBUG TEST", + id: uuid.v4(), + staticHostmap: { + "10.1.0.1": StaticHost( + lighthouse: true, + destinations: [IPAndPort(ip: '10.1.1.53', port: 4242), IPAndPort(ip: '1::1', port: 4242)]) + }, + ca: [CertificateInfo.debug(rawCert: ca)], + certInfo: CertificateInfo.debug(rawCert: cert), + unsafeRoutes: [UnsafeRoute(route: '10.3.3.3/32', via: '10.1.0.1')]); s.key = '''-----BEGIN NEBULA X25519 PRIVATE KEY----- rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg= @@ -239,7 +238,8 @@ rmXnR1yvDZi1VPVmnNVY8NMsQpEpbbYlq7rul+ByQvg= } if (hasErrors) { - Utils.popError(context, "Site Error(s)", "1 or more sites have errors and need your attention, problem sites have a red border."); + Utils.popError(context, "Site Error(s)", + "1 or more sites have errors and need your attention, problem sites have a red border."); } sites.sort((a, b) { diff --git a/lib/screens/SiteDetailScreen.dart b/lib/screens/SiteDetailScreen.dart index 86b1600..c42e03d 100644 --- a/lib/screens/SiteDetailScreen.dart +++ b/lib/screens/SiteDetailScreen.dart @@ -62,7 +62,6 @@ class _SiteDetailScreenState extends State { } } setState(() {}); - }, onError: (err) { setState(() {}); Utils.popError(context, "Error", err); @@ -111,7 +110,8 @@ class _SiteDetailScreenState extends State { List items = []; site.errors.forEach((error) { items.add(ConfigItem( - labelWidth: 0, content: Padding(padding: EdgeInsets.symmetric(vertical: 10), child: SpecialSelectableText(error)))); + labelWidth: 0, + content: Padding(padding: EdgeInsets.symmetric(vertical: 10), child: SpecialSelectableText(error)))); }); return ConfigSection( diff --git a/lib/screens/SiteLogsScreen.dart b/lib/screens/SiteLogsScreen.dart index d6e26e5..efbe1a0 100644 --- a/lib/screens/SiteLogsScreen.dart +++ b/lib/screens/SiteLogsScreen.dart @@ -54,7 +54,10 @@ class _SiteLogsScreenState extends State { refreshController.loadComplete(); }, refreshController: refreshController, - child: Container(padding: EdgeInsets.all(5), constraints: logBoxConstraints(context), child: SpecialSelectableText(logs.trim(), style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14))), + child: Container( + padding: EdgeInsets.all(5), + constraints: logBoxConstraints(context), + child: SpecialSelectableText(logs.trim(), style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14))), bottomBar: _buildBottomBar(), ); } @@ -78,7 +81,10 @@ class _SiteLogsScreenState extends State { padding: padding, icon: Icon(context.platformIcons.share, size: 30), onPressed: () { - Share.shareFile(title: '${widget.site.name} logs', filePath: widget.site.logFile, filename: '${widget.site.name}.log'); + Share.shareFile( + title: '${widget.site.name} logs', + filePath: widget.site.logFile, + filename: '${widget.site.name}.log'); }, )), Expanded( @@ -94,7 +100,8 @@ class _SiteLogsScreenState extends State { padding: padding, icon: Icon(context.platformIcons.downArrow, size: 30), onPressed: () async { - controller.animateTo(controller.position.maxScrollExtent, duration: const Duration(milliseconds: 500), curve: Curves.linearToEaseOut); + controller.animateTo(controller.position.maxScrollExtent, + duration: const Duration(milliseconds: 500), curve: Curves.linearToEaseOut); }, )), ])); diff --git a/lib/screens/siteConfig/AddCertificateScreen.dart b/lib/screens/siteConfig/AddCertificateScreen.dart index 011a062..89171a0 100644 --- a/lib/screens/siteConfig/AddCertificateScreen.dart +++ b/lib/screens/siteConfig/AddCertificateScreen.dart @@ -72,9 +72,7 @@ class _AddCertificateScreenState extends State { items.addAll(_buildShare()); items.addAll(_buildLoadCert()); - return SimplePage( - title: 'Certificate', - child: Column(children: items)); + return SimplePage(title: 'Certificate', child: Column(children: items)); } _generateKeys() async { @@ -215,8 +213,8 @@ class _AddCertificateScreenState extends State { if (certs.length > 0) { var tryCertInfo = CertificateInfo.fromJson(certs.first); if (tryCertInfo.cert.details.isCa) { - return Utils.popError(context, 'Error loading certificate content', 'A certificate authority is not appropriate for a client certificate.'); - + return Utils.popError(context, 'Error loading certificate content', + 'A certificate authority is not appropriate for a client certificate.'); } else if (!tryCertInfo.validity.valid) { return Utils.popError(context, 'Certificate was invalid', tryCertInfo.validity.reason); } @@ -232,10 +230,12 @@ class _AddCertificateScreenState extends State { // We have a cert, pop the details screen where they can hit save Utils.openPage(context, (context) { - return CertificateDetailsScreen(certInfo: tryCertInfo, onSave: () { - Navigator.pop(context); - widget.onSave(CertificateResult(certInfo: tryCertInfo, key: privKey)); - }); + return CertificateDetailsScreen( + certInfo: tryCertInfo, + onSave: () { + Navigator.pop(context); + widget.onSave(CertificateResult(certInfo: tryCertInfo, key: privKey)); + }); }); } } on PlatformException catch (err) { @@ -255,4 +255,4 @@ uDneQqytYS+BUfgNnGX5wsMxOEst/kkC const _testKey = '''-----BEGIN NEBULA X25519 PRIVATE KEY----- UlyDdFn/2mLFykeWjCEwWVRSDHtMF7nz3At3O77Faf4= ------END NEBULA X25519 PRIVATE KEY-----'''; \ No newline at end of file +-----END NEBULA X25519 PRIVATE KEY-----'''; diff --git a/lib/screens/siteConfig/AdvancedScreen.dart b/lib/screens/siteConfig/AdvancedScreen.dart index b80c8f8..7bb07ab 100644 --- a/lib/screens/siteConfig/AdvancedScreen.dart +++ b/lib/screens/siteConfig/AdvancedScreen.dart @@ -148,19 +148,21 @@ class _AdvancedScreenState extends State { }); }), ConfigPageItem( - label: Text('Unsafe routes'), - labelWidth: 150, - content: Text(Utils.itemCountFormat(settings.unsafeRoutes.length), textAlign: TextAlign.end), - onPressed: () { - Utils.openPage(context, (context) { - return UnsafeRoutesScreen(unsafeRoutes: settings.unsafeRoutes, onSave: (routes) { - setState(() { - settings.unsafeRoutes = routes; - changed = true; + label: Text('Unsafe routes'), + labelWidth: 150, + content: Text(Utils.itemCountFormat(settings.unsafeRoutes.length), textAlign: TextAlign.end), + onPressed: () { + Utils.openPage(context, (context) { + return UnsafeRoutesScreen( + unsafeRoutes: settings.unsafeRoutes, + onSave: (routes) { + setState(() { + settings.unsafeRoutes = routes; + changed = true; + }); }); - }); - }); - }, + }); + }, ) ], ), diff --git a/lib/screens/siteConfig/CAListScreen.dart b/lib/screens/siteConfig/CAListScreen.dart index bea726e..0a307c4 100644 --- a/lib/screens/siteConfig/CAListScreen.dart +++ b/lib/screens/siteConfig/CAListScreen.dart @@ -201,7 +201,6 @@ class _CAListScreenState extends State { setState(() {}); } }); - } catch (err) { return Utils.popError(context, 'Failed to load CA file', err.toString()); } diff --git a/lib/screens/siteConfig/CertificateDetailsScreen.dart b/lib/screens/siteConfig/CertificateDetailsScreen.dart index 2e86cf8..559c38a 100644 --- a/lib/screens/siteConfig/CertificateDetailsScreen.dart +++ b/lib/screens/siteConfig/CertificateDetailsScreen.dart @@ -12,7 +12,8 @@ import 'package:mobile_nebula/services/utils.dart'; /// Displays the details of a CertificateInfo object. Respects incomplete objects (missing validity or rawCert) class CertificateDetailsScreen extends StatefulWidget { - const CertificateDetailsScreen({Key key, this.certInfo, this.onDelete, this.onSave, this.onReplace}) : super(key: key); + const CertificateDetailsScreen({Key key, this.certInfo, this.onDelete, this.onSave, this.onReplace}) + : super(key: key); final CertificateInfo certInfo; @@ -78,8 +79,7 @@ class _CertificateDetailsScreenState extends State { return ConfigSection(children: [ ConfigItem(label: Text('Name'), content: SpecialSelectableText(certInfo.cert.details.name)), ConfigItem( - label: Text('Type'), - content: Text(certInfo.cert.details.isCa ? 'CA certificate' : 'Client certificate')), + label: Text('Type'), content: Text(certInfo.cert.details.isCa ? 'CA certificate' : 'Client certificate')), ]); } @@ -106,18 +106,17 @@ class _CertificateDetailsScreenState extends State { Widget _buildFilters() { List items = []; if (certInfo.cert.details.groups.length > 0) { - items.add(ConfigItem( - label: Text('Groups'), content: SpecialSelectableText(certInfo.cert.details.groups.join(', ')))); + items.add( + ConfigItem(label: Text('Groups'), content: SpecialSelectableText(certInfo.cert.details.groups.join(', ')))); } if (certInfo.cert.details.ips.length > 0) { - items - .add(ConfigItem(label: Text('IPs'), content: SpecialSelectableText(certInfo.cert.details.ips.join(', ')))); + items.add(ConfigItem(label: Text('IPs'), content: SpecialSelectableText(certInfo.cert.details.ips.join(', ')))); } if (certInfo.cert.details.subnets.length > 0) { - items.add(ConfigItem( - label: Text('Subnets'), content: SpecialSelectableText(certInfo.cert.details.subnets.join(', ')))); + items.add( + ConfigItem(label: Text('Subnets'), content: SpecialSelectableText(certInfo.cert.details.subnets.join(', ')))); } return items.length > 0 @@ -141,8 +140,8 @@ class _CertificateDetailsScreenState extends State { certInfo.rawCert != null ? ConfigItem( label: Text('PEM Format'), - content: SpecialSelectableText(certInfo.rawCert, - style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)), + content: + SpecialSelectableText(certInfo.rawCert, style: TextStyle(fontFamily: 'RobotoMono', fontSize: 14)), crossAxisAlignment: CrossAxisAlignment.start) : Container(), ], @@ -155,26 +154,26 @@ class _CertificateDetailsScreenState extends State { } return Padding( - padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10), - child: SizedBox( - width: double.infinity, - child: PlatformButton( - child: Text('Replace certificate'), - color: CupertinoColors.systemRed.resolveFrom(context), - onPressed: () { - Utils.openPage(context, (context) { - return AddCertificateScreen( - onReplace: (result) { - setState(() { - changed = true; - certResult = result; - certInfo = certResult.certInfo; + padding: EdgeInsets.only(top: 50, bottom: 10, left: 10, right: 10), + child: SizedBox( + width: double.infinity, + child: PlatformButton( + child: Text('Replace certificate'), + color: CupertinoColors.systemRed.resolveFrom(context), + onPressed: () { + Utils.openPage(context, (context) { + return AddCertificateScreen(onReplace: (result) { + setState(() { + changed = true; + certResult = result; + certInfo = certResult.certInfo; + }); + // Slam the page back to the top + controller.animateTo(0, + duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut); }); - // Slam the page back to the top - controller.animateTo(0, duration: const Duration(milliseconds: 10), curve: Curves.linearToEaseOut); }); - }); - }))); + }))); } Widget _buildDelete() { diff --git a/lib/screens/siteConfig/SiteConfigScreen.dart b/lib/screens/siteConfig/SiteConfigScreen.dart index c2aabcc..af7e03a 100644 --- a/lib/screens/siteConfig/SiteConfigScreen.dart +++ b/lib/screens/siteConfig/SiteConfigScreen.dart @@ -140,25 +140,23 @@ class _SiteConfigScreenState extends State { Utils.openPage(context, (context) { if (site.certInfo != null) { return CertificateDetailsScreen( - certInfo: site.certInfo, - onReplace: (result) { - setState(() { - changed = true; - site.certInfo = result.certInfo; - site.key = result.key; + certInfo: site.certInfo, + onReplace: (result) { + setState(() { + changed = true; + site.certInfo = result.certInfo; + site.key = result.key; + }); }); - } - ); } - return AddCertificateScreen( - onSave: (result) { - setState(() { - changed = true; - site.certInfo = result.certInfo; - site.key = result.key; - }); - }); + return AddCertificateScreen(onSave: (result) { + setState(() { + changed = true; + site.certInfo = result.certInfo; + site.key = result.key; + }); + }); }); }, ), diff --git a/lib/screens/siteConfig/UnsafeRoutesScreen.dart b/lib/screens/siteConfig/UnsafeRoutesScreen.dart index af5fda7..44d0aaa 100644 --- a/lib/screens/siteConfig/UnsafeRoutesScreen.dart +++ b/lib/screens/siteConfig/UnsafeRoutesScreen.dart @@ -83,12 +83,14 @@ class _UnsafeRoutesScreenState extends State { content: Text('Add a new route'), onPressed: () { Utils.openPage(context, (context) { - return UnsafeRouteScreen(route: UnsafeRoute(), onSave: (route) { - setState(() { - changed = true; - unsafeRoutes[UniqueKey()] = route; - }); - }); + return UnsafeRouteScreen( + route: UnsafeRoute(), + onSave: (route) { + setState(() { + changed = true; + unsafeRoutes[UniqueKey()] = route; + }); + }); }); }, )); diff --git a/lib/services/share.dart b/lib/services/share.dart index 4127d39..d8a3623 100644 --- a/lib/services/share.dart +++ b/lib/services/share.dart @@ -44,8 +44,7 @@ class Share { throw FlutterError('FilePath cannot be null'); } - final bool success = - await _channel.invokeMethod('shareFile', { + final bool success = await _channel.invokeMethod('shareFile', { 'title': title, 'filePath': filePath, 'filename': filename, @@ -53,4 +52,4 @@ class Share { return success; } -} \ No newline at end of file +} diff --git a/lib/validators/ipValidator.dart b/lib/validators/ipValidator.dart index ab0d80e..69a9226 100644 --- a/lib/validators/ipValidator.dart +++ b/lib/validators/ipValidator.dart @@ -7,15 +7,19 @@ bool ipValidator(String str, bool enableIPV6) { } switch (ia.type) { - case InternetAddressType.IPv6: { - if (enableIPV6) { + case InternetAddressType.IPv6: + { + if (enableIPV6) { + return true; + } + } + break; + + case InternetAddressType.IPv4: + { return true; } - } - break; - - case InternetAddressType.IPv4: { return true; } - break; + break; } return false;