From 36f08778816c738092b1d77e4db9aae6bfced25e Mon Sep 17 00:00:00 2001 From: Elias Bonnici Date: Tue, 19 Dec 2023 16:09:55 +0100 Subject: [PATCH 1/3] Change FIDO bio enrollment UI. --- lib/fido/views/add_fingerprint_dialog.dart | 109 +++++++++++++-------- lib/l10n/app_de.arb | 1 + lib/l10n/app_en.arb | 1 + lib/l10n/app_fr.arb | 1 + lib/l10n/app_ja.arb | 1 + lib/l10n/app_pl.arb | 1 + 6 files changed, 73 insertions(+), 41 deletions(-) diff --git a/lib/fido/views/add_fingerprint_dialog.dart b/lib/fido/views/add_fingerprint_dialog.dart index 138a7d399..bc9bfeab8 100755 --- a/lib/fido/views/add_fingerprint_dialog.dart +++ b/lib/fido/views/add_fingerprint_dialog.dart @@ -65,10 +65,15 @@ class _AddFingerprintDialogState extends ConsumerState super.dispose(); } - Animation _animateColor(Color color, + Animation _animateColor(bool success, {Function? atPeak, bool reverse = true}) { + final theme = Theme.of(context); + final darkMode = theme.brightness == Brightness.dark; + final beginColor = darkMode ? Colors.white : Colors.black; + final endColor = + success ? theme.colorScheme.primary : theme.colorScheme.error; final animation = - ColorTween(begin: Colors.black, end: color).animate(_animator); + ColorTween(begin: beginColor, end: endColor).animate(_animator); _animator.forward().then((_) { if (reverse) { atPeak?.call(); @@ -85,8 +90,7 @@ class _AddFingerprintDialogState extends ConsumerState _nameFocus = FocusNode(); _animator = AnimationController( vsync: this, duration: const Duration(milliseconds: 250)); - _color = - ColorTween(begin: Colors.black, end: Colors.black).animate(_animator); + _color = ColorTween().animate(_animator); _subscription = ref .read(fingerprintProvider(widget.devicePath).notifier) @@ -94,7 +98,7 @@ class _AddFingerprintDialogState extends ConsumerState .listen((event) { setState(() { event.when(capture: (remaining) { - _color = _animateColor(Colors.lightGreenAccent, atPeak: () { + _color = _animateColor(true, atPeak: () { setState(() { _samples += 1; _remaining = remaining; @@ -107,7 +111,7 @@ class _AddFingerprintDialogState extends ConsumerState Timer(const Duration(milliseconds: 100), _nameFocus.requestFocus); }, error: (code) { _log.debug('Fingerprint capture error (code: $code)'); - _color = _animateColor(Colors.redAccent); + _color = _animateColor(false); }); }); }, onError: (error, stacktrace) { @@ -176,19 +180,26 @@ class _AddFingerprintDialogState extends ConsumerState Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final progress = _samples == 0 ? 0.0 : _samples / (_samples + _remaining); - return ResponsiveDialog( title: Text(l10n.s_add_fingerprint), child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 18.0), + padding: const EdgeInsets.only(top: 38, bottom: 0, right: 18, left: 18), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text(l10n.l_fp_step_1_capture), Column( children: [ Padding( - padding: const EdgeInsets.all(36.0), + padding: const EdgeInsets.all(8.0), + child: Text( + _getMessage(), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.normal), + ), + ), + Padding( + padding: const EdgeInsets.all(28), child: AnimatedBuilder( animation: _color, builder: (context, _) { @@ -200,35 +211,49 @@ class _AddFingerprintDialogState extends ConsumerState }, ), ), - LinearProgressIndicator(value: progress), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text(_getMessage()), - ), + if (_fingerprint == null) ...[ + Container( + constraints: const BoxConstraints(maxWidth: 360), + child: LinearProgressIndicator( + value: progress, + ), + ) + ] ], ), - Text(l10n.l_fp_step_2_name), - AppTextFormField( - focusNode: _nameFocus, - maxLength: 15, - inputFormatters: [limitBytesLength(15)], - buildCounter: buildByteCounterFor(_label), - autofocus: true, - decoration: AppInputDecoration( - enabled: _fingerprint != null, - border: const OutlineInputBorder(), - labelText: l10n.s_name, - prefixIcon: const Icon(Icons.fingerprint_outlined), + if (_fingerprint != null) ...[ + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + l10n.l_name_fingerprint, + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.normal), + ), ), - onChanged: (value) { - setState(() { - _label = value.trim(); - }); - }, - onFieldSubmitted: (_) { - _submit(); - }, - ), + Container( + constraints: const BoxConstraints(maxWidth: 360), + child: AppTextFormField( + focusNode: _nameFocus, + maxLength: 15, + inputFormatters: [limitBytesLength(15)], + buildCounter: buildByteCounterFor(_label), + autofocus: true, + decoration: AppInputDecoration( + border: const OutlineInputBorder(), + labelText: l10n.s_name, + prefixIcon: const Icon(Icons.fingerprint_outlined), + ), + onChanged: (value) { + setState(() { + _label = value.trim(); + }); + }, + onFieldSubmitted: (_) { + _submit(); + }, + ), + ) + ], ] .map((e) => Padding( child: e, @@ -241,10 +266,12 @@ class _AddFingerprintDialogState extends ConsumerState _subscription.cancel(); }, actions: [ - TextButton( - onPressed: _fingerprint != null && _label.isNotEmpty ? _submit : null, - child: Text(l10n.s_save), - ), + if (_fingerprint != null) ...[ + TextButton( + onPressed: _label.isNotEmpty ? _submit : null, + child: Text(l10n.s_save), + ), + ] ], ); } diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 91a0187d0..c5974cb45 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -444,6 +444,7 @@ }, "p_press_fingerprint_begin": "Drücken Sie Ihren Finger gegen den YubiKey um zu beginnen.", "p_will_change_label_fp": "Das ändert die Beschriftung des Fingerabdrucks.", + "l_name_fingerprint": null, "@_fido_errors": {}, "l_user_action_timeout_error": null, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 4c178a2ea..558875a15 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -444,6 +444,7 @@ }, "p_press_fingerprint_begin": "Press your finger against the YubiKey to begin.", "p_will_change_label_fp": "This will change the label of the fingerprint.", + "l_name_fingerprint": "Name this fingerprint", "@_fido_errors": {}, "l_user_action_timeout_error": "Failed due to user inactivity", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index eaa70c801..75c70e4cd 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -444,6 +444,7 @@ }, "p_press_fingerprint_begin": "Posez votre doigt sur votre YubiKey pour commencer.", "p_will_change_label_fp": "Cette action changera la description de votre empreinte.", + "l_name_fingerprint": null, "@_fido_errors": {}, "l_user_action_timeout_error": null, diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index c40b1f619..7b2074f6e 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -444,6 +444,7 @@ }, "p_press_fingerprint_begin": "YubiKeyに指を押し当てて開始します", "p_will_change_label_fp": "これにより指紋のラベルが変更されます", + "l_name_fingerprint": null, "@_fido_errors": {}, "l_user_action_timeout_error": null, diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 4e9a6fe4e..6477d32e8 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -444,6 +444,7 @@ }, "p_press_fingerprint_begin": "Przytrzymaj palec na kluczu YubiKey, aby rozpocząć.", "p_will_change_label_fp": "Spowoduje to zmianę etykiety odcisku palca.", + "l_name_fingerprint": null, "@_fido_errors": {}, "l_user_action_timeout_error": null, From 7e384dae2e5b700d0368b7ab96708f7bf69253a7 Mon Sep 17 00:00:00 2001 From: Elias Bonnici Date: Tue, 19 Dec 2023 16:11:43 +0100 Subject: [PATCH 2/3] Fix margins in FIDO bio enrollment view. --- lib/fido/views/add_fingerprint_dialog.dart | 81 ++++++++++++---------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/lib/fido/views/add_fingerprint_dialog.dart b/lib/fido/views/add_fingerprint_dialog.dart index bc9bfeab8..47fd61134 100755 --- a/lib/fido/views/add_fingerprint_dialog.dart +++ b/lib/fido/views/add_fingerprint_dialog.dart @@ -106,9 +106,14 @@ class _AddFingerprintDialogState extends ConsumerState }, reverse: remaining > 0); }, complete: (fingerprint) { _remaining = 0; - _fingerprint = fingerprint; - // This needs a short delay to ensure the field is enabled first - Timer(const Duration(milliseconds: 100), _nameFocus.requestFocus); + // Add delay to show that progressbar is filled + Timer(const Duration(milliseconds: 200), () { + setState(() { + _fingerprint = fingerprint; + }); + // This needs a short delay to ensure the field is enabled first + Timer(const Duration(milliseconds: 100), _nameFocus.requestFocus); + }); }, error: (code) { _log.debug('Fingerprint capture error (code: $code)'); _color = _animateColor(false); @@ -183,7 +188,7 @@ class _AddFingerprintDialogState extends ConsumerState return ResponsiveDialog( title: Text(l10n.s_add_fingerprint), child: Padding( - padding: const EdgeInsets.only(top: 38, bottom: 0, right: 18, left: 18), + padding: const EdgeInsets.only(top: 38, bottom: 4, right: 18, left: 18), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -199,7 +204,9 @@ class _AddFingerprintDialogState extends ConsumerState ), ), Padding( - padding: const EdgeInsets.all(28), + padding: _fingerprint == null + ? const EdgeInsets.all(34) + : const EdgeInsets.only(top: 4, bottom: 12), child: AnimatedBuilder( animation: _color, builder: (context, _) { @@ -218,42 +225,40 @@ class _AddFingerprintDialogState extends ConsumerState value: progress, ), ) + ], + if (_fingerprint != null) ...[ + Text( + l10n.l_name_fingerprint, + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.normal), + ), + const SizedBox(height: 16), + Container( + constraints: const BoxConstraints(maxWidth: 360), + child: AppTextFormField( + focusNode: _nameFocus, + maxLength: 15, + inputFormatters: [limitBytesLength(15)], + buildCounter: buildByteCounterFor(_label), + autofocus: true, + decoration: AppInputDecoration( + border: const OutlineInputBorder(), + labelText: l10n.s_name, + prefixIcon: const Icon(Icons.fingerprint_outlined), + ), + onChanged: (value) { + setState(() { + _label = value.trim(); + }); + }, + onFieldSubmitted: (_) { + _submit(); + }, + ), + ) ] ], ), - if (_fingerprint != null) ...[ - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Text( - l10n.l_name_fingerprint, - style: const TextStyle( - fontSize: 16, fontWeight: FontWeight.normal), - ), - ), - Container( - constraints: const BoxConstraints(maxWidth: 360), - child: AppTextFormField( - focusNode: _nameFocus, - maxLength: 15, - inputFormatters: [limitBytesLength(15)], - buildCounter: buildByteCounterFor(_label), - autofocus: true, - decoration: AppInputDecoration( - border: const OutlineInputBorder(), - labelText: l10n.s_name, - prefixIcon: const Icon(Icons.fingerprint_outlined), - ), - onChanged: (value) { - setState(() { - _label = value.trim(); - }); - }, - onFieldSubmitted: (_) { - _submit(); - }, - ), - ) - ], ] .map((e) => Padding( child: e, From 06c33e6205bc77d4d4b166fbd1f1f1a1f060ec06 Mon Sep 17 00:00:00 2001 From: Elias Bonnici Date: Wed, 20 Dec 2023 14:36:46 +0100 Subject: [PATCH 3/3] Remove unnecessary usage of spread operator. --- lib/fido/views/add_fingerprint_dialog.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/fido/views/add_fingerprint_dialog.dart b/lib/fido/views/add_fingerprint_dialog.dart index 47fd61134..444eb2431 100755 --- a/lib/fido/views/add_fingerprint_dialog.dart +++ b/lib/fido/views/add_fingerprint_dialog.dart @@ -218,14 +218,13 @@ class _AddFingerprintDialogState extends ConsumerState }, ), ), - if (_fingerprint == null) ...[ + if (_fingerprint == null) Container( constraints: const BoxConstraints(maxWidth: 360), child: LinearProgressIndicator( value: progress, ), - ) - ], + ), if (_fingerprint != null) ...[ Text( l10n.l_name_fingerprint, @@ -271,12 +270,11 @@ class _AddFingerprintDialogState extends ConsumerState _subscription.cancel(); }, actions: [ - if (_fingerprint != null) ...[ + if (_fingerprint != null) TextButton( onPressed: _label.isNotEmpty ? _submit : null, child: Text(l10n.s_save), - ), - ] + ) ], ); }