Browse Source

ui modal for install apps

master
Valentin D. Pinkman 7 years ago
parent
commit
0a6e864b09
No known key found for this signature in database GPG Key ID: E7D110669FFB8D3E
  1. 9
      README.md
  2. 106
      src/components/ManagerPage/AppsList.js
  3. 4
      src/components/ManagerPage/Dashboard.js
  4. 2
      src/components/ManagerPage/FinalFirmwareUpdate.js
  5. 10
      src/components/ManagerPage/FirmwareUpdate.js
  6. 5
      src/components/ManagerPage/ManagerApp.js
  7. 6
      src/components/ManagerPage/PlugYourDevice.js
  8. 4
      src/components/ManagerPage/UpdateFirmwareButton.js
  9. 4
      src/components/ManagerPage/WorkflowDefault.js
  10. 82
      src/components/base/Progress/index.js
  11. 1
      src/helpers/deviceAccess.js
  12. 19
      static/i18n/en/app.yml
  13. 21
      static/i18n/en/manager.yml

9
README.md

@ -37,12 +37,15 @@ yarn
# Where errors will be tracked (you may not want to edit this line)
# SENTRY_URL=
# api base url
API_BASE_URL=http://...
# OPTIONAL ENV VARIABLES
# ----------------------
# API base url, fallback to our API if not set
API_BASE_URL=http://...
# Setup device debug mode
DEBUG_DEVICE=0
# Developer tools position (used only in dev)
# can be one of: right, bottom, undocked, detach
DEV_TOOLS_MODE=bottom

106
src/components/ManagerPage/AppsList.js

@ -14,9 +14,12 @@ import Box from 'components/base/Box'
import Modal, { ModalBody } from 'components/base/Modal'
import Tooltip from 'components/base/Tooltip'
import Text from 'components/base/Text'
import Progress from 'components/base/Progress'
import ExclamationCircle from 'icons/ExclamationCircle'
import Update from 'icons/Update'
import Trash from 'icons/Trash'
import CheckCircle from 'icons/CheckCircle'
import ManagerApp from './ManagerApp'
import AppSearchBar from './AppSearchBar'
@ -35,6 +38,7 @@ const ICONS_FALLBACK = {
}
type Status = 'loading' | 'idle' | 'busy' | 'success' | 'error'
type Mode = '' | 'installing' | 'uninstalling'
type LedgerApp = {
name: string,
@ -57,6 +61,8 @@ type State = {
status: Status,
error: string | null,
appsList: LedgerApp[],
app: string,
mode: Mode,
}
class AppsList extends PureComponent<Props, State> {
@ -64,6 +70,8 @@ class AppsList extends PureComponent<Props, State> {
status: 'loading',
error: null,
appsList: [],
app: '',
mode: '',
}
componentDidMount() {
@ -89,40 +97,87 @@ class AppsList extends PureComponent<Props, State> {
}
}
handleInstallApp = (args: { app: any }) => async () => {
const appParams = args.app
this.setState({ status: 'busy' })
handleInstallApp = (args: { app: any, name: string }) => async () => {
const { app: appParams, name } = args
this.setState({ status: 'busy', app: name, mode: 'installing' })
try {
const {
device: { path: devicePath },
} = this.props
const data = { appParams, devicePath }
await installApp.send(data).toPromise()
this.setState({ status: 'success' })
this.setState({ status: 'success', app: '' })
} catch (err) {
this.setState({ status: 'error', error: err.message })
this.setState({ status: 'error', error: err.message, app: '', mode: '' })
}
}
handleUninstallApp = (args: { app: any }) => async () => {
const appParams = args.app
this.setState({ status: 'busy' })
handleUninstallApp = (args: { app: any, name: string }) => async () => {
const { app: appParams, name } = args
this.setState({ status: 'busy', app: name, mode: 'uninstalling' })
try {
const {
device: { path: devicePath },
} = this.props
const data = { appParams, devicePath }
await uninstallApp.send(data).toPromise()
this.setState({ status: 'success' })
this.setState({ status: 'success', app: '' })
} catch (err) {
this.setState({ status: 'error', error: err.message })
this.setState({ status: 'error', error: err.message, app: '', mode: '' })
}
}
handleCloseModal = () => this.setState({ status: 'idle' })
handleCloseModal = () => this.setState({ status: 'idle', mode: '' })
renderModal = () => {
const { t } = this.props
const { app, status, error, mode } = this.state
return (
<Modal
isOpened={status !== 'idle' && status !== 'loading'}
render={() => (
<ModalBody p={6} align="center" justify="center" style={{ height: 300 }}>
{status === 'busy' || status === 'idle' ? (
<Box align="center" justify="center" flow={3}>
{mode === 'installing' ? <Update size={30} /> : <Trash size={30} />}
<Text ff="Museo Sans|Regular" fontSize={6} color="dark">
{t(`app:manager.apps.${mode}`, { app })}
</Text>
<Box my={5} style={{ width: 250 }}>
<Progress style={{ width: '100%' }} infinite />
</Box>
</Box>
) : status === 'error' ? (
<Box align="center" justify="center" flow={3}>
<div>{'error happened'}</div>
{error}
<button onClick={this.handleCloseModal}>close</button>
</Box>
) : status === 'success' ? (
<Box align="center" justify="center" flow={3}>
<Box color="positiveGreen">
<CheckCircle size={30} />
</Box>
<Text ff="Museo Sans|Regular" fontSize={6} color="dark">
{t(
`app:manager.apps.${
mode === 'installing' ? 'installSuccess' : 'uninstallSuccess'
}`,
{ app },
)}
</Text>
<button onClick={this.handleCloseModal}>close</button>
</Box>
) : null}
</ModalBody>
)}
/>
)
}
renderList() {
const { status, error, appsList } = this.state
const { appsList } = this.state
return (
<Box>
<AppSearchBar list={appsList}>
@ -141,30 +196,7 @@ class AppsList extends PureComponent<Props, State> {
</List>
)}
</AppSearchBar>
<Modal
isOpened={status !== 'idle' && status !== 'loading'}
render={() => (
<ModalBody p={6} align="center" justify="center" style={{ height: 300 }}>
<Update size={30} />
{status === 'busy' ? (
<Box>
<Text />
</Box>
) : status === 'error' ? (
<Box>
<div>{'error happened'}</div>
{error}
<button onClick={this.handleCloseModal}>close</button>
</Box>
) : status === 'success' ? (
<Box>
{'success'}
<button onClick={this.handleCloseModal}>close</button>
</Box>
) : null}
</ModalBody>
)}
/>
{this.renderModal()}
</Box>
)
}
@ -175,7 +207,7 @@ class AppsList extends PureComponent<Props, State> {
<Box flow={6}>
<Box>
<Box mb={4} color="dark" ff="Museo Sans" fontSize={5} flow={2} horizontal>
<span>{t('app:manager.allApps')}</span>
<span>{t('app:manager.apps.all')}</span>
<span>
<Tooltip
render={() => (

4
src/components/ManagerPage/Dashboard.js

@ -27,10 +27,10 @@ const Dashboard = ({ device, deviceInfo, t }: Props) => (
<Box flow={4}>
<Box>
<Text ff="Museo Sans|Regular" fontSize={7} color="black">
{t('manager:title')}
{t('app:manager.title')}
</Text>
<Text ff="Museo Sans|Light" fontSize={5}>
{t('manager:subtitle')}
{t('app:manager.subtitle')}
</Text>
</Box>
<Box mt={7}>

2
src/components/ManagerPage/FinalFirmwareUpdate.js

@ -41,7 +41,7 @@ class FirmwareUpdate extends PureComponent<Props, State> {
return (
<Box flow={4} {...props}>
<Box color="dark" ff="Museo Sans" fontSize={6}>
{t('app:manager.firmwareUpdate')}
{t('app:manager.firmware.update')}
</Box>
<Card flow={2} {...props}>
<Box horizontal align="center" flow={2} />

10
src/components/ManagerPage/FirmwareUpdate.js

@ -1,11 +1,12 @@
// @flow
import React, { PureComponent } from 'react'
import { translate } from 'react-i18next'
import isEqual from 'lodash/isEqual'
import isEmpty from 'lodash/isEmpty'
import invariant from 'invariant'
import logger from 'logger'
import type { Device } from 'types/common'
import type { Device, T } from 'types/common'
import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice'
import installOsuFirmware from 'commands/installOsuFirmware'
@ -31,6 +32,7 @@ type DeviceInfos = {
}
type Props = {
t: T,
device: Device,
infos: DeviceInfos,
}
@ -96,7 +98,7 @@ class FirmwareUpdate extends PureComponent<Props, State> {
}
render() {
const { infos } = this.props
const { infos, t } = this.props
const { latestFirmware } = this.state
return (
@ -115,7 +117,7 @@ class FirmwareUpdate extends PureComponent<Props, State> {
</Box>
</Box>
<Text ff="Open Sans|SemiBold" fontSize={2}>
Firmware {infos.version}
{t('app:manager.firmware.installed', { version: infos.version })}
</Text>
</Box>
<UpdateFirmwareButton firmware={latestFirmware} installFirmware={this.installFirmware} />
@ -125,4 +127,4 @@ class FirmwareUpdate extends PureComponent<Props, State> {
}
}
export default FirmwareUpdate
export default translate()(FirmwareUpdate)

5
src/components/ManagerPage/ManagerApp.js

@ -52,8 +52,7 @@ type Props = {
onUninstall: Function,
}
function ManagerApp(props: Props) {
const { name, version, icon, onInstall, onUninstall, t } = props
function ManagerApp({ name, version, icon, onInstall, onUninstall, t }: Props) {
const iconUrl = `https://api.ledgerwallet.com/update/assets/icons/${icon}`
return (
<Container>
@ -65,7 +64,7 @@ function ManagerApp(props: Props) {
</Text>
</Box>
<Button outline onClick={onInstall}>
{t('app:manager.installApps')}
{t('app:manager.apps.install')}
</Button>
<Button outline onClick={onUninstall} outlineColor="grey">
<Trash size={16} fill="grey" />

6
src/components/ManagerPage/PlugYourDevice.js

@ -21,12 +21,12 @@ function PlugYourDevice(props: Props) {
<Box align="center" style={{ width: 365 }}>
<Box mb={5}>hey</Box>
<Box textAlign="center" mb={1} ff="Museo Sans|Regular" color="dark" fontSize={6}>
{t('app:manager.plugYourDevice.title')}
{t('app:manager.device.title')}
</Box>
<Box textAlign="center" mb={5} ff="Open Sans|Regular" color="smoke" fontSize={4}>
{t('app:manager.plugYourDevice.desc')}
{t('app:manager.device.desc')}
</Box>
<Button primary>{t('app:manager.plugYourDevice.cta')}</Button>
<Button primary>{t('app:manager.device.cta')}</Button>
</Box>
</Card>
)

4
src/components/ManagerPage/UpdateFirmwareButton.js

@ -22,10 +22,10 @@ const UpdateFirmwareButton = ({ t, firmware, installFirmware }: Props) =>
firmware ? (
<Fragment>
<Text ff="Open Sans|Regular" fontSize={4} style={{ marginLeft: 'auto', marginRight: 15 }}>
{t('app:manager.latestFirmware', { version: firmware.name })}
{t('app:manager.firmware.latest', { version: firmware.name })}
</Text>
<Button primary onClick={installFirmware}>
{t('app:manager.installFirmware')}
{t('app:manager.firmware.update')}
</Button>
</Fragment>
) : null

4
src/components/ManagerPage/WorkflowDefault.js

@ -111,10 +111,10 @@ const WorkflowDefault = ({ device, deviceInfo, dashboardError, isGenuine, t }: P
style={{ marginBottom: 30, maxWidth: 362, width: '100%' }}
/>
<Text ff="Museo Sans|Regular" fontSize={7} color="black" style={{ marginBottom: 10 }}>
{t('manager:device.title')}
{t('app:manager.device.title')}
</Text>
<Text ff="Museo Sans|Light" fontSize={5} color="grey" align="center">
{t('manager:device.desc')}
{t('app:manager.device.desc')}
</Text>
</Box>
<Box flow={4} style={{ maxWidth: 460, padding: '60px 10px 0' }}>

82
src/components/base/Progress/index.js

@ -0,0 +1,82 @@
// @flow
import React, { Component } from 'react'
import styled, { keyframes } from 'styled-components'
import Box from 'components/base/Box'
const inifiteAnimation = keyframes`
0% {
left: 0
}
100% {
left: 102%
}
`
const fillInAnimation = keyframes`
0% {
transform: translate3d(-110%, 0, 0);
}
50% {
transform: translate3d(-30%, 0, 0);
}
100% {
transform: translate3d(0);
}
`
const Bar = styled(Box).attrs({
color: 'fog',
borderRadius: '2.5px',
})`
height: 5px;
width: 100%;
position: relative;
background-color: currentColor;
overflow: hidden;
`
const Progression = styled(Bar).attrs({
color: 'wallet',
})`
position: absolute;
top: 0;
left: 0;
${p =>
p.infinite
? `
animation: 1000ms ${inifiteAnimation} ease-out infinite;
`
: `
animation: ${p.timing}ms ${fillInAnimation} ease-out;
animation-fill-mode: forwards;
`};
`
type Props = {
infinite: boolean,
timing?: number,
color?: string,
}
type State = {}
class Progress extends Component<Props, State> {
static defaultProps = {
infinite: false,
timing: 3000,
color: 'wallet',
}
render() {
const { infinite, color, timing } = this.props
const styles = infinite ? { width: '20%' } : { width: '100%' }
return (
<Bar>
<Progression infinite={infinite} color={color} style={styles} timing={timing} />
</Bar>
)
}
}
export default Progress

1
src/helpers/deviceAccess.js

@ -19,6 +19,7 @@ export const withDevice: WithDevice = devicePath => {
return job =>
takeSemaphorePromise(sem, async () => {
const t = await retry(() => TransportNodeHid.open(devicePath), { maxRetry: 1 })
if (process.env.DEBUG_DEVICE) t.setDebugMode(true)
try {
const res = await job(t)
// $FlowFixMe

19
static/i18n/en/app.yml

@ -132,16 +132,23 @@ manager:
tabs:
apps: Apps
device: My device
installApps: Install
installFirmware: Update firmware
allApps: Apps
apps:
install: Install
all: Apps
installing: 'Installing {{app}}...'
uninstalling: 'Uninstalling {{app}}...'
installSuccess: '{{app}} app successfully installed'
uninstallSuccess: '{{app}} app successfully uninstalled'
alreadyInstalled: '{{app}} app is already installed'
help: To update an app, you have to uninstall the app and re install it.
firmware:
installed: 'Firmware {{version}}'
update: Update firmware
updateTitle: Firmware update
latest: 'A new firmware {{version}} is available'
title: Manager
subtitle: Get all your apps here
firmwareUpdate: Firmware update
latestFirmware: A new firmware {{version}} is available
plugYourDevice:
device:
title: Plug your device
desc: Please connect your Ledger device and follow the steps below to access the manager
cta: Plug my device

21
static/i18n/en/manager.yml

@ -1,21 +0,0 @@
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