Introduce helper flags to avoid extra polling

This commit is contained in:
Dain Nilsson 2024-07-09 15:14:29 +02:00
parent 903f96acc8
commit 19d33af7bc
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
6 changed files with 92 additions and 43 deletions

View File

@ -89,7 +89,7 @@ def process(
send(dict(kind="signal", status=status, body=body))
def success(response: RpcResponse):
send(dict(kind="success", body=response.body))
send(dict(kind="success", body=response.body, flags=response.side_effects))
event = Event()
cmd_queue: Queue = Queue(1)

View File

@ -224,9 +224,11 @@ final _desktopDeviceDataProvider =
ref.watch(rpcProvider).valueOrNull,
ref.watch(currentDeviceProvider),
);
ref.listen<WindowState>(windowStateProvider, (_, windowState) {
notifier._notifyWindowState(windowState);
}, fireImmediately: true);
if (notifier._deviceNode is NfcReaderNode) {
ref.listen<WindowState>(windowStateProvider, (_, windowState) {
notifier._notifyWindowState(windowState);
}, fireImmediately: true);
}
return notifier;
});
@ -240,6 +242,7 @@ class CurrentDeviceDataNotifier extends StateNotifier<AsyncValue<YubiKeyData>> {
final RpcSession? _rpc;
final DeviceNode? _deviceNode;
Timer? _pollTimer;
StreamSubscription? _flagSubscription;
CurrentDeviceDataNotifier(this._rpc, this._deviceNode)
: super(const AsyncValue.loading()) {
@ -252,15 +255,27 @@ class CurrentDeviceDataNotifier extends StateNotifier<AsyncValue<YubiKeyData>> {
state = AsyncValue.error('device-inaccessible', StackTrace.current);
}
}
_flagSubscription = _rpc?.flags.listen(
(flag) {
if (flag == 'device_info') {
_pollDevice();
}
},
);
}
void _pollDevice() {
switch (_deviceNode) {
case UsbYubiKeyNode _:
_refreshUsb();
case NfcReaderNode _:
_pollCard();
}
}
void _notifyWindowState(WindowState windowState) {
if (windowState.active) {
if (_deviceNode is UsbYubiKeyNode?) {
_pollUsb();
} else {
_pollCard();
}
_pollCard();
} else {
_pollTimer?.cancel();
// TODO: Should we clear the key here?
@ -272,12 +287,12 @@ class CurrentDeviceDataNotifier extends StateNotifier<AsyncValue<YubiKeyData>> {
@override
void dispose() {
_flagSubscription?.cancel();
_pollTimer?.cancel();
super.dispose();
}
void _pollUsb() async {
_pollTimer?.cancel();
void _refreshUsb() async {
final node = _deviceNode!;
var result = await _rpc?.command('get', node.path.segments);
if (mounted && result != null) {
@ -288,9 +303,6 @@ class CurrentDeviceDataNotifier extends StateNotifier<AsyncValue<YubiKeyData>> {
state = AsyncValue.data(newState);
}
}
if (mounted) {
_pollTimer = Timer(_usbPollDelay, _pollUsb);
}
}
void _pollCard() async {
@ -303,11 +315,13 @@ class CurrentDeviceDataNotifier extends StateNotifier<AsyncValue<YubiKeyData>> {
final oldState = state.valueOrNull;
final newState = YubiKeyData(node, result['data']['name'],
DeviceInfo.fromJson(result['data']['info']));
if (oldState != null && oldState != newState) {
// Ensure state is cleared
state = const AsyncValue.loading();
if (oldState != newState) {
if (oldState != null) {
// Ensure state is cleared
state = const AsyncValue.loading();
}
state = AsyncValue.data(newState);
}
state = AsyncValue.data(newState);
} else {
final status = result['data']['status'];
// Only update if status is not changed

View File

@ -21,7 +21,8 @@ part 'models.g.dart';
@Freezed(unionKey: 'kind')
class RpcResponse with _$RpcResponse {
factory RpcResponse.success(Map<String, dynamic> body) = Success;
factory RpcResponse.success(Map<String, dynamic> body, List<String> flags) =
Success;
factory RpcResponse.signal(String status, Map<String, dynamic> body) = Signal;
factory RpcResponse.error(
String status, String message, Map<String, dynamic> body) = RpcError;

View File

@ -34,7 +34,8 @@ mixin _$RpcResponse {
Map<String, dynamic> get body => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(Map<String, dynamic> body) success,
required TResult Function(Map<String, dynamic> body, List<String> flags)
success,
required TResult Function(String status, Map<String, dynamic> body) signal,
required TResult Function(
String status, String message, Map<String, dynamic> body)
@ -43,7 +44,7 @@ mixin _$RpcResponse {
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Map<String, dynamic> body)? success,
TResult? Function(Map<String, dynamic> body, List<String> flags)? success,
TResult? Function(String status, Map<String, dynamic> body)? signal,
TResult? Function(String status, String message, Map<String, dynamic> body)?
error,
@ -51,7 +52,7 @@ mixin _$RpcResponse {
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(Map<String, dynamic> body, List<String> flags)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
@ -127,7 +128,7 @@ abstract class _$$SuccessImplCopyWith<$Res>
__$$SuccessImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({Map<String, dynamic> body});
$Res call({Map<String, dynamic> body, List<String> flags});
}
/// @nodoc
@ -142,12 +143,17 @@ class __$$SuccessImplCopyWithImpl<$Res>
@override
$Res call({
Object? body = null,
Object? flags = null,
}) {
return _then(_$SuccessImpl(
null == body
? _value._body
: body // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
null == flags
? _value._flags
: flags // ignore: cast_nullable_to_non_nullable
as List<String>,
));
}
}
@ -155,8 +161,10 @@ class __$$SuccessImplCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$SuccessImpl implements Success {
_$SuccessImpl(final Map<String, dynamic> body, {final String? $type})
_$SuccessImpl(final Map<String, dynamic> body, final List<String> flags,
{final String? $type})
: _body = body,
_flags = flags,
$type = $type ?? 'success';
factory _$SuccessImpl.fromJson(Map<String, dynamic> json) =>
@ -170,12 +178,20 @@ class _$SuccessImpl implements Success {
return EqualUnmodifiableMapView(_body);
}
final List<String> _flags;
@override
List<String> get flags {
if (_flags is EqualUnmodifiableListView) return _flags;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_flags);
}
@JsonKey(name: 'kind')
final String $type;
@override
String toString() {
return 'RpcResponse.success(body: $body)';
return 'RpcResponse.success(body: $body, flags: $flags)';
}
@override
@ -183,13 +199,16 @@ class _$SuccessImpl implements Success {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SuccessImpl &&
const DeepCollectionEquality().equals(other._body, _body));
const DeepCollectionEquality().equals(other._body, _body) &&
const DeepCollectionEquality().equals(other._flags, _flags));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_body));
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(_body),
const DeepCollectionEquality().hash(_flags));
@JsonKey(ignore: true)
@override
@ -200,37 +219,38 @@ class _$SuccessImpl implements Success {
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(Map<String, dynamic> body) success,
required TResult Function(Map<String, dynamic> body, List<String> flags)
success,
required TResult Function(String status, Map<String, dynamic> body) signal,
required TResult Function(
String status, String message, Map<String, dynamic> body)
error,
}) {
return success(body);
return success(body, flags);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Map<String, dynamic> body)? success,
TResult? Function(Map<String, dynamic> body, List<String> flags)? success,
TResult? Function(String status, Map<String, dynamic> body)? signal,
TResult? Function(String status, String message, Map<String, dynamic> body)?
error,
}) {
return success?.call(body);
return success?.call(body, flags);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(Map<String, dynamic> body, List<String> flags)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
required TResult orElse(),
}) {
if (success != null) {
return success(body);
return success(body, flags);
}
return orElse();
}
@ -278,12 +298,14 @@ class _$SuccessImpl implements Success {
}
abstract class Success implements RpcResponse {
factory Success(final Map<String, dynamic> body) = _$SuccessImpl;
factory Success(final Map<String, dynamic> body, final List<String> flags) =
_$SuccessImpl;
factory Success.fromJson(Map<String, dynamic> json) = _$SuccessImpl.fromJson;
@override
Map<String, dynamic> get body;
List<String> get flags;
@override
@JsonKey(ignore: true)
_$$SuccessImplCopyWith<_$SuccessImpl> get copyWith =>
@ -380,7 +402,8 @@ class _$SignalImpl implements Signal {
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(Map<String, dynamic> body) success,
required TResult Function(Map<String, dynamic> body, List<String> flags)
success,
required TResult Function(String status, Map<String, dynamic> body) signal,
required TResult Function(
String status, String message, Map<String, dynamic> body)
@ -392,7 +415,7 @@ class _$SignalImpl implements Signal {
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Map<String, dynamic> body)? success,
TResult? Function(Map<String, dynamic> body, List<String> flags)? success,
TResult? Function(String status, Map<String, dynamic> body)? signal,
TResult? Function(String status, String message, Map<String, dynamic> body)?
error,
@ -403,7 +426,7 @@ class _$SignalImpl implements Signal {
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(Map<String, dynamic> body, List<String> flags)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
@ -570,7 +593,8 @@ class _$RpcErrorImpl implements RpcError {
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(Map<String, dynamic> body) success,
required TResult Function(Map<String, dynamic> body, List<String> flags)
success,
required TResult Function(String status, Map<String, dynamic> body) signal,
required TResult Function(
String status, String message, Map<String, dynamic> body)
@ -582,7 +606,7 @@ class _$RpcErrorImpl implements RpcError {
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(Map<String, dynamic> body)? success,
TResult? Function(Map<String, dynamic> body, List<String> flags)? success,
TResult? Function(String status, Map<String, dynamic> body)? signal,
TResult? Function(String status, String message, Map<String, dynamic> body)?
error,
@ -593,7 +617,7 @@ class _$RpcErrorImpl implements RpcError {
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(Map<String, dynamic> body, List<String> flags)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,

View File

@ -9,12 +9,14 @@ part of 'models.dart';
_$SuccessImpl _$$SuccessImplFromJson(Map<String, dynamic> json) =>
_$SuccessImpl(
json['body'] as Map<String, dynamic>,
(json['flags'] as List<dynamic>).map((e) => e as String).toList(),
$type: json['kind'] as String?,
);
Map<String, dynamic> _$$SuccessImplToJson(_$SuccessImpl instance) =>
<String, dynamic>{
'body': instance.body,
'flags': instance.flags,
'kind': instance.$type,
};

View File

@ -102,8 +102,12 @@ class RpcSession {
final String executable;
late _RpcConnection _connection;
final StreamController<_Request> _requests = StreamController();
final StreamController<String> _flags = StreamController();
late final Stream<String> flags;
RpcSession(this.executable);
RpcSession(this.executable) {
flags = _flags.stream.asBroadcastStream();
}
static void _logEntry(String entry) {
try {
@ -230,7 +234,7 @@ class RpcSession {
Future<Map<String, dynamic>> command(String action, List<String>? target,
{Map? params, Signaler? signal}) {
var request = _Request(action, target ?? [], params ?? {}, signal);
final request = _Request(action, target ?? [], params ?? {}, signal);
_requests.add(request);
return request.completer.future;
}
@ -278,6 +282,10 @@ class RpcSession {
},
success: (success) {
request.completer.complete(success.body);
for (final flag in success.flags) {
_log.traffic('FLAG', flag);
_flags.add(flag);
}
completed = true;
},
error: (error) {