mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-22 17:51:29 +03:00
Clean up device picker and avatar.
This commit is contained in:
parent
a8c14637f7
commit
16b492638f
@ -1,35 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:yubico_authenticator/management/models.dart';
|
||||
|
||||
import '../models.dart';
|
||||
import 'device_images.dart';
|
||||
|
||||
/*
|
||||
TODO: This class should be refactored once we settle more on the final design.
|
||||
We may want to have two separate implementations depending on if it's an NFC reader or a USB YubiKey.
|
||||
*/
|
||||
class DeviceAvatar extends StatelessWidget {
|
||||
final DeviceNode node;
|
||||
final String name;
|
||||
final DeviceInfo? info;
|
||||
final bool selected;
|
||||
|
||||
const DeviceAvatar(this.node, this.name, this.info,
|
||||
{this.selected = false, Key? key})
|
||||
final Widget child;
|
||||
final IconData? badge;
|
||||
const DeviceAvatar._(
|
||||
{Key? key, this.selected = false, required this.child, this.badge})
|
||||
: super(key: key);
|
||||
|
||||
factory DeviceAvatar.yubiKeyData(YubiKeyData data, {bool selected = false}) =>
|
||||
DeviceAvatar._(
|
||||
child: getProductImage(data.info, data.name),
|
||||
badge: data.node is NfcReaderNode ? Icons.nfc : null,
|
||||
selected: selected,
|
||||
);
|
||||
|
||||
factory DeviceAvatar.deviceNode(DeviceNode node, {bool selected = false}) =>
|
||||
node.map(
|
||||
usbYubiKey: (node) => DeviceAvatar.yubiKeyData(
|
||||
YubiKeyData(node, node.name, node.info),
|
||||
selected: selected,
|
||||
),
|
||||
nfcReader: (_) => DeviceAvatar._(
|
||||
child: const Icon(Icons.nfc),
|
||||
selected: selected,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CircleAvatar(
|
||||
child: CircleAvatar(
|
||||
child:
|
||||
info != null ? getProductImage(info!, name) : const Icon(Icons.nfc),
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
),
|
||||
radius: 22,
|
||||
backgroundColor: selected
|
||||
? Theme.of(context).colorScheme.secondary
|
||||
: Colors.transparent,
|
||||
return Stack(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 22,
|
||||
backgroundColor: selected
|
||||
? Theme.of(context).colorScheme.secondary
|
||||
: Colors.transparent,
|
||||
child: CircleAvatar(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
if (badge != null)
|
||||
CircleAvatar(
|
||||
radius: 8,
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
child: Icon(
|
||||
badge!,
|
||||
size: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -26,29 +26,32 @@ class MainActionsDialog extends ConsumerWidget {
|
||||
return SimpleDialog(
|
||||
children: [
|
||||
if (currentNode != null)
|
||||
CurrentDeviceRow(
|
||||
_CurrentDeviceRow(
|
||||
currentNode,
|
||||
data?.name,
|
||||
info: data?.info,
|
||||
data: data,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
...devices.map(
|
||||
(e) => DeviceRow(
|
||||
(e) => _DeviceRow(
|
||||
e,
|
||||
e.name,
|
||||
info: e.when(
|
||||
usbYubiKey: (path, name, pid, info) => info,
|
||||
nfcReader: (path, name) => null,
|
||||
info: e.map(
|
||||
usbYubiKey: (node) => node.info,
|
||||
nfcReader: (_) => null,
|
||||
),
|
||||
selected: false,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
ref.read(currentDeviceProvider.notifier).setCurrentDevice(e);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (currentNode == null && devices.isEmpty)
|
||||
Center(
|
||||
child: Text(
|
||||
'No YubiKey found',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
)),
|
||||
if (actions.isNotEmpty) const Divider(),
|
||||
...actions.map((a) => ListTile(
|
||||
dense: true,
|
||||
@ -64,35 +67,41 @@ class MainActionsDialog extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class CurrentDeviceRow extends StatelessWidget {
|
||||
class _CurrentDeviceRow extends StatelessWidget {
|
||||
final DeviceNode node;
|
||||
final String? name;
|
||||
final DeviceInfo? info;
|
||||
final YubiKeyData? data;
|
||||
final Function() onTap;
|
||||
|
||||
const CurrentDeviceRow(
|
||||
this.node,
|
||||
this.name, {
|
||||
required this.info,
|
||||
const _CurrentDeviceRow(
|
||||
this.node, {
|
||||
this.data,
|
||||
required this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final subtitle = node is NfcReaderNode
|
||||
? info != null
|
||||
? '${node.name}\nS/N: ${info!.serial} F/W: ${info!.version}'
|
||||
: node.name
|
||||
: 'S/N: ${info!.serial} F/W: ${info!.version}';
|
||||
final subtitle = node.when(
|
||||
usbYubiKey: (_, __, ___, info) =>
|
||||
'S/N: ${info.serial} F/W: ${info.version}',
|
||||
nfcReader: (_, name) {
|
||||
final info = data?.info;
|
||||
return info == null
|
||||
? name
|
||||
: '$name\nS/N: ${info.serial} F/W: ${info.version}';
|
||||
});
|
||||
|
||||
return ListTile(
|
||||
leading: DeviceAvatar(
|
||||
node,
|
||||
name ?? '',
|
||||
info,
|
||||
selected: true,
|
||||
),
|
||||
title: Text(name ?? 'No YubiKey present'),
|
||||
leading: data != null
|
||||
? DeviceAvatar.yubiKeyData(
|
||||
data!,
|
||||
selected: true,
|
||||
)
|
||||
: DeviceAvatar.deviceNode(
|
||||
node,
|
||||
selected: true,
|
||||
),
|
||||
title: Text(data?.name ?? 'No YubiKey present'),
|
||||
isThreeLine: subtitle.contains('\n'),
|
||||
subtitle: Text(subtitle),
|
||||
onTap: onTap,
|
||||
@ -100,36 +109,29 @@ class CurrentDeviceRow extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceRow extends StatelessWidget {
|
||||
class _DeviceRow extends StatelessWidget {
|
||||
final DeviceNode node;
|
||||
final String name;
|
||||
final DeviceInfo? info;
|
||||
final bool selected;
|
||||
final Function() onTap;
|
||||
|
||||
const DeviceRow(
|
||||
this.node,
|
||||
this.name, {
|
||||
const _DeviceRow(
|
||||
this.node, {
|
||||
required this.info,
|
||||
required this.onTap,
|
||||
this.selected = false,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: DeviceAvatar(
|
||||
node,
|
||||
name,
|
||||
info,
|
||||
selected: selected,
|
||||
),
|
||||
title: Text(name),
|
||||
leading: DeviceAvatar.deviceNode(node),
|
||||
title: Text(node.name),
|
||||
subtitle: Text(
|
||||
info == null
|
||||
? (selected ? 'No YubiKey present' : 'Select to scan')
|
||||
: 'S/N: ${info!.serial} F/W: ${info!.version}',
|
||||
node.when(
|
||||
usbYubiKey: (_, __, ___, info) =>
|
||||
'S/N: ${info.serial} F/W: ${info.version}',
|
||||
nfcReader: (_, __) => 'Select to scan',
|
||||
),
|
||||
),
|
||||
onTap: onTap,
|
||||
);
|
||||
|
@ -28,9 +28,30 @@ class MainPage extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final deviceNode = ref.watch(currentDeviceProvider);
|
||||
final deviceData = ref.watch(currentDeviceDataProvider);
|
||||
final subPage = ref.watch(subPageProvider);
|
||||
|
||||
Widget deviceWidget;
|
||||
if (deviceNode != null) {
|
||||
if (deviceData != null) {
|
||||
deviceWidget = DeviceAvatar.yubiKeyData(
|
||||
deviceData,
|
||||
selected: true,
|
||||
);
|
||||
} else {
|
||||
deviceWidget = DeviceAvatar.deviceNode(
|
||||
deviceNode,
|
||||
selected: true,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
deviceWidget = const CircleAvatar(
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Icon(Icons.usb_off),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
/*
|
||||
@ -59,20 +80,7 @@ class MainPage extends ConsumerWidget {
|
||||
InkWell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: deviceData == null
|
||||
? SizedBox.square(
|
||||
dimension: 44,
|
||||
child: Icon(
|
||||
Icons.usb_off,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
),
|
||||
)
|
||||
: DeviceAvatar(
|
||||
deviceData.node,
|
||||
deviceData.name,
|
||||
deviceData.info,
|
||||
selected: true,
|
||||
),
|
||||
child: deviceWidget,
|
||||
),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
|
Loading…
Reference in New Issue
Block a user