Merge 'main' into fix/YADESK-637-refresh-creds

This commit is contained in:
Adam Velebil 2022-05-12 11:44:19 +02:00
commit 7c30aeb41c
No known key found for this signature in database
GPG Key ID: AC6D6B9D715FC084
34 changed files with 277 additions and 231 deletions

View File

@ -26,7 +26,7 @@ jobs:
- name: Install Flutter
uses: subosito/flutter-action@v2
with:
channel: 'beta'
channel: 'stable'
- run: |
flutter config
flutter --version

View File

@ -27,7 +27,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: 'beta'
channel: 'stable'
- run: flutter config --enable-linux-desktop
- run: flutter --version

View File

@ -24,7 +24,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: 'beta'
channel: 'stable'
architecture: 'x64'
- run: flutter config --enable-macos-desktop
- run: flutter --version

View File

@ -23,7 +23,7 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: 'beta'
channel: 'stable'
- run: flutter config --enable-windows-desktop
- run: flutter --version

View File

@ -25,11 +25,11 @@ String randomPadded() {
}
String generateRandomIssuer() {
return 'i' + randomPadded();
return 'i${randomPadded()}';
}
String generateRandomName() {
return 'n' + randomPadded();
return 'n${randomPadded()}';
}
String generateRandomSecret() {

View File

@ -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...'),
),
@ -85,7 +90,11 @@ class LoggingPanel extends ConsumerWidget {
_log.info('Copying log to clipboard...');
final logs = await ref.read(logLevelProvider.notifier).getLogs();
await Clipboard.setData(ClipboardData(text: logs.join('\n')));
showMessage(context, 'Log copied to clipboard');
await ref.read(withContextProvider)(
(context) async {
showMessage(context, 'Log copied to clipboard');
},
);
},
),
],

View File

@ -27,11 +27,11 @@ class OverlayClipper extends CustomClipper<Path> {
bool shouldReclip(covariant CustomClipper<Path> 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<QrScannerView> createState() => _QrScannerViewState();
}
class _QrScannerViewState extends State<QrScannerView> {
@ -166,7 +166,7 @@ class _QrScannerViewState extends State<QrScannerView> {
),
),
body: Stack(children: [
MobileScannerWrapper(
_MobileScannerWrapper(
status: _status,
onDetect: (scannedData) => handleResult(scannedData),
),

View File

@ -39,8 +39,8 @@ class FDialogApiImpl extends FDialogApi {
size: 64,
),
onCancel: () {
HDialogApi _api = HDialogApi();
_api.dialogClosed();
HDialogApi api = HDialogApi();
api.dialogClosed();
},
));
}

View File

@ -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),
),
);

View File

@ -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,
);
}

View File

@ -40,8 +40,8 @@ class NoDeviceScreen extends ConsumerWidget {
}),
]
.map((e) => Padding(
child: e,
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: e,
))
.toList();
}

View File

@ -64,7 +64,7 @@ Future<Widget> initialize(List<String> 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';
}

View File

@ -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<AddFingerprintDialog>
await ref
.read(fingerprintProvider(widget.devicePath).notifier)
.renameFingerprint(_fingerprint!, _label);
if (!mounted) return;
Navigator.of(context).pop(true);
showMessage(context, 'Fingerprint added');
}

View File

@ -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'),
),

View File

@ -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'),
),
],
);
}
}

View File

@ -85,9 +85,9 @@ class FidoScreen extends ConsumerWidget {
}),
]
.map((e) => Padding(
child: e,
padding:
const EdgeInsets.symmetric(vertical: 8.0),
child: e,
))
.toList(),
));

View File

@ -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'),
),
),
],

View File

@ -39,6 +39,12 @@ class _FidoPinDialogState extends ConsumerState<FidoPinDialog> {
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<FidoPinDialog> {
))
.toList(),
),
actions: [
TextButton(
child: const Text('Save'),
onPressed: isValid ? _submit : null,
),
],
);
}

View File

@ -33,6 +33,7 @@ class _RenameAccountDialogState extends ConsumerState<RenameFingerprintDialog> {
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<RenameFingerprintDialog> {
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<RenameFingerprintDialog> {
),
]
.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'),
),
],
);
}
}

View File

@ -55,26 +55,6 @@ class _ResetDialogState extends ConsumerState<ResetDialog> {
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<ResetDialog> {
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(),
),
);
}
}

View File

@ -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),
);
}

View File

@ -31,10 +31,10 @@ void main(List<String> 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())),
),
);
}

View File

@ -129,6 +129,7 @@ class ManagementScreen extends ConsumerStatefulWidget {
class _ManagementScreenState extends ConsumerState<ManagementScreen> {
late Map<Transport, int> _enabled;
late int _interfaces;
bool _canSave = false;
@override
void initState() {
@ -182,6 +183,7 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
.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<ManagementScreen> {
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<ManagementScreen> {
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<ManagementScreen> {
);
},
),
actions: [
TextButton(
onPressed: canSave ? _submitForm : null,
child: const Text('Save'),
),
],
);
}
}

View File

@ -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<bool> 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;
}

View File

@ -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)}';
}
}

View File

@ -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,

View File

@ -144,6 +144,45 @@ class _OathAddAccountPageState extends ConsumerState<OathAddAccountPage> {
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<OathAddAccountPage> {
.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')),
),
],
);
}
}

View File

@ -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'),
),
],
);
}
}

View File

@ -30,6 +30,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
.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<ManagePasswordDialog> {
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<ManagePasswordDialog> {
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<ManagePasswordDialog> {
}
}
: null,
child: const Text('Remove password'),
),
if (widget.state.remembered)
OutlinedButton(
@ -102,6 +110,7 @@ class _ManagePasswordDialogState extends ConsumerState<ManagePasswordDialog> {
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<ManagePasswordDialog> {
))
.toList(),
),
actions: [
TextButton(
onPressed: isValid ? _submit : null,
child: const Text('Save'),
)
],
);
}
}

View File

@ -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'),
),
)
],

View File

@ -56,6 +56,22 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
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<RenameAccountDialog> {
),
]
.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'),
),
],
);
}
}

View File

@ -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'),
),
],
);
}
}

View File

@ -28,7 +28,7 @@ packages:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "2.3.1"
async:
dependency: "direct main"
description:
@ -49,7 +49,7 @@ packages:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
version: "2.3.0"
build_config:
dependency: transitive
description:
@ -63,7 +63,7 @@ packages:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "3.1.0"
build_resolvers:
dependency: transitive
description:
@ -77,7 +77,7 @@ packages:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.8"
version: "2.1.10"
build_runner_core:
dependency: transitive
description:
@ -98,7 +98,7 @@ packages:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.4"
version: "8.3.0"
characters:
dependency: transitive
description:
@ -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"
@ -161,7 +161,7 @@ packages:
name: cross_file
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.2"
version: "0.3.3"
crypto:
dependency: transitive
description:
@ -227,7 +227,7 @@ packages:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.1"
flutter_riverpod:
dependency: "direct main"
description:
@ -324,21 +324,21 @@ packages:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "4.4.0"
version: "4.5.0"
json_serializable:
dependency: "direct dev"
description:
name: json_serializable
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.5"
version: "6.2.0"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "2.0.0"
logging:
dependency: "direct main"
description:
@ -373,7 +373,7 @@ packages:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
package_config:
dependency: transitive
description:
@ -394,28 +394,28 @@ packages:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
version: "2.1.6"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.0.6"
pigeon:
dependency: "direct dev"
description:
name: pigeon
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "3.0.3"
platform:
dependency: transitive
description:
@ -478,35 +478,35 @@ packages:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.13"
version: "2.0.15"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
version: "2.0.12"
shared_preferences_ios:
dependency: transitive
description:
name: shared_preferences_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
shared_preferences_platform_interface:
dependency: transitive
description:
@ -520,14 +520,14 @@ packages:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
shelf:
dependency: transitive
description:
@ -553,14 +553,14 @@ packages:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
version: "1.2.2"
source_helper:
dependency: transitive
description:
name: source_helper
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
version: "1.3.2"
source_span:
dependency: transitive
description:
@ -665,7 +665,7 @@ packages:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.2.0"
webdriver:
dependency: transitive
description:
@ -679,14 +679,14 @@ packages:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.1"
version: "2.5.2"
window_manager:
dependency: "direct main"
description:
name: window_manager
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.1"
version: "0.2.3"
xdg_directories:
dependency: transitive
description:
@ -702,5 +702,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.17.0-266.1.beta <3.0.0"
dart: ">=2.17.0 <3.0.0"
flutter: ">=2.8.0"

7
pubspec.yaml Normal file → Executable file
View File

@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.15.0-268.18.beta <3.0.0"
sdk: ">=2.17.0 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
@ -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
@ -57,12 +58,12 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
flutter_lints: ^2.0.1
build_runner: ^2.1.4
freezed: ^1.0.0
json_serializable: ^6.0.0
pigeon: ^2.0.2
pigeon: ^3.0.3
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec