update beta dialog code to follow common pattern

This commit is contained in:
Adam Velebil 2022-09-14 12:03:36 +02:00
parent e17feb792d
commit fa37316b52
No known key found for this signature in database
GPG Key ID: AC6D6B9D715FC084
5 changed files with 152 additions and 109 deletions

View File

@ -3,7 +3,7 @@ import 'package:integration_test/integration_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:yubico_authenticator/android/keys.dart' as keys;
import '../test_util.dart';
import '../android/util.dart' as android_test_util;
import 'constants.dart';
void main() {
@ -11,29 +11,28 @@ void main() {
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
group('Beta welcome dialog', () {
// this is here to make sure yubikey is connected before we test the dialog
testWidgets('startup', (WidgetTester tester) async {
await tester.startUp({
'dlg.beta.enabled': false,
});
});
testWidgets('shows welcome screen', (WidgetTester tester) async {
await tester.startUp({
await android_test_util.startUp(tester, {
'dlg.beta.enabled': true,
'needs_yubikey': false,
});
expect(find.byKey(keys.betaDialogView), findsOneWidget);
});
testWidgets('does not show welcome dialog', (WidgetTester tester) async {
await tester.startUp();
await android_test_util.startUp(tester, {
'dlg.beta.enabled': false,
'needs_yubikey': false,
});
expect(find.byKey(keys.betaDialogView), findsNothing);
});
testWidgets('updates preferences', (WidgetTester tester) async {
await tester.startUp({'dlg.beta.enabled': true});
await android_test_util.startUp(tester, {
'dlg.beta.enabled': true,
'needs_yubikey': false,
});
var prefs = await SharedPreferences.getInstance();
await tester.tap(find.byKey(keys.okButton));
await expectLater(prefs.getBool(betaDialogPrefName), equals(false));

View File

@ -22,11 +22,15 @@ Future<void> startUp(WidgetTester tester,
await tester.pumpWidget(await initialize());
// wait for a YubiKey connection
await tester.waitForFinder(find.descendant(
of: tester.findDeviceButton(),
matching: find.byWidgetPredicate((widget) =>
widget is DeviceAvatar && widget.key != app_keys.noDeviceAvatar)));
// only wait for yubikey connection when needed
// needs_yubikey defaults to true
if (startUpParams['needs_yubikey'] != false) {
// wait for a YubiKey connection
await tester.waitForFinder(find.descendant(
of: tester.findDeviceButton(),
matching: find.byWidgetPredicate((widget) =>
widget is DeviceAvatar && widget.key != app_keys.noDeviceAvatar)));
}
await tester.pump(const Duration(milliseconds: 500));
}

View File

@ -65,8 +65,8 @@ Future<Widget> initialize() async {
/// initializes global handler for dialogs
ref.read(androidDialogProvider);
var betaDialog = BetaDialog(context, ref);
betaDialog.request();
/// if the beta dialog was not shown yet, this will show it
requestBetaDialog(ref);
return const MainPage();
},

View File

@ -2,103 +2,142 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../app/message.dart';
import '../../app/state.dart';
import '../../core/state.dart';
import '../keys.dart' as keys;
class BetaDialog {
final BuildContext context;
final WidgetRef ref;
void requestBetaDialog(WidgetRef ref) async {
const String prefBetaDialogShouldBeShown = 'prefBetaDialogShouldBeShown';
var sharedPrefs = ref.read(prefProvider);
await sharedPrefs.reload();
var dialogShouldBeShown =
sharedPrefs.getBool(prefBetaDialogShouldBeShown) ?? true;
if (dialogShouldBeShown) {
final withContext = ref.read(withContextProvider);
const BetaDialog(this.context, this.ref);
void request() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
var sharedPrefs = ref.read(prefProvider);
await sharedPrefs.reload();
var dialogShouldBeShown =
sharedPrefs.getBool(prefBetaDialogShouldBeShown) ?? true;
if (dialogShouldBeShown) {
Future.delayed(const Duration(milliseconds: 100), () async {
await showBetaDialog();
});
}
});
}
Future<void> showBetaDialog() async {
await showBlurDialog(
context: context,
builder: (context) {
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
return WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
key: keys.betaDialogView,
content: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.asset(
isDarkTheme
? 'assets/graphics/beta-dark.png'
: 'assets/graphics/beta-light.png',
alignment: Alignment.topCenter,
height: 124,
filterQuality: FilterQuality.medium,
),
const SizedBox(height: 24),
Text(
'Welcome to Yubico Authenticator Beta!',
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
color: isDarkTheme ? Colors.white : Colors.black),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
const Text(
'• Preview the latest beta: Try out the newest features. (Sometimes these may be a little rough around the edges.)'),
const SizedBox(height: 8),
const Text(
'• Give early feedback: Let us know what you think and help make Authenticator for Android a better experience. Go to “Send us feedback” under Help and about.'),
],
),
actions: <Widget>[
// FIXME: enable and add correct uri
// TextButton(
// style: TextButton.styleFrom(
// textStyle: Theme.of(context)
// .textTheme
// .labelLarge
// ?.copyWith(fontWeight: FontWeight.bold),
// ),
// child: const Text('Learn more'),
// onPressed: () {
// launchUrl(Uri.parse('https://learn more uri'),
// mode: LaunchMode.externalApplication);
// onBetaDialogClosed(context, ref);
// },
// ),
TextButton(
key: keys.okButton,
style: TextButton.styleFrom(
textStyle: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(fontWeight: FontWeight.bold),
),
child: const Text('Got it'),
onPressed: () => onBetaDialogClosed(context, ref),
),
],
),
await withContext(
(context) async {
await showBlurDialog(
context: context,
builder: (context) => const _BetaDialog(),
routeSettings: const RouteSettings(name: 'android_beta_dialog'),
);
},
);
}
final String prefBetaDialogShouldBeShown = 'prefBetaDialogShouldBeShown';
void onBetaDialogClosed(BuildContext context, WidgetRef ref) async {
Navigator.of(context).pop(true);
await ref.read(prefProvider).setBool(prefBetaDialogShouldBeShown, false);
await sharedPrefs.setBool(prefBetaDialogShouldBeShown, false);
}
}
class _BetaDialog extends StatefulWidget {
const _BetaDialog();
@override
State<StatefulWidget> createState() => _BetaDialogState();
}
class _BetaDialogState extends State<_BetaDialog> {
late FocusScopeNode _focus;
@override
void initState() {
super.initState();
_focus = FocusScopeNode();
}
@override
void dispose() {
_focus.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// This keeps the focus in the dialog, even if the underlying page
// changes as it does when a new device is selected.
return FocusScope(
node: _focus,
autofocus: true,
onFocusChange: (focused) {
if (!focused) {
_focus.requestFocus();
}
},
child: const _BetaDialogContent(),
);
}
}
class _BetaDialogContent extends ConsumerWidget {
const _BetaDialogContent();
@override
Widget build(BuildContext context, WidgetRef ref) {
final isDarkTheme = Theme.of(context).brightness == Brightness.dark;
return WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
key: keys.betaDialogView,
content: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.asset(
isDarkTheme
? 'assets/graphics/beta-dark.png'
: 'assets/graphics/beta-light.png',
alignment: Alignment.topCenter,
height: 124,
filterQuality: FilterQuality.medium,
),
const SizedBox(height: 24),
Text(
'Welcome to Yubico Authenticator Beta!',
style: Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(color: isDarkTheme ? Colors.white : Colors.black),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
const Text(
'• Preview the latest beta: Try out the newest features. (Sometimes these may be a little rough around the edges.)'),
const SizedBox(height: 8),
const Text(
'• Give early feedback: Let us know what you think and help make Authenticator for Android a better experience. Go to “Send us feedback” under Help and about.'),
],
),
actions: <Widget>[
// FIXME: enable and add correct uri
// TextButton(
// style: TextButton.styleFrom(
// textStyle: Theme.of(context)
// .textTheme
// .labelLarge
// ?.copyWith(fontWeight: FontWeight.bold),
// ),
// child: const Text('Learn more'),
// onPressed: () {
// launchUrl(Uri.parse('https://learn more uri'),
// mode: LaunchMode.externalApplication);
// onBetaDialogClosed(context, ref);
// },
// ),
TextButton(
key: keys.okButton,
style: TextButton.styleFrom(
textStyle: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(fontWeight: FontWeight.bold),
),
child: const Text('Got it'),
onPressed: () => Navigator.of(context)
.pop(true) //{}, //onBetaDialogClosed(context, ref),
),
],
),
);
}
}

View File

@ -26,6 +26,7 @@ class MainPage extends ConsumerWidget {
Navigator.of(context).popUntil((route) {
return route.isFirst ||
[
'android_beta_dialog',
'device_picker',
'settings',
'about',