mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 10:11:52 +03:00
Extract ActionList classes.
This commit is contained in:
parent
3f821497cd
commit
4bd322a268
89
lib/app/views/action_list.dart
Normal file
89
lib/app/views/action_list.dart
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Yubico.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../widgets/list_title.dart';
|
||||
|
||||
class ActionListItem extends StatelessWidget {
|
||||
final String title;
|
||||
final String? subtitle;
|
||||
final Widget? leading;
|
||||
final Widget? icon;
|
||||
final Color? foregroundColor;
|
||||
final Color? backgroundColor;
|
||||
final Widget? trailing;
|
||||
final void Function()? onTap;
|
||||
|
||||
const ActionListItem({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.leading,
|
||||
this.icon,
|
||||
this.foregroundColor,
|
||||
this.backgroundColor,
|
||||
this.trailing,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Either leading is defined only, or we need at least an icon.
|
||||
assert((leading != null &&
|
||||
(icon == null &&
|
||||
foregroundColor == null &&
|
||||
backgroundColor == null)) ||
|
||||
(leading == null && icon != null));
|
||||
|
||||
final theme =
|
||||
ButtonTheme.of(context).colorScheme ?? Theme.of(context).colorScheme;
|
||||
|
||||
return ListTile(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
|
||||
title: Text(title),
|
||||
subtitle: subtitle != null ? Text(subtitle!) : null,
|
||||
leading: leading ??
|
||||
CircleAvatar(
|
||||
foregroundColor: foregroundColor ?? theme.onSecondary,
|
||||
backgroundColor: backgroundColor ?? theme.secondary,
|
||||
child: icon,
|
||||
),
|
||||
trailing: trailing,
|
||||
onTap: onTap,
|
||||
enabled: onTap != null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ActionListSection extends StatelessWidget {
|
||||
final String title;
|
||||
final List<ActionListItem> children;
|
||||
|
||||
const ActionListSection(this.title, {super.key, required this.children});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SizedBox(
|
||||
width: 360,
|
||||
child: Column(children: [
|
||||
ListTitle(
|
||||
title,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
...children,
|
||||
]),
|
||||
);
|
||||
}
|
@ -6,7 +6,7 @@ import '../../app/message.dart';
|
||||
import '../../app/shortcuts.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../models.dart';
|
||||
import 'delete_credential_dialog.dart';
|
||||
|
||||
@ -24,6 +24,9 @@ class CredentialDialog extends ConsumerWidget {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final theme =
|
||||
ButtonTheme.of(context).colorScheme ?? Theme.of(context).colorScheme;
|
||||
return Actions(
|
||||
actions: {
|
||||
DeleteIntent: CallbackAction<DeleteIntent>(onInvoke: (_) async {
|
||||
@ -77,9 +80,21 @@ class CredentialDialog extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTitle(AppLocalizations.of(context)!.s_actions,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
_CredentialDialogActions(),
|
||||
ActionListSection(
|
||||
l10n.s_actions,
|
||||
children: [
|
||||
ActionListItem(
|
||||
backgroundColor: theme.error,
|
||||
foregroundColor: theme.onError,
|
||||
icon: const Icon(Icons.delete),
|
||||
title: l10n.s_delete_passkey,
|
||||
subtitle: l10n.l_delete_account_desc,
|
||||
onTap: () {
|
||||
Actions.invoke(context, const DeleteIntent());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -87,28 +102,3 @@ class CredentialDialog extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import '../../app/message.dart';
|
||||
import '../../app/shortcuts.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../models.dart';
|
||||
import 'delete_fingerprint_dialog.dart';
|
||||
import 'rename_fingerprint_dialog.dart';
|
||||
@ -25,6 +25,9 @@ class FingerprintDialog extends ConsumerWidget {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final theme =
|
||||
ButtonTheme.of(context).colorScheme ?? Theme.of(context).colorScheme;
|
||||
return Actions(
|
||||
actions: {
|
||||
EditIntent: CallbackAction<EditIntent>(onInvoke: (_) async {
|
||||
@ -93,9 +96,29 @@ class FingerprintDialog extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTitle(AppLocalizations.of(context)!.s_actions,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
_FingerprintDialogActions(),
|
||||
ActionListSection(
|
||||
l10n.s_actions,
|
||||
children: [
|
||||
ActionListItem(
|
||||
icon: const Icon(Icons.edit),
|
||||
title: l10n.s_rename_fp,
|
||||
subtitle: l10n.l_rename_fp_desc,
|
||||
onTap: () {
|
||||
Actions.invoke(context, const EditIntent());
|
||||
},
|
||||
),
|
||||
ActionListItem(
|
||||
backgroundColor: theme.error,
|
||||
foregroundColor: theme.onError,
|
||||
icon: const Icon(Icons.delete),
|
||||
title: l10n.s_delete_fingerprint,
|
||||
subtitle: l10n.l_delete_fingerprint_desc,
|
||||
onTap: () {
|
||||
Actions.invoke(context, const DeleteIntent());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -103,40 +126,3 @@ class FingerprintDialog extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FingerprintDialogActions 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.secondary,
|
||||
foregroundColor: theme.onSecondary,
|
||||
child: const Icon(Icons.edit),
|
||||
),
|
||||
title: Text(l10n.s_rename_fp),
|
||||
subtitle: Text(l10n.l_rename_fp_desc),
|
||||
onTap: () {
|
||||
Actions.invoke(context, const EditIntent());
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.error,
|
||||
foregroundColor: theme.onError,
|
||||
child: const Icon(Icons.delete),
|
||||
),
|
||||
title: Text(l10n.s_delete_fingerprint),
|
||||
subtitle: Text(l10n.l_delete_fingerprint_desc),
|
||||
onTap: () {
|
||||
Actions.invoke(context, const DeleteIntent());
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../models.dart';
|
||||
import 'add_fingerprint_dialog.dart';
|
||||
import 'pin_dialog.dart';
|
||||
@ -39,73 +39,69 @@ Widget fidoBuildActions(
|
||||
return FsDialog(
|
||||
child: Column(
|
||||
children: [
|
||||
if (state.bioEnroll != null) ...[
|
||||
ListTitle(l10n.s_setup,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.primary,
|
||||
foregroundColor: theme.onPrimary,
|
||||
child: const Icon(Icons.fingerprint_outlined),
|
||||
),
|
||||
title: Text(l10n.s_add_fingerprint),
|
||||
subtitle: state.unlocked
|
||||
? Text(l10n.l_fingerprints_used(fingerprints))
|
||||
: Text(state.hasPin
|
||||
? l10n.l_unlock_pin_first
|
||||
: l10n.l_set_pin_first),
|
||||
trailing:
|
||||
fingerprints == 0 ? const Icon(Icons.warning_amber) : null,
|
||||
enabled: state.unlocked && fingerprints < 5,
|
||||
onTap: state.unlocked && fingerprints < 5
|
||||
? () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => AddFingerprintDialog(node.path),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
if (state.bioEnroll != null)
|
||||
ActionListSection(
|
||||
l10n.s_setup,
|
||||
children: [
|
||||
ActionListItem(
|
||||
backgroundColor: theme.primary,
|
||||
foregroundColor: theme.onPrimary,
|
||||
icon: const Icon(Icons.fingerprint_outlined),
|
||||
title: l10n.s_add_fingerprint,
|
||||
subtitle: state.unlocked
|
||||
? l10n.l_fingerprints_used(fingerprints)
|
||||
: state.hasPin
|
||||
? l10n.l_unlock_pin_first
|
||||
: l10n.l_set_pin_first,
|
||||
trailing:
|
||||
fingerprints == 0 ? const Icon(Icons.warning_amber) : null,
|
||||
onTap: state.unlocked && fingerprints < 5
|
||||
? () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => AddFingerprintDialog(node.path),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
ListTitle(l10n.s_manage,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.secondary,
|
||||
foregroundColor: theme.onSecondary,
|
||||
child: const Icon(Icons.pin_outlined),
|
||||
ActionListSection(
|
||||
l10n.s_manage,
|
||||
children: [
|
||||
ActionListItem(
|
||||
icon: const Icon(Icons.pin_outlined),
|
||||
title: state.hasPin ? l10n.s_change_pin : l10n.s_set_pin,
|
||||
subtitle: state.hasPin
|
||||
? l10n.s_fido_pin_protection
|
||||
: l10n.l_fido_pin_protection_optional,
|
||||
trailing: state.alwaysUv && !state.hasPin
|
||||
? const Icon(Icons.warning_amber)
|
||||
: null,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => FidoPinDialog(node.path, state),
|
||||
);
|
||||
}),
|
||||
ActionListItem(
|
||||
foregroundColor: theme.onError,
|
||||
backgroundColor: theme.error,
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
title: l10n.s_reset_fido,
|
||||
subtitle: l10n.l_factory_reset_this_app,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ResetDialog(node),
|
||||
);
|
||||
},
|
||||
),
|
||||
title: Text(state.hasPin ? l10n.s_change_pin : l10n.s_set_pin),
|
||||
subtitle: Text(state.hasPin
|
||||
? l10n.s_fido_pin_protection
|
||||
: l10n.l_fido_pin_protection_optional),
|
||||
trailing: state.alwaysUv && !state.hasPin
|
||||
? const Icon(Icons.warning_amber)
|
||||
: null,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => FidoPinDialog(node.path, state),
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
foregroundColor: theme.onError,
|
||||
backgroundColor: theme.error,
|
||||
child: const Icon(Icons.delete_outline),
|
||||
),
|
||||
title: Text(l10n.s_reset_fido),
|
||||
subtitle: Text(l10n.l_factory_reset_this_app),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ResetDialog(node),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -24,9 +24,9 @@ import '../../app/message.dart';
|
||||
import '../../app/shortcuts.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../../core/models.dart';
|
||||
import '../../core/state.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
import 'account_helper.dart';
|
||||
@ -39,7 +39,8 @@ class AccountDialog extends ConsumerWidget {
|
||||
|
||||
const AccountDialog(this.credential, {super.key});
|
||||
|
||||
List<Widget> _buildActions(BuildContext context, AccountHelper helper) {
|
||||
List<ActionListItem> _buildActions(
|
||||
BuildContext context, AccountHelper helper) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final actions = helper.buildActions();
|
||||
|
||||
@ -66,7 +67,7 @@ class AccountDialog extends ConsumerWidget {
|
||||
final intent = e.intent;
|
||||
final (firstColor, secondColor) =
|
||||
colors[e] ?? (theme.secondary, theme.onSecondary);
|
||||
return ListTile(
|
||||
return ActionListItem(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
intent != null ? firstColor : theme.secondary.withOpacity(0.2),
|
||||
@ -74,8 +75,8 @@ class AccountDialog extends ConsumerWidget {
|
||||
//disabledBackgroundColor: theme.onSecondary.withOpacity(0.2),
|
||||
child: e.icon,
|
||||
),
|
||||
title: Text(e.text),
|
||||
subtitle: e.trailing != null ? Text(e.trailing!) : null,
|
||||
title: e.text,
|
||||
subtitle: e.trailing,
|
||||
onTap: intent != null
|
||||
? () {
|
||||
Actions.invoke(context, intent);
|
||||
@ -200,9 +201,10 @@ class AccountDialog extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
ListTitle(AppLocalizations.of(context)!.s_actions,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
..._buildActions(context, helper),
|
||||
ActionListSection(
|
||||
AppLocalizations.of(context)!.s_actions,
|
||||
children: _buildActions(context, helper),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -23,9 +23,9 @@ import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../../core/state.dart';
|
||||
import '../../exception/cancellation_exception.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
import '../keys.dart' as keys;
|
||||
@ -47,108 +47,98 @@ Widget oathBuildActions(
|
||||
return FsDialog(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTitle(l10n.s_setup,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
ListTile(
|
||||
title: Text(l10n.s_add_account),
|
||||
key: keys.addAccountAction,
|
||||
leading: CircleAvatar(
|
||||
ActionListSection(l10n.s_setup, children: [
|
||||
ActionListItem(
|
||||
key: keys.addAccountAction,
|
||||
title: l10n.s_add_account,
|
||||
backgroundColor: theme.primary,
|
||||
foregroundColor: theme.onPrimary,
|
||||
child: const Icon(Icons.person_add_alt_1_outlined),
|
||||
),
|
||||
subtitle: Text(used == null
|
||||
? l10n.l_unlock_first
|
||||
: (capacity != null ? l10n.l_accounts_used(used, capacity) : '')),
|
||||
enabled: used != null && (capacity == null || capacity > used),
|
||||
onTap: used != null && (capacity == null || capacity > used)
|
||||
? () async {
|
||||
final credentials = ref.read(credentialsProvider);
|
||||
final withContext = ref.read(withContextProvider);
|
||||
Navigator.of(context).pop();
|
||||
CredentialData? otpauth;
|
||||
if (isAndroid) {
|
||||
final scanner = ref.read(qrScannerProvider);
|
||||
if (scanner != null) {
|
||||
try {
|
||||
final url = await scanner.scanQr();
|
||||
if (url != null) {
|
||||
otpauth = CredentialData.fromUri(Uri.parse(url));
|
||||
icon: const Icon(Icons.person_add_alt_1_outlined),
|
||||
subtitle: used == null
|
||||
? l10n.l_unlock_first
|
||||
: (capacity != null
|
||||
? l10n.l_accounts_used(used, capacity)
|
||||
: ''),
|
||||
onTap: used != null && (capacity == null || capacity > used)
|
||||
? () async {
|
||||
final credentials = ref.read(credentialsProvider);
|
||||
final withContext = ref.read(withContextProvider);
|
||||
Navigator.of(context).pop();
|
||||
CredentialData? otpauth;
|
||||
if (isAndroid) {
|
||||
final scanner = ref.read(qrScannerProvider);
|
||||
if (scanner != null) {
|
||||
try {
|
||||
final url = await scanner.scanQr();
|
||||
if (url != null) {
|
||||
otpauth = CredentialData.fromUri(Uri.parse(url));
|
||||
}
|
||||
} on CancellationException catch (_) {
|
||||
// ignored - user cancelled
|
||||
return;
|
||||
}
|
||||
} on CancellationException catch (_) {
|
||||
// ignored - user cancelled
|
||||
return;
|
||||
}
|
||||
}
|
||||
await withContext((context) async {
|
||||
await showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => OathAddAccountPage(
|
||||
devicePath,
|
||||
oathState,
|
||||
credentials: credentials,
|
||||
credentialData: otpauth,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
await withContext((context) async {
|
||||
await showBlurDialog(
|
||||
: null,
|
||||
),
|
||||
]),
|
||||
ActionListSection(l10n.s_manage, children: [
|
||||
ActionListItem(
|
||||
key: keys.customIconsAction,
|
||||
title: l10n.s_custom_icons,
|
||||
subtitle: l10n.l_set_icons_for_accounts,
|
||||
icon: const Icon(Icons.image_outlined),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
await ref.read(withContextProvider)((context) => showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => OathAddAccountPage(
|
||||
devicePath,
|
||||
oathState,
|
||||
credentials: credentials,
|
||||
credentialData: otpauth,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
: null,
|
||||
),
|
||||
ListTitle(l10n.s_manage,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
ListTile(
|
||||
key: keys.customIconsAction,
|
||||
title: Text(l10n.s_custom_icons),
|
||||
subtitle: Text(l10n.l_set_icons_for_accounts),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.secondary,
|
||||
foregroundColor: theme.onSecondary,
|
||||
child: const Icon(Icons.image_outlined),
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
await ref.read(withContextProvider)((context) => showBlurDialog(
|
||||
context: context,
|
||||
routeSettings:
|
||||
const RouteSettings(name: 'oath_icon_pack_dialog'),
|
||||
builder: (context) => const IconPackDialog(),
|
||||
));
|
||||
}),
|
||||
ListTile(
|
||||
key: keys.setOrManagePasswordAction,
|
||||
title: Text(oathState.hasKey
|
||||
? l10n.s_manage_password
|
||||
: l10n.s_set_password),
|
||||
subtitle: Text(l10n.l_optional_password_protection),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.secondary,
|
||||
foregroundColor: theme.onSecondary,
|
||||
child: const Icon(Icons.password_outlined)),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
ManagePasswordDialog(devicePath, oathState),
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
key: keys.resetAction,
|
||||
title: Text(l10n.s_reset_oath),
|
||||
subtitle: Text(l10n.l_factory_reset_this_app),
|
||||
leading: CircleAvatar(
|
||||
routeSettings:
|
||||
const RouteSettings(name: 'oath_icon_pack_dialog'),
|
||||
builder: (context) => const IconPackDialog(),
|
||||
));
|
||||
}),
|
||||
ActionListItem(
|
||||
key: keys.setOrManagePasswordAction,
|
||||
title: oathState.hasKey
|
||||
? l10n.s_manage_password
|
||||
: l10n.s_set_password,
|
||||
subtitle: l10n.l_optional_password_protection,
|
||||
icon: const Icon(Icons.password_outlined),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
ManagePasswordDialog(devicePath, oathState),
|
||||
);
|
||||
}),
|
||||
ActionListItem(
|
||||
key: keys.resetAction,
|
||||
title: l10n.s_reset_oath,
|
||||
subtitle: l10n.l_factory_reset_this_app,
|
||||
foregroundColor: theme.onError,
|
||||
backgroundColor: theme.error,
|
||||
child: const Icon(Icons.delete_outline),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ResetDialog(devicePath),
|
||||
);
|
||||
}),
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ResetDialog(devicePath),
|
||||
);
|
||||
}),
|
||||
]),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -21,7 +21,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../models.dart';
|
||||
import '../keys.dart' as keys;
|
||||
import 'manage_key_dialog.dart';
|
||||
@ -43,105 +43,95 @@ Widget pivBuildActions(BuildContext context, DevicePath devicePath,
|
||||
return FsDialog(
|
||||
child: Column(
|
||||
children: [
|
||||
ListTitle(l10n.s_manage,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
ListTile(
|
||||
key: keys.managePinAction,
|
||||
title: Text(l10n.s_pin),
|
||||
subtitle: Text(pinBlocked
|
||||
? l10n.l_piv_pin_blocked
|
||||
: l10n.l_attempts_remaining(pivState.pinAttempts)),
|
||||
leading: CircleAvatar(
|
||||
foregroundColor: theme.onSecondary,
|
||||
backgroundColor: theme.secondary,
|
||||
child: const Icon(Icons.pin_outlined),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ManagePinPukDialog(
|
||||
devicePath,
|
||||
target: pinBlocked ? ManageTarget.unblock : ManageTarget.pin,
|
||||
),
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
key: keys.managePukAction,
|
||||
title: Text(l10n.s_puk),
|
||||
subtitle: pukAttempts != null
|
||||
? Text(l10n.l_attempts_remaining(pukAttempts))
|
||||
: null,
|
||||
leading: CircleAvatar(
|
||||
foregroundColor: theme.onSecondary,
|
||||
backgroundColor: theme.secondary,
|
||||
child: const Icon(Icons.pin_outlined),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
ManagePinPukDialog(devicePath, target: ManageTarget.puk),
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
key: keys.manageManagementKeyAction,
|
||||
title: Text(l10n.s_management_key),
|
||||
subtitle: Text(usingDefaultMgmtKey
|
||||
? l10n.l_warning_default_key
|
||||
: (pivState.protectedKey
|
||||
? l10n.l_pin_protected_key
|
||||
: l10n.l_change_management_key)),
|
||||
leading: CircleAvatar(
|
||||
foregroundColor: theme.onSecondary,
|
||||
backgroundColor: theme.secondary,
|
||||
child: const Icon(Icons.key_outlined),
|
||||
),
|
||||
trailing:
|
||||
usingDefaultMgmtKey ? const Icon(Icons.warning_amber) : null,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ManageKeyDialog(devicePath, pivState),
|
||||
);
|
||||
}),
|
||||
ListTile(
|
||||
key: keys.resetAction,
|
||||
title: Text(l10n.s_reset_piv),
|
||||
subtitle: Text(l10n.l_factory_reset_this_app),
|
||||
leading: CircleAvatar(
|
||||
foregroundColor: theme.onError,
|
||||
backgroundColor: theme.error,
|
||||
child: const Icon(Icons.delete_outline),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ResetDialog(devicePath),
|
||||
);
|
||||
}),
|
||||
ActionListSection(
|
||||
l10n.s_manage,
|
||||
children: [
|
||||
ActionListItem(
|
||||
key: keys.managePinAction,
|
||||
title: l10n.s_pin,
|
||||
subtitle: pinBlocked
|
||||
? l10n.l_piv_pin_blocked
|
||||
: l10n.l_attempts_remaining(pivState.pinAttempts),
|
||||
icon: const Icon(Icons.pin_outlined),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ManagePinPukDialog(
|
||||
devicePath,
|
||||
target:
|
||||
pinBlocked ? ManageTarget.unblock : ManageTarget.pin,
|
||||
),
|
||||
);
|
||||
}),
|
||||
ActionListItem(
|
||||
key: keys.managePukAction,
|
||||
title: l10n.s_puk,
|
||||
subtitle: pukAttempts != null
|
||||
? l10n.l_attempts_remaining(pukAttempts)
|
||||
: null,
|
||||
icon: const Icon(Icons.pin_outlined),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ManagePinPukDialog(devicePath,
|
||||
target: ManageTarget.puk),
|
||||
);
|
||||
}),
|
||||
ActionListItem(
|
||||
key: keys.manageManagementKeyAction,
|
||||
title: l10n.s_management_key,
|
||||
subtitle: usingDefaultMgmtKey
|
||||
? l10n.l_warning_default_key
|
||||
: (pivState.protectedKey
|
||||
? l10n.l_pin_protected_key
|
||||
: l10n.l_change_management_key),
|
||||
icon: const Icon(Icons.key_outlined),
|
||||
trailing: usingDefaultMgmtKey
|
||||
? const Icon(Icons.warning_amber)
|
||||
: null,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ManageKeyDialog(devicePath, pivState),
|
||||
);
|
||||
}),
|
||||
ActionListItem(
|
||||
key: keys.resetAction,
|
||||
title: l10n.s_reset_piv,
|
||||
subtitle: l10n.l_factory_reset_this_app,
|
||||
foregroundColor: theme.onError,
|
||||
backgroundColor: theme.error,
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ResetDialog(devicePath),
|
||||
);
|
||||
})
|
||||
],
|
||||
),
|
||||
// TODO
|
||||
/*
|
||||
if (false == true) ...[
|
||||
ListTitle(l10n.s_setup,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge),
|
||||
ListTile(
|
||||
key: keys.setupMacOsAction,
|
||||
title: Text('Setup for macOS'),
|
||||
subtitle: Text('Create certificates for macOS login'),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.secondary,
|
||||
foregroundColor: theme.onSecondary,
|
||||
child: const Icon(Icons.laptop),
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
],
|
||||
*/
|
||||
if (false == true) ...[
|
||||
KeyActionTitle(l10n.s_setup),
|
||||
KeyActionItem(
|
||||
key: keys.setupMacOsAction,
|
||||
title: Text('Setup for macOS'),
|
||||
subtitle: Text('Create certificates for macOS login'),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.secondary,
|
||||
foregroundColor: theme.onSecondary,
|
||||
child: const Icon(Icons.laptop),
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
],
|
||||
*/
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -5,7 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../../app/shortcuts.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../widgets/list_title.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
import 'actions.dart';
|
||||
@ -27,6 +27,8 @@ class SlotDialog extends ConsumerWidget {
|
||||
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
final theme =
|
||||
ButtonTheme.of(context).colorScheme ?? Theme.of(context).colorScheme;
|
||||
|
||||
final slotData = ref.watch(pivSlotsProvider(node.path).select((value) =>
|
||||
value.whenOrNull(
|
||||
@ -115,8 +117,49 @@ class SlotDialog extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTitle(l10n.s_actions, textStyle: textTheme.bodyLarge),
|
||||
_SlotDialogActions(certInfo),
|
||||
ActionListSection(
|
||||
l10n.s_actions,
|
||||
children: [
|
||||
ActionListItem(
|
||||
backgroundColor: theme.primary,
|
||||
foregroundColor: theme.onPrimary,
|
||||
icon: const Icon(Icons.add_outlined),
|
||||
title: l10n.s_generate_key,
|
||||
subtitle: l10n.l_generate_desc,
|
||||
onTap: () {
|
||||
Actions.invoke(context, const GenerateIntent());
|
||||
},
|
||||
),
|
||||
ActionListItem(
|
||||
icon: const Icon(Icons.file_download_outlined),
|
||||
title: l10n.l_import_file,
|
||||
subtitle: l10n.l_import_desc,
|
||||
onTap: () {
|
||||
Actions.invoke(context, const ImportIntent());
|
||||
},
|
||||
),
|
||||
if (certInfo != null) ...[
|
||||
ActionListItem(
|
||||
icon: const Icon(Icons.file_upload_outlined),
|
||||
title: l10n.l_export_certificate,
|
||||
subtitle: l10n.l_export_certificate_desc,
|
||||
onTap: () {
|
||||
Actions.invoke(context, const ExportIntent());
|
||||
},
|
||||
),
|
||||
ActionListItem(
|
||||
backgroundColor: theme.error,
|
||||
foregroundColor: theme.onError,
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
title: l10n.l_delete_certificate,
|
||||
subtitle: l10n.l_delete_certificate_desc,
|
||||
onTap: () {
|
||||
Actions.invoke(context, const DeleteIntent());
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -124,69 +167,3 @@ class SlotDialog extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SlotDialogActions extends StatelessWidget {
|
||||
final CertInfo? certInfo;
|
||||
const _SlotDialogActions(this.certInfo);
|
||||
|
||||
@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.primary,
|
||||
foregroundColor: theme.onPrimary,
|
||||
child: const Icon(Icons.add_outlined),
|
||||
),
|
||||
title: Text(l10n.s_generate_key),
|
||||
subtitle: Text(l10n.l_generate_desc),
|
||||
onTap: () {
|
||||
Actions.invoke(context, const GenerateIntent());
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.secondary,
|
||||
foregroundColor: theme.onSecondary,
|
||||
child: const Icon(Icons.file_download_outlined),
|
||||
),
|
||||
title: Text(l10n.l_import_file),
|
||||
subtitle: Text(l10n.l_import_desc),
|
||||
onTap: () {
|
||||
Actions.invoke(context, const ImportIntent());
|
||||
},
|
||||
),
|
||||
if (certInfo != null) ...[
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.secondary,
|
||||
foregroundColor: theme.onSecondary,
|
||||
child: const Icon(Icons.file_upload_outlined),
|
||||
),
|
||||
title: Text(l10n.l_export_certificate),
|
||||
subtitle: Text(l10n.l_export_certificate_desc),
|
||||
onTap: () {
|
||||
Actions.invoke(context, const ExportIntent());
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: theme.error,
|
||||
foregroundColor: theme.onError,
|
||||
child: const Icon(Icons.delete_outline),
|
||||
),
|
||||
title: Text(l10n.l_delete_certificate),
|
||||
subtitle: Text(l10n.l_delete_certificate_desc),
|
||||
onTap: () {
|
||||
Actions.invoke(context, const DeleteIntent());
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user