ddasilva
7 years ago
2 changed files with 321 additions and 2 deletions
@ -0,0 +1,321 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import styled from 'styled-components' |
||||
|
import { Trans, translate } from 'react-i18next' |
||||
|
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' |
||||
|
|
||||
|
import type { T, Device } from 'types/common' |
||||
|
|
||||
|
import noop from 'lodash/noop' |
||||
|
|
||||
|
import Box from 'components/base/Box' |
||||
|
import Spinner from 'components/base/Spinner' |
||||
|
import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon' |
||||
|
import TranslatedError from 'components/TranslatedError' |
||||
|
|
||||
|
import IconCheck from 'icons/Check' |
||||
|
import IconExclamationCircle from 'icons/ExclamationCircle' |
||||
|
import IconInfoCircle from 'icons/InfoCircle' |
||||
|
import IconUsb from 'icons/Usb' |
||||
|
import IconHome from 'icons/Home' |
||||
|
|
||||
|
import * as IconDevice from 'icons/device' |
||||
|
|
||||
|
// TODO: CHECK IF COMPONENT CAN BE REMOVED
|
||||
|
|
||||
|
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 ListDevices = styled(Box).attrs({ |
||||
|
p: 3, |
||||
|
pt: 1, |
||||
|
flow: 2, |
||||
|
})`` |
||||
|
|
||||
|
const DeviceItem = styled(Box).attrs({ |
||||
|
bg: 'lightGrey', |
||||
|
borderRadius: 1, |
||||
|
alignItems: 'center', |
||||
|
color: 'dark', |
||||
|
ff: 'Open Sans|SemiBold', |
||||
|
fontSize: 4, |
||||
|
horizontal: true, |
||||
|
pr: 3, |
||||
|
pl: 0, |
||||
|
})` |
||||
|
cursor: pointer; |
||||
|
height: 54px; |
||||
|
` |
||||
|
const DeviceIcon = styled(Box).attrs({ |
||||
|
alignItems: 'center', |
||||
|
justifyContent: 'center', |
||||
|
color: 'graphite', |
||||
|
})` |
||||
|
width: 55px; |
||||
|
` |
||||
|
const DeviceSelected = styled(Box).attrs({ |
||||
|
alignItems: 'center', |
||||
|
bg: p => (p.selected ? 'wallet' : 'white'), |
||||
|
color: 'white', |
||||
|
justifyContent: 'center', |
||||
|
})` |
||||
|
border-radius: 50%; |
||||
|
border: 1px solid ${p => (p.selected ? p.theme.colors.wallet : p.theme.colors.fog)}; |
||||
|
height: 18px; |
||||
|
width: 18px; |
||||
|
` |
||||
|
|
||||
|
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 Info = styled(Box).attrs({ |
||||
|
alignItems: 'center', |
||||
|
color: p => (p.hasErrors ? 'alertRed' : 'grey'), |
||||
|
flow: 2, |
||||
|
fontSize: 3, |
||||
|
horizontal: true, |
||||
|
ml: 1, |
||||
|
})` |
||||
|
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 color="grey" size={16} /> |
||||
|
)} |
||||
|
</Box> |
||||
|
) |
||||
|
|
||||
|
StepCheck.defaultProps = { |
||||
|
hasErrors: false, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
appOpened: null | 'success' | 'fail', |
||||
|
genuineCheckStatus: null | 'success' | 'fail', |
||||
|
withGenuineCheck: boolean, |
||||
|
currency: CryptoCurrency, |
||||
|
devices: Device[], |
||||
|
deviceSelected: ?Device, |
||||
|
onChangeDevice: Device => void, |
||||
|
t: T, |
||||
|
error: ?Error, |
||||
|
} |
||||
|
|
||||
|
const emitChangeDevice = props => { |
||||
|
const { onChangeDevice, deviceSelected, devices } = props |
||||
|
|
||||
|
if (deviceSelected === null && devices.length > 0) { |
||||
|
onChangeDevice(devices[0]) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class DeviceConnect extends PureComponent<Props> { |
||||
|
static defaultProps = { |
||||
|
accountName: null, |
||||
|
appOpened: null, |
||||
|
devices: [], |
||||
|
deviceSelected: null, |
||||
|
onChangeDevice: noop, |
||||
|
withGenuineCheck: false, |
||||
|
} |
||||
|
|
||||
|
componentDidMount() { |
||||
|
emitChangeDevice(this.props) |
||||
|
} |
||||
|
|
||||
|
componentWillReceiveProps(nextProps) { |
||||
|
emitChangeDevice(nextProps) |
||||
|
} |
||||
|
|
||||
|
getStepState = stepStatus => ({ |
||||
|
success: stepStatus === 'success', |
||||
|
fail: stepStatus === 'fail', |
||||
|
}) |
||||
|
|
||||
|
render() { |
||||
|
const { |
||||
|
deviceSelected, |
||||
|
genuineCheckStatus, |
||||
|
withGenuineCheck, |
||||
|
appOpened, |
||||
|
error, |
||||
|
currency, |
||||
|
t, |
||||
|
onChangeDevice, |
||||
|
devices, |
||||
|
} = this.props |
||||
|
|
||||
|
const appState = this.getStepState(appOpened) |
||||
|
const genuineCheckState = this.getStepState(genuineCheckStatus) |
||||
|
|
||||
|
const hasDevice = devices.length > 0 |
||||
|
const hasMultipleDevices = devices.length > 1 |
||||
|
// TODO: place custom wording in trans tags into yml file
|
||||
|
/* eslint-disable react/jsx-no-literals */ |
||||
|
return ( |
||||
|
<Box flow={4} ff="Open Sans"> |
||||
|
<Step validated={hasDevice}> |
||||
|
<StepContent> |
||||
|
<StepIcon> |
||||
|
<IconUsb size={36} /> |
||||
|
</StepIcon> |
||||
|
<Box grow shrink> |
||||
|
<Trans i18nKey="app:deviceConnect.step1.connect" parent="div"> |
||||
|
Connect and unlock your <strong>Ledger device</strong> |
||||
|
</Trans> |
||||
|
</Box> |
||||
|
<StepCheck checked={hasDevice} /> |
||||
|
</StepContent> |
||||
|
{hasMultipleDevices && ( |
||||
|
<ListDevices> |
||||
|
<Box color="graphite" fontSize={3}> |
||||
|
{t('app:deviceConnect.step1.choose', { count: devices.length })} |
||||
|
</Box> |
||||
|
<Box flow={2}> |
||||
|
{devices.map(d => { |
||||
|
const Icon = IconDevice[d.product.replace(/\s/g, '')] |
||||
|
return ( |
||||
|
<DeviceItem key={d.path} onClick={() => onChangeDevice(d)}> |
||||
|
<DeviceIcon> |
||||
|
<Icon size={28} /> |
||||
|
</DeviceIcon> |
||||
|
<Box grow noShrink> |
||||
|
{`${d.manufacturer} ${d.product}`} |
||||
|
</Box> |
||||
|
<Box> |
||||
|
<DeviceSelected selected={d === deviceSelected}> |
||||
|
<IconCheck size={10} /> |
||||
|
</DeviceSelected> |
||||
|
</Box> |
||||
|
</DeviceItem> |
||||
|
) |
||||
|
})} |
||||
|
</Box> |
||||
|
</ListDevices> |
||||
|
)} |
||||
|
</Step> |
||||
|
|
||||
|
<Step validated={appState.success} hasErrors={appState.fail}> |
||||
|
{currency ? ( |
||||
|
<StepContent> |
||||
|
<StepIcon> |
||||
|
<WrapperIconCurrency> |
||||
|
<CryptoCurrencyIcon currency={currency} size={12} /> |
||||
|
</WrapperIconCurrency> |
||||
|
</StepIcon> |
||||
|
<Box grow shrink> |
||||
|
<Trans i18nKey="deviceConnect:step2.open" parent="div"> |
||||
|
{'Open the '} |
||||
|
<strong>{currency.name}</strong> |
||||
|
{' app on your device'} |
||||
|
</Trans> |
||||
|
</Box> |
||||
|
<StepCheck checked={appState.success} hasErrors={appState.fail} /> |
||||
|
</StepContent> |
||||
|
) : ( |
||||
|
<StepContent> |
||||
|
<StepIcon> |
||||
|
<WrapperIconCurrency> |
||||
|
<IconHome size={12} /> |
||||
|
</WrapperIconCurrency> |
||||
|
</StepIcon> |
||||
|
<Box grow shrink> |
||||
|
<Trans i18nKey="app:dashboard.open" parent="div"> |
||||
|
{'Navigate to the '} |
||||
|
<strong>{'dashboard'}</strong> |
||||
|
{' on your device'} |
||||
|
</Trans> |
||||
|
</Box> |
||||
|
<StepCheck checked={appState.success} hasErrors={appState.fail} /> |
||||
|
</StepContent> |
||||
|
)} |
||||
|
</Step> |
||||
|
|
||||
|
{/* GENUINE CHECK */} |
||||
|
{/* ------------- */} |
||||
|
|
||||
|
{withGenuineCheck && ( |
||||
|
<Step validated={genuineCheckState.success} hasErrors={genuineCheckState.fail}> |
||||
|
<StepContent> |
||||
|
<StepIcon> |
||||
|
<WrapperIconCurrency> |
||||
|
<IconCheck size={12} /> |
||||
|
</WrapperIconCurrency> |
||||
|
</StepIcon> |
||||
|
<Box grow shrink> |
||||
|
<Trans i18nKey="deviceConnect:stepGenuine.open" parent="div"> |
||||
|
{'Allow '} |
||||
|
<strong>{'Ledger Manager'}</strong> |
||||
|
{' on your device'} |
||||
|
</Trans> |
||||
|
</Box> |
||||
|
<StepCheck checked={genuineCheckState.success} hasErrors={genuineCheckState.fail} /> |
||||
|
</StepContent> |
||||
|
</Step> |
||||
|
)} |
||||
|
|
||||
|
{error ? ( |
||||
|
<Info hasErrors> |
||||
|
<IconInfoCircle size={12} /> |
||||
|
<Box shrink selectable> |
||||
|
<TranslatedError error={error} /> |
||||
|
</Box> |
||||
|
</Info> |
||||
|
) : null} |
||||
|
</Box> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default translate()(DeviceConnect) |
Loading…
Reference in new issue