From 8fc3a40467fc9e35fd9d55879154fc0ea3039b8a Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Tue, 22 Nov 2022 10:12:38 -0600 Subject: [PATCH] Add a manual enrollment flow to support chromeos (#99) --- lib/main.dart | 13 +++----- lib/screens/AboutScreen.dart | 2 +- lib/screens/EnrollmentScreen.dart | 51 ++++++++++++++++++++++++------- lib/screens/MainScreen.dart | 6 ++-- lib/screens/SettingsScreen.dart | 22 +++++++++++-- 5 files changed, 68 insertions(+), 26 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 4c9627d..5d68fcc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -109,21 +109,18 @@ class _AppState extends State { ), onGenerateRoute: (settings) { if (settings.name == '/') { - return platformPageRoute(context: context, builder: (context) => MainScreen(this.dnEnrolled.stream)); + return platformPageRoute(context: context, builder: (context) => MainScreen(this.dnEnrolled)); } final uri = Uri.parse(settings.name!); if (uri.path == EnrollmentScreen.routeName) { - String? code; - if (uri.hasFragment) { - final qp = Uri.splitQueryString(uri.fragment); - code = qp["code"]; - } - // TODO: maybe implement this as a dialog instead of a page, you can stack multiple enrollment screens which is annoying in dev return platformPageRoute( context: context, - builder: (context) => EnrollmentScreen(code: code, stream: this.dnEnrolled), + builder: (context) => EnrollmentScreen( + code: EnrollmentScreen.parseCode(settings.name!), + stream: this.dnEnrolled + ), ); } diff --git a/lib/screens/AboutScreen.dart b/lib/screens/AboutScreen.dart index 72135b3..7e5e617 100644 --- a/lib/screens/AboutScreen.dart +++ b/lib/screens/AboutScreen.dart @@ -73,7 +73,7 @@ class _AboutScreenState extends State { Padding( padding: EdgeInsets.only(top: 20), child: Text( - 'Copyright © 2020 Defined Networking, Inc', + 'Copyright © 2022 Defined Networking, Inc', textAlign: TextAlign.center, )), ]), diff --git a/lib/screens/EnrollmentScreen.dart b/lib/screens/EnrollmentScreen.dart index b9ba9d9..d2ac592 100644 --- a/lib/screens/EnrollmentScreen.dart +++ b/lib/screens/EnrollmentScreen.dart @@ -1,10 +1,10 @@ import 'dart:async'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:mobile_nebula/components/SimplePage.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -16,6 +16,22 @@ class EnrollmentScreen extends StatefulWidget { static const routeName = '/v1/mobile-enrollment'; + // Attempts to find an enrollment code in the provided url. If one is not found then assume the input was + // an enrollment code. Primarily to support manual dn enrollment where the user can input a code or a url. + static String parseCode(String url) { + final uri = Uri.parse(url); + if (uri.path != EnrollmentScreen.routeName) { + return url; + } + + if (uri.hasFragment) { + final qp = Uri.splitQueryString(uri.fragment); + return qp["code"] ?? ""; + } + + return url; + } + const EnrollmentScreen({super.key, this.code, this.stream, this.allowCodeEntry = false}); @override @@ -138,20 +154,33 @@ class _EnrollmentScreenState extends State { )); } - return SimplePage(title: Text('DN Enrollment'), child: Padding(child: child, padding: EdgeInsets.symmetric(horizontal: 10)), alignment: alignment); + final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg'; + return SimplePage( + title: Text('Enroll with DN', style: TextStyle(fontWeight: FontWeight.bold)), + child: Padding(child: child, padding: EdgeInsets.symmetric(horizontal: 10)), + alignment: alignment + ); } Widget _codeEntry() { return Column(children: [ - Container(height: 20), - PlatformTextField(controller: enrollInput), - CupertinoButton(child: Text('Enroll'), onPressed: () { - setState(() { - code = enrollInput.text; - error = null; - _enroll(); - }); - }), + Padding( + padding: EdgeInsets.only(top: 20), + child: PlatformTextField( + hintText: 'Enrollment code or link', + controller: enrollInput + ) + ), + PlatformTextButton( + child: Text('Submit'), + onPressed: () { + setState(() { + code = EnrollmentScreen.parseCode(enrollInput.text); + error = null; + _enroll(); + }); + }, + ) ]); } } diff --git a/lib/screens/MainScreen.dart b/lib/screens/MainScreen.dart index 06a9cc8..1f13e7a 100644 --- a/lib/screens/MainScreen.dart +++ b/lib/screens/MainScreen.dart @@ -63,7 +63,7 @@ MAIH7gzreMGgrH/yR6rZpIHR3DxJ3E0aHtEI class MainScreen extends StatefulWidget { const MainScreen(this.dnEnrollStream, {Key? key}) : super(key: key); - final Stream dnEnrollStream; + final StreamController dnEnrollStream; @override _MainScreenState createState() => _MainScreenState(); @@ -82,7 +82,7 @@ class _MainScreenState extends State { void initState() { _loadSites(); - widget.dnEnrollStream.listen((_) { + widget.dnEnrollStream.stream.listen((_) { _loadSites(); }); @@ -146,7 +146,7 @@ class _MainScreenState extends State { PlatformIconButton( padding: EdgeInsets.zero, icon: Icon(Icons.menu, size: 28.0), - onPressed: () => Utils.openPage(context, (_) => SettingsScreen()), + onPressed: () => Utils.openPage(context, (_) => SettingsScreen(widget.dnEnrollStream)), ), ], bottomBar: debugSite, diff --git a/lib/screens/SettingsScreen.dart b/lib/screens/SettingsScreen.dart index 43d723f..ee0af8f 100644 --- a/lib/screens/SettingsScreen.dart +++ b/lib/screens/SettingsScreen.dart @@ -1,18 +1,24 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:mobile_nebula/components/SimplePage.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/screens/EnrollmentScreen.dart'; import 'package:mobile_nebula/services/settings.dart'; import 'package:mobile_nebula/services/utils.dart'; import 'AboutScreen.dart'; class SettingsScreen extends StatefulWidget { + final StreamController stream; + + const SettingsScreen(this.stream, {super.key}); + @override - _SettingsScreenState createState() { - return _SettingsScreenState(); - } + _SettingsScreenState createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { @@ -79,6 +85,16 @@ class _SettingsScreenState extends State { }, )), )); + + final dnIcon = Theme.of(context).brightness == Brightness.dark ? 'images/dn-logo-dark.svg' : 'images/dn-logo-light.svg'; + items.add(ConfigSection(children: [ + ConfigPageItem( + label: Text('Enroll with DN'), + labelWidth: 200, + onPressed: () => Utils.openPage(context, (context) => EnrollmentScreen(stream: widget.stream, allowCodeEntry: true)) + ) + ])); + items.add(ConfigSection(children: [ ConfigPageItem( label: Text('About'),