Browse Source

refactor manager page into smaller components

master
Valentin D. Pinkman 7 years ago
parent
commit
15650a0e1d
No known key found for this signature in database GPG Key ID: E7D110669FFB8D3E
  1. 12
      src/components/ManagerPage/AppsList.js
  2. 51
      src/components/ManagerPage/Dashboard.js
  3. 2
      src/components/ManagerPage/PlugYourDevice.js
  4. 193
      src/components/ManagerPage/Workflow.js
  5. 187
      src/components/ManagerPage/WorkflowDefault.js
  6. 101
      src/components/ManagerPage/index.js
  7. 21
      static/i18n/en/manager.yml

12
src/components/ManagerPage/AppsList.js

@ -4,6 +4,8 @@ import React, { PureComponent } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { translate } from 'react-i18next' import { translate } from 'react-i18next'
import type { Device, T } from 'types/common'
import listApps from 'commands/listApps' import listApps from 'commands/listApps'
import installApp from 'commands/installApp' import installApp from 'commands/installApp'
import uninstallApp from 'commands/uninstallApp' import uninstallApp from 'commands/uninstallApp'
@ -11,9 +13,10 @@ import uninstallApp from 'commands/uninstallApp'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import Modal, { ModalBody } from 'components/base/Modal' import Modal, { ModalBody } from 'components/base/Modal'
import Tooltip from 'components/base/Tooltip' import Tooltip from 'components/base/Tooltip'
import ExclamationCircle from 'icons/ExclamationCircle' import Text from 'components/base/Text'
import type { Device, T } from 'types/common' import ExclamationCircle from 'icons/ExclamationCircle'
import Update from 'icons/Update'
import ManagerApp from './ManagerApp' import ManagerApp from './ManagerApp'
import AppSearchBar from './AppSearchBar' import AppSearchBar from './AppSearchBar'
@ -142,8 +145,11 @@ class AppsList extends PureComponent<Props, State> {
isOpened={status !== 'idle' && status !== 'loading'} isOpened={status !== 'idle' && status !== 'loading'}
render={() => ( render={() => (
<ModalBody p={6} align="center" justify="center" style={{ height: 300 }}> <ModalBody p={6} align="center" justify="center" style={{ height: 300 }}>
<Update size={30} />
{status === 'busy' ? ( {status === 'busy' ? (
<Box>{'Loading...'}</Box> <Box>
<Text />
</Box>
) : status === 'error' ? ( ) : status === 'error' ? (
<Box> <Box>
<div>{'error happened'}</div> <div>{'error happened'}</div>

51
src/components/ManagerPage/Dashboard.js

@ -0,0 +1,51 @@
// @flow
import React from 'react'
import { translate } from 'react-i18next'
import type { T, Device } from 'types/common'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import AppsList from './AppsList'
import FirmwareUpdate from './FirmwareUpdate'
type DeviceInfo = {
targetId: number | string,
version: string,
final: boolean,
mcu: boolean,
}
type Props = {
t: T,
device: Device,
deviceInfo: DeviceInfo,
}
const Dashboard = ({ device, deviceInfo, t }: Props) => (
<Box flow={4}>
<Box>
<Text ff="Museo Sans|Regular" fontSize={7} color="black">
{t('manager:title')}
</Text>
<Text ff="Museo Sans|Light" fontSize={5}>
{t('manager:subtitle')}
</Text>
</Box>
<Box mt={7}>
<FirmwareUpdate
infos={{
targetId: deviceInfo.targetId,
version: deviceInfo.version,
}}
device={device}
/>
</Box>
<Box>
<AppsList device={device} targetId={deviceInfo.targetId} />
</Box>
</Box>
)
export default translate()(Dashboard)

2
src/components/ManagerPage/PlugYourDevice.js

@ -8,6 +8,8 @@ import type { T } from 'types/common'
import Box, { Card } from 'components/base/Box' import Box, { Card } from 'components/base/Box'
import Button from 'components/base/Button' import Button from 'components/base/Button'
// TODO: NOT IN USE, REMOVE
type Props = { type Props = {
t: T, t: T,
} }

193
src/components/ManagerPage/Workflow.js

@ -1,23 +1,9 @@
// @flow // @flow
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { Trans, translate } from 'react-i18next'
import isNull from 'lodash/isNull'
import type { Node } from 'react' import type { Node } from 'react'
import type { Device, T } from 'types/common' import type { Device } from 'types/common'
import { i } from 'helpers/staticPath'
import Box from 'components/base/Box'
import Space from 'components/base/Space'
import Spinner from 'components/base/Spinner'
import Text from 'components/base/Text'
import IconCheck from 'icons/Check'
import IconExclamationCircle from 'icons/ExclamationCircle'
import IconUsb from 'icons/Usb'
import IconHome from 'icons/Home'
import EnsureDevice from './EnsureDevice' import EnsureDevice from './EnsureDevice'
import EnsureDashboard from './EnsureDashboard' import EnsureDashboard from './EnsureDashboard'
@ -36,7 +22,12 @@ type Error = {
} }
type Props = { type Props = {
t: T, renderDefault: (
device: ?Device,
deviceInfo: ?DeviceInfo,
dashboardError: ?Error,
isGenuine: ?boolean,
) => Node,
renderMcuUpdate: (deviceInfo: DeviceInfo) => Node, renderMcuUpdate: (deviceInfo: DeviceInfo) => Node,
renderFinalUpdate: (deviceInfo: DeviceInfo) => Node, renderFinalUpdate: (deviceInfo: DeviceInfo) => Node,
renderDashboard: (device: Device, deviceInfo: DeviceInfo) => Node, renderDashboard: (device: Device, deviceInfo: DeviceInfo) => Node,
@ -44,73 +35,15 @@ type Props = {
} }
type State = {} type State = {}
const Step = styled(Box).attrs({
borderRadius: 1,
justifyContent: 'center',
fontSize: 4,
})`
border: 1px solid
${p =>
p.validated
? p.theme.colors.wallet
: p.hasErrors
? p.theme.colors.alertRed
: p.theme.colors.fog};
`
const StepIcon = styled(Box).attrs({
alignItems: 'center',
justifyContent: 'center',
})`
width: 64px;
`
const StepContent = styled(Box).attrs({
color: 'dark',
horizontal: true,
alignItems: 'center',
})`
height: 60px;
line-height: 1.2;
strong {
font-weight: 600;
}
`
const StepCheck = ({ checked, hasErrors }: { checked: ?boolean, hasErrors?: boolean }) => (
<Box pr={5}>
{checked ? (
<Box color="wallet">
<IconCheck size={16} />
</Box>
) : hasErrors ? (
<Box color="alertRed">
<IconExclamationCircle size={16} />
</Box>
) : (
<Spinner size={16} />
)}
</Box>
)
StepCheck.defaultProps = {
hasErrors: false,
}
const WrapperIconCurrency = styled(Box).attrs({
alignItems: 'center',
justifyContent: 'center',
})`
border: 1px solid ${p => p.theme.colors[p.color]};
border-radius: 8px;
height: 24px;
width: 24px;
`
class Workflow extends PureComponent<Props, State> { class Workflow extends PureComponent<Props, State> {
render() { render() {
const { renderDashboard, renderFinalUpdate, renderMcuUpdate, renderError, t } = this.props const {
renderDashboard,
renderFinalUpdate,
renderMcuUpdate,
renderError,
renderDefault,
} = this.props
return ( return (
<EnsureDevice> <EnsureDevice>
{(device: Device) => ( {(device: Device) => (
@ -141,101 +74,7 @@ class Workflow extends PureComponent<Props, State> {
return renderDashboard(device, deviceInfo) return renderDashboard(device, deviceInfo)
} }
return ( return renderDefault(device, deviceInfo, dashboardError, isGenuine)
<Box align="center">
<Space of={152} />
<Box align="center" style={{ maxWidth: 460, padding: '0 10px' }}>
<img
src={i('logos/connectDevice.png')}
alt="connect your device"
style={{ marginBottom: 30, maxWidth: 362, width: '100%' }}
/>
<Text
ff="Museo Sans|Regular"
fontSize={7}
color="black"
style={{ marginBottom: 10 }}
>
{t('app:manager.plugYourDevice:title')}
</Text>
<Text ff="Museo Sans|Light" fontSize={5} color="grey" align="center">
{t('app:manager.plugYourDevice:desc')}
</Text>
</Box>
<Box flow={4} style={{ maxWidth: 460, padding: '60px 10px 0' }}>
{/* DEVICE CHECK */}
<Step validated={!!device}>
<StepContent>
<StepIcon>
<IconUsb size={36} />
</StepIcon>
<Box grow shrink>
<Trans i18nKey="deviceConnect:step1.connect" parent="div">
{'Connect your '}
<strong>Ledger device</strong>
{' to your computer and enter your '}
<strong>PIN code</strong>
{' on your device'}
</Trans>
</Box>
<StepCheck checked={!!device} />
</StepContent>
</Step>
{/* DASHBOARD CHECK */}
<Step
validated={!!device && !!deviceInfo}
hasErrors={!!device && !!dashboardError}
>
<StepContent>
<StepIcon>
<WrapperIconCurrency>
<IconHome size={12} />
</WrapperIconCurrency>
</StepIcon>
<Box grow shrink>
<Trans i18nKey="deviceConnect:dashboard.open" parent="div">
{'Go to the '}
<strong>{'dashboard'}</strong>
{' on your device'}
</Trans>
</Box>
<StepCheck
checked={!!device && !!deviceInfo}
hasErrors={!!device && !!dashboardError}
/>
</StepContent>
</Step>
{/* GENUINE CHECK */}
<Step
validated={(!!device && !isNull(isGenuine) && isGenuine) || undefined}
hasErrors={(!!device && !isNull(isGenuine) && !isGenuine) || undefined}
>
<StepContent>
<StepIcon>
<WrapperIconCurrency>
<IconCheck size={12} />
</WrapperIconCurrency>
</StepIcon>
<Box grow shrink>
<Trans i18nKey="deviceConnect:stepGenuine.open" parent="div">
{'Confirm '}
<strong>{'authentication'}</strong>
{' on your device'}
</Trans>
</Box>
<StepCheck
checked={(!!device && !isNull(isGenuine) && isGenuine) || undefined}
hasErrors={
(!!device && !isNull(isGenuine) && !isGenuine) || undefined
}
/>
</StepContent>
</Step>
</Box>
</Box>
)
}} }}
</EnsureGenuine> </EnsureGenuine>
)} )}
@ -246,4 +85,4 @@ class Workflow extends PureComponent<Props, State> {
} }
} }
export default translate()(Workflow) export default Workflow

187
src/components/ManagerPage/WorkflowDefault.js

@ -0,0 +1,187 @@
// @flow
import React from 'react'
import { Trans, translate } from 'react-i18next'
import styled from 'styled-components'
import isNull from 'lodash/isNull'
import type { Device, T } from 'types/common'
import { i } from 'helpers/staticPath'
import Box from 'components/base/Box'
import Space from 'components/base/Space'
import Text from 'components/base/Text'
import Spinner from 'components/base/Spinner'
import IconCheck from 'icons/Check'
import IconExclamationCircle from 'icons/ExclamationCircle'
import IconUsb from 'icons/Usb'
import IconHome from 'icons/Home'
const WrapperIconCurrency = styled(Box).attrs({
alignItems: 'center',
justifyContent: 'center',
})`
border: 1px solid ${p => p.theme.colors[p.color]};
border-radius: 8px;
height: 24px;
width: 24px;
`
const Step = styled(Box).attrs({
borderRadius: 1,
justifyContent: 'center',
fontSize: 4,
})`
border: 1px solid
${p =>
p.validated
? p.theme.colors.wallet
: p.hasErrors
? p.theme.colors.alertRed
: p.theme.colors.fog};
`
const StepIcon = styled(Box).attrs({
alignItems: 'center',
justifyContent: 'center',
})`
width: 64px;
`
const StepContent = styled(Box).attrs({
color: 'dark',
horizontal: true,
alignItems: 'center',
})`
height: 60px;
line-height: 1.2;
strong {
font-weight: 600;
}
`
const StepCheck = ({ checked, hasErrors }: { checked: ?boolean, hasErrors?: boolean }) => (
<Box pr={5}>
{checked ? (
<Box color="wallet">
<IconCheck size={16} />
</Box>
) : hasErrors ? (
<Box color="alertRed">
<IconExclamationCircle size={16} />
</Box>
) : (
<Spinner size={16} />
)}
</Box>
)
StepCheck.defaultProps = {
hasErrors: false,
}
type DeviceInfo = {
targetId: number | string,
version: string,
final: boolean,
mcu: boolean,
}
type Error = {
message: string,
stack: string,
}
type Props = {
t: T,
device: ?Device,
deviceInfo: ?DeviceInfo,
dashboardError: ?Error,
isGenuine: boolean,
}
const WorkflowDefault = ({ device, deviceInfo, dashboardError, isGenuine, t }: Props) => (
<Box align="center">
<Space of={152} />
<Box align="center" style={{ maxWidth: 460, padding: '0 10px' }}>
<img
src={i('logos/connectDevice.png')}
alt="connect your device"
style={{ marginBottom: 30, maxWidth: 362, width: '100%' }}
/>
<Text ff="Museo Sans|Regular" fontSize={7} color="black" style={{ marginBottom: 10 }}>
{t('manager:device.title')}
</Text>
<Text ff="Museo Sans|Light" fontSize={5} color="grey" align="center">
{t('manager:device.desc')}
</Text>
</Box>
<Box flow={4} style={{ maxWidth: 460, padding: '60px 10px 0' }}>
{/* DEVICE CHECK */}
<Step validated={!!device}>
<StepContent>
<StepIcon>
<IconUsb size={36} />
</StepIcon>
<Box grow shrink>
<Trans i18nKey="deviceConnect:step1.connect" parent="div">
{'Connect your '}
<strong>Ledger device</strong>
{' to your computer and enter your '}
<strong>PIN code</strong>
{' on your device'}
</Trans>
</Box>
<StepCheck checked={!!device} />
</StepContent>
</Step>
{/* DASHBOARD CHECK */}
<Step validated={!!device && !!deviceInfo} hasErrors={!!device && !!dashboardError}>
<StepContent>
<StepIcon>
<WrapperIconCurrency>
<IconHome size={12} />
</WrapperIconCurrency>
</StepIcon>
<Box grow shrink>
<Trans i18nKey="deviceConnect:dashboard.open" parent="div">
{'Go to the '}
<strong>{'dashboard'}</strong>
{' on your device'}
</Trans>
</Box>
<StepCheck checked={!!device && !!deviceInfo} hasErrors={!!device && !!dashboardError} />
</StepContent>
</Step>
{/* GENUINE CHECK */}
<Step
validated={(!!device && !isNull(isGenuine) && isGenuine) || undefined}
hasErrors={(!!device && !isNull(isGenuine) && !isGenuine) || undefined}
>
<StepContent>
<StepIcon>
<WrapperIconCurrency>
<IconCheck size={12} />
</WrapperIconCurrency>
</StepIcon>
<Box grow shrink>
<Trans i18nKey="deviceConnect:stepGenuine.open" parent="div">
{'Confirm '}
<strong>{'authentication'}</strong>
{' on your device'}
</Trans>
</Box>
<StepCheck
checked={(!!device && !isNull(isGenuine) && isGenuine) || undefined}
hasErrors={(!!device && !isNull(isGenuine) && !isGenuine) || undefined}
/>
</StepContent>
</Step>
</Box>
</Box>
)
export default translate()(WorkflowDefault)

101
src/components/ManagerPage/index.js

@ -1,17 +1,13 @@
// @flow // @flow
import React, { PureComponent } from 'react' import React from 'react'
import { translate } from 'react-i18next'
import type { Node } from 'react' import type { Node } from 'react'
import type { T, Device } from 'types/common' import type { Device } from 'types/common'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import AppsList from './AppsList'
import FirmwareUpdate from './FirmwareUpdate'
import Workflow from './Workflow' import Workflow from './Workflow'
import WorkflowDefault from './WorkflowDefault'
import Dashboard from './Dashboard'
type DeviceInfo = { type DeviceInfo = {
targetId: number | string, targetId: number | string,
@ -25,61 +21,38 @@ type Error = {
stack: string, stack: string,
} }
type Props = { function ManagerPage(): Node {
t: T, return (
} <Workflow
renderError={(dashboardError: ?Error, genuineError: ?Error) => {
type State = { if (dashboardError) return <span>Dashboard Error: {dashboardError.message}</span>
modalOpen: boolean, if (genuineError) return <span>Genuine Error: {genuineError.message}</span>
} return <span>Error</span>
}}
class ManagerPage extends PureComponent<Props, State> { renderFinalUpdate={(deviceInfo: DeviceInfo) => (
renderDashboard = (device: Device, deviceInfo: DeviceInfo) => { <p>UPDATE FINAL FIRMARE (TEMPLATE + ACTION WIP) {deviceInfo.final}</p>
const { t } = this.props )}
return ( renderMcuUpdate={(deviceInfo: DeviceInfo) => (
<Box flow={4}> <p>FLASH MCU (TEMPLATE + ACTION WIP) {deviceInfo.mcu}</p>
<Box> )}
<Text ff="Museo Sans|Regular" fontSize={7} color="black"> renderDashboard={(device: Device, deviceInfo: DeviceInfo) => (
{t('app:manager.title')} <Dashboard device={device} deviceInfo={deviceInfo} />
</Text> )}
<Text ff="Museo Sans|Light" fontSize={5}> renderDefault={(
{t('app:manager.subtitle')} device: ?Device,
</Text> deviceInfo: ?DeviceInfo,
</Box> dashboardError: ?Error,
<Box mt={7}> isGenuine: ?boolean,
<FirmwareUpdate ) => (
infos={{ <WorkflowDefault
targetId: deviceInfo.targetId, device={device}
version: deviceInfo.version, deviceInfo={deviceInfo}
}} dashboardError={dashboardError}
device={device} isGenuine={isGenuine}
/> />
</Box> )}
<Box> />
<AppsList device={device} targetId={deviceInfo.targetId} /> )
</Box>
</Box>
)
}
render(): Node {
return (
<Workflow
renderError={(dashboardError: ?Error, genuineError: ?Error) => {
if (dashboardError) return <span>Dashboard Error: {dashboardError.message}</span>
if (genuineError) return <span>Genuine Error: {genuineError.message}</span>
return <span>Error</span>
}}
renderFinalUpdate={(deviceInfo: DeviceInfo) => (
<p>UPDATE FINAL FIRMARE (TEMPLATE + ACTION WIP) {deviceInfo.final}</p>
)}
renderMcuUpdate={(deviceInfo: DeviceInfo) => (
<p>FLASH MCU (TEMPLATE + ACTION WIP) {deviceInfo.mcu}</p>
)}
renderDashboard={this.renderDashboard}
/>
)
}
} }
export default translate()(ManagerPage) export default ManagerPage

21
static/i18n/en/manager.yml

@ -0,0 +1,21 @@
tabs:
apps: Apps
device: My device
apps:
install: Install
all: Apps
help: To update an app, you have to uninstall the app and re install it.
firmware:
update: Update firmware
updateTitle: Firmware update
latest: A new firmware {{version}} is available
title: Manager
subtitle: Get all your apps here
device:
title: Plug your device
desc: Please connect your Ledger device and follow the steps below to access the manager
cta: Plug my device
errors:
noDevice: Please make sur your device is connected (TEMPLATE NEEDED)
noDashboard: Please make sure your device is on the dashboard screen (TEMPLATED NEEDED)
noGenuine: You did not approve request on your device or your device is not genuine (TEMPLATE NEEDED)
Loading…
Cancel
Save