mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-22 17:51:29 +03:00
Merge PR #19.
This commit is contained in:
commit
edc488985a
@ -3,22 +3,21 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import '../../management/models.dart';
|
||||
|
||||
part 'models.freezed.dart';
|
||||
part 'models.g.dart';
|
||||
|
||||
enum SubPage { authenticator, yubikey }
|
||||
|
||||
@freezed
|
||||
class DeviceNode with _$DeviceNode {
|
||||
factory DeviceNode(
|
||||
List<String> path,
|
||||
int pid,
|
||||
Transport transport,
|
||||
String name,
|
||||
DeviceInfo info,
|
||||
) = _DeviceNode;
|
||||
class YubiKeyData with _$YubiKeyData {
|
||||
factory YubiKeyData(DeviceNode node, String name, DeviceInfo info) =
|
||||
_YubiKeyData;
|
||||
}
|
||||
|
||||
factory DeviceNode.fromJson(Map<String, dynamic> json) =>
|
||||
_$DeviceNodeFromJson(json);
|
||||
@freezed
|
||||
class DeviceNode with _$DeviceNode {
|
||||
factory DeviceNode.usbYubiKey(
|
||||
List<String> path, String name, int pid, DeviceInfo info) =
|
||||
UsbYubiKeyNode;
|
||||
factory DeviceNode.nfcReader(List<String> path, String name) = NfcReaderNode;
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -1,5 +1,6 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||
|
||||
part of 'models.dart';
|
||||
@ -13,27 +14,212 @@ T _$identity<T>(T value) => value;
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
|
||||
|
||||
DeviceNode _$DeviceNodeFromJson(Map<String, dynamic> json) {
|
||||
return _DeviceNode.fromJson(json);
|
||||
/// @nodoc
|
||||
class _$YubiKeyDataTearOff {
|
||||
const _$YubiKeyDataTearOff();
|
||||
|
||||
_YubiKeyData call(DeviceNode node, String name, DeviceInfo info) {
|
||||
return _YubiKeyData(
|
||||
node,
|
||||
name,
|
||||
info,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
const $YubiKeyData = _$YubiKeyDataTearOff();
|
||||
|
||||
/// @nodoc
|
||||
mixin _$YubiKeyData {
|
||||
DeviceNode get node => throw _privateConstructorUsedError;
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
DeviceInfo get info => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$YubiKeyDataCopyWith<YubiKeyData> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $YubiKeyDataCopyWith<$Res> {
|
||||
factory $YubiKeyDataCopyWith(
|
||||
YubiKeyData value, $Res Function(YubiKeyData) then) =
|
||||
_$YubiKeyDataCopyWithImpl<$Res>;
|
||||
$Res call({DeviceNode node, String name, DeviceInfo info});
|
||||
|
||||
$DeviceNodeCopyWith<$Res> get node;
|
||||
$DeviceInfoCopyWith<$Res> get info;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$YubiKeyDataCopyWithImpl<$Res> implements $YubiKeyDataCopyWith<$Res> {
|
||||
_$YubiKeyDataCopyWithImpl(this._value, this._then);
|
||||
|
||||
final YubiKeyData _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function(YubiKeyData) _then;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? node = freezed,
|
||||
Object? name = freezed,
|
||||
Object? info = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
node: node == freezed
|
||||
? _value.node
|
||||
: node // ignore: cast_nullable_to_non_nullable
|
||||
as DeviceNode,
|
||||
name: name == freezed
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
info: info == freezed
|
||||
? _value.info
|
||||
: info // ignore: cast_nullable_to_non_nullable
|
||||
as DeviceInfo,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
$DeviceNodeCopyWith<$Res> get node {
|
||||
return $DeviceNodeCopyWith<$Res>(_value.node, (value) {
|
||||
return _then(_value.copyWith(node: value));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
$DeviceInfoCopyWith<$Res> get info {
|
||||
return $DeviceInfoCopyWith<$Res>(_value.info, (value) {
|
||||
return _then(_value.copyWith(info: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$YubiKeyDataCopyWith<$Res>
|
||||
implements $YubiKeyDataCopyWith<$Res> {
|
||||
factory _$YubiKeyDataCopyWith(
|
||||
_YubiKeyData value, $Res Function(_YubiKeyData) then) =
|
||||
__$YubiKeyDataCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call({DeviceNode node, String name, DeviceInfo info});
|
||||
|
||||
@override
|
||||
$DeviceNodeCopyWith<$Res> get node;
|
||||
@override
|
||||
$DeviceInfoCopyWith<$Res> get info;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$YubiKeyDataCopyWithImpl<$Res> extends _$YubiKeyDataCopyWithImpl<$Res>
|
||||
implements _$YubiKeyDataCopyWith<$Res> {
|
||||
__$YubiKeyDataCopyWithImpl(
|
||||
_YubiKeyData _value, $Res Function(_YubiKeyData) _then)
|
||||
: super(_value, (v) => _then(v as _YubiKeyData));
|
||||
|
||||
@override
|
||||
_YubiKeyData get _value => super._value as _YubiKeyData;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? node = freezed,
|
||||
Object? name = freezed,
|
||||
Object? info = freezed,
|
||||
}) {
|
||||
return _then(_YubiKeyData(
|
||||
node == freezed
|
||||
? _value.node
|
||||
: node // ignore: cast_nullable_to_non_nullable
|
||||
as DeviceNode,
|
||||
name == freezed
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
info == freezed
|
||||
? _value.info
|
||||
: info // ignore: cast_nullable_to_non_nullable
|
||||
as DeviceInfo,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$_YubiKeyData implements _YubiKeyData {
|
||||
_$_YubiKeyData(this.node, this.name, this.info);
|
||||
|
||||
@override
|
||||
final DeviceNode node;
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final DeviceInfo info;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'YubiKeyData(node: $node, name: $name, info: $info)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _YubiKeyData &&
|
||||
const DeepCollectionEquality().equals(other.node, node) &&
|
||||
const DeepCollectionEquality().equals(other.name, name) &&
|
||||
const DeepCollectionEquality().equals(other.info, info));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(node),
|
||||
const DeepCollectionEquality().hash(name),
|
||||
const DeepCollectionEquality().hash(info));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
_$YubiKeyDataCopyWith<_YubiKeyData> get copyWith =>
|
||||
__$YubiKeyDataCopyWithImpl<_YubiKeyData>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _YubiKeyData implements YubiKeyData {
|
||||
factory _YubiKeyData(DeviceNode node, String name, DeviceInfo info) =
|
||||
_$_YubiKeyData;
|
||||
|
||||
@override
|
||||
DeviceNode get node;
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
DeviceInfo get info;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$YubiKeyDataCopyWith<_YubiKeyData> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$DeviceNodeTearOff {
|
||||
const _$DeviceNodeTearOff();
|
||||
|
||||
_DeviceNode call(List<String> path, int pid, Transport transport, String name,
|
||||
DeviceInfo info) {
|
||||
return _DeviceNode(
|
||||
UsbYubiKeyNode usbYubiKey(
|
||||
List<String> path, String name, int pid, DeviceInfo info) {
|
||||
return UsbYubiKeyNode(
|
||||
path,
|
||||
pid,
|
||||
transport,
|
||||
name,
|
||||
pid,
|
||||
info,
|
||||
);
|
||||
}
|
||||
|
||||
DeviceNode fromJson(Map<String, Object?> json) {
|
||||
return DeviceNode.fromJson(json);
|
||||
NfcReaderNode nfcReader(List<String> path, String name) {
|
||||
return NfcReaderNode(
|
||||
path,
|
||||
name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,12 +229,51 @@ const $DeviceNode = _$DeviceNodeTearOff();
|
||||
/// @nodoc
|
||||
mixin _$DeviceNode {
|
||||
List<String> get path => throw _privateConstructorUsedError;
|
||||
int get pid => throw _privateConstructorUsedError;
|
||||
Transport get transport => throw _privateConstructorUsedError;
|
||||
String get name => throw _privateConstructorUsedError;
|
||||
DeviceInfo get info => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(
|
||||
List<String> path, String name, int pid, DeviceInfo info)
|
||||
usbYubiKey,
|
||||
required TResult Function(List<String> path, String name) nfcReader,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult Function(List<String> path, String name, int pid, DeviceInfo info)?
|
||||
usbYubiKey,
|
||||
TResult Function(List<String> path, String name)? nfcReader,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(List<String> path, String name, int pid, DeviceInfo info)?
|
||||
usbYubiKey,
|
||||
TResult Function(List<String> path, String name)? nfcReader,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(UsbYubiKeyNode value) usbYubiKey,
|
||||
required TResult Function(NfcReaderNode value) nfcReader,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult Function(UsbYubiKeyNode value)? usbYubiKey,
|
||||
TResult Function(NfcReaderNode value)? nfcReader,
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(UsbYubiKeyNode value)? usbYubiKey,
|
||||
TResult Function(NfcReaderNode value)? nfcReader,
|
||||
required TResult orElse(),
|
||||
}) =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$DeviceNodeCopyWith<DeviceNode> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
@ -59,14 +284,7 @@ abstract class $DeviceNodeCopyWith<$Res> {
|
||||
factory $DeviceNodeCopyWith(
|
||||
DeviceNode value, $Res Function(DeviceNode) then) =
|
||||
_$DeviceNodeCopyWithImpl<$Res>;
|
||||
$Res call(
|
||||
{List<String> path,
|
||||
int pid,
|
||||
Transport transport,
|
||||
String name,
|
||||
DeviceInfo info});
|
||||
|
||||
$DeviceInfoCopyWith<$Res> get info;
|
||||
$Res call({List<String> path, String name});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -80,29 +298,64 @@ class _$DeviceNodeCopyWithImpl<$Res> implements $DeviceNodeCopyWith<$Res> {
|
||||
@override
|
||||
$Res call({
|
||||
Object? path = freezed,
|
||||
Object? pid = freezed,
|
||||
Object? transport = freezed,
|
||||
Object? name = freezed,
|
||||
Object? info = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
path: path == freezed
|
||||
? _value.path
|
||||
: path // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
pid: pid == freezed
|
||||
? _value.pid
|
||||
: pid // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
transport: transport == freezed
|
||||
? _value.transport
|
||||
: transport // ignore: cast_nullable_to_non_nullable
|
||||
as Transport,
|
||||
name: name == freezed
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
info: info == freezed
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $UsbYubiKeyNodeCopyWith<$Res>
|
||||
implements $DeviceNodeCopyWith<$Res> {
|
||||
factory $UsbYubiKeyNodeCopyWith(
|
||||
UsbYubiKeyNode value, $Res Function(UsbYubiKeyNode) then) =
|
||||
_$UsbYubiKeyNodeCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call({List<String> path, String name, int pid, DeviceInfo info});
|
||||
|
||||
$DeviceInfoCopyWith<$Res> get info;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$UsbYubiKeyNodeCopyWithImpl<$Res> extends _$DeviceNodeCopyWithImpl<$Res>
|
||||
implements $UsbYubiKeyNodeCopyWith<$Res> {
|
||||
_$UsbYubiKeyNodeCopyWithImpl(
|
||||
UsbYubiKeyNode _value, $Res Function(UsbYubiKeyNode) _then)
|
||||
: super(_value, (v) => _then(v as UsbYubiKeyNode));
|
||||
|
||||
@override
|
||||
UsbYubiKeyNode get _value => super._value as UsbYubiKeyNode;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? path = freezed,
|
||||
Object? name = freezed,
|
||||
Object? pid = freezed,
|
||||
Object? info = freezed,
|
||||
}) {
|
||||
return _then(UsbYubiKeyNode(
|
||||
path == freezed
|
||||
? _value.path
|
||||
: path // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
name == freezed
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
pid == freezed
|
||||
? _value.pid
|
||||
: pid // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
info == freezed
|
||||
? _value.info
|
||||
: info // ignore: cast_nullable_to_non_nullable
|
||||
as DeviceInfo,
|
||||
@ -118,137 +371,282 @@ class _$DeviceNodeCopyWithImpl<$Res> implements $DeviceNodeCopyWith<$Res> {
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$DeviceNodeCopyWith<$Res> implements $DeviceNodeCopyWith<$Res> {
|
||||
factory _$DeviceNodeCopyWith(
|
||||
_DeviceNode value, $Res Function(_DeviceNode) then) =
|
||||
__$DeviceNodeCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call(
|
||||
{List<String> path,
|
||||
int pid,
|
||||
Transport transport,
|
||||
String name,
|
||||
DeviceInfo info});
|
||||
|
||||
@override
|
||||
$DeviceInfoCopyWith<$Res> get info;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$DeviceNodeCopyWithImpl<$Res> extends _$DeviceNodeCopyWithImpl<$Res>
|
||||
implements _$DeviceNodeCopyWith<$Res> {
|
||||
__$DeviceNodeCopyWithImpl(
|
||||
_DeviceNode _value, $Res Function(_DeviceNode) _then)
|
||||
: super(_value, (v) => _then(v as _DeviceNode));
|
||||
|
||||
@override
|
||||
_DeviceNode get _value => super._value as _DeviceNode;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? path = freezed,
|
||||
Object? pid = freezed,
|
||||
Object? transport = freezed,
|
||||
Object? name = freezed,
|
||||
Object? info = freezed,
|
||||
}) {
|
||||
return _then(_DeviceNode(
|
||||
path == freezed
|
||||
? _value.path
|
||||
: path // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
pid == freezed
|
||||
? _value.pid
|
||||
: pid // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
transport == freezed
|
||||
? _value.transport
|
||||
: transport // ignore: cast_nullable_to_non_nullable
|
||||
as Transport,
|
||||
name == freezed
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
info == freezed
|
||||
? _value.info
|
||||
: info // ignore: cast_nullable_to_non_nullable
|
||||
as DeviceInfo,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$_DeviceNode implements _DeviceNode {
|
||||
_$_DeviceNode(this.path, this.pid, this.transport, this.name, this.info);
|
||||
|
||||
factory _$_DeviceNode.fromJson(Map<String, dynamic> json) =>
|
||||
_$$_DeviceNodeFromJson(json);
|
||||
class _$UsbYubiKeyNode implements UsbYubiKeyNode {
|
||||
_$UsbYubiKeyNode(this.path, this.name, this.pid, this.info);
|
||||
|
||||
@override
|
||||
final List<String> path;
|
||||
@override
|
||||
final int pid;
|
||||
@override
|
||||
final Transport transport;
|
||||
@override
|
||||
final String name;
|
||||
@override
|
||||
final int pid;
|
||||
@override
|
||||
final DeviceInfo info;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DeviceNode(path: $path, pid: $pid, transport: $transport, name: $name, info: $info)';
|
||||
return 'DeviceNode.usbYubiKey(path: $path, name: $name, pid: $pid, info: $info)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _DeviceNode &&
|
||||
other is UsbYubiKeyNode &&
|
||||
const DeepCollectionEquality().equals(other.path, path) &&
|
||||
(identical(other.pid, pid) || other.pid == pid) &&
|
||||
(identical(other.transport, transport) ||
|
||||
other.transport == transport) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.info, info) || other.info == info));
|
||||
const DeepCollectionEquality().equals(other.name, name) &&
|
||||
const DeepCollectionEquality().equals(other.pid, pid) &&
|
||||
const DeepCollectionEquality().equals(other.info, info));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,
|
||||
const DeepCollectionEquality().hash(path), pid, transport, name, info);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(path),
|
||||
const DeepCollectionEquality().hash(name),
|
||||
const DeepCollectionEquality().hash(pid),
|
||||
const DeepCollectionEquality().hash(info));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
_$DeviceNodeCopyWith<_DeviceNode> get copyWith =>
|
||||
__$DeviceNodeCopyWithImpl<_DeviceNode>(this, _$identity);
|
||||
$UsbYubiKeyNodeCopyWith<UsbYubiKeyNode> get copyWith =>
|
||||
_$UsbYubiKeyNodeCopyWithImpl<UsbYubiKeyNode>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$_DeviceNodeToJson(this);
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(
|
||||
List<String> path, String name, int pid, DeviceInfo info)
|
||||
usbYubiKey,
|
||||
required TResult Function(List<String> path, String name) nfcReader,
|
||||
}) {
|
||||
return usbYubiKey(path, name, pid, info);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult Function(List<String> path, String name, int pid, DeviceInfo info)?
|
||||
usbYubiKey,
|
||||
TResult Function(List<String> path, String name)? nfcReader,
|
||||
}) {
|
||||
return usbYubiKey?.call(path, name, pid, info);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(List<String> path, String name, int pid, DeviceInfo info)?
|
||||
usbYubiKey,
|
||||
TResult Function(List<String> path, String name)? nfcReader,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (usbYubiKey != null) {
|
||||
return usbYubiKey(path, name, pid, info);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(UsbYubiKeyNode value) usbYubiKey,
|
||||
required TResult Function(NfcReaderNode value) nfcReader,
|
||||
}) {
|
||||
return usbYubiKey(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult Function(UsbYubiKeyNode value)? usbYubiKey,
|
||||
TResult Function(NfcReaderNode value)? nfcReader,
|
||||
}) {
|
||||
return usbYubiKey?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(UsbYubiKeyNode value)? usbYubiKey,
|
||||
TResult Function(NfcReaderNode value)? nfcReader,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (usbYubiKey != null) {
|
||||
return usbYubiKey(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _DeviceNode implements DeviceNode {
|
||||
factory _DeviceNode(List<String> path, int pid, Transport transport,
|
||||
String name, DeviceInfo info) = _$_DeviceNode;
|
||||
|
||||
factory _DeviceNode.fromJson(Map<String, dynamic> json) =
|
||||
_$_DeviceNode.fromJson;
|
||||
abstract class UsbYubiKeyNode implements DeviceNode {
|
||||
factory UsbYubiKeyNode(
|
||||
List<String> path, String name, int pid, DeviceInfo info) =
|
||||
_$UsbYubiKeyNode;
|
||||
|
||||
@override
|
||||
List<String> get path;
|
||||
@override
|
||||
int get pid;
|
||||
@override
|
||||
Transport get transport;
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
int get pid;
|
||||
DeviceInfo get info;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$DeviceNodeCopyWith<_DeviceNode> get copyWith =>
|
||||
$UsbYubiKeyNodeCopyWith<UsbYubiKeyNode> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $NfcReaderNodeCopyWith<$Res>
|
||||
implements $DeviceNodeCopyWith<$Res> {
|
||||
factory $NfcReaderNodeCopyWith(
|
||||
NfcReaderNode value, $Res Function(NfcReaderNode) then) =
|
||||
_$NfcReaderNodeCopyWithImpl<$Res>;
|
||||
@override
|
||||
$Res call({List<String> path, String name});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$NfcReaderNodeCopyWithImpl<$Res> extends _$DeviceNodeCopyWithImpl<$Res>
|
||||
implements $NfcReaderNodeCopyWith<$Res> {
|
||||
_$NfcReaderNodeCopyWithImpl(
|
||||
NfcReaderNode _value, $Res Function(NfcReaderNode) _then)
|
||||
: super(_value, (v) => _then(v as NfcReaderNode));
|
||||
|
||||
@override
|
||||
NfcReaderNode get _value => super._value as NfcReaderNode;
|
||||
|
||||
@override
|
||||
$Res call({
|
||||
Object? path = freezed,
|
||||
Object? name = freezed,
|
||||
}) {
|
||||
return _then(NfcReaderNode(
|
||||
path == freezed
|
||||
? _value.path
|
||||
: path // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
name == freezed
|
||||
? _value.name
|
||||
: name // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$NfcReaderNode implements NfcReaderNode {
|
||||
_$NfcReaderNode(this.path, this.name);
|
||||
|
||||
@override
|
||||
final List<String> path;
|
||||
@override
|
||||
final String name;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DeviceNode.nfcReader(path: $path, name: $name)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is NfcReaderNode &&
|
||||
const DeepCollectionEquality().equals(other.path, path) &&
|
||||
const DeepCollectionEquality().equals(other.name, name));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(path),
|
||||
const DeepCollectionEquality().hash(name));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
$NfcReaderNodeCopyWith<NfcReaderNode> get copyWith =>
|
||||
_$NfcReaderNodeCopyWithImpl<NfcReaderNode>(this, _$identity);
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult when<TResult extends Object?>({
|
||||
required TResult Function(
|
||||
List<String> path, String name, int pid, DeviceInfo info)
|
||||
usbYubiKey,
|
||||
required TResult Function(List<String> path, String name) nfcReader,
|
||||
}) {
|
||||
return nfcReader(path, name);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? whenOrNull<TResult extends Object?>({
|
||||
TResult Function(List<String> path, String name, int pid, DeviceInfo info)?
|
||||
usbYubiKey,
|
||||
TResult Function(List<String> path, String name)? nfcReader,
|
||||
}) {
|
||||
return nfcReader?.call(path, name);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeWhen<TResult extends Object?>({
|
||||
TResult Function(List<String> path, String name, int pid, DeviceInfo info)?
|
||||
usbYubiKey,
|
||||
TResult Function(List<String> path, String name)? nfcReader,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (nfcReader != null) {
|
||||
return nfcReader(path, name);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult map<TResult extends Object?>({
|
||||
required TResult Function(UsbYubiKeyNode value) usbYubiKey,
|
||||
required TResult Function(NfcReaderNode value) nfcReader,
|
||||
}) {
|
||||
return nfcReader(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult? mapOrNull<TResult extends Object?>({
|
||||
TResult Function(UsbYubiKeyNode value)? usbYubiKey,
|
||||
TResult Function(NfcReaderNode value)? nfcReader,
|
||||
}) {
|
||||
return nfcReader?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
@optionalTypeArgs
|
||||
TResult maybeMap<TResult extends Object?>({
|
||||
TResult Function(UsbYubiKeyNode value)? usbYubiKey,
|
||||
TResult Function(NfcReaderNode value)? nfcReader,
|
||||
required TResult orElse(),
|
||||
}) {
|
||||
if (nfcReader != null) {
|
||||
return nfcReader(this);
|
||||
}
|
||||
return orElse();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class NfcReaderNode implements DeviceNode {
|
||||
factory NfcReaderNode(List<String> path, String name) = _$NfcReaderNode;
|
||||
|
||||
@override
|
||||
List<String> get path;
|
||||
@override
|
||||
String get name;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
$NfcReaderNodeCopyWith<NfcReaderNode> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
@ -383,13 +781,17 @@ class _$_MenuAction implements _MenuAction {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _MenuAction &&
|
||||
(identical(other.text, text) || other.text == text) &&
|
||||
(identical(other.icon, icon) || other.icon == icon) &&
|
||||
const DeepCollectionEquality().equals(other.text, text) &&
|
||||
const DeepCollectionEquality().equals(other.icon, icon) &&
|
||||
(identical(other.action, action) || other.action == action));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, text, icon, action);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(text),
|
||||
const DeepCollectionEquality().hash(icon),
|
||||
action);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -548,13 +950,17 @@ class _$_WindowState implements _WindowState {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _WindowState &&
|
||||
(identical(other.focused, focused) || other.focused == focused) &&
|
||||
(identical(other.visible, visible) || other.visible == visible) &&
|
||||
(identical(other.active, active) || other.active == active));
|
||||
const DeepCollectionEquality().equals(other.focused, focused) &&
|
||||
const DeepCollectionEquality().equals(other.visible, visible) &&
|
||||
const DeepCollectionEquality().equals(other.active, active));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, focused, visible, active);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(focused),
|
||||
const DeepCollectionEquality().hash(visible),
|
||||
const DeepCollectionEquality().hash(active));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
|
@ -1,30 +0,0 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'models.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$_DeviceNode _$$_DeviceNodeFromJson(Map<String, dynamic> json) =>
|
||||
_$_DeviceNode(
|
||||
(json['path'] as List<dynamic>).map((e) => e as String).toList(),
|
||||
json['pid'] as int,
|
||||
$enumDecode(_$TransportEnumMap, json['transport']),
|
||||
json['name'] as String,
|
||||
DeviceInfo.fromJson(json['info'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$_DeviceNodeToJson(_$_DeviceNode instance) =>
|
||||
<String, dynamic>{
|
||||
'path': instance.path,
|
||||
'pid': instance.pid,
|
||||
'transport': _$TransportEnumMap[instance.transport],
|
||||
'name': instance.name,
|
||||
'info': instance.info,
|
||||
};
|
||||
|
||||
const _$TransportEnumMap = {
|
||||
Transport.usb: 'usb',
|
||||
Transport.nfc: 'nfc',
|
||||
};
|
@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:yubico_authenticator/management/models.dart';
|
||||
|
||||
import '../core/models.dart';
|
||||
import '../core/state.dart';
|
||||
@ -13,6 +14,12 @@ import '../core/rpc.dart';
|
||||
import '../oath/menu_actions.dart';
|
||||
import 'models.dart';
|
||||
|
||||
const _usbPollDelay = Duration(milliseconds: 500);
|
||||
|
||||
const _nfcPollDelay = Duration(milliseconds: 2500);
|
||||
const _nfcAttachPollDelay = Duration(seconds: 1);
|
||||
const _nfcDetachPollDelay = Duration(seconds: 5);
|
||||
|
||||
final log = Logger('app.state');
|
||||
|
||||
final windowStateProvider =
|
||||
@ -118,24 +125,24 @@ class SearchNotifier extends StateNotifier<String> {
|
||||
}
|
||||
}
|
||||
|
||||
final attachedDevicesProvider =
|
||||
StateNotifierProvider<AttachedDeviceNotifier, List<DeviceNode>>((ref) {
|
||||
final notifier = AttachedDeviceNotifier(ref.watch(rpcProvider));
|
||||
final _usbDevicesProvider =
|
||||
StateNotifierProvider<UsbDeviceNotifier, List<UsbYubiKeyNode>>((ref) {
|
||||
final notifier = UsbDeviceNotifier(ref.watch(rpcProvider));
|
||||
ref.listen<WindowState>(windowStateProvider, (_, windowState) {
|
||||
notifier._notifyWindowState(windowState);
|
||||
}, fireImmediately: true);
|
||||
return notifier;
|
||||
});
|
||||
|
||||
class AttachedDeviceNotifier extends StateNotifier<List<DeviceNode>> {
|
||||
class UsbDeviceNotifier extends StateNotifier<List<UsbYubiKeyNode>> {
|
||||
final RpcSession _rpc;
|
||||
Timer? _pollTimer;
|
||||
int _usbState = -1;
|
||||
AttachedDeviceNotifier(this._rpc) : super([]);
|
||||
UsbDeviceNotifier(this._rpc) : super([]);
|
||||
|
||||
void _notifyWindowState(WindowState windowState) {
|
||||
if (windowState.active) {
|
||||
_pollUsb();
|
||||
_pollDevices();
|
||||
} else {
|
||||
_pollTimer?.cancel();
|
||||
// Release any held device
|
||||
@ -149,37 +156,110 @@ class AttachedDeviceNotifier extends StateNotifier<List<DeviceNode>> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _pollUsb() async {
|
||||
void _pollDevices() async {
|
||||
_pollTimer?.cancel();
|
||||
|
||||
try {
|
||||
var scan = await _rpc.command('scan', ['usb']);
|
||||
|
||||
if (_usbState != scan['state'] || state.length != scan['pids'].length) {
|
||||
var usbResult = await _rpc.command('get', ['usb']);
|
||||
log.info('USB state change', jsonEncode(usbResult));
|
||||
_usbState = usbResult['data']['state'];
|
||||
List<UsbYubiKeyNode> usbDevices = [];
|
||||
|
||||
List<DeviceNode> devices = [];
|
||||
for (String id in (usbResult['children'] as Map).keys) {
|
||||
var path = ['usb', id];
|
||||
var deviceResult = await _rpc.command('get', path);
|
||||
devices.add(
|
||||
DeviceNode.fromJson({'path': path, ...deviceResult['data']}));
|
||||
var deviceData = deviceResult['data'];
|
||||
usbDevices.add(DeviceNode.usbYubiKey(
|
||||
path,
|
||||
deviceData['name'],
|
||||
deviceData['pid'],
|
||||
DeviceInfo.fromJson(deviceData['info']),
|
||||
) as UsbYubiKeyNode);
|
||||
}
|
||||
_usbState = usbResult['data']['state'];
|
||||
|
||||
log.info('USB state updated');
|
||||
if (mounted) {
|
||||
state = devices;
|
||||
state = usbDevices;
|
||||
}
|
||||
}
|
||||
} on RpcError catch (e) {
|
||||
log.severe('Error polling USB', jsonEncode(e));
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
_pollTimer = Timer(const Duration(milliseconds: 500), _pollUsb);
|
||||
_pollTimer = Timer(_usbPollDelay, _pollDevices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final _nfcDevicesProvider =
|
||||
StateNotifierProvider<NfcDeviceNotifier, List<NfcReaderNode>>((ref) {
|
||||
final notifier = NfcDeviceNotifier(ref.watch(rpcProvider));
|
||||
ref.listen<WindowState>(windowStateProvider, (_, windowState) {
|
||||
notifier._notifyWindowState(windowState);
|
||||
}, fireImmediately: true);
|
||||
return notifier;
|
||||
});
|
||||
|
||||
class NfcDeviceNotifier extends StateNotifier<List<NfcReaderNode>> {
|
||||
final RpcSession _rpc;
|
||||
Timer? _pollTimer;
|
||||
String _nfcState = '';
|
||||
NfcDeviceNotifier(this._rpc) : super([]);
|
||||
|
||||
void _notifyWindowState(WindowState windowState) {
|
||||
if (windowState.active) {
|
||||
_pollReaders();
|
||||
} else {
|
||||
_pollTimer?.cancel();
|
||||
// Release any held device
|
||||
_rpc.command('get', ['nfc']);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pollTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _pollReaders() async {
|
||||
_pollTimer?.cancel();
|
||||
|
||||
try {
|
||||
var children = await _rpc.command('scan', ['nfc']);
|
||||
var newState = children.keys.join(':');
|
||||
|
||||
if (mounted && newState != _nfcState) {
|
||||
log.info('NFC state change', jsonEncode(children));
|
||||
_nfcState = newState;
|
||||
state = children.entries
|
||||
.map((e) =>
|
||||
DeviceNode.nfcReader(['nfc', e.key], e.value['name'] as String)
|
||||
as NfcReaderNode)
|
||||
.toList();
|
||||
}
|
||||
} on RpcError catch (e) {
|
||||
log.severe('Error polling NFC', jsonEncode(e));
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
_pollTimer = Timer(_nfcPollDelay, _pollReaders);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final attachedDevicesProvider = Provider<List<DeviceNode>>((ref) {
|
||||
final usbDevices = ref.watch(_usbDevicesProvider).toList();
|
||||
final nfcDevices = ref.watch(_nfcDevicesProvider).toList();
|
||||
usbDevices.sort((a, b) => a.name.compareTo(b.name));
|
||||
nfcDevices.sort((a, b) => a.name.compareTo(b.name));
|
||||
return [...usbDevices, ...nfcDevices];
|
||||
});
|
||||
|
||||
final currentDeviceProvider =
|
||||
StateNotifierProvider<CurrentDeviceNotifier, DeviceNode?>((ref) {
|
||||
final provider = CurrentDeviceNotifier(ref.watch(prefProvider));
|
||||
@ -188,42 +268,112 @@ final currentDeviceProvider =
|
||||
});
|
||||
|
||||
class CurrentDeviceNotifier extends StateNotifier<DeviceNode?> {
|
||||
static const String _lastDeviceSerial = 'APP_STATE_LAST_SERIAL';
|
||||
static const String _lastDevice = 'APP_STATE_LAST_DEVICE';
|
||||
final SharedPreferences _prefs;
|
||||
CurrentDeviceNotifier(this._prefs) : super(null);
|
||||
|
||||
_updateAttachedDevices(List<DeviceNode>? previous, List<DeviceNode> devices) {
|
||||
if (devices.isEmpty) {
|
||||
state = null;
|
||||
} else if (!devices.contains(state)) {
|
||||
// Prefer last selected device
|
||||
final serial = _prefs.getInt(_lastDeviceSerial) ?? -1;
|
||||
state = devices.firstWhere(
|
||||
(element) => element.info.serial == serial,
|
||||
orElse: () => devices.first,
|
||||
);
|
||||
if (!devices.contains(state)) {
|
||||
final lastDevice = _prefs.getString(_lastDevice) ?? '';
|
||||
try {
|
||||
state = devices.firstWhere(
|
||||
(dev) => dev.when(
|
||||
usbYubiKey: (path, name, pid, info) =>
|
||||
lastDevice == 'serial:${info.serial}',
|
||||
nfcReader: (path, name) => lastDevice == 'name:$name',
|
||||
),
|
||||
orElse: () => devices.whereType<UsbYubiKeyNode>().first);
|
||||
} on StateError {
|
||||
state = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentDevice(DeviceNode device) {
|
||||
state = device;
|
||||
final serial = device.info.serial;
|
||||
if (serial != null) {
|
||||
_prefs.setInt(_lastDeviceSerial, serial);
|
||||
}
|
||||
device.when(
|
||||
usbYubiKey: (path, name, pid, info) {
|
||||
final serial = info.serial;
|
||||
if (serial != null) {
|
||||
_prefs.setString(_lastDevice, 'serial:$serial');
|
||||
}
|
||||
},
|
||||
nfcReader: (path, name) {
|
||||
_prefs.setString(_lastDevice, 'name:$name');
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final sortedDevicesProvider = Provider<List<DeviceNode>>((ref) {
|
||||
final devices = ref.watch(attachedDevicesProvider).toList();
|
||||
devices.sort((a, b) => a.name.compareTo(b.name));
|
||||
final device = ref.watch(currentDeviceProvider);
|
||||
if (device != null) {
|
||||
return [device, ...devices.where((e) => e != device)];
|
||||
final currentDeviceDataProvider =
|
||||
StateNotifierProvider<CurrentDeviceDataNotifier, YubiKeyData?>((ref) {
|
||||
final notifier = CurrentDeviceDataNotifier(
|
||||
ref.watch(rpcProvider),
|
||||
ref.watch(currentDeviceProvider),
|
||||
);
|
||||
if (notifier._deviceNode is NfcReaderNode) {
|
||||
// If this is an NFC reader, listen on WindowState.
|
||||
ref.listen<WindowState>(windowStateProvider, (_, windowState) {
|
||||
notifier._notifyWindowState(windowState);
|
||||
}, fireImmediately: true);
|
||||
}
|
||||
return devices;
|
||||
return notifier;
|
||||
});
|
||||
|
||||
class CurrentDeviceDataNotifier extends StateNotifier<YubiKeyData?> {
|
||||
final RpcSession _rpc;
|
||||
final DeviceNode? _deviceNode;
|
||||
Timer? _pollTimer;
|
||||
|
||||
CurrentDeviceDataNotifier(this._rpc, this._deviceNode) : super(null) {
|
||||
final dev = _deviceNode;
|
||||
if (dev is UsbYubiKeyNode) {
|
||||
state = YubiKeyData(dev, dev.name, dev.info);
|
||||
}
|
||||
}
|
||||
|
||||
void _notifyWindowState(WindowState windowState) {
|
||||
if (windowState.active) {
|
||||
_pollReader();
|
||||
} else {
|
||||
_pollTimer?.cancel();
|
||||
// TODO: Should we clear the key here?
|
||||
/*if (mounted) {
|
||||
state = null;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pollTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _pollReader() async {
|
||||
_pollTimer?.cancel();
|
||||
final node = _deviceNode!;
|
||||
try {
|
||||
var result = await _rpc.command('get', node.path);
|
||||
if (mounted) {
|
||||
if (result['data']['present']) {
|
||||
state = YubiKeyData(node, result['data']['name'],
|
||||
DeviceInfo.fromJson(result['data']['info']));
|
||||
} else {
|
||||
state = null;
|
||||
}
|
||||
}
|
||||
} on RpcError catch (e) {
|
||||
log.severe('Error polling NFC', jsonEncode(e));
|
||||
}
|
||||
if (mounted) {
|
||||
_pollTimer = Timer(
|
||||
state == null ? _nfcAttachPollDelay : _nfcDetachPollDelay,
|
||||
_pollReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final subPageProvider = StateNotifierProvider<SubPageNotifier, SubPage>(
|
||||
(ref) => SubPageNotifier(SubPage.authenticator));
|
||||
|
||||
|
@ -1,20 +1,29 @@
|
||||
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 device;
|
||||
final DeviceNode node;
|
||||
final String name;
|
||||
final DeviceInfo? info;
|
||||
final bool selected;
|
||||
|
||||
const DeviceAvatar(this.device, {this.selected = false, Key? key})
|
||||
const DeviceAvatar(this.node, this.name, this.info,
|
||||
{this.selected = false, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CircleAvatar(
|
||||
child: CircleAvatar(
|
||||
child: getProductImage(device),
|
||||
child:
|
||||
info != null ? getProductImage(info!, name) : const Icon(Icons.nfc),
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
),
|
||||
radius: 22,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../management/models.dart';
|
||||
import '../models.dart';
|
||||
|
||||
const _imagesForName = {
|
||||
'YubiKey 4': 'yk4series',
|
||||
@ -30,11 +29,11 @@ const _imagesForFormFactorNfc = {
|
||||
FormFactor.usbCKeychain: 'yk5cnfc',
|
||||
};
|
||||
|
||||
Image getProductImage(DeviceNode device) {
|
||||
var image = _imagesForName[device.name];
|
||||
image ??= device.info.supportedCapabilities.containsKey(Transport.nfc)
|
||||
? _imagesForFormFactorNfc[device.info.formFactor]
|
||||
: _imagesForFormFactor[device.info.formFactor];
|
||||
Image getProductImage(DeviceInfo info, String name) {
|
||||
var image = _imagesForName[name];
|
||||
image ??= info.supportedCapabilities.containsKey(Transport.nfc)
|
||||
? _imagesForFormFactorNfc[info.formFactor]
|
||||
: _imagesForFormFactor[info.formFactor];
|
||||
image ??= 'yk5series';
|
||||
|
||||
return Image.asset('assets/product-images/$image.png');
|
||||
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import '../models.dart';
|
||||
|
||||
class DeviceInfoScreen extends StatelessWidget {
|
||||
final DeviceNode device;
|
||||
final YubiKeyData device;
|
||||
const DeviceInfoScreen(this.device, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -1,32 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:yubico_authenticator/management/models.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
import 'device_avatar.dart';
|
||||
|
||||
Function _listEquals = const ListEquality().equals;
|
||||
|
||||
class MainActionsDialog extends ConsumerWidget {
|
||||
const MainActionsDialog({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final devices = ref.watch(sortedDevicesProvider);
|
||||
final device = ref.watch(currentDeviceProvider);
|
||||
final devices = ref.watch(attachedDevicesProvider).toList();
|
||||
final currentNode = ref.watch(currentDeviceProvider);
|
||||
final data = ref.watch(currentDeviceDataProvider);
|
||||
final actions = ref.watch(menuActionsProvider)(context);
|
||||
|
||||
if (currentNode != null) {
|
||||
devices.removeWhere((e) => _listEquals(e.path, currentNode.path));
|
||||
}
|
||||
|
||||
return SimpleDialog(
|
||||
children: [
|
||||
...devices.map((e) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: DeviceRow(
|
||||
e,
|
||||
selected: e == device,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
ref.read(currentDeviceProvider.notifier).setCurrentDevice(e);
|
||||
},
|
||||
),
|
||||
)),
|
||||
if (currentNode != null)
|
||||
CurrentDeviceRow(
|
||||
currentNode,
|
||||
data?.name,
|
||||
info: data?.info,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
...devices.map(
|
||||
(e) => DeviceRow(
|
||||
e,
|
||||
e.name,
|
||||
info: e.when(
|
||||
usbYubiKey: (path, name, pid, info) => info,
|
||||
nfcReader: (path, name) => null,
|
||||
),
|
||||
selected: false,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
ref.read(currentDeviceProvider.notifier).setCurrentDevice(e);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (actions.isNotEmpty) const Divider(),
|
||||
...actions.map((a) => ListTile(
|
||||
dense: true,
|
||||
@ -42,43 +64,74 @@ class MainActionsDialog extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceRow extends StatelessWidget {
|
||||
final DeviceNode device;
|
||||
final bool selected;
|
||||
final Function() onPressed;
|
||||
const DeviceRow(
|
||||
this.device, {
|
||||
this.selected = false,
|
||||
required this.onPressed,
|
||||
class CurrentDeviceRow extends StatelessWidget {
|
||||
final DeviceNode node;
|
||||
final String? name;
|
||||
final DeviceInfo? info;
|
||||
final Function() onTap;
|
||||
|
||||
const CurrentDeviceRow(
|
||||
this.node,
|
||||
this.name, {
|
||||
required this.info,
|
||||
required this.onTap,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton(
|
||||
onPressed: onPressed,
|
||||
child: Row(
|
||||
children: [
|
||||
DeviceAvatar(
|
||||
device,
|
||||
selected: selected,
|
||||
),
|
||||
const SizedBox(width: 16.0),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
device.name,
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
Text(
|
||||
'S/N: ${device.info.serial} F/W: ${device.info.version}',
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
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}';
|
||||
return ListTile(
|
||||
leading: DeviceAvatar(
|
||||
node,
|
||||
name ?? '',
|
||||
info,
|
||||
selected: true,
|
||||
),
|
||||
title: Text(name ?? 'No YubiKey present'),
|
||||
isThreeLine: subtitle.contains('\n'),
|
||||
subtitle: Text(subtitle),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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, {
|
||||
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),
|
||||
subtitle: Text(
|
||||
info == null
|
||||
? (selected ? 'No YubiKey present' : 'Select to scan')
|
||||
: 'S/N: ${info!.serial} F/W: ${info!.version}',
|
||||
),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import '../../oath/views/oath_screen.dart';
|
||||
class MainPage extends ConsumerWidget {
|
||||
const MainPage({Key? key}) : super(key: key);
|
||||
|
||||
Widget _buildSubPage(SubPage subPage, DeviceNode? device) {
|
||||
Widget _buildSubPage(SubPage subPage, YubiKeyData? device) {
|
||||
if (device == null) {
|
||||
return const NoDeviceScreen();
|
||||
}
|
||||
@ -28,7 +28,7 @@ class MainPage extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final currentDevice = ref.watch(currentDeviceProvider);
|
||||
final deviceData = ref.watch(currentDeviceDataProvider);
|
||||
final subPage = ref.watch(subPageProvider);
|
||||
|
||||
return Scaffold(
|
||||
@ -59,7 +59,7 @@ class MainPage extends ConsumerWidget {
|
||||
InkWell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: currentDevice == null
|
||||
child: deviceData == null
|
||||
? SizedBox.square(
|
||||
dimension: 44,
|
||||
child: Icon(
|
||||
@ -67,7 +67,12 @@ class MainPage extends ConsumerWidget {
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
),
|
||||
)
|
||||
: DeviceAvatar(currentDevice, selected: true),
|
||||
: DeviceAvatar(
|
||||
deviceData.node,
|
||||
deviceData.name,
|
||||
deviceData.info,
|
||||
selected: true,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
@ -79,7 +84,7 @@ class MainPage extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
drawer: const MainPageDrawer(),
|
||||
body: _buildSubPage(subPage, currentDevice),
|
||||
body: _buildSubPage(subPage, deviceData),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||
|
||||
part of 'models.dart';
|
||||
@ -134,13 +135,17 @@ class _$_Version extends _Version {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _Version &&
|
||||
(identical(other.major, major) || other.major == major) &&
|
||||
(identical(other.minor, minor) || other.minor == minor) &&
|
||||
(identical(other.patch, patch) || other.patch == patch));
|
||||
const DeepCollectionEquality().equals(other.major, major) &&
|
||||
const DeepCollectionEquality().equals(other.minor, minor) &&
|
||||
const DeepCollectionEquality().equals(other.patch, patch));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, major, minor, patch);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(major),
|
||||
const DeepCollectionEquality().hash(minor),
|
||||
const DeepCollectionEquality().hash(patch));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -516,13 +521,15 @@ class _$Signal implements Signal {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is Signal &&
|
||||
(identical(other.status, status) || other.status == status) &&
|
||||
const DeepCollectionEquality().equals(other.status, status) &&
|
||||
const DeepCollectionEquality().equals(other.body, body));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, status, const DeepCollectionEquality().hash(body));
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(status),
|
||||
const DeepCollectionEquality().hash(body));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -689,14 +696,17 @@ class _$RpcError implements RpcError {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is RpcError &&
|
||||
(identical(other.status, status) || other.status == status) &&
|
||||
(identical(other.message, message) || other.message == message) &&
|
||||
const DeepCollectionEquality().equals(other.status, status) &&
|
||||
const DeepCollectionEquality().equals(other.message, message) &&
|
||||
const DeepCollectionEquality().equals(other.body, body));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, status, message, const DeepCollectionEquality().hash(body));
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(status),
|
||||
const DeepCollectionEquality().hash(message),
|
||||
const DeepCollectionEquality().hash(body));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -896,11 +906,12 @@ class _$_RpcState implements _RpcState {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _RpcState &&
|
||||
(identical(other.version, version) || other.version == version));
|
||||
const DeepCollectionEquality().equals(other.version, version));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, version);
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(version));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
|
@ -1,5 +1,6 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||
|
||||
part of 'models.dart';
|
||||
@ -183,22 +184,21 @@ class _$_DeviceConfig implements _DeviceConfig {
|
||||
other is _DeviceConfig &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.enabledCapabilities, enabledCapabilities) &&
|
||||
(identical(other.autoEjectTimeout, autoEjectTimeout) ||
|
||||
other.autoEjectTimeout == autoEjectTimeout) &&
|
||||
(identical(
|
||||
other.challengeResponseTimeout, challengeResponseTimeout) ||
|
||||
other.challengeResponseTimeout == challengeResponseTimeout) &&
|
||||
(identical(other.deviceFlags, deviceFlags) ||
|
||||
other.deviceFlags == deviceFlags));
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.autoEjectTimeout, autoEjectTimeout) &&
|
||||
const DeepCollectionEquality().equals(
|
||||
other.challengeResponseTimeout, challengeResponseTimeout) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.deviceFlags, deviceFlags));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(enabledCapabilities),
|
||||
autoEjectTimeout,
|
||||
challengeResponseTimeout,
|
||||
deviceFlags);
|
||||
const DeepCollectionEquality().hash(autoEjectTimeout),
|
||||
const DeepCollectionEquality().hash(challengeResponseTimeout),
|
||||
const DeepCollectionEquality().hash(deviceFlags));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -495,30 +495,29 @@ class _$_DeviceInfo implements _DeviceInfo {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _DeviceInfo &&
|
||||
(identical(other.config, config) || other.config == config) &&
|
||||
(identical(other.serial, serial) || other.serial == serial) &&
|
||||
(identical(other.version, version) || other.version == version) &&
|
||||
(identical(other.formFactor, formFactor) ||
|
||||
other.formFactor == formFactor) &&
|
||||
const DeepCollectionEquality().equals(other.config, config) &&
|
||||
const DeepCollectionEquality().equals(other.serial, serial) &&
|
||||
const DeepCollectionEquality().equals(other.version, version) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.formFactor, formFactor) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.supportedCapabilities, supportedCapabilities) &&
|
||||
(identical(other.isLocked, isLocked) ||
|
||||
other.isLocked == isLocked) &&
|
||||
(identical(other.isFips, isFips) || other.isFips == isFips) &&
|
||||
(identical(other.isSky, isSky) || other.isSky == isSky));
|
||||
const DeepCollectionEquality().equals(other.isLocked, isLocked) &&
|
||||
const DeepCollectionEquality().equals(other.isFips, isFips) &&
|
||||
const DeepCollectionEquality().equals(other.isSky, isSky));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
config,
|
||||
serial,
|
||||
version,
|
||||
formFactor,
|
||||
const DeepCollectionEquality().hash(config),
|
||||
const DeepCollectionEquality().hash(serial),
|
||||
const DeepCollectionEquality().hash(version),
|
||||
const DeepCollectionEquality().hash(formFactor),
|
||||
const DeepCollectionEquality().hash(supportedCapabilities),
|
||||
isLocked,
|
||||
isFips,
|
||||
isSky);
|
||||
const DeepCollectionEquality().hash(isLocked),
|
||||
const DeepCollectionEquality().hash(isFips),
|
||||
const DeepCollectionEquality().hash(isSky));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
|
@ -1,5 +1,6 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
||||
|
||||
part of 'models.dart';
|
||||
@ -230,21 +231,26 @@ class _$_OathCredential implements _OathCredential {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _OathCredential &&
|
||||
(identical(other.deviceId, deviceId) ||
|
||||
other.deviceId == deviceId) &&
|
||||
(identical(other.id, id) || other.id == id) &&
|
||||
(identical(other.issuer, issuer) || other.issuer == issuer) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.oathType, oathType) ||
|
||||
other.oathType == oathType) &&
|
||||
(identical(other.period, period) || other.period == period) &&
|
||||
(identical(other.touchRequired, touchRequired) ||
|
||||
other.touchRequired == touchRequired));
|
||||
const DeepCollectionEquality().equals(other.deviceId, deviceId) &&
|
||||
const DeepCollectionEquality().equals(other.id, id) &&
|
||||
const DeepCollectionEquality().equals(other.issuer, issuer) &&
|
||||
const DeepCollectionEquality().equals(other.name, name) &&
|
||||
const DeepCollectionEquality().equals(other.oathType, oathType) &&
|
||||
const DeepCollectionEquality().equals(other.period, period) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.touchRequired, touchRequired));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, deviceId, id, issuer, name, oathType, period, touchRequired);
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(deviceId),
|
||||
const DeepCollectionEquality().hash(id),
|
||||
const DeepCollectionEquality().hash(issuer),
|
||||
const DeepCollectionEquality().hash(name),
|
||||
const DeepCollectionEquality().hash(oathType),
|
||||
const DeepCollectionEquality().hash(period),
|
||||
const DeepCollectionEquality().hash(touchRequired));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -429,14 +435,17 @@ class _$_OathCode implements _OathCode {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _OathCode &&
|
||||
(identical(other.value, value) || other.value == value) &&
|
||||
(identical(other.validFrom, validFrom) ||
|
||||
other.validFrom == validFrom) &&
|
||||
(identical(other.validTo, validTo) || other.validTo == validTo));
|
||||
const DeepCollectionEquality().equals(other.value, value) &&
|
||||
const DeepCollectionEquality().equals(other.validFrom, validFrom) &&
|
||||
const DeepCollectionEquality().equals(other.validTo, validTo));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, value, validFrom, validTo);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(value),
|
||||
const DeepCollectionEquality().hash(validFrom),
|
||||
const DeepCollectionEquality().hash(validTo));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -605,13 +614,16 @@ class _$_OathPair implements _OathPair {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _OathPair &&
|
||||
(identical(other.credential, credential) ||
|
||||
other.credential == credential) &&
|
||||
(identical(other.code, code) || other.code == code));
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.credential, credential) &&
|
||||
const DeepCollectionEquality().equals(other.code, code));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, credential, code);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(credential),
|
||||
const DeepCollectionEquality().hash(code));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -772,14 +784,17 @@ class _$_OathState implements _OathState {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _OathState &&
|
||||
(identical(other.deviceId, deviceId) ||
|
||||
other.deviceId == deviceId) &&
|
||||
(identical(other.hasKey, hasKey) || other.hasKey == hasKey) &&
|
||||
(identical(other.locked, locked) || other.locked == locked));
|
||||
const DeepCollectionEquality().equals(other.deviceId, deviceId) &&
|
||||
const DeepCollectionEquality().equals(other.hasKey, hasKey) &&
|
||||
const DeepCollectionEquality().equals(other.locked, locked));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, deviceId, hasKey, locked);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(deviceId),
|
||||
const DeepCollectionEquality().hash(hasKey),
|
||||
const DeepCollectionEquality().hash(locked));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -1037,19 +1052,19 @@ class _$_CredentialData extends _CredentialData {
|
||||
final String name;
|
||||
@override
|
||||
final String secret;
|
||||
@JsonKey(defaultValue: OathType.totp)
|
||||
@JsonKey()
|
||||
@override
|
||||
final OathType oathType;
|
||||
@JsonKey(defaultValue: HashAlgorithm.sha1)
|
||||
@JsonKey()
|
||||
@override
|
||||
final HashAlgorithm hashAlgorithm;
|
||||
@JsonKey(defaultValue: 6)
|
||||
@JsonKey()
|
||||
@override
|
||||
final int digits;
|
||||
@JsonKey(defaultValue: 30)
|
||||
@JsonKey()
|
||||
@override
|
||||
final int period;
|
||||
@JsonKey(defaultValue: 0)
|
||||
@JsonKey()
|
||||
@override
|
||||
final int counter;
|
||||
|
||||
@ -1063,21 +1078,28 @@ class _$_CredentialData extends _CredentialData {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _CredentialData &&
|
||||
(identical(other.issuer, issuer) || other.issuer == issuer) &&
|
||||
(identical(other.name, name) || other.name == name) &&
|
||||
(identical(other.secret, secret) || other.secret == secret) &&
|
||||
(identical(other.oathType, oathType) ||
|
||||
other.oathType == oathType) &&
|
||||
(identical(other.hashAlgorithm, hashAlgorithm) ||
|
||||
other.hashAlgorithm == hashAlgorithm) &&
|
||||
(identical(other.digits, digits) || other.digits == digits) &&
|
||||
(identical(other.period, period) || other.period == period) &&
|
||||
(identical(other.counter, counter) || other.counter == counter));
|
||||
const DeepCollectionEquality().equals(other.issuer, issuer) &&
|
||||
const DeepCollectionEquality().equals(other.name, name) &&
|
||||
const DeepCollectionEquality().equals(other.secret, secret) &&
|
||||
const DeepCollectionEquality().equals(other.oathType, oathType) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.hashAlgorithm, hashAlgorithm) &&
|
||||
const DeepCollectionEquality().equals(other.digits, digits) &&
|
||||
const DeepCollectionEquality().equals(other.period, period) &&
|
||||
const DeepCollectionEquality().equals(other.counter, counter));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, issuer, name, secret, oathType,
|
||||
hashAlgorithm, digits, period, counter);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
const DeepCollectionEquality().hash(issuer),
|
||||
const DeepCollectionEquality().hash(name),
|
||||
const DeepCollectionEquality().hash(secret),
|
||||
const DeepCollectionEquality().hash(oathType),
|
||||
const DeepCollectionEquality().hash(hashAlgorithm),
|
||||
const DeepCollectionEquality().hash(digits),
|
||||
const DeepCollectionEquality().hash(period),
|
||||
const DeepCollectionEquality().hash(counter));
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
|
@ -5,10 +5,11 @@ import '../models.dart';
|
||||
import 'account_view.dart';
|
||||
|
||||
class AccountList extends StatelessWidget {
|
||||
final DeviceNode device;
|
||||
final YubiKeyData deviceData;
|
||||
final List<OathPair> credentials;
|
||||
final List<String> favorites;
|
||||
const AccountList(this.device, this.credentials, this.favorites, {Key? key})
|
||||
const AccountList(this.deviceData, this.credentials, this.favorites,
|
||||
{Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -34,7 +35,7 @@ class AccountList extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
...favCreds.map(
|
||||
(entry) => AccountView(device, entry.credential, entry.code),
|
||||
(entry) => AccountView(deviceData, entry.credential, entry.code),
|
||||
),
|
||||
if (creds.isNotEmpty)
|
||||
ListTile(
|
||||
@ -44,7 +45,7 @@ class AccountList extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
...creds.map(
|
||||
(entry) => AccountView(device, entry.credential, entry.code),
|
||||
(entry) => AccountView(deviceData, entry.credential, entry.code),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -35,10 +35,10 @@ class _ExpireNotifier extends StateNotifier<bool> {
|
||||
}
|
||||
|
||||
class AccountView extends ConsumerWidget {
|
||||
final DeviceNode device;
|
||||
final YubiKeyData deviceData;
|
||||
final OathCredential credential;
|
||||
final OathCode? code;
|
||||
const AccountView(this.device, this.credential, this.code, {Key? key})
|
||||
const AccountView(this.deviceData, this.credential, this.code, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
String formatCode() {
|
||||
@ -73,7 +73,8 @@ class AccountView extends ConsumerWidget {
|
||||
ref.read(favoritesProvider.notifier).toggleFavorite(credential.id);
|
||||
},
|
||||
),
|
||||
if (device.info.version.major >= 5 && device.info.version.minor >= 3)
|
||||
if (deviceData.info.version.major >= 5 &&
|
||||
deviceData.info.version.minor >= 3)
|
||||
PopupMenuItem(
|
||||
child: const ListTile(
|
||||
leading: Icon(Icons.edit),
|
||||
@ -117,7 +118,7 @@ class AccountView extends ConsumerWidget {
|
||||
}
|
||||
try {
|
||||
await ref
|
||||
.read(credentialListProvider(device.path).notifier)
|
||||
.read(credentialListProvider(deviceData.node.path).notifier)
|
||||
.calculate(credential);
|
||||
} finally {
|
||||
close?.call();
|
||||
|
@ -6,12 +6,12 @@ import '../state.dart';
|
||||
import 'account_list.dart';
|
||||
|
||||
class OathScreen extends ConsumerWidget {
|
||||
final DeviceNode device;
|
||||
const OathScreen(this.device, {Key? key}) : super(key: key);
|
||||
final YubiKeyData deviceData;
|
||||
const OathScreen(this.deviceData, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(oathStateProvider(device.path));
|
||||
final state = ref.watch(oathStateProvider(deviceData.node.path));
|
||||
|
||||
if (state == null) {
|
||||
return Column(
|
||||
@ -35,7 +35,7 @@ class OathScreen extends ConsumerWidget {
|
||||
decoration: const InputDecoration(labelText: 'Password'),
|
||||
onSubmitted: (value) async {
|
||||
final result = await ref
|
||||
.read(oathStateProvider(device.path).notifier)
|
||||
.read(oathStateProvider(deviceData.node.path).notifier)
|
||||
.unlock(value);
|
||||
if (!result) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@ -51,7 +51,7 @@ class OathScreen extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final accounts = ref.watch(credentialListProvider(device.path));
|
||||
final accounts = ref.watch(credentialListProvider(deviceData.node.path));
|
||||
if (accounts == null) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@ -61,7 +61,7 @@ class OathScreen extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
return AccountList(
|
||||
device,
|
||||
deviceData,
|
||||
ref.watch(filteredCredentialsProvider(accounts)),
|
||||
ref.watch(favoritesProvider),
|
||||
);
|
||||
|
65
pubspec.lock
65
pubspec.lock
@ -7,14 +7,14 @@ packages:
|
||||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "31.0.0"
|
||||
version: "32.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.8.0"
|
||||
version: "3.0.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -42,7 +42,7 @@ packages:
|
||||
name: build
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.2.1"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -63,21 +63,21 @@ packages:
|
||||
name: build_resolvers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.0.6"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
version: "2.1.7"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.2.2"
|
||||
version: "7.2.3"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -161,7 +161,7 @@ packages:
|
||||
name: dart_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.2.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -208,7 +208,7 @@ packages:
|
||||
name: flutter_riverpod
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "1.0.3"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -225,14 +225,14 @@ packages:
|
||||
name: freezed
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.1"
|
||||
freezed_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: freezed_annotation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -295,7 +295,7 @@ packages:
|
||||
name: json_serializable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
version: "6.1.3"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -317,6 +317,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.11"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -351,35 +358,35 @@ packages:
|
||||
name: path_provider_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.5"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.3"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
version: "2.0.5"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.1.0"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.1.2"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -407,42 +414,42 @@ packages:
|
||||
name: pubspec_parse
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.2.0"
|
||||
riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: riverpod
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "1.0.3"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.9"
|
||||
version: "2.0.12"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.9"
|
||||
version: "2.0.10"
|
||||
shared_preferences_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_ios
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
version: "2.0.9"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.4"
|
||||
shared_preferences_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -463,14 +470,14 @@ packages:
|
||||
name: shared_preferences_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.0.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "2.0.4"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -496,14 +503,14 @@ packages:
|
||||
name: source_gen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.2.1"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.3.1"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -559,7 +566,7 @@ packages:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.3"
|
||||
version: "0.4.8"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -601,14 +608,14 @@ packages:
|
||||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.3.3"
|
||||
window_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: window_manager
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
version: "0.1.4"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -37,11 +37,11 @@ dependencies:
|
||||
|
||||
async: ^2.8.2
|
||||
logging: ^1.0.2
|
||||
shared_preferences: ^2.0.8
|
||||
shared_preferences: ^2.0.12
|
||||
flutter_riverpod: ^1.0.0
|
||||
json_annotation: ^4.3.0
|
||||
freezed_annotation: ^1.0.0
|
||||
window_manager: ^0.1.1
|
||||
window_manager: ^0.1.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit d161b31898e834eb085365a73f349a5b4683da17
|
||||
Subproject commit 765ccf63d9ccc972858d71730b9712514a9b0e0d
|
Loading…
Reference in New Issue
Block a user