From 0641418e346a5015ff6c270acb2df97d4edc4e0f Mon Sep 17 00:00:00 2001 From: Dain Nilsson Date: Thu, 12 May 2022 08:34:51 +0200 Subject: [PATCH] Fix lint warnings. --- integration_test/app_test.dart | 4 +- lib/about_page.dart | 13 +++- lib/android/qr_scanner/qr_scanner_view.dart | 8 +- lib/android/views/tap_request_dialog.dart | 4 +- lib/app/views/device_avatar.dart | 6 +- lib/app/views/message_page.dart | 2 +- lib/app/views/no_device_screen.dart | 2 +- lib/desktop/init.dart | 2 +- lib/fido/views/add_fingerprint_dialog.dart | 3 + lib/fido/views/delete_credential_dialog.dart | 10 ++- lib/fido/views/delete_fingerprint_dialog.dart | 28 +++---- lib/fido/views/fido_screen.dart | 2 +- lib/fido/views/locked_page.dart | 2 +- lib/fido/views/pin_dialog.dart | 12 +-- lib/fido/views/rename_fingerprint_dialog.dart | 15 ++-- lib/fido/views/reset_dialog.dart | 40 +++++----- lib/fido/views/unlocked_page.dart | 2 +- lib/main.dart | 2 +- lib/management/views/management_screen.dart | 50 ++++++------ lib/oath/views/account_dialog.dart | 21 +++-- lib/oath/views/account_mixin.dart | 2 +- lib/oath/views/account_view.dart | 19 +++-- lib/oath/views/add_account_page.dart | 77 ++++++++++--------- lib/oath/views/delete_account_dialog.dart | 30 ++++---- lib/oath/views/manage_password_dialog.dart | 17 ++-- lib/oath/views/oath_screen.dart | 5 +- lib/oath/views/rename_account_dialog.dart | 33 ++++---- lib/oath/views/reset_dialog.dart | 24 +++--- pubspec.lock | 2 +- pubspec.yaml | 1 + 30 files changed, 242 insertions(+), 196 deletions(-) diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 04ec4c4c..18bbf38f 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -25,11 +25,11 @@ String randomPadded() { } String generateRandomIssuer() { - return 'i' + randomPadded(); + return 'i${randomPadded()}'; } String generateRandomName() { - return 'n' + randomPadded(); + return 'n${randomPadded()}'; } String generateRandomSecret() { diff --git a/lib/about_page.dart b/lib/about_page.dart index 19f9bd50..f8780eab 100755 --- a/lib/about_page.dart +++ b/lib/about_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:logging/logging.dart'; +import 'package:yubico_authenticator/app/state.dart'; import 'app/logging.dart'; import 'app/message.dart'; @@ -43,7 +44,11 @@ class AboutPage extends ConsumerWidget { final data = response['diagnostics']; final text = const JsonEncoder.withIndent(' ').convert(data); await Clipboard.setData(ClipboardData(text: text)); - showMessage(context, 'Diagnostic data copied to clipboard'); + await ref.read(withContextProvider)( + (context) async { + showMessage(context, 'Diagnostic data copied to clipboard'); + }, + ); }, child: const Text('Run diagnostics...'), ), @@ -86,7 +91,11 @@ class LoggingPanel extends ConsumerWidget { final logs = ref.read(logLevelProvider.notifier).getLogs().join('\n'); await Clipboard.setData(ClipboardData(text: logs)); - showMessage(context, 'Log copied to clipboard'); + await ref.read(withContextProvider)( + (context) async { + showMessage(context, 'Log copied to clipboard'); + }, + ); }, ), ], diff --git a/lib/android/qr_scanner/qr_scanner_view.dart b/lib/android/qr_scanner/qr_scanner_view.dart index eee5e413..6bfa9c6e 100755 --- a/lib/android/qr_scanner/qr_scanner_view.dart +++ b/lib/android/qr_scanner/qr_scanner_view.dart @@ -27,11 +27,11 @@ class OverlayClipper extends CustomClipper { bool shouldReclip(covariant CustomClipper oldClipper) => true; } -class MobileScannerWrapper extends StatelessWidget { +class _MobileScannerWrapper extends StatelessWidget { final Function(String) onDetect; final _ScanStatus status; - const MobileScannerWrapper({ + const _MobileScannerWrapper({ Key? key, required this.onDetect, required this.status, @@ -93,7 +93,7 @@ class QrScannerView extends StatefulWidget { const QrScannerView({Key? key}) : super(key: key); @override - _QrScannerViewState createState() => _QrScannerViewState(); + State createState() => _QrScannerViewState(); } class _QrScannerViewState extends State { @@ -166,7 +166,7 @@ class _QrScannerViewState extends State { ), ), body: Stack(children: [ - MobileScannerWrapper( + _MobileScannerWrapper( status: _status, onDetect: (scannedData) => handleResult(scannedData), ), diff --git a/lib/android/views/tap_request_dialog.dart b/lib/android/views/tap_request_dialog.dart index 134bbd18..abc02a21 100755 --- a/lib/android/views/tap_request_dialog.dart +++ b/lib/android/views/tap_request_dialog.dart @@ -39,8 +39,8 @@ class FDialogApiImpl extends FDialogApi { size: 64, ), onCancel: () { - HDialogApi _api = HDialogApi(); - _api.dialogClosed(); + HDialogApi api = HDialogApi(); + api.dialogClosed(); }, )); } diff --git a/lib/app/views/device_avatar.dart b/lib/app/views/device_avatar.dart index dc6e87ca..00f802be 100755 --- a/lib/app/views/device_avatar.dart +++ b/lib/app/views/device_avatar.dart @@ -13,9 +13,9 @@ class DeviceAvatar extends StatelessWidget { factory DeviceAvatar.yubiKeyData(YubiKeyData data, {bool selected = false}) => DeviceAvatar( - child: getProductImage(data.info, data.name), badge: data.node is NfcReaderNode ? Icons.wifi : null, selected: selected, + child: getProductImage(data.info, data.name), ); factory DeviceAvatar.deviceNode(DeviceNode node, {bool selected = false}) => @@ -29,13 +29,13 @@ class DeviceAvatar extends StatelessWidget { ); } return DeviceAvatar( - child: const Icon(Icons.device_unknown), selected: selected, + child: const Icon(Icons.device_unknown), ); }, nfcReader: (_) => DeviceAvatar( - child: const Icon(Icons.wifi), selected: selected, + child: const Icon(Icons.wifi), ), ); diff --git a/lib/app/views/message_page.dart b/lib/app/views/message_page.dart index 4feb3a9c..36c8b9d1 100755 --- a/lib/app/views/message_page.dart +++ b/lib/app/views/message_page.dart @@ -20,6 +20,7 @@ class MessagePage extends StatelessWidget { Widget build(BuildContext context) => AppPage( title: title, centered: true, + floatingActionButton: floatingActionButton, child: Column( children: [ Text(header, style: Theme.of(context).textTheme.headline6), @@ -27,6 +28,5 @@ class MessagePage extends StatelessWidget { Text(message, textAlign: TextAlign.center), ], ), - floatingActionButton: floatingActionButton, ); } diff --git a/lib/app/views/no_device_screen.dart b/lib/app/views/no_device_screen.dart index abb426a4..0843f5b0 100755 --- a/lib/app/views/no_device_screen.dart +++ b/lib/app/views/no_device_screen.dart @@ -40,8 +40,8 @@ class NoDeviceScreen extends ConsumerWidget { }), ] .map((e) => Padding( - child: e, padding: const EdgeInsets.symmetric(vertical: 8.0), + child: e, )) .toList(); } diff --git a/lib/desktop/init.dart b/lib/desktop/init.dart index 79d5829c..5bab8281 100755 --- a/lib/desktop/init.dart +++ b/lib/desktop/init.dart @@ -64,7 +64,7 @@ Future initialize(List argv) async { if (exe?.isEmpty ?? true) { var relativePath = 'helper/authenticator-helper'; if (Platform.isMacOS) { - relativePath = '../Resources/' + relativePath; + relativePath = '../Resources/$relativePath'; } else if (Platform.isWindows) { relativePath += '.exe'; } diff --git a/lib/fido/views/add_fingerprint_dialog.dart b/lib/fido/views/add_fingerprint_dialog.dart index f9f40977..90717208 100755 --- a/lib/fido/views/add_fingerprint_dialog.dart +++ b/lib/fido/views/add_fingerprint_dialog.dart @@ -1,3 +1,5 @@ +// ignore_for_file: sort_child_properties_last + import 'dart:async'; import 'package:flutter/material.dart'; @@ -110,6 +112,7 @@ class _AddFingerprintDialogState extends ConsumerState await ref .read(fingerprintProvider(widget.devicePath).notifier) .renameFingerprint(_fingerprint!, _label); + if (!mounted) return; Navigator.of(context).pop(true); showMessage(context, 'Fingerprint added'); } diff --git a/lib/fido/views/delete_credential_dialog.dart b/lib/fido/views/delete_credential_dialog.dart index 29575b04..e851c1c1 100755 --- a/lib/fido/views/delete_credential_dialog.dart +++ b/lib/fido/views/delete_credential_dialog.dart @@ -1,3 +1,5 @@ +// ignore_for_file: sort_child_properties_last + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -43,8 +45,12 @@ class DeleteCredentialDialog extends ConsumerWidget { await ref .read(credentialProvider(devicePath).notifier) .deleteCredential(credential); - Navigator.of(context).pop(true); - showMessage(context, 'Credential deleted'); + await ref.read(withContextProvider)( + (context) async { + Navigator.of(context).pop(true); + showMessage(context, 'Credential deleted'); + }, + ); }, child: const Text('Delete'), ), diff --git a/lib/fido/views/delete_fingerprint_dialog.dart b/lib/fido/views/delete_fingerprint_dialog.dart index cc21d201..f6a6984e 100755 --- a/lib/fido/views/delete_fingerprint_dialog.dart +++ b/lib/fido/views/delete_fingerprint_dialog.dart @@ -25,6 +25,20 @@ class DeleteFingerprintDialog extends ConsumerWidget { return ResponsiveDialog( title: const Text('Delete fingerprint'), + actions: [ + TextButton( + onPressed: () async { + await ref + .read(fingerprintProvider(devicePath).notifier) + .deleteFingerprint(fingerprint); + await ref.read(withContextProvider)((context) async { + Navigator.of(context).pop(true); + showMessage(context, 'Fingerprint deleted'); + }); + }, + child: const Text('Delete'), + ), + ], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -32,23 +46,11 @@ class DeleteFingerprintDialog extends ConsumerWidget { Text('Fingerprint: $label'), ] .map((e) => Padding( - child: e, padding: const EdgeInsets.symmetric(vertical: 8.0), + child: e, )) .toList(), ), - actions: [ - TextButton( - onPressed: () async { - await ref - .read(fingerprintProvider(devicePath).notifier) - .deleteFingerprint(fingerprint); - Navigator.of(context).pop(true); - showMessage(context, 'Fingerprint deleted'); - }, - child: const Text('Delete'), - ), - ], ); } } diff --git a/lib/fido/views/fido_screen.dart b/lib/fido/views/fido_screen.dart index e2cf0ff3..9a8a0606 100755 --- a/lib/fido/views/fido_screen.dart +++ b/lib/fido/views/fido_screen.dart @@ -85,9 +85,9 @@ class FidoScreen extends ConsumerWidget { }), ] .map((e) => Padding( - child: e, padding: const EdgeInsets.symmetric(vertical: 8.0), + child: e, )) .toList(), )); diff --git a/lib/fido/views/locked_page.dart b/lib/fido/views/locked_page.dart index bce35d5f..0f84888f 100755 --- a/lib/fido/views/locked_page.dart +++ b/lib/fido/views/locked_page.dart @@ -191,9 +191,9 @@ class _PinEntryFormState extends ConsumerState<_PinEntryForm> { contentPadding: const EdgeInsets.symmetric(horizontal: 0), minLeadingWidth: 0, trailing: ElevatedButton( - child: const Text('Unlock'), onPressed: _pinController.text.isNotEmpty && !_blocked ? _submit : null, + child: const Text('Unlock'), ), ), ], diff --git a/lib/fido/views/pin_dialog.dart b/lib/fido/views/pin_dialog.dart index 08a6c953..5fed8697 100755 --- a/lib/fido/views/pin_dialog.dart +++ b/lib/fido/views/pin_dialog.dart @@ -39,6 +39,12 @@ class _FidoPinDialogState extends ConsumerState { return ResponsiveDialog( title: Text(hasPin ? 'Change PIN' : 'Set PIN'), + actions: [ + TextButton( + onPressed: isValid ? _submit : null, + child: const Text('Save'), + ), + ], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -109,12 +115,6 @@ class _FidoPinDialogState extends ConsumerState { )) .toList(), ), - actions: [ - TextButton( - child: const Text('Save'), - onPressed: isValid ? _submit : null, - ), - ], ); } diff --git a/lib/fido/views/rename_fingerprint_dialog.dart b/lib/fido/views/rename_fingerprint_dialog.dart index acb7db7f..15ad1cc1 100755 --- a/lib/fido/views/rename_fingerprint_dialog.dart +++ b/lib/fido/views/rename_fingerprint_dialog.dart @@ -33,6 +33,7 @@ class _RenameAccountDialogState extends ConsumerState { final renamed = await ref .read(fingerprintProvider(widget.devicePath).notifier) .renameFingerprint(widget.fingerprint, _label); + if (!mounted) return; Navigator.of(context).pop(renamed); showMessage(context, 'Fingerprint renamed'); } @@ -46,6 +47,12 @@ class _RenameAccountDialogState extends ConsumerState { return ResponsiveDialog( title: const Text('Rename fingerprint'), + actions: [ + TextButton( + onPressed: _label.isNotEmpty ? _submit : null, + child: const Text('Save'), + ), + ], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -71,17 +78,11 @@ class _RenameAccountDialogState extends ConsumerState { ), ] .map((e) => Padding( - child: e, padding: const EdgeInsets.symmetric(vertical: 8.0), + child: e, )) .toList(), ), - actions: [ - TextButton( - onPressed: _label.isNotEmpty ? _submit : null, - child: const Text('Save'), - ), - ], ); } } diff --git a/lib/fido/views/reset_dialog.dart b/lib/fido/views/reset_dialog.dart index 5e4bcb6e..fcdd3bc1 100755 --- a/lib/fido/views/reset_dialog.dart +++ b/lib/fido/views/reset_dialog.dart @@ -55,26 +55,6 @@ class _ResetDialogState extends ConsumerState { return ResponsiveDialog( title: const Text('Factory reset'), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Warning! This will irrevocably delete all U2F and FIDO2 accounts from your YubiKey.'), - Text( - 'Your credentials, as well as any PIN set, will be removed from this YubiKey. Make sure to first disable these from their respective web sites to avoid being locked out of your accounts.', - style: Theme.of(context).textTheme.bodyText1, - ), - Center( - child: Text(_getMessage(), - style: Theme.of(context).textTheme.headline6), - ), - ] - .map((e) => Padding( - child: e, - padding: const EdgeInsets.symmetric(vertical: 8.0), - )) - .toList(), - ), onCancel: () { _subscription?.cancel(); }, @@ -103,6 +83,26 @@ class _ResetDialogState extends ConsumerState { child: const Text('Reset'), ), ], + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Warning! This will irrevocably delete all U2F and FIDO2 accounts from your YubiKey.'), + Text( + 'Your credentials, as well as any PIN set, will be removed from this YubiKey. Make sure to first disable these from their respective web sites to avoid being locked out of your accounts.', + style: Theme.of(context).textTheme.bodyText1, + ), + Center( + child: Text(_getMessage(), + style: Theme.of(context).textTheme.headline6), + ), + ] + .map((e) => Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: e, + )) + .toList(), + ), ); } } diff --git a/lib/fido/views/unlocked_page.dart b/lib/fido/views/unlocked_page.dart index 54902190..13421aba 100755 --- a/lib/fido/views/unlocked_page.dart +++ b/lib/fido/views/unlocked_page.dart @@ -97,10 +97,10 @@ class FidoUnlockedPage extends ConsumerWidget { if (children.isNotEmpty) { return AppPage( title: const Text('WebAuthn'), + floatingActionButton: _buildFab(context), child: Column( children: children, ), - floatingActionButton: _buildFab(context), ); } diff --git a/lib/main.dart b/lib/main.dart index e3a79e8f..c4d7d55d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,10 +31,10 @@ void main(List argv) async { _log.warning('Platform initialization failed: $e'); runApp( ProviderScope( - child: YubicoAuthenticatorApp(page: ErrorPage(error: e.toString())), overrides: [ prefProvider.overrideWithValue(await SharedPreferences.getInstance()) ], + child: YubicoAuthenticatorApp(page: ErrorPage(error: e.toString())), ), ); } diff --git a/lib/management/views/management_screen.dart b/lib/management/views/management_screen.dart index 42217743..43dfb8ce 100755 --- a/lib/management/views/management_screen.dart +++ b/lib/management/views/management_screen.dart @@ -129,6 +129,7 @@ class ManagementScreen extends ConsumerStatefulWidget { class _ManagementScreenState extends ConsumerState { late Map _enabled; late int _interfaces; + bool _canSave = false; @override void initState() { @@ -182,6 +183,7 @@ class _ManagementScreenState extends ConsumerState { .copyWith(enabledCapabilities: _enabled), reboot: reboot, ); + if (!mounted) return; if (!reboot) Navigator.pop(context); showMessage(context, 'Configuration updated'); } finally { @@ -203,6 +205,7 @@ class _ManagementScreenState extends ConsumerState { await ref .read(managementStateProvider(widget.deviceData.node.path).notifier) .setMode(interfaces: _interfaces); + if (!mounted) return; showMessage( context, widget.deviceData.node.maybeMap( @@ -227,32 +230,37 @@ class _ManagementScreenState extends ConsumerState { Navigator.of(context).popUntil((route) => route.isFirst); }); - bool canSave = false; - return ResponsiveDialog( title: const Text('Toggle applications'), + actions: [ + TextButton( + onPressed: _canSave ? _submitForm : null, + child: const Text('Save'), + ), + ], child: ref.watch(managementStateProvider(widget.deviceData.node.path)).when( loading: () => const AppLoadingScreen(), error: (error, _) => AppFailureScreen('$error'), data: (info) { bool hasConfig = info.version.major > 4; - // TODO: Check mode for < YK5 intead - if (hasConfig) { - canSave = !_mapEquals( - _enabled, - info.config.enabledCapabilities, - ); - } else { - canSave = _interfaces != 0 && - _interfaces != - UsbInterfaces.forCapabilites(widget - .deviceData - .info - .config - .enabledCapabilities[Transport.usb] ?? - 0); - } + setState(() { + if (hasConfig) { + _canSave = !_mapEquals( + _enabled, + info.config.enabledCapabilities, + ); + } else { + _canSave = _interfaces != 0 && + _interfaces != + UsbInterfaces.forCapabilites(widget + .deviceData + .info + .config + .enabledCapabilities[Transport.usb] ?? + 0); + } + }); return Column( children: [ hasConfig @@ -262,12 +270,6 @@ class _ManagementScreenState extends ConsumerState { ); }, ), - actions: [ - TextButton( - onPressed: canSave ? _submitForm : null, - child: const Text('Save'), - ), - ], ); } } diff --git a/lib/oath/views/account_dialog.dart b/lib/oath/views/account_dialog.dart index 84a342a5..34da8f43 100755 --- a/lib/oath/views/account_dialog.dart +++ b/lib/oath/views/account_dialog.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../app/state.dart'; import '../../core/state.dart'; import '../../widgets/dialog_frame.dart'; import '../models.dart'; @@ -19,13 +20,15 @@ class AccountDialog extends ConsumerWidget with AccountMixin { final renamed = await super.renameCredential(context, ref); if (renamed != null) { // Replace this dialog with a new one, for the renamed credential. - Navigator.of(context).pop(); - await showDialog( - context: context, - builder: (context) { - return AccountDialog(renamed); - }, - ); + await ref.read(withContextProvider)((context) async { + Navigator.of(context).pop(); + await showDialog( + context: context, + builder: (context) { + return AccountDialog(renamed); + }, + ); + }); } return renamed; } @@ -34,7 +37,9 @@ class AccountDialog extends ConsumerWidget with AccountMixin { Future deleteCredential(BuildContext context, WidgetRef ref) async { final deleted = await super.deleteCredential(context, ref); if (deleted) { - Navigator.of(context).pop(); + await ref.read(withContextProvider)((context) async { + Navigator.of(context).pop(); + }); } return deleted; } diff --git a/lib/oath/views/account_mixin.dart b/lib/oath/views/account_mixin.dart index 457bd53c..5a80d6d3 100755 --- a/lib/oath/views/account_mixin.dart +++ b/lib/oath/views/account_mixin.dart @@ -81,7 +81,7 @@ mixin AccountMixin { return value; } else { var i = value.length ~/ 2; - return value.substring(0, i) + ' ' + value.substring(i); + return '${value.substring(0, i)} ${value.substring(i)}'; } } diff --git a/lib/oath/views/account_view.dart b/lib/oath/views/account_view.dart index 00f36c47..739bc96b 100755 --- a/lib/oath/views/account_view.dart +++ b/lib/oath/views/account_view.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:yubico_authenticator/app/state.dart'; import '../models.dart'; import '../state.dart'; @@ -43,12 +44,6 @@ class AccountView extends ConsumerWidget with AccountMixin { return buildActions(context, ref).map((e) { final action = e.action; return PopupMenuItem( - child: ListTile( - leading: e.icon, - title: Text(e.text), - dense: true, - contentPadding: EdgeInsets.zero, - ), enabled: action != null, onTap: () { // As soon as onTap returns, the Navigator is popped, @@ -58,6 +53,12 @@ class AccountView extends ConsumerWidget with AccountMixin { action?.call(context); }); }, + child: ListTile( + leading: e.icon, + title: Text(e.text), + dense: true, + contentPadding: EdgeInsets.zero, + ), ); }).toList(); } @@ -104,7 +105,11 @@ class AccountView extends ConsumerWidget with AccountMixin { ref, ); } - copyToClipboard(context, ref); + await ref.read(withContextProvider)( + (context) async { + copyToClipboard(context, ref); + }, + ); }, leading: CircleAvatar( foregroundColor: darkMode ? Colors.black : Colors.white, diff --git a/lib/oath/views/add_account_page.dart b/lib/oath/views/add_account_page.dart index b1ff91a2..fa2aa653 100755 --- a/lib/oath/views/add_account_page.dart +++ b/lib/oath/views/add_account_page.dart @@ -144,6 +144,45 @@ class _OathAddAccountPageState extends ConsumerState { return ResponsiveDialog( title: const Text('Add account'), + actions: [ + TextButton( + onPressed: isValid + ? () async { + if (secretLengthValid) { + final issuer = _issuerController.text; + + final cred = CredentialData( + issuer: issuer.isEmpty ? null : issuer, + name: _accountController.text, + secret: secret, + oathType: _oathType, + hashAlgorithm: _hashAlgorithm, + digits: _digits, + period: period, + ); + + try { + await ref + .read(credentialListProvider(widget.devicePath) + .notifier) + .addAccount(cred.toUri(), requireTouch: _touch); + if (!mounted) return; + Navigator.of(context).pop(); + showMessage(context, 'Account added'); + } catch (e) { + _log.error('Failed to add account', e); + showMessage(context, 'Failed adding account'); + } + } else { + setState(() { + _validateSecretLength = true; + }); + } + } + : null, + child: const Text('Save', key: Key('save_btn')), + ), + ], child: FileDropTarget( onFileDropped: (fileData) async { if (qrScanner != null) { @@ -359,44 +398,6 @@ class _OathAddAccountPageState extends ConsumerState { .toList(), ), ), - actions: [ - TextButton( - onPressed: isValid - ? () async { - if (secretLengthValid) { - final issuer = _issuerController.text; - - final cred = CredentialData( - issuer: issuer.isEmpty ? null : issuer, - name: _accountController.text, - secret: secret, - oathType: _oathType, - hashAlgorithm: _hashAlgorithm, - digits: _digits, - period: period, - ); - - try { - await ref - .read(credentialListProvider(widget.devicePath) - .notifier) - .addAccount(cred.toUri(), requireTouch: _touch); - Navigator.of(context).pop(); - showMessage(context, 'Account added'); - } catch (e) { - _log.error('Failed to add account', e); - showMessage(context, 'Failed adding account'); - } - } else { - setState(() { - _validateSecretLength = true; - }); - } - } - : null, - child: const Text('Save', key: Key('save_btn')), - ), - ], ); } } diff --git a/lib/oath/views/delete_account_dialog.dart b/lib/oath/views/delete_account_dialog.dart index 668a891a..1d124266 100755 --- a/lib/oath/views/delete_account_dialog.dart +++ b/lib/oath/views/delete_account_dialog.dart @@ -27,6 +27,22 @@ class DeleteAccountDialog extends ConsumerWidget { return ResponsiveDialog( title: const Text('Delete account'), + actions: [ + TextButton( + onPressed: () async { + await ref + .read(credentialListProvider(device.path).notifier) + .deleteAccount(credential); + await ref.read(withContextProvider)( + (context) async { + Navigator.of(context).pop(); + showMessage(context, 'Account deleted'); + }, + ); + }, + child: const Text('Delete'), + ), + ], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -39,23 +55,11 @@ class DeleteAccountDialog extends ConsumerWidget { Text('Account: $label'), ] .map((e) => Padding( - child: e, padding: const EdgeInsets.symmetric(vertical: 8.0), + child: e, )) .toList(), ), - actions: [ - TextButton( - onPressed: () async { - await ref - .read(credentialListProvider(device.path).notifier) - .deleteAccount(credential); - Navigator.of(context).pop(true); - showMessage(context, 'Account deleted'); - }, - child: const Text('Delete'), - ), - ], ); } } diff --git a/lib/oath/views/manage_password_dialog.dart b/lib/oath/views/manage_password_dialog.dart index adca4afd..98ea76d1 100755 --- a/lib/oath/views/manage_password_dialog.dart +++ b/lib/oath/views/manage_password_dialog.dart @@ -30,6 +30,7 @@ class _ManagePasswordDialogState extends ConsumerState { .read(oathStateProvider(widget.path).notifier) .setPassword(_currentPassword, _newPassword); if (result) { + if (!mounted) return; Navigator.of(context).pop(); showMessage(context, 'Password set'); } else { @@ -52,6 +53,12 @@ class _ManagePasswordDialogState extends ConsumerState { return ResponsiveDialog( title: const Text('Manage password'), + actions: [ + TextButton( + onPressed: isValid ? _submit : null, + child: const Text('Save'), + ) + ], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -78,13 +85,13 @@ class _ManagePasswordDialogState extends ConsumerState { runSpacing: 8.0, children: [ OutlinedButton( - child: const Text('Remove password'), onPressed: _currentPassword.isNotEmpty ? () async { final result = await ref .read(oathStateProvider(widget.path).notifier) .unsetPassword(_currentPassword); if (result) { + if (!mounted) return; Navigator.of(context).pop(); showMessage(context, 'Password removed'); } else { @@ -94,6 +101,7 @@ class _ManagePasswordDialogState extends ConsumerState { } } : null, + child: const Text('Remove password'), ), if (widget.state.remembered) OutlinedButton( @@ -102,6 +110,7 @@ class _ManagePasswordDialogState extends ConsumerState { await ref .read(oathStateProvider(widget.path).notifier) .forgetPassword(); + if (!mounted) return; Navigator.of(context).pop(); showMessage(context, 'Password forgotten'); }, @@ -153,12 +162,6 @@ class _ManagePasswordDialogState extends ConsumerState { )) .toList(), ), - actions: [ - TextButton( - onPressed: isValid ? _submit : null, - child: const Text('Save'), - ) - ], ); } } diff --git a/lib/oath/views/oath_screen.dart b/lib/oath/views/oath_screen.dart index ee3e25c2..e47276a0 100755 --- a/lib/oath/views/oath_screen.dart +++ b/lib/oath/views/oath_screen.dart @@ -112,8 +112,8 @@ class _UnlockedView extends ConsumerWidget { ); }), ), - child: AccountList(devicePath, oathState), floatingActionButton: _buildFab(context), + child: AccountList(devicePath, oathState), ); } @@ -188,6 +188,7 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> { final result = await ref .read(oathStateProvider(widget._devicePath).notifier) .unlock(_passwordController.text, remember: _remember); + if (!mounted) return; if (!result.first) { setState(() { _wrong = true; @@ -283,8 +284,8 @@ class _UnlockFormState extends ConsumerState<_UnlockForm> { Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: ElevatedButton( - child: const Text('Unlock'), onPressed: _passwordController.text.isNotEmpty ? _submit : null, + child: const Text('Unlock'), ), ) ], diff --git a/lib/oath/views/rename_account_dialog.dart b/lib/oath/views/rename_account_dialog.dart index 86cada92..cc518e9c 100755 --- a/lib/oath/views/rename_account_dialog.dart +++ b/lib/oath/views/rename_account_dialog.dart @@ -56,6 +56,22 @@ class _RenameAccountDialogState extends ConsumerState { return ResponsiveDialog( title: const Text('Rename account'), + actions: [ + TextButton( + onPressed: isValid + ? () async { + final renamed = await ref + .read(credentialListProvider(widget.device.path).notifier) + .renameAccount(credential, + _issuer.isNotEmpty ? _issuer : null, _account); + if (!mounted) return; + Navigator.of(context).pop(renamed); + showMessage(context, 'Account renamed'); + } + : null, + child: const Text('Save'), + ), + ], child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -94,26 +110,11 @@ class _RenameAccountDialogState extends ConsumerState { ), ] .map((e) => Padding( - child: e, padding: const EdgeInsets.symmetric(vertical: 8.0), + child: e, )) .toList(), ), - actions: [ - TextButton( - onPressed: isValid - ? () async { - final renamed = await ref - .read(credentialListProvider(widget.device.path).notifier) - .renameAccount(credential, - _issuer.isNotEmpty ? _issuer : null, _account); - Navigator.of(context).pop(renamed); - showMessage(context, 'Account renamed'); - } - : null, - child: const Text('Save'), - ), - ], ); } } diff --git a/lib/oath/views/reset_dialog.dart b/lib/oath/views/reset_dialog.dart index 3771d570..dc674eae 100755 --- a/lib/oath/views/reset_dialog.dart +++ b/lib/oath/views/reset_dialog.dart @@ -20,6 +20,18 @@ class ResetDialog extends ConsumerWidget { return ResponsiveDialog( title: const Text('Factory reset'), + actions: [ + TextButton( + onPressed: () async { + await ref.read(oathStateProvider(devicePath).notifier).reset(); + await ref.read(withContextProvider)((context) async { + Navigator.of(context).pop(); + showMessage(context, 'OATH application reset'); + }); + }, + child: const Text('Reset'), + ), + ], child: Column( children: [ const Text( @@ -30,21 +42,11 @@ class ResetDialog extends ConsumerWidget { ), ] .map((e) => Padding( - child: e, padding: const EdgeInsets.symmetric(vertical: 8.0), + child: e, )) .toList(), ), - actions: [ - TextButton( - onPressed: () async { - await ref.read(oathStateProvider(devicePath).notifier).reset(); - Navigator.of(context).pop(); - showMessage(context, 'OATH application reset'); - }, - child: const Text('Reset'), - ), - ], ); } } diff --git a/pubspec.lock b/pubspec.lock index d1f0f06b..7836cc82 100755 --- a/pubspec.lock +++ b/pubspec.lock @@ -142,7 +142,7 @@ packages: source: hosted version: "4.1.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index 803a38bb..99000ad5 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: async: ^2.8.2 logging: ^1.0.2 + collection: ^1.16.0 shared_preferences: ^2.0.12 flutter_riverpod: ^1.0.0 json_annotation: ^4.4.0