Cleanups in code.

This commit is contained in:
Dain Nilsson 2024-01-22 16:54:01 +01:00
parent 7920d53430
commit 1f29024cf8
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
3 changed files with 310 additions and 343 deletions

View File

@ -133,7 +133,7 @@ class _FidoLockedPage extends ConsumerWidget {
}
Widget _buildActions(BuildContext context) =>
fidoBuildActions(context, node, state, -1);
fingerprintsBuildActions(context, node, state, -1);
}
class _FidoUnlockedPage extends ConsumerStatefulWidget {
@ -153,168 +153,156 @@ class _FidoUnlockedPageState extends ConsumerState<_FidoUnlockedPage> {
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
List<Widget Function(bool expanded)> children = [];
int nFingerprints = 0;
if (widget.state.bioEnroll != null) {
final data = ref.watch(fingerprintProvider(widget.node.path)).asData;
if (data == null) {
return _buildLoadingPage(context);
}
final fingerprints = data.value;
if (fingerprints.isNotEmpty) {
nFingerprints = fingerprints.length;
children.addAll(fingerprints.map(
(fp) => (expanded) => _FingerprintListItem(
fp,
expanded: expanded,
selected: fp == _selected,
),
));
}
}
final hasFeature = ref.watch(featureProvider);
final hasActions = hasFeature(features.actions);
final fingerprint = _selected;
if (children.isNotEmpty) {
return FidoActions(
devicePath: widget.node.path,
actions: (context) => {
EscapeIntent: CallbackAction<EscapeIntent>(onInvoke: (intent) {
if (_selected != null) {
setState(() {
_selected = null;
});
} else {
Actions.invoke(context, intent);
}
return false;
}),
OpenIntent<Fingerprint>:
CallbackAction<OpenIntent<Fingerprint>>(onInvoke: (intent) {
return showBlurDialog(
context: context,
barrierColor: Colors.transparent,
builder: (context) => FingerprintDialog(intent.target),
);
}),
if (hasFeature(features.fingerprintsEdit))
EditIntent<Fingerprint>: CallbackAction<EditIntent<Fingerprint>>(
onInvoke: (intent) async {
final renamed =
await (Actions.invoke(context, intent) as Future<dynamic>?);
if (_selected == intent.target && renamed is Fingerprint) {
setState(() {
_selected = renamed;
});
}
return renamed;
}),
if (hasFeature(features.fingerprintsDelete))
DeleteIntent<Fingerprint>:
CallbackAction<DeleteIntent<Fingerprint>>(
onInvoke: (intent) async {
final deleted =
await (Actions.invoke(context, intent) as Future<dynamic>?);
if (deleted == true && _selected == intent.target) {
setState(() {
_selected = null;
});
}
return deleted;
}),
},
builder: (context) => AppPage(
title: l10n.s_fingerprints,
detailViewBuilder: fingerprint != null
? (context) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ListTitle(l10n.s_details),
Card(
child: Padding(
padding: const EdgeInsets.all(16),
// TODO: Reuse from fingerprint_dialog
child: Column(
children: [
Text(
fingerprint.label,
style:
Theme.of(context).textTheme.headlineSmall,
softWrap: true,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
const Icon(Icons.fingerprint, size: 72),
],
),
),
),
ActionListSection.fromMenuActions(
context,
l10n.s_actions,
actions: buildFingerprintActions(fingerprint, l10n),
),
],
)
: null,
keyActionsBuilder: hasActions
? (context) => fidoBuildActions(
context, widget.node, widget.state, nFingerprints)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
builder: (context, expanded) {
// De-select if window is resized to be non-expanded.
if (!expanded && _selected != null) {
Timer.run(() {
setState(() {
_selected = null;
});
});
}
return Actions(
actions: {
if (expanded) ...{
OpenIntent<Fingerprint>:
CallbackAction<OpenIntent<Fingerprint>>(
onInvoke: (intent) {
setState(() {
_selected = intent.target;
});
return null;
}),
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children.map((f) => f(expanded)).toList()),
);
},
),
final data = ref.watch(fingerprintProvider(widget.node.path)).asData;
if (data == null) {
return _buildLoadingPage(context);
}
final fingerprints = data.value;
if (fingerprints.isEmpty) {
return MessagePage(
actions: [
ActionChip(
label: Text(l10n.s_add_fingerprint),
onPressed: () async {
await showBlurDialog(
context: context,
builder: (context) => AddFingerprintDialog(widget.node.path));
},
avatar: const Icon(Icons.fingerprint_outlined),
)
],
title: l10n.s_fingerprints,
header: '${l10n.s_fingerprints_get_started} (2/2)',
message: l10n.l_add_one_or_more_fps,
keyActionsBuilder: hasActions
? (context) =>
fingerprintsBuildActions(context, widget.node, widget.state, 0)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
);
}
return MessagePage(
actions: [
ActionChip(
label: Text(l10n.s_add_fingerprint),
onPressed: () async {
await showBlurDialog(
context: context,
builder: (context) => AddFingerprintDialog(widget.node.path));
},
avatar: const Icon(Icons.fingerprint_outlined),
)
],
title: l10n.s_webauthn,
header: '${l10n.s_fingerprints_get_started} (2/2)',
message: l10n.l_add_one_or_more_fps,
keyActionsBuilder: hasActions
? (context) => fidoBuildActions(context, widget.node, widget.state, 0)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
final fingerprint = _selected;
return FidoActions(
devicePath: widget.node.path,
actions: (context) => {
EscapeIntent: CallbackAction<EscapeIntent>(onInvoke: (intent) {
if (_selected != null) {
setState(() {
_selected = null;
});
} else {
Actions.invoke(context, intent);
}
return false;
}),
OpenIntent<Fingerprint>:
CallbackAction<OpenIntent<Fingerprint>>(onInvoke: (intent) {
return showBlurDialog(
context: context,
barrierColor: Colors.transparent,
builder: (context) => FingerprintDialog(intent.target),
);
}),
if (hasFeature(features.fingerprintsEdit))
EditIntent<Fingerprint>:
CallbackAction<EditIntent<Fingerprint>>(onInvoke: (intent) async {
final renamed =
await (Actions.invoke(context, intent) as Future<dynamic>?);
if (_selected == intent.target && renamed is Fingerprint) {
setState(() {
_selected = renamed;
});
}
return renamed;
}),
if (hasFeature(features.fingerprintsDelete))
DeleteIntent<Fingerprint>: CallbackAction<DeleteIntent<Fingerprint>>(
onInvoke: (intent) async {
final deleted =
await (Actions.invoke(context, intent) as Future<dynamic>?);
if (deleted == true && _selected == intent.target) {
setState(() {
_selected = null;
});
}
return deleted;
}),
},
builder: (context) => AppPage(
title: l10n.s_fingerprints,
detailViewBuilder: fingerprint != null
? (context) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ListTitle(l10n.s_details),
Card(
child: Padding(
padding: const EdgeInsets.all(16),
// TODO: Reuse from fingerprint_dialog
child: Column(
children: [
Text(
fingerprint.label,
style: Theme.of(context).textTheme.headlineSmall,
softWrap: true,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
const Icon(Icons.fingerprint, size: 72),
],
),
),
),
ActionListSection.fromMenuActions(
context,
l10n.s_actions,
actions: buildFingerprintActions(fingerprint, l10n),
),
],
)
: null,
keyActionsBuilder: hasActions
? (context) => fingerprintsBuildActions(
context, widget.node, widget.state, fingerprints.length)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
builder: (context, expanded) {
// De-select if window is resized to be non-expanded.
if (!expanded && _selected != null) {
Timer.run(() {
setState(() {
_selected = null;
});
});
}
return Actions(
actions: {
if (expanded) ...{
OpenIntent<Fingerprint>:
CallbackAction<OpenIntent<Fingerprint>>(onInvoke: (intent) {
setState(() {
_selected = intent.target;
});
return null;
}),
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: fingerprints
.map((fp) => _FingerprintListItem(
fp,
expanded: expanded,
selected: fp == _selected,
))
.toList()),
);
},
),
);
}

View File

@ -33,51 +33,22 @@ bool fidoShowActionsNotifier(FidoState state) {
}
Widget passkeysBuildActions(
BuildContext context, DeviceNode node, FidoState state) {
BuildContext context, DeviceNode node, FidoState state) =>
_fidoBuildActions(context, node, state);
Widget fingerprintsBuildActions(BuildContext context, DeviceNode node,
FidoState state, int fingerprints) =>
_fidoBuildActions(context, node, state, fingerprints);
Widget _fidoBuildActions(BuildContext context, DeviceNode node, FidoState state,
[int? fingerprints]) {
final l10n = AppLocalizations.of(context)!;
final colors = Theme.of(context).buttonTheme.colorScheme ??
Theme.of(context).colorScheme;
return Column(
children: [
ActionListSection(
l10n.s_manage,
children: [
ActionListItem(
key: keys.managePinAction,
feature: features.actionsPin,
icon: const Icon(Icons.pin_outlined),
title: state.hasPin ? l10n.s_change_pin : l10n.s_set_pin,
subtitle: state.hasPin
? (state.forcePinChange
? l10n.s_pin_change_required
: l10n.s_fido_pin_protection)
: l10n.l_fido_pin_protection_optional,
trailing: state.alwaysUv && !state.hasPin || state.forcePinChange
? Icon(Icons.warning_amber, color: colors.tertiary)
: null,
onTap: (context) {
Navigator.of(context).popUntil((route) => route.isFirst);
showBlurDialog(
context: context,
builder: (context) => FidoPinDialog(node.path, state),
);
}),
],
)
],
);
}
Widget fidoBuildActions(
BuildContext context, DeviceNode node, FidoState state, int fingerprints) {
final l10n = AppLocalizations.of(context)!;
final colors = Theme.of(context).buttonTheme.colorScheme ??
Theme.of(context).colorScheme;
return Column(
children: [
if (state.bioEnroll != null)
if (fingerprints != null)
ActionListSection(
l10n.s_setup,
children: [

View File

@ -152,159 +152,167 @@ class _FidoUnlockedPageState extends ConsumerState<_FidoUnlockedPage> {
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
List<Widget Function(bool expanded)> children = [];
if (widget.state.credMgmt) {
final data = ref.watch(credentialProvider(widget.node.path)).asData;
if (data == null) {
return _buildLoadingPage(context);
}
final creds = data.value;
if (creds.isNotEmpty) {
children.addAll(creds.map(
(cred) => (expanded) => _CredentialListItem(
cred,
expanded: expanded,
selected: _selected == cred,
),
));
}
}
final hasFeature = ref.watch(featureProvider);
final hasActions = hasFeature(features.actions);
final credential = _selected;
if (children.isNotEmpty) {
return FidoActions(
devicePath: widget.node.path,
actions: (context) => {
EscapeIntent: CallbackAction<EscapeIntent>(onInvoke: (intent) {
if (_selected != null) {
setState(() {
_selected = null;
});
} else {
Actions.invoke(context, intent);
}
return false;
}),
OpenIntent<FidoCredential>:
CallbackAction<OpenIntent<FidoCredential>>(onInvoke: (intent) {
return showBlurDialog(
context: context,
barrierColor: Colors.transparent,
builder: (context) => CredentialDialog(intent.target),
);
}),
if (hasFeature(features.credentialsDelete))
DeleteIntent<FidoCredential>:
CallbackAction<DeleteIntent<FidoCredential>>(
onInvoke: (intent) async {
final deleted =
await (Actions.invoke(context, intent) as Future<dynamic>?);
if (deleted == true && _selected == intent.target) {
setState(() {
_selected = null;
});
}
return deleted;
}),
},
builder: (context) => AppPage(
title: l10n.s_passkeys,
detailViewBuilder: credential != null
? (context) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ListTitle(l10n.s_details),
Card(
child: Padding(
padding: const EdgeInsets.all(16),
// TODO: Reuse from credential_dialog
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),
],
),
),
),
ActionListSection.fromMenuActions(
context,
l10n.s_actions,
actions: buildCredentialActions(credential, l10n),
),
],
)
: null,
keyActionsBuilder: hasActions
? (context) =>
passkeysBuildActions(context, widget.node, widget.state)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
builder: (context, expanded) {
// De-select if window is resized to be non-expanded.
if (!expanded && _selected != null) {
Timer.run(() {
setState(() {
_selected = null;
});
});
}
return Actions(
actions: {
if (expanded) ...{
OpenIntent<FidoCredential>:
CallbackAction<OpenIntent<FidoCredential>>(
onInvoke: (intent) {
setState(() {
_selected = intent.target;
});
return null;
}),
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children.map((f) => f(expanded)).toList()),
);
},
),
if (!widget.state.credMgmt) {
// TODO: Special handling for credMgmt not supported
return MessagePage(
title: l10n.s_passkeys,
header: l10n.l_no_discoverable_accounts,
message: l10n.l_register_sk_on_websites,
keyActionsBuilder: hasActions
? (context) =>
passkeysBuildActions(context, widget.node, widget.state)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
);
}
return MessagePage(
title: l10n.s_passkeys,
header: l10n.l_no_discoverable_accounts,
message: l10n.l_register_sk_on_websites,
keyActionsBuilder: hasActions
? (context) =>
passkeysBuildActions(context, widget.node, widget.state)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
final data = ref.watch(credentialProvider(widget.node.path)).asData;
if (data == null) {
return _buildLoadingPage(context);
}
final credentials = data.value;
if (credentials.isEmpty) {
return MessagePage(
title: l10n.s_passkeys,
header: l10n.l_no_discoverable_accounts,
message: l10n.l_register_sk_on_websites,
keyActionsBuilder: hasActions
? (context) =>
passkeysBuildActions(context, widget.node, widget.state)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
);
}
final credential = _selected;
return FidoActions(
devicePath: widget.node.path,
actions: (context) => {
EscapeIntent: CallbackAction<EscapeIntent>(onInvoke: (intent) {
if (_selected != null) {
setState(() {
_selected = null;
});
} else {
Actions.invoke(context, intent);
}
return false;
}),
OpenIntent<FidoCredential>:
CallbackAction<OpenIntent<FidoCredential>>(onInvoke: (intent) {
return showBlurDialog(
context: context,
barrierColor: Colors.transparent,
builder: (context) => CredentialDialog(intent.target),
);
}),
if (hasFeature(features.credentialsDelete))
DeleteIntent<FidoCredential>:
CallbackAction<DeleteIntent<FidoCredential>>(
onInvoke: (intent) async {
final deleted =
await (Actions.invoke(context, intent) as Future<dynamic>?);
if (deleted == true && _selected == intent.target) {
setState(() {
_selected = null;
});
}
return deleted;
}),
},
builder: (context) => AppPage(
title: l10n.s_passkeys,
detailViewBuilder: credential != null
? (context) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ListTitle(l10n.s_details),
Card(
child: Padding(
padding: const EdgeInsets.all(16),
// TODO: Reuse from credential_dialog
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),
],
),
),
),
ActionListSection.fromMenuActions(
context,
l10n.s_actions,
actions: buildCredentialActions(credential, l10n),
),
],
)
: null,
keyActionsBuilder: hasActions
? (context) =>
passkeysBuildActions(context, widget.node, widget.state)
: null,
keyActionsBadge: fidoShowActionsNotifier(widget.state),
builder: (context, expanded) {
// De-select if window is resized to be non-expanded.
if (!expanded && _selected != null) {
Timer.run(() {
setState(() {
_selected = null;
});
});
}
return Actions(
actions: {
if (expanded) ...{
OpenIntent<FidoCredential>:
CallbackAction<OpenIntent<FidoCredential>>(
onInvoke: (intent) {
setState(() {
_selected = intent.target;
});
return null;
}),
}
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: credentials
.map(
(cred) => _CredentialListItem(
cred,
expanded: expanded,
selected: _selected == cred,
),
)
.toList()),
);
},
),
);
}