Browse Source

add busy indicators

master
Gaëtan Renaudeau 7 years ago
parent
commit
009f74725a
  1. 38
      src/components/DeviceBusyIndicator.js
  2. 37
      src/components/LibcoreBusyIndicator.js
  3. 5
      src/components/layout/Default.js
  4. 1
      src/config/constants.js
  5. 19
      src/helpers/deviceAccess.js
  6. 18
      src/helpers/withLibcore.js
  7. 15
      src/main/bridge.js
  8. 16
      src/renderer/events.js

38
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 <Indicator busy={busy} />
}
}
export default DeviceBusyIndicator

37
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 <Indicator busy={busy} />
}
}
export default LibcoreBusyIndicator

5
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<Props> {
</Main>
</Box>
</Box>
<LibcoreBusyIndicator />
<DeviceBusyIndicator />
</IsUnlocked>
</Fragment>
)

1
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

19
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<T>(sem, f: () => Promise<T>): Promise<T> {
function takeSemaphorePromise<T>(sem, devicePath, f: () => Promise<T>): Promise<T> {
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,
})
},
)
})

18
src/helpers/withLibcore.js

@ -3,8 +3,24 @@
// TODO: `core` should be typed
type Job<A> = Object => Promise<A>
export default function withLibcore<A>(job: Job<A>): Promise<A> {
let counter = 0
export default async function withLibcore<A>(job: Job<A>): Promise<A> {
const core = require('./init-libcore').default
core.getPoolInstance()
try {
if (counter++ === 0) {
process.send({
type: 'setLibcoreBusy',
busy: true,
})
}
return job(core)
} finally {
if (--counter === 0) {
process.send({
type: 'setLibcoreBusy',
busy: false,
})
}
}
}

15
src/main/bridge.js

@ -97,16 +97,19 @@ ipcMainListenReceiveCommands({
})
function handleGlobalInternalMessage(payload) {
if (payload.type === 'executeHttpQueryOnRenderer') {
switch (payload.type) {
case 'setLibcoreBusy':
case 'setDeviceBusy':
case 'executeHttpQueryOnRenderer': {
const win = getMainWindow && getMainWindow()
if (!win) {
logger.warn("can't executeHttpQueryOnRenderer because no renderer")
logger.warn(`can't ${payload.type} because no renderer`)
return
}
win.webContents.send('executeHttpQuery', {
id: payload.id,
networkArg: payload.networkArg,
})
win.webContents.send(payload.type, payload)
break
}
default:
}
}

16
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 = {

Loading…
Cancel
Save