From 34a1621c0ae9f6fe787bd640b224bc2333d200df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Wed, 6 Jun 2018 10:31:18 +0200 Subject: [PATCH] debounce the device add/remove + better logs --- src/commands/listenDevices.js | 51 ++++++++++++++++++++++++++++++++++- src/helpers/ipc.js | 9 ++++--- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/commands/listenDevices.js b/src/commands/listenDevices.js index e523750a..529250fb 100644 --- a/src/commands/listenDevices.js +++ b/src/commands/listenDevices.js @@ -4,6 +4,55 @@ import { createCommand } from 'helpers/ipc' import { Observable } from 'rxjs' import CommNodeHid from '@ledgerhq/hw-transport-node-hid' -const cmd = createCommand('listenDevices', () => Observable.create(CommNodeHid.listen)) +const DEBOUNCE_REMOVE_DEVICE_EVENT = 500 + +const cmd = createCommand('listenDevices', () => + Observable.create(o => { + const pendingRemovePerPath = {} + const sub = CommNodeHid.listen({ + next: e => { + // debounce the add/remove in case we see quick `remove,add` events on same path. + switch (e.type) { + case 'add': { + const pendingRemove = pendingRemovePerPath[e.descriptor] + if (pendingRemove) { + console.warn(`Skipping remove/add usb event for ${e.descriptor}`) + // there where a recent "remove" event, we don't emit add because we didn't emit "remove" yet. + clearTimeout(pendingRemove) + delete pendingRemovePerPath[e.descriptor] + } else { + // if there were no recent "remove", we just emit the "add" + o.next(e) + } + break + } + case 'remove': { + // we we always debounce the "remove" event. emit it a bit later in case a "add" of same descriptor happen soon. + if (pendingRemovePerPath[e.descriptor]) { + clearTimeout(pendingRemovePerPath[e.descriptor]) + } + pendingRemovePerPath[e.descriptor] = setTimeout(() => { + delete pendingRemovePerPath[e.descriptor] + o.next(e) + }, DEBOUNCE_REMOVE_DEVICE_EVENT) + break + } + default: + o.next(e) + } + }, + complete: () => { + o.complete() + }, + error: err => { + o.error(err) + }, + }) + return () => { + Object.keys(pendingRemovePerPath).map(k => clearTimeout(pendingRemovePerPath[k])) + sub.unsubscribe() + } + }), +) export default cmd diff --git a/src/helpers/ipc.js b/src/helpers/ipc.js index c7fa28f7..25053ef5 100644 --- a/src/helpers/ipc.js +++ b/src/helpers/ipc.js @@ -37,6 +37,7 @@ function ipcRendererSendCommand(id: string, data: In): Observable { const { ipcRenderer } = require('electron') return Observable.create(o => { const requestId: string = uuidv4() + const startTime = Date.now() const unsubscribe = () => { ipcRenderer.send('command-unsubscribe', { requestId }) @@ -47,20 +48,20 @@ function ipcRendererSendCommand(id: string, data: In): Observable { if (requestId !== msg.requestId) return switch (msg.type) { case 'NEXT': - console.log('<= COMMAND next', msg) + console.log(`● CMD ${id}`, msg.data) if (msg.data) { o.next(msg.data) } break case 'COMPLETE': - console.log('<= COMMAND complete', msg) + console.log(`✔ CMD ${id} finished in ${(Date.now() - startTime).toFixed(0)}ms`) o.complete() ipcRenderer.removeListener('command-event', handleCommandEvent) break case 'ERROR': - console.warn('<= COMMAND error', msg) + console.warn(`✖ CMD ${id} error`, msg.data) o.error(msg.data) ipcRenderer.removeListener('command-event', handleCommandEvent) break @@ -73,7 +74,7 @@ function ipcRendererSendCommand(id: string, data: In): Observable { ipcRenderer.send('command', { id, data, requestId }) - console.log('=> COMMAND', { id, data, requestId }) + console.log(`CMD ${id}.send(`, data, ')') return unsubscribe })