mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-25 23:14:18 +03:00
Update FIDO passkeys views.
This commit is contained in:
parent
7011f816d4
commit
9eeb44f3ac
114
lib/fido/views/credential_dialog.dart
Normal file
114
lib/fido/views/credential_dialog.dart
Normal file
@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../app/message.dart';
|
||||
import '../../app/shortcuts.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../models.dart';
|
||||
import 'delete_credential_dialog.dart';
|
||||
|
||||
class CredentialDialog extends ConsumerWidget {
|
||||
final FidoCredential credential;
|
||||
const CredentialDialog(this.credential, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// TODO: Solve this in a cleaner way
|
||||
final node = ref.watch(currentDeviceDataProvider).valueOrNull?.node;
|
||||
if (node == null) {
|
||||
// The rest of this method assumes there is a device, and will throw an exception if not.
|
||||
// This will never be shown, as the dialog will be immediately closed
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
return Actions(
|
||||
actions: {
|
||||
DeleteIntent: CallbackAction<DeleteIntent>(onInvoke: (_) async {
|
||||
final withContext = ref.read(withContextProvider);
|
||||
final bool? deleted =
|
||||
await ref.read(withContextProvider)((context) async =>
|
||||
await showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => DeleteCredentialDialog(
|
||||
node.path,
|
||||
credential,
|
||||
),
|
||||
) ??
|
||||
false);
|
||||
|
||||
// Pop the account dialog if deleted
|
||||
if (deleted == true) {
|
||||
await withContext((context) async {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
}
|
||||
return deleted;
|
||||
}),
|
||||
},
|
||||
child: FocusScope(
|
||||
autofocus: true,
|
||||
child: FsDialog(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 48, bottom: 32),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
credential.userName,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
credential.rpId,
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.center,
|
||||
// This is what ListTile uses for subtitle
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).textTheme.bodySmall!.color,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Icon(Icons.person, size: 72),
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTitle(AppLocalizations.of(context)!.s_actions,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
_CredentialDialogActions(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CredentialDialogActions extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final theme =
|
||||
ButtonTheme.of(context).colorScheme ?? Theme.of(context).colorScheme;
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.error,
|
||||
foregroundColor: theme.onError,
|
||||
child: const Icon(Icons.delete),
|
||||
),
|
||||
title: Text(l10n.s_delete_passkey),
|
||||
subtitle: Text(l10n.l_delete_account_desc),
|
||||
onTap: () {
|
||||
Actions.invoke(context, const DeleteIntent());
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -38,14 +38,14 @@ class DeleteCredentialDialog extends ConsumerWidget {
|
||||
final label = credential.userName;
|
||||
|
||||
return ResponsiveDialog(
|
||||
title: Text(l10n.s_delete_credential),
|
||||
title: Text(l10n.s_delete_passkey),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(l10n.p_warning_delete_credential),
|
||||
Text(l10n.l_credential(label)),
|
||||
Text(l10n.p_warning_delete_passkey),
|
||||
Text(l10n.l_passkey(label)),
|
||||
]
|
||||
.map((e) => Padding(
|
||||
child: e,
|
||||
@ -63,7 +63,7 @@ class DeleteCredentialDialog extends ConsumerWidget {
|
||||
await ref.read(withContextProvider)(
|
||||
(context) async {
|
||||
Navigator.of(context).pop(true);
|
||||
showMessage(context, l10n.s_credential_deleted);
|
||||
showMessage(context, l10n.s_passkey_deleted);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -27,7 +27,7 @@ import '../../app/views/message_page.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
import 'delete_credential_dialog.dart';
|
||||
import 'credential_dialog.dart';
|
||||
import 'fingerprint_dialog.dart';
|
||||
import 'key_actions.dart';
|
||||
|
||||
@ -48,42 +48,19 @@ class FidoUnlockedPage extends ConsumerWidget {
|
||||
}
|
||||
final creds = data.value;
|
||||
if (creds.isNotEmpty) {
|
||||
children.add(ListTitle(l10n.s_credentials));
|
||||
children.addAll(
|
||||
creds.map(
|
||||
(cred) => ListTile(
|
||||
leading: CircleAvatar(
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
child: const Icon(Icons.person),
|
||||
),
|
||||
title: Text(
|
||||
cred.userName,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
subtitle: Text(
|
||||
cred.rpId,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
showBlurDialog(
|
||||
children.add(ListTitle(l10n.s_passkeys));
|
||||
children.addAll(creds.map((cred) => Actions(
|
||||
actions: {
|
||||
OpenIntent: CallbackAction<OpenIntent>(onInvoke: (_) async {
|
||||
await showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
DeleteCredentialDialog(node.path, cred),
|
||||
builder: (context) => CredentialDialog(cred),
|
||||
);
|
||||
return null;
|
||||
}),
|
||||
},
|
||||
icon: const Icon(Icons.delete_outline)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
child: _CredentialListItem(cred),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,6 +130,38 @@ class FidoUnlockedPage extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
class _CredentialListItem extends StatelessWidget {
|
||||
final FidoCredential credential;
|
||||
const _CredentialListItem(this.credential);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
child: const Icon(Icons.person),
|
||||
),
|
||||
title: Text(
|
||||
credential.userName,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
subtitle: Text(
|
||||
credential.rpId,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
trailing: OutlinedButton(
|
||||
onPressed: () {
|
||||
Actions.maybeInvoke<OpenIntent>(context, const OpenIntent());
|
||||
},
|
||||
child: const Icon(Icons.more_horiz),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FingerprintListItem extends StatelessWidget {
|
||||
final Fingerprint fingerprint;
|
||||
const _FingerprintListItem(this.fingerprint);
|
||||
|
@ -291,19 +291,20 @@
|
||||
"l_calculate_code_desc": "Get a new code from your YubiKey",
|
||||
|
||||
"@_fido_credentials": {},
|
||||
"l_credential": "Credential: {label}",
|
||||
"@l_credential" : {
|
||||
"l_passkey": "Passkey: {label}",
|
||||
"@l_passkey" : {
|
||||
"placeholders": {
|
||||
"label": {}
|
||||
}
|
||||
},
|
||||
"s_credentials": "Credentials",
|
||||
"s_passkeys": "Passkeys",
|
||||
"l_ready_to_use": "Ready to use",
|
||||
"l_register_sk_on_websites": "Register as a Security Key on websites",
|
||||
"l_no_discoverable_accounts": "No discoverable accounts",
|
||||
"s_delete_credential": "Delete credential",
|
||||
"s_credential_deleted": "Credential deleted",
|
||||
"p_warning_delete_credential": "This will delete the credential from your YubiKey.",
|
||||
"l_no_discoverable_accounts": "No Passkeys stored",
|
||||
"s_delete_passkey": "Delete Passkey",
|
||||
"l_delete_passkey_desc": "Remove the Passkey from the YubiKey",
|
||||
"s_passkey_deleted": "Passkey deleted",
|
||||
"p_warning_delete_passkey": "This will delete the Passkey from your YubiKey.",
|
||||
|
||||
"@_fingerprints": {},
|
||||
"l_fingerprint": "Fingerprint: {label}",
|
||||
|
Loading…
Reference in New Issue
Block a user