From 009f74725a0a6838ed0264d4f92f3bab82f01e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Tue, 19 Jun 2018 18:34:17 +0200 Subject: [PATCH] add busy indicators --- src/components/DeviceBusyIndicator.js | 38 ++++++++++++++++++++++++++ src/components/LibcoreBusyIndicator.js | 37 +++++++++++++++++++++++++ src/components/layout/Default.js | 5 ++++ src/config/constants.js | 1 + src/helpers/deviceAccess.js | 19 +++++++++++-- src/helpers/withLibcore.js | 20 ++++++++++++-- src/main/bridge.js | 21 ++++++++------ src/renderer/events.js | 16 +++++++++-- 8 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 src/components/DeviceBusyIndicator.js create mode 100644 src/components/LibcoreBusyIndicator.js diff --git a/src/components/DeviceBusyIndicator.js b/src/components/DeviceBusyIndicator.js new file mode 100644 index 00000000..b77523bc --- /dev/null +++ b/src/components/DeviceBusyIndicator.js @@ -0,0 +1,38 @@ +import React, { PureComponent } from 'react' +import styled from 'styled-components' + +const Indicator = styled.div` + opacity: ${p => (p.busy ? 0.2 : 0)}; + width: 6px; + height: 6px; + border-radius: 3px; + background-color: black; + position: fixed; + bottom: 4px; + right: 4px; + z-index: 999; +` + +// NB this is done like this to be extremely performant. we don't want redux for this.. +const perPaths = {} +const instances = [] +export const onSetDeviceBusy = (path, busy) => { + perPaths[path] = busy + instances.forEach(i => i.forceUpdate()) +} + +class DeviceBusyIndicator extends PureComponent<{}> { + componentDidMount() { + instances.push(this) + } + componentWillUnmount() { + const i = instances.indexOf(this) + instances.splice(i, 1) + } + render() { + const busy = Object.values(perPaths).reduce((busy, b) => busy || b, false) + return + } +} + +export default DeviceBusyIndicator diff --git a/src/components/LibcoreBusyIndicator.js b/src/components/LibcoreBusyIndicator.js new file mode 100644 index 00000000..251f955b --- /dev/null +++ b/src/components/LibcoreBusyIndicator.js @@ -0,0 +1,37 @@ +import React, { PureComponent } from 'react' +import styled from 'styled-components' + +const Indicator = styled.div` + opacity: ${p => (p.busy ? 0.2 : 0)}; + width: 6px; + height: 6px; + border-radius: 3px; + background-color: black; + position: fixed; + bottom: 4px; + right: 4px; + z-index: 999; +` + +// NB this is done like this to be extremely performant. we don't want redux for this.. +let busy = false +const instances = [] +export const onSetLibcoreBusy = b => { + busy = b + instances.forEach(i => i.forceUpdate()) +} + +class LibcoreBusyIndicator extends PureComponent<{}> { + componentDidMount() { + instances.push(this) + } + componentWillUnmount() { + const i = instances.indexOf(this) + instances.splice(i, 1) + } + render() { + return + } +} + +export default LibcoreBusyIndicator diff --git a/src/components/layout/Default.js b/src/components/layout/Default.js index e7286fa0..c9a16c8c 100644 --- a/src/components/layout/Default.js +++ b/src/components/layout/Default.js @@ -17,6 +17,8 @@ import DashboardPage from 'components/DashboardPage' import ManagerPage from 'components/ManagerPage' import ExchangePage from 'components/ExchangePage' import SettingsPage from 'components/SettingsPage' +import LibcoreBusyIndicator from 'components/LibcoreBusyIndicator' +import DeviceBusyIndicator from 'components/DeviceBusyIndicator' import AppRegionDrag from 'components/AppRegionDrag' import IsUnlocked from 'components/IsUnlocked' @@ -96,6 +98,9 @@ class Default extends Component { + + + ) diff --git a/src/config/constants.js b/src/config/constants.js index 74ca72ef..473559a4 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -59,6 +59,7 @@ export const SKIP_GENUINE = boolFromEnv('SKIP_GENUINE') export const SKIP_ONBOARDING = boolFromEnv('SKIP_ONBOARDING') export const SHOW_LEGACY_NEW_ACCOUNT = boolFromEnv('SHOW_LEGACY_NEW_ACCOUNT') export const HIGHLIGHT_I18N = boolFromEnv('HIGHLIGHT_I18N') +export const DISABLE_ACTIVITY_INDICATORS = boolFromEnv('DISABLE_ACTIVITY_INDICATORS') // Other constants diff --git a/src/helpers/deviceAccess.js b/src/helpers/deviceAccess.js index 34aa3499..c380fe0c 100644 --- a/src/helpers/deviceAccess.js +++ b/src/helpers/deviceAccess.js @@ -18,7 +18,7 @@ export const withDevice: WithDevice = devicePath => { semaphorePerDevice[devicePath] || (semaphorePerDevice[devicePath] = createSemaphore(1)) return job => - takeSemaphorePromise(sem, async () => { + takeSemaphorePromise(sem, devicePath, async () => { const t = await retry(() => TransportNodeHid.open(devicePath), { maxRetry: 1 }) if (DEBUG_DEVICE) t.setDebugMode(true) @@ -32,17 +32,32 @@ export const withDevice: WithDevice = devicePath => { }) } -function takeSemaphorePromise(sem, f: () => Promise): Promise { +function takeSemaphorePromise(sem, devicePath, f: () => Promise): Promise { return new Promise((resolve, reject) => { sem.take(() => { + process.send({ + type: 'setDeviceBusy', + busy: true, + devicePath, + }) f().then( r => { sem.leave() resolve(r) + process.send({ + type: 'setDeviceBusy', + busy: false, + devicePath, + }) }, e => { sem.leave() reject(e) + process.send({ + type: 'setDeviceBusy', + busy: false, + devicePath, + }) }, ) }) diff --git a/src/helpers/withLibcore.js b/src/helpers/withLibcore.js index e2d42054..f1fe3fd4 100644 --- a/src/helpers/withLibcore.js +++ b/src/helpers/withLibcore.js @@ -3,8 +3,24 @@ // TODO: `core` should be typed type Job = Object => Promise -export default function withLibcore(job: Job): Promise { +let counter = 0 +export default async function withLibcore(job: Job): Promise { const core = require('./init-libcore').default core.getPoolInstance() - return job(core) + try { + if (counter++ === 0) { + process.send({ + type: 'setLibcoreBusy', + busy: true, + }) + } + return job(core) + } finally { + if (--counter === 0) { + process.send({ + type: 'setLibcoreBusy', + busy: false, + }) + } + } } diff --git a/src/main/bridge.js b/src/main/bridge.js index 2b71fcd1..79323d2e 100644 --- a/src/main/bridge.js +++ b/src/main/bridge.js @@ -97,16 +97,19 @@ ipcMainListenReceiveCommands({ }) function handleGlobalInternalMessage(payload) { - if (payload.type === 'executeHttpQueryOnRenderer') { - const win = getMainWindow && getMainWindow() - if (!win) { - logger.warn("can't executeHttpQueryOnRenderer because no renderer") - return + switch (payload.type) { + case 'setLibcoreBusy': + case 'setDeviceBusy': + case 'executeHttpQueryOnRenderer': { + const win = getMainWindow && getMainWindow() + if (!win) { + logger.warn(`can't ${payload.type} because no renderer`) + return + } + win.webContents.send(payload.type, payload) + break } - win.webContents.send('executeHttpQuery', { - id: payload.id, - networkArg: payload.networkArg, - }) + default: } } diff --git a/src/renderer/events.js b/src/renderer/events.js index bb106f27..63d266f2 100644 --- a/src/renderer/events.js +++ b/src/renderer/events.js @@ -15,7 +15,9 @@ import network from 'api/network' import { ipcRenderer } from 'electron' import debug from 'debug' -import { CHECK_UPDATE_DELAY } from 'config/constants' +import { CHECK_UPDATE_DELAY, DISABLE_ACTIVITY_INDICATORS } from 'config/constants' +import { onSetDeviceBusy } from 'components/DeviceBusyIndicator' +import { onSetLibcoreBusy } from 'components/LibcoreBusyIndicator' import { hasPassword } from 'reducers/settings' import { lock } from 'reducers/application' @@ -87,7 +89,7 @@ export default ({ store }: { store: Object }) => { } }) - ipcRenderer.on('executeHttpQuery', (event: any, { networkArg, id }) => { + ipcRenderer.on('executeHttpQueryOnRenderer', (event: any, { networkArg, id }) => { network(networkArg).then( result => { ipcRenderer.send('executeHttpQueryPayload', { type: 'success', id, result }) @@ -98,6 +100,16 @@ export default ({ store }: { store: Object }) => { ) }) + if (!DISABLE_ACTIVITY_INDICATORS) { + ipcRenderer.on('setLibcoreBusy', (event: any, { busy }) => { + onSetLibcoreBusy(busy) + }) + + ipcRenderer.on('setDeviceBusy', (event: any, { busy, devicePath }) => { + onSetDeviceBusy(devicePath, busy) + }) + } + if (__PROD__) { // TODO move this to "command" pattern const updaterHandlers = {