Browse Source

Merge branch 'master' into libcore-features-2

master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
28177567da
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      .eslintrc
  2. 8
      src/components/AccountPage/index.js
  3. 8
      src/components/Breadcrumb/Step.js
  4. 3
      src/components/CurrentAddress/index.js
  5. 1
      src/components/DeviceConnect/index.js
  6. 3
      src/components/ExchangePage/ExchangeCard.js
  7. 22
      src/components/ExchangePage/index.js
  8. 16
      src/components/ExchangePage/logos/bigmama.js
  9. 26
      src/components/ExchangePage/logos/changelly.js
  10. 12
      src/components/ExchangePage/logos/coinhouse.js
  11. 18
      src/components/FeesField/BitcoinKind.js
  12. 10
      src/components/MainSideBar/index.js
  13. 9
      src/components/ManagerPage/AppSearchBar.js
  14. 27
      src/components/ManagerPage/AppsList.js
  15. 4
      src/components/ManagerPage/Dashboard.js
  16. 4
      src/components/ManagerPage/FirmwareUpdate.js
  17. 12
      src/components/ManagerPage/ManagerApp.js
  18. 1
      src/components/ManagerPage/PlugYourDevice.js
  19. 26
      src/components/ManagerPage/Workflow.js
  20. 30
      src/components/ManagerPage/WorkflowDefault.js
  21. 13
      src/components/ManagerPage/index.js
  22. 17
      src/components/Onboarding/OnboardingFooter.js
  23. 13
      src/components/Onboarding/helperComponents.js
  24. 2
      src/components/Onboarding/index.js
  25. 25
      src/components/Onboarding/steps/Analytics.js
  26. 4
      src/components/Onboarding/steps/Finish.js
  27. 116
      src/components/Onboarding/steps/GenuineCheck.js
  28. 24
      src/components/Onboarding/steps/Init.js
  29. 71
      src/components/Onboarding/steps/NoDevice.js
  30. 15
      src/components/Onboarding/steps/SelectDevice.js
  31. 6
      src/components/Onboarding/steps/SelectPIN/SelectPINblue.js
  32. 8
      src/components/Onboarding/steps/SelectPIN/SelectPINnano.js
  33. 8
      src/components/Onboarding/steps/SelectPIN/index.js
  34. 34
      src/components/Onboarding/steps/SetPassword.js
  35. 6
      src/components/Onboarding/steps/WriteSeed/WriteSeedBlue.js
  36. 6
      src/components/Onboarding/steps/WriteSeed/WriteSeedNano.js
  37. 8
      src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js
  38. 8
      src/components/Onboarding/steps/WriteSeed/index.js
  39. 2
      src/components/RequestAmount/index.js
  40. 10
      src/components/SettingsPage/index.js
  41. 1
      src/components/SettingsPage/sections/Tools.js
  42. 13
      src/components/ThrowBlock.js
  43. 6
      src/components/base/AccountsList/AccountRow.js
  44. 1
      src/components/base/AccountsList/index.js
  45. 3
      src/components/base/InputPassword/index.js
  46. 10
      src/components/base/LegacySelect/index.js
  47. 2
      src/components/base/SideBar/SideBarList.js
  48. 1
      src/components/base/SideBar/SideBarListItem.js
  49. 4
      src/components/base/StepperNumber/index.js
  50. 2
      src/components/modals/AddAccounts/index.js
  51. 6
      src/components/modals/AddAccounts/steps/04-step-finish.js
  52. 1
      src/components/modals/Debug.js
  53. 19
      src/components/modals/ReleaseNotes.js
  54. 4
      src/components/modals/Send/Footer.js
  55. 1
      src/config/global-tab.js
  56. 16
      src/icons/Cart.js
  57. 4
      src/icons/CheckCircle.js
  58. 16
      src/icons/EyeOff.js
  59. 16
      src/icons/Truck.js
  60. 9
      src/reducers/onboarding.js
  61. 6
      src/renderer/init.js
  62. 18
      static/i18n/en/app.yml
  63. 16
      static/i18n/en/onboarding.yml
  64. 13
      static/i18n/fr/app.yml
  65. 16
      static/i18n/fr/onboarding.yml
  66. BIN
      static/images/logos/shapeshift.png

7
.eslintrc

@ -54,6 +54,7 @@
"react/jsx-curly-brace-presence": 0,
"react/jsx-filename-extension": 0,
"react/jsx-no-target-blank": 0,
"react/jsx-no-literals": [1, {"noStrings": false}],
"react/prefer-stateless-function": 0,
"react/require-default-props": 0,
"react/sort-comp": [1, {
@ -73,4 +74,10 @@
"onlyFilesWithFlowAnnotation": true,
}
},
"overrides": [{
"files": [ "*stories.js" ],
"rules": {
"react/jsx-no-literals": 0
}
}]
}

8
src/components/AccountPage/index.js

@ -130,9 +130,11 @@ class AccountPage extends PureComponent<Props, State> {
)}
<Tooltip render={() => t('app:account.settings.title')}>
<ButtonSettings onClick={() => openModal(MODAL_SETTINGS_ACCOUNT, { account })}>
<Box align="center">
<IconAccountSettings size={16} />
</Box>
<Button small outlineGrey>
<Box justifyContent="center">
<IconAccountSettings size={16} />
</Box>
</Button>
</ButtonSettings>
</Tooltip>
</Box>

8
src/components/Breadcrumb/Step.js

@ -35,7 +35,13 @@ const StepNumber = styled(Box).attrs({
ff: 'Rubik|Regular',
})`
border-radius: 50%;
border: 1px solid ${p => (['active', 'valid'].includes(p.status) ? colors.wallet : colors.fog)};
border: 1px solid
${p =>
['active', 'valid'].includes(p.status)
? colors.wallet
: p.status === 'error'
? colors.alertRed
: colors.fog};
font-size: 10px;
height: ${RADIUS}px;
line-height: 10px;

3
src/components/CurrentAddress/index.js

@ -183,7 +183,8 @@ class CurrentAddress extends PureComponent<Props> {
<Box>
{accountName ? (
<Trans i18nKey="app:currentAddress.for" parent="div">
Address for <strong>{accountName}</strong>
{'Address for '}
<strong>{accountName}</strong>
</Trans>
) : (
t('app:currentAddress.title')

1
src/components/DeviceConnect/index.js

@ -200,6 +200,7 @@ class DeviceConnect extends PureComponent<Props> {
const hasDevice = devices.length > 0
const hasMultipleDevices = devices.length > 1
/* eslint-disable react/jsx-no-literals */
return (
<Box flow={4} ff="Open Sans">
<Step validated={hasDevice}>

3
src/components/ExchangePage/ExchangeCard.js

@ -20,7 +20,8 @@ export default function ExchangeCard({ t, card }: { t: T, card: CardType }) {
return (
<Card
horizontal
p={5}
py={5}
px={6}
style={{ cursor: 'pointer' }}
onClick={() => shell.openExternal(card.url)}
>

22
src/components/ExchangePage/index.js

@ -5,12 +5,12 @@ import { translate } from 'react-i18next'
import type { T } from 'types/common'
import { i } from 'helpers/staticPath'
import Box from 'components/base/Box'
import ExchangeCard from './ExchangeCard'
import CoinhouseLogo from './logos/coinhouse'
import ChangellyLogo from './logos/changelly'
import CoinmamaLogo from './logos/bigmama'
type Props = {
t: T,
@ -22,21 +22,27 @@ class ExchangePage extends PureComponent<Props> {
const cards = [
{
key: 'coinhouse',
url: 'https://www.coinhouse.com/',
url: 'https://www.coinhouse.com/r/157530',
logo: <CoinhouseLogo width={150} />,
desc: t('app:exchange.coinhouse'),
},
{
key: 'shapeshift',
url: 'https://shapeshift.io',
logo: <img alt="shapeshift logo" src={i('logos/shapeshift.png')} style={{ width: 150 }} />,
desc: t('app:exchange.shapeshift'),
key: 'changelly',
url: 'https://changelly.com/?ref_id=aac789605a01',
logo: <ChangellyLogo width={150} />,
desc: t('app:exchange.changelly'),
},
{
key: 'coinmama',
url: 'http://go.coinmama.com/visit/?bta=51801&nci=5343',
logo: <CoinmamaLogo width={150} />,
desc: t('app:exchange.coinmama'),
},
]
return (
<Box>
<Box ff="Museo Sans|Regular" color="dark" fontSize={7} mb={4}>
<Box ff="Museo Sans|Regular" color="dark" fontSize={7} mb={5}>
{t('app:exchange.title')}
</Box>
<Box flow={5}>{cards.map(card => <ExchangeCard key={card.key} t={t} card={card} />)}</Box>

16
src/components/ExchangePage/logos/bigmama.js

File diff suppressed because one or more lines are too long

26
src/components/ExchangePage/logos/changelly.js

@ -0,0 +1,26 @@
// @flow
import React from 'react'
const inner = (
<g fill="none" fillRule="evenodd">
<path
fill="#2D2E2C"
d="M42.48 17.066c0-3.537 2.813-6.542 6.538-6.542 2.417 0 3.922.994 5.106 2.23l-1.208 1.26c-1.012-1.043-2.196-1.842-3.923-1.842-2.59 0-4.564 2.156-4.564 4.894 0 2.714 2.023 4.87 4.687 4.87 1.629 0 2.838-.823 3.849-1.89l1.258 1.14c-1.283 1.405-2.812 2.422-5.205 2.422-3.725 0-6.537-2.98-6.537-6.542M56.355 6.549l1.9-.92v7.342c.838-1.356 2.17-2.447 4.366-2.447 3.083 0 4.884 2.036 4.884 5.016v7.778h-1.9V16c0-2.326-1.282-3.78-3.527-3.78-2.195 0-3.824 1.575-3.824 3.926v7.172h-1.899V6.548M79.105 18.714v-1.163a13.322 13.322 0 0 0-3.725-.533c-2.368 0-3.725 1.018-3.725 2.568 0 1.575 1.48 2.496 3.207 2.496 2.343 0 4.243-1.381 4.243-3.368zm-9.374.92c0-2.689 2.22-4.094 5.452-4.094 1.628 0 2.762.218 3.897.533v-.388c0-2.23-1.381-3.392-3.774-3.392-1.505 0-2.689.363-3.873.896l-.567-1.526c1.406-.63 2.763-1.066 4.637-1.066 1.801 0 3.207.484 4.12 1.38.888.873 1.332 2.085 1.332 3.684v7.657H79.08v-1.866c-.888 1.114-2.343 2.132-4.588 2.132-2.393 0-4.761-1.332-4.761-3.95zM84.165 10.79h1.9v2.181c.839-1.356 2.17-2.447 4.366-2.447 3.084 0 4.885 2.036 4.885 5.016v7.778h-1.9V16c0-2.326-1.283-3.78-3.527-3.78-2.196 0-3.824 1.575-3.824 3.926v7.172h-1.9V10.79M108.748 16.412c0-2.568-2.27-4.24-4.687-4.24-2.417 0-4.416 1.648-4.416 4.264 0 2.52 2.048 4.265 4.416 4.265 2.418 0 4.687-1.72 4.687-4.29zm-10.46 9.086l.864-1.453c1.455 1.042 3.084 1.599 4.909 1.599 2.812 0 4.638-1.527 4.638-4.459v-1.478c-1.11 1.454-2.665 2.642-5.008 2.642-3.059 0-5.994-2.254-5.994-5.913 0-3.658 2.96-5.912 5.994-5.912 2.393 0 3.947 1.163 4.983 2.52v-2.253h1.9v10.346c0 1.939-.593 3.416-1.629 4.434-1.134 1.115-2.836 1.672-4.86 1.672-2.121 0-4.095-.582-5.796-1.745zM123.323 16.315c-.222-2.278-1.554-4.216-4.07-4.216-2.196 0-3.873 1.793-4.145 4.216h8.215zm-3.725 5.67c1.751 0 2.985-.703 4.02-1.769l1.185 1.042c-1.283 1.405-2.837 2.35-5.254 2.35-3.504 0-6.365-2.64-6.365-6.542 0-3.634 2.59-6.542 6.118-6.542 3.774 0 5.945 2.956 5.945 6.64 0 .169 0 .362-.025.653h-10.114c.272 2.641 2.245 4.168 4.49 4.168zM127.897 7.356l1.9-.919v16.88h-1.9V7.358M133.27 6.549l1.9-.92v17.689h-1.9V6.548M137.688 26.637l.642-1.478c.616.315 1.184.46 1.997.46 1.135 0 1.85-.58 2.616-2.35l-5.798-12.478h2.097l4.638 10.443 4.095-10.443h2.023l-5.353 12.938c-1.086 2.593-2.319 3.538-4.243 3.538-1.061 0-1.85-.218-2.714-.63"
/>
<path
fill="#46B06E"
d="M29.688 18.399c-5.523 4.77-7.827 5.228-10.113 4.182l2.495-3.095c.368-.453.636-.991.773-1.555l1.007-4.128-4.204.988a4.103 4.103 0 0 0-1.58.758L14.912 18c-1.064-2.245-.599-4.508 4.259-9.933 4.589-5.125 14.3-6.302 17.178-6.543-.245 2.827-1.443 12.366-6.661 16.874zm-3.677 7.394a.893.893 0 0 1-.38.563c-.581.393-1.478.938-2.273 1.41l.04-.146c.386-1.491.382-2.64-.01-3.424 1.046-.312 2.177-.886 3.458-1.745-.31 1.155-.669 2.533-.835 3.342zm-8.736-2.676c-.329.408-.841.636-1.37.61l-.02-.002-1.256.002 4.354-5.144-5.238 4.276.003-1.213-.002-.04a1.603 1.603 0 0 1 .62-1.346l4.612-3.585a2.619 2.619 0 0 1 1.01-.484l1.88-.443-.45 1.848a2.54 2.54 0 0 1-.495.994l-3.648 4.527zm-7.493-8.87l-.15.038a52.609 52.609 0 0 1 1.437-2.233.912.912 0 0 1 .573-.372c.823-.163 2.226-.515 3.402-.82-.875 1.258-1.458 2.369-1.776 3.397-.797-.386-1.967-.39-3.486-.01zM37.11.03C36.57.053 23.883.627 18.07 7.118a53.482 53.482 0 0 0-1.609 1.876c-.842.227-3.8 1.016-5.109 1.275a2.37 2.37 0 0 0-1.49.968c-.89 1.273-2.445 3.888-2.51 3.999l-1.127 1.898 2.052-.876c1.344-.574 3.6-1.164 4.405-.678.132.08.27.211.293.583h.003c.016.935.277 1.832.758 2.752l-.279.216a3.035 3.035 0 0 0-1.175 2.53l-.007 3.51 3.574-.007a3.124 3.124 0 0 0 2.575-1.154l.22-.274c.937.473 1.85.727 2.803.742v.007c.378.022.512.157.593.287.495.79-.106 3.006-.69 4.327l-.896 2.018 1.936-1.11c.113-.064 2.776-1.59 4.071-2.465.513-.346.863-.866.986-1.464.264-1.285 1.067-4.19 1.298-5.018.603-.475 1.238-1 1.91-1.58 6.608-5.71 7.193-18.173 7.215-18.7l.032-.78-.793.031z"
/>
<path
fill="#46B06E"
d="M26.865 7.512a2.412 2.412 0 0 1 1.695-.688c.613 0 1.227.23 1.694.688a2.327 2.327 0 0 1 0 3.329 2.43 2.43 0 0 1-3.389 0 2.318 2.318 0 0 1-.702-1.665c0-.628.25-1.22.702-1.664zm1.695 5.455c.988 0 1.977-.37 2.73-1.109a3.75 3.75 0 0 0 0-5.364 3.915 3.915 0 0 0-5.46 0 3.735 3.735 0 0 0-1.132 2.682c0 1.014.402 1.966 1.131 2.682a3.885 3.885 0 0 0 2.73 1.11zM17.91 28.672a9.974 9.974 0 0 1-.899.742 13.68 13.68 0 0 1-1.966 1.216c-.685.35-1.395.645-2.12.891a17.22 17.22 0 0 1-2.22.598c-.373.084-.754.138-1.13.205l-.572.073-.285.037-.287.026-.574.054-.576.03c-.384.03-.77.028-1.155.038a32.09 32.09 0 0 1-3.384-.152c.132-.181.261-.365.386-.553.361-.55.685-1.126.97-1.715.562-1.184.97-2.402 1.318-3.618A36.241 36.241 0 0 1 6.6 23.02a32.838 32.838 0 0 1 1.574-3.41c-.422.476-.8.99-1.158 1.514-.361.525-.695 1.067-1.01 1.622a25.011 25.011 0 0 0-1.609 3.466c-.449 1.162-.913 2.305-1.488 3.365-.567 1.064-1.27 2.032-2.071 2.966l-.836.975 1.297.182c1.602.224 3.228.342 4.858.284.407-.02.814-.027 1.221-.07l.61-.057.608-.081c.405-.045.806-.135 1.208-.205.4-.095.801-.17 1.194-.29.395-.103.783-.235 1.171-.363.384-.144.766-.289 1.139-.458a14.047 14.047 0 0 0 2.138-1.177 11.66 11.66 0 0 0 1.863-1.544c.553-.58 1.063-1.208 1.412-1.916a7.616 7.616 0 0 1-.811.848"
/>
</g>
)
export default ({ width }: { width: number }) => (
<svg width={width} viewBox="0 0 150 34">
{inner}
</svg>
)

12
src/components/ExchangePage/logos/coinhouse.js

@ -1,9 +1,9 @@
// @flow
import React from 'react'
import React, { Fragment } from 'react'
export default ({ width }: { width: number }) => (
<svg width={width} version="1.1" viewBox="0 0 149.25 25.901">
const inner = (
<Fragment>
<path
d="m0 6.498c1.691-0.99 3.379-1.987 5.074-2.97 1.916-1.11 3.841-2.205 5.76-3.31 1.051-0.604 2.388 0.116 2.396 1.315 0.013 2.517 0 5.033 7e-3 7.55 2e-3 0.585-0.21 1-0.768 1.24-0.648 0.278-1.27 0.619-1.923 0.88-0.74 0.297-1.369-0.115-1.405-0.912-0.031-0.682-8e-3 -1.366-8e-3 -2.05 0-0.186 0.01-0.373 0-0.559-0.048-0.883-0.66-1.287-1.466-0.944-0.365 0.156-0.708 0.367-1.056 0.562-0.51 0.285-1.006 0.598-1.527 0.862-0.436 0.22-0.638 0.528-0.636 1.026 0.013 2.516 0.012 5.033 0 7.55-1e-3 0.46 0.193 0.75 0.584 0.966 1.852 1.018 1.45 0.786 1.494 2.663 0.012 0.497 0.02 0.995-3e-3 1.491-0.041 0.879-0.785 1.317-1.564 0.882-1.477-0.824-2.932-1.689-4.39-2.546-0.206-0.122-0.38-0.298-0.568-0.449v-13.247z"
fill="#222246"
@ -48,5 +48,11 @@ export default ({ width }: { width: number }) => (
d="m61.74 19.887h-1.658v-14.089h1.587c0.154 0.726 0.214 11.88 0.071 14.089"
fill="#202045"
/>
</Fragment>
)
export default ({ width }: { width: number }) => (
<svg width={width} viewBox="0 0 149.25 25.901">
{inner}
</svg>
)

18
src/components/FeesField/BitcoinKind.js

@ -3,6 +3,9 @@
import React, { Component } from 'react'
import type { Account } from '@ledgerhq/live-common/lib/types'
import styled from 'styled-components'
import { translate } from 'react-i18next'
import type { T } from 'types/common'
import InputCurrency from 'components/base/InputCurrency'
import Select from 'components/base/Select'
@ -15,6 +18,7 @@ type Props = {
account: Account,
feePerByte: number,
onChange: number => void,
t: T,
}
type FeeItem = {
@ -104,14 +108,14 @@ class FeesField extends Component<
}
render() {
const { account, feePerByte, error, onChange } = this.props
const { account, feePerByte, error, onChange, t } = this.props
const { items, selectedItem } = this.state
const { units } = account.currency
const satoshi = units[units.length - 1]
return (
<GenericContainer error={error} help="fee per byte">
<GenericContainer error={error} help={t('app:send.steps.amount.unitPerByte')}>
<Select width={156} options={items} value={selectedItem} onChange={this.onSelectChange} />
<InputCurrency
defaultUnit={satoshi}
@ -120,18 +124,22 @@ class FeesField extends Component<
value={feePerByte}
onChange={onChange}
onChangeFocus={this.onChangeFocus}
renderRight={<InputRight>{satoshi.code} per Byte</InputRight>}
renderRight={
<InputRight>
{t('app:send.steps.amount.unitPerByte', { unit: satoshi.code })}
</InputRight>
}
/>
</GenericContainer>
)
}
}
export default (props: Props) => (
export default translate()((props: Props) => (
<WithFeesAPI
currency={props.account.currency}
renderError={error => <FeesField {...props} error={error} />}
renderLoading={() => <FeesField {...props} />}
render={fees => <FeesField {...props} fees={fees} />}
/>
)
))

10
src/components/MainSideBar/index.js

@ -92,7 +92,7 @@ class MainSideBar extends PureComponent<Props> {
<SideBarListItem
label={t('app:dashboard.title')}
icon={IconPieChart}
iconActiveColor={'wallet'}
iconActiveColor="wallet"
onClick={this.handleClickDashboard}
isActive={pathname === '/'}
hasNotif={updateStatus === 'downloaded'}
@ -100,28 +100,28 @@ class MainSideBar extends PureComponent<Props> {
<SideBarListItem
label={t('app:send.title')}
icon={IconSend}
iconActiveColor={'wallet'}
iconActiveColor="wallet"
onClick={this.handleOpenSendModal}
disabled={accounts.length === 0}
/>
<SideBarListItem
label={t('app:receive.title')}
icon={IconReceive}
iconActiveColor={'wallet'}
iconActiveColor="wallet"
onClick={this.handleOpenReceiveModal}
disabled={accounts.length === 0}
/>
<SideBarListItem
label={t('app:sidebar.manager')}
icon={IconManager}
iconActiveColor={'wallet'}
iconActiveColor="wallet"
onClick={this.handleClickManager}
isActive={pathname === '/manager'}
/>
<SideBarListItem
label={t('app:sidebar.exchange')}
icon={IconExchange}
iconActiveColor={'wallet'}
iconActiveColor="wallet"
onClick={this.handleClickExchange}
isActive={pathname === '/exchange'}
/>

9
src/components/ManagerPage/AppSearchBar.js

@ -42,6 +42,13 @@ const SearchBarWrapper = styled(Box).attrs({
margin: 0 0 20px 0;
background-color: white;
padding: 0 13px;
${p =>
p.focused
? `
border: 1px solid #6490f1;
`
: 'border: 1px solid white;'};
`
const Input = styled.input.attrs({
@ -91,7 +98,7 @@ class AppSearchBar extends PureComponent<Props, State> {
return (
<Box>
<SearchBarWrapper align="center">
<SearchBarWrapper align="center" focused={focused}>
<SearchIcon size={16} style={{ color }} />
<Input
innerRef={c => (this.input = c)}

27
src/components/ManagerPage/AppsList.js

@ -1,4 +1,5 @@
// @flow
/* eslint-disable react/jsx-no-literals */ // FIXME
import React, { PureComponent } from 'react'
import styled from 'styled-components'
@ -15,6 +16,8 @@ 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 Spinner from 'components/base/Spinner'
import Button from 'components/base/Button'
import ExclamationCircle from 'icons/ExclamationCircle'
import Update from 'icons/Update'
@ -38,7 +41,7 @@ const ICONS_FALLBACK = {
}
type Status = 'loading' | 'idle' | 'busy' | 'success' | 'error'
type Mode = '' | 'installing' | 'uninstalling'
type Mode = 'home' | 'installing' | 'uninstalling'
type LedgerApp = {
name: string,
@ -71,7 +74,7 @@ class AppsList extends PureComponent<Props, State> {
error: null,
appsList: [],
app: '',
mode: '',
mode: 'home',
}
componentDidMount() {
@ -108,7 +111,7 @@ class AppsList extends PureComponent<Props, State> {
await installApp.send(data).toPromise()
this.setState({ status: 'success', app: '' })
} catch (err) {
this.setState({ status: 'error', error: err.message, app: '', mode: '' })
this.setState({ status: 'error', error: err.message, app: '', mode: 'home' })
}
}
@ -123,11 +126,11 @@ class AppsList extends PureComponent<Props, State> {
await uninstallApp.send(data).toPromise()
this.setState({ status: 'success', app: '' })
} catch (err) {
this.setState({ status: 'error', error: err.message, app: '', mode: '' })
this.setState({ status: 'error', error: err.message, app: '', mode: 'home' })
}
}
handleCloseModal = () => this.setState({ status: 'idle', mode: '' })
handleCloseModal = () => this.setState({ status: 'idle', mode: 'home' })
renderModal = () => {
const { t } = this.props
@ -152,7 +155,9 @@ class AppsList extends PureComponent<Props, State> {
<Box align="center" justify="center" flow={3}>
<div>{'error happened'}</div>
{error}
<button onClick={this.handleCloseModal}>close</button>
<Button primary onClick={this.handleCloseModal}>
close
</Button>
</Box>
) : status === 'success' ? (
<Box align="center" justify="center" flow={3}>
@ -167,7 +172,9 @@ class AppsList extends PureComponent<Props, State> {
{ app },
)}
</Text>
<button onClick={this.handleCloseModal}>close</button>
<Button primary onClick={this.handleCloseModal}>
close
</Button>
</Box>
) : null}
</ModalBody>
@ -178,7 +185,7 @@ class AppsList extends PureComponent<Props, State> {
renderList() {
const { appsList } = this.state
return (
return appsList.length > 0 ? (
<Box>
<AppSearchBar list={appsList}>
{items => (
@ -198,6 +205,10 @@ class AppsList extends PureComponent<Props, State> {
</AppSearchBar>
{this.renderModal()}
</Box>
) : (
<Box align="center" justify="center">
<Spinner size={50} />
</Box>
)
}

4
src/components/ManagerPage/Dashboard.js

@ -33,7 +33,7 @@ const Dashboard = ({ device, deviceInfo, t }: Props) => (
{t('app:manager.subtitle')}
</Text>
</Box>
<Box mt={7}>
<Box mt={5}>
<FirmwareUpdate
infos={{
targetId: deviceInfo.targetId,
@ -42,7 +42,7 @@ const Dashboard = ({ device, deviceInfo, t }: Props) => (
device={device}
/>
</Box>
<Box>
<Box mt={5}>
<AppsList device={device} targetId={deviceInfo.targetId} />
</Box>
</Box>

4
src/components/ManagerPage/FirmwareUpdate.js

@ -1,4 +1,6 @@
// @flow
/* eslint-disable react/jsx-no-literals */ // FIXME
import React, { PureComponent } from 'react'
import { translate } from 'react-i18next'
import isEqual from 'lodash/isEqual'
@ -102,7 +104,7 @@ class FirmwareUpdate extends PureComponent<Props, State> {
const { latestFirmware } = this.state
return (
<Card px={4} py={25}>
<Card p={4}>
<Box horizontal align="center" flow={2}>
<Box color="dark">
<NanoS size={30} />

12
src/components/ManagerPage/ManagerApp.js

@ -14,13 +14,14 @@ import Button from 'components/base/Button'
const Container = styled(Box).attrs({
horizontal: true,
m: 3,
my: 2,
mx: 3,
p: 4,
boxShadow: 0,
borderRadius: 4,
flow: 3,
flow: 2,
})`
width: 342px;
width: calc(50% - 30px);
background: white;
line-height: normal;
`
@ -37,7 +38,6 @@ const AppName = styled(Box).attrs({
color: 'dark',
})`
display: block;
width: 115px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -57,8 +57,8 @@ function ManagerApp({ name, version, icon, onInstall, onUninstall, t }: Props) {
return (
<Container>
<AppIcon src={iconUrl} />
<Box flex="1">
<AppName>{name}</AppName>
<Box flex="1" ml={3}>
<AppName flex={1}>{name}</AppName>
<Text ff="Open Sans|Regular" fontSize={3} color="grey">
{version}
</Text>

1
src/components/ManagerPage/PlugYourDevice.js

@ -1,4 +1,5 @@
// @flow
/* eslint-disable react/jsx-no-literals */ // FIXME
import React from 'react'
import { translate } from 'react-i18next'

26
src/components/ManagerPage/Workflow.js

@ -25,13 +25,16 @@ type Props = {
renderDefault: (
device: ?Device,
deviceInfo: ?DeviceInfo,
dashboardError: ?Error,
isGenuine: ?boolean,
error: {
dashboardError: ?Error,
genuineError: ?Error,
},
) => Node,
renderMcuUpdate: (deviceInfo: DeviceInfo) => Node,
renderFinalUpdate: (deviceInfo: DeviceInfo) => Node,
renderDashboard: (device: Device, deviceInfo: DeviceInfo) => Node,
renderError: (dashboardError: ?Error, genuineError: ?Error) => Node,
renderError?: (dashboardError: ?Error, genuineError: ?Error) => Node,
}
type State = {}
@ -52,14 +55,12 @@ class Workflow extends PureComponent<Props, State> {
<EnsureGenuine device={device}>
{(isGenuine: ?boolean, genuineError: ?Error) => {
if (dashboardError || genuineError) {
return renderError ? (
renderError(dashboardError, genuineError)
) : (
<div>
{dashboardError && <span>{dashboardError.message}</span>}
{genuineError && <span>{genuineError.message}</span>}
</div>
)
return renderError
? renderError(dashboardError, genuineError)
: renderDefault(device, deviceInfo, isGenuine, {
genuineError,
dashboardError,
})
}
if (deviceInfo && deviceInfo.mcu) {
@ -74,7 +75,10 @@ class Workflow extends PureComponent<Props, State> {
return renderDashboard(device, deviceInfo)
}
return renderDefault(device, deviceInfo, dashboardError, isGenuine)
return renderDefault(device, deviceInfo, isGenuine, {
genuineError,
dashboardError,
})
}}
</EnsureGenuine>
)}

30
src/components/ManagerPage/WorkflowDefault.js

@ -1,4 +1,6 @@
// @flow
/* eslint-disable react/jsx-no-literals */ // FIXME
import React from 'react'
import { Trans, translate } from 'react-i18next'
import styled from 'styled-components'
@ -8,7 +10,6 @@ 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'
@ -97,13 +98,15 @@ type Props = {
t: T,
device: ?Device,
deviceInfo: ?DeviceInfo,
dashboardError: ?Error,
errors: {
dashboardError: ?Error,
genuineError: ?Error,
},
isGenuine: boolean,
}
const WorkflowDefault = ({ device, deviceInfo, dashboardError, isGenuine, t }: Props) => (
<Box align="center">
<Space of={152} />
const WorkflowDefault = ({ device, deviceInfo, errors, isGenuine, t }: Props) => (
<Box align="center" justify="center" sticky>
<Box align="center" style={{ maxWidth: 460, padding: '0 10px' }}>
<img
src={i('logos/connectDevice.png')}
@ -117,7 +120,7 @@ const WorkflowDefault = ({ device, deviceInfo, dashboardError, isGenuine, t }: P
{t('app:manager.device.desc')}
</Text>
</Box>
<Box flow={4} style={{ maxWidth: 460, padding: '60px 10px 0' }}>
<Box flow={4} style={{ maxWidth: 460, padding: '60px 10px 0' }} ff="Open Sans|Regular">
{/* DEVICE CHECK */}
<Step validated={!!device}>
<StepContent>
@ -138,7 +141,7 @@ const WorkflowDefault = ({ device, deviceInfo, dashboardError, isGenuine, t }: P
</Step>
{/* DASHBOARD CHECK */}
<Step validated={!!device && !!deviceInfo} hasErrors={!!device && !!dashboardError}>
<Step validated={!!device && !!deviceInfo} hasErrors={!!device && !!errors.dashboardError}>
<StepContent>
<StepIcon>
<WrapperIconCurrency>
@ -152,14 +155,21 @@ const WorkflowDefault = ({ device, deviceInfo, dashboardError, isGenuine, t }: P
{' on your device'}
</Trans>
</Box>
<StepCheck checked={!!device && !!deviceInfo} hasErrors={!!device && !!dashboardError} />
<StepCheck
checked={!!device && !!deviceInfo}
hasErrors={!!device && !!errors.dashboardError}
/>
</StepContent>
</Step>
{/* GENUINE CHECK */}
<Step
validated={(!!device && !isNull(isGenuine) && isGenuine) || undefined}
hasErrors={(!!device && !isNull(isGenuine) && !isGenuine) || undefined}
validated={
(!!device && !isNull(isGenuine) && isGenuine && !errors.genuineError) || undefined
}
hasErrors={
(!!device && !isNull(isGenuine) && !isGenuine) || errors.genuineError || undefined
}
>
<StepContent>
<StepIcon>

13
src/components/ManagerPage/index.js

@ -1,4 +1,5 @@
// @flow
/* eslint-disable react/jsx-no-literals */ // FIXME
import React from 'react'
@ -24,11 +25,6 @@ type Error = {
function ManagerPage(): 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>
)}
@ -41,13 +37,16 @@ function ManagerPage(): Node {
renderDefault={(
device: ?Device,
deviceInfo: ?DeviceInfo,
dashboardError: ?Error,
isGenuine: ?boolean,
errors: {
dashboardError: ?Error,
genuineError: ?Error,
},
) => (
<WorkflowDefault
device={device}
deviceInfo={deviceInfo}
dashboardError={dashboardError}
errors={errors}
isGenuine={isGenuine}
/>
)}

17
src/components/Onboarding/OnboardingFooter.js

@ -1,22 +1,11 @@
// @flow
import React from 'react'
import styled from 'styled-components'
import { radii } from 'styles/theme'
import type { T } from 'types/common'
import Button from 'components/base/Button'
import Box from 'components/base/Box'
const Wrapper = styled(Box).attrs({
px: 5,
py: 3,
})`
border-top: 1px solid ${p => p.theme.colors.lightFog};
border-bottom-left-radius: ${radii[1]}px;
border-bottom-right-radius: ${radii[1]}px;
`
import { OnboardingFooterWrapper } from './helperComponents'
type Props = {
t: T,
@ -33,13 +22,13 @@ const OnboardingFooter = ({
...props
}: Props) => (
<Wrapper {...props}>
<OnboardingFooterWrapper {...props}>
<Button padded outlineGrey onClick={() => prevStep()}>
{t('app:common.back')}
</Button>
<Button padded disabled={isContinueDisabled} primary onClick={() => nextStep()} ml="auto">
{t('app:common.continue')}
</Button>
</Wrapper>
</OnboardingFooterWrapper>
)
export default OnboardingFooter

13
src/components/Onboarding/helperComponents.js

@ -32,12 +32,19 @@ export const Inner = styled(Box).attrs({
flow: 4,
})``
export const FixedTopContainer = styled(Box).attrs({
sticky: true,
mt: 170,
backgroundColor: 'red',
})``
// FOOTER
export const OnboardingFooter = styled(Box).attrs({
export const OnboardingFooterWrapper = styled(Box).attrs({
px: 5,
py: 3,
horizontal: true,
})`
border-top: 2px solid ${p => p.theme.colors.lightGrey};
border-top: 2px solid ${p => p.theme.colors.lightFog};
border-bottom-left-radius: ${radii[1]}px;
border-bottom-right-radius: ${radii[1]}px;
`
@ -50,7 +57,7 @@ type StepType = {
export function OptionRow({ step }: { step: StepType }) {
const { icon, desc } = step
return (
<Box horizontal m={'7px'} style={{ minWidth: 420 }}>
<Box horizontal m="7px" style={{ minWidth: 420 }}>
<Box justify="center">{icon}</Box>
<Box justify="center" shrink>
<OptionRowDesc>{desc}</OptionRowDesc>

2
src/components/Onboarding/index.js

@ -27,6 +27,7 @@ import Box from 'components/base/Box'
import Start from './steps/Start'
import InitStep from './steps/Init'
import NoDeviceStep from './steps/NoDevice'
import OnboardingBreadcrumb from './OnboardingBreadcrumb'
import SelectDevice from './steps/SelectDevice'
import SelectPIN from './steps/SelectPIN/index'
@ -46,6 +47,7 @@ const STEPS = {
analytics: Analytics,
finish: Finish,
start: Start,
noDevice: NoDeviceStep,
}
const mapStateToProps = state => ({

25
src/components/Onboarding/steps/Analytics.js

@ -6,7 +6,7 @@ import { connect } from 'react-redux'
import { saveSettings } from 'actions/settings'
import Box from 'components/base/Box'
import CheckBox from 'components/base/CheckBox'
import { Title, Description } from '../helperComponents'
import { Title, Description, FixedTopContainer } from '../helperComponents'
import OnboardingFooter from '../OnboardingFooter'
import type { StepProps } from '..'
@ -15,13 +15,11 @@ const mapDispatchToProps = { saveSettings }
type State = {
analyticsToggle: boolean,
termsConditionsToggle: boolean,
sentryLogsToggle: boolean,
}
const INITIAL_STATE = {
analyticsToggle: true,
termsConditionsToggle: false,
sentryLogsToggle: true,
}
@ -40,9 +38,6 @@ class Analytics extends PureComponent<StepProps, State> {
shareAnalytics: isChecked,
})
}
handleTermsToggle = () => {
this.setState({ termsConditionsToggle: !this.state.termsConditionsToggle })
}
handleNavBack = () => {
const { savePassword, prevStep } = this.props
@ -52,11 +47,11 @@ class Analytics extends PureComponent<StepProps, State> {
render() {
const { nextStep, t } = this.props
const { analyticsToggle, termsConditionsToggle, sentryLogsToggle } = this.state
const { analyticsToggle, sentryLogsToggle } = this.state
return (
<Box sticky pt={50}>
<Box grow alignItems="center" justifyContent="center">
<FixedTopContainer>
<Box grow alignItems="center">
<Title>{t('onboarding:analytics.title')}</Title>
<Description>{t('onboarding:analytics.desc')}</Description>
<Box mt={5}>
@ -78,15 +73,6 @@ class Analytics extends PureComponent<StepProps, State> {
<CheckBox isChecked={analyticsToggle} onChange={this.handleAnalyticsToggle} />
</Box>
</Container>
<Container>
<Box>
<AnalyticsTitle>{t('onboarding:analytics.termsConditions.title')}</AnalyticsTitle>
<AnalyticsText>{t('onboarding:analytics.termsConditions.desc')}</AnalyticsText>
</Box>
<Box justifyContent="center">
<CheckBox isChecked={termsConditionsToggle} onChange={this.handleTermsToggle} />
</Box>
</Container>
</Box>
</Box>
<OnboardingFooter
@ -96,9 +82,8 @@ class Analytics extends PureComponent<StepProps, State> {
t={t}
nextStep={nextStep}
prevStep={this.handleNavBack}
isContinueDisabled={!termsConditionsToggle}
/>
</Box>
</FixedTopContainer>
)
}
}

4
src/components/Onboarding/steps/Finish.js

@ -39,8 +39,8 @@ const socialMedia = [
export default (props: StepProps) => {
const { finish, t } = props
return (
<Box sticky pt={200}>
<Box grow alignItems="center">
<Box sticky justifyContent="center">
<Box alignItems="center">
<Box color="positiveGreen">
<IconCheckCircle size={44} />
</Box>

116
src/components/Onboarding/steps/GenuineCheck.js

@ -4,13 +4,13 @@ import React, { PureComponent, Fragment } from 'react'
import { shell } from 'electron'
import { connect } from 'react-redux'
import styled from 'styled-components'
import { radii, colors } from 'styles/theme'
import { colors } from 'styles/theme'
import type { T } from 'types/common'
import { updateGenuineCheck } from 'reducers/onboarding'
import Box, { Card } from 'components/base/Box'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import RadioGroup from 'components/base/RadioGroup'
import GenuineCheckModal from 'components/GenuineCheckModal'
@ -19,7 +19,13 @@ import IconLedgerNanoError from 'icons/illustrations/LedgerNanoError'
import IconLedgerBlueError from 'icons/illustrations/LedgerBlueError'
import IconCheck from 'icons/Check'
import { Title, Description, IconOptionRow } from '../helperComponents'
import {
Title,
Description,
IconOptionRow,
FixedTopContainer,
OnboardingFooterWrapper,
} from '../helperComponents'
import type { StepProps } from '..'
import OnboardingFooter from '../OnboardingFooter'
@ -120,15 +126,15 @@ class GenuineCheck extends PureComponent<StepProps, State> {
}
return (
<Box sticky pt={50}>
<Box grow alignItems="center" justifyContent="center">
<FixedTopContainer>
<Box grow alignItems="center">
<Title>{t('onboarding:genuineCheck.title')}</Title>
<Description>{t('onboarding:genuineCheck.desc')}</Description>
<Box mt={5}>
<CardWrapper>
<Box justify="center">
<Box horizontal>
<IconOptionRow>1.</IconOptionRow>
<IconOptionRow>{'1.'}</IconOptionRow>
<CardTitle>{t('onboarding:genuineCheck.steps.step1.title')}</CardTitle>
</Box>
</Box>
@ -145,16 +151,20 @@ class GenuineCheck extends PureComponent<StepProps, State> {
<CardWrapper isDisabled={!genuine.pinStepPass}>
<Box justify="center">
<Box horizontal>
<IconOptionRow>2.</IconOptionRow>
<IconOptionRow color={!genuine.pinStepPass ? 'grey' : 'wallet'}>
{'2.'}
</IconOptionRow>
<CardTitle>{t('onboarding:genuineCheck.steps.step2.title')}</CardTitle>
</Box>
</Box>
<Box justify="center">
<RadioGroup
items={this.getButtonLabel()}
activeKey={cachedRecoveryStepButton}
onChange={item => this.handleButtonPass(item, 'recoveryStepPass')}
/>
{genuine.pinStepPass && (
<RadioGroup
items={this.getButtonLabel()}
activeKey={cachedRecoveryStepButton}
onChange={item => this.handleButtonPass(item, 'recoveryStepPass')}
/>
)}
</Box>
</CardWrapper>
</Box>
@ -162,28 +172,32 @@ class GenuineCheck extends PureComponent<StepProps, State> {
<CardWrapper isDisabled={!genuine.recoveryStepPass}>
<Box justify="center">
<Box horizontal>
<IconOptionRow>3.</IconOptionRow>
<IconOptionRow color={!genuine.recoveryStepPass ? 'grey' : 'wallet'}>
{'3.'}
</IconOptionRow>
<CardTitle>{t('onboarding:genuineCheck.steps.step3.title')}</CardTitle>
</Box>
</Box>
<Box justify="center">
{genuine.isDeviceGenuine ? (
<Box horizontal align="center" flow={1} color={colors.wallet}>
<IconCheck size={16} />
<GenuineSuccessText>
{t('onboarding:genuineCheck.isGenuinePassed')}
</GenuineSuccessText>
</Box>
) : (
<Button
primary
disabled={!genuine.recoveryStepPass}
onClick={this.handleOpenGenuineCheckModal}
>
{t('onboarding:genuineCheck.buttons.genuineCheck')}
</Button>
)}
</Box>
{genuine.recoveryStepPass && (
<Box justify="center">
{genuine.isDeviceGenuine ? (
<Box horizontal align="center" flow={1} color={colors.wallet}>
<IconCheck size={16} />
<GenuineSuccessText>
{t('onboarding:genuineCheck.isGenuinePassed')}
</GenuineSuccessText>
</Box>
) : (
<Button
primary
disabled={!genuine.recoveryStepPass}
onClick={this.handleOpenGenuineCheckModal}
>
{t('onboarding:genuineCheck.buttons.genuineCheck')}
</Button>
)}
</Box>
)}
</CardWrapper>
</Box>
</Box>
@ -201,7 +215,7 @@ class GenuineCheck extends PureComponent<StepProps, State> {
onClose={this.handleCloseGenuineCheckModal}
onGenuineCheck={this.handleGenuineCheck}
/>
</Box>
</FixedTopContainer>
)
}
}
@ -246,27 +260,14 @@ export function GenuineCheckFail({
</Fragment>
)}
</Box>
<Wrapper horizontal>
<Button
padded
outline
onClick={() => {
redoGenuineCheck()
}}
>
<OnboardingFooterWrapper>
<Button padded outlineGrey onClick={() => redoGenuineCheck()}>
{t('app:common.back')}
</Button>
<Button
padded
danger
onClick={() => {
contactSupport()
}}
ml="auto"
>
<Button padded danger onClick={() => contactSupport()} ml="auto">
{t('onboarding:genuineCheck.buttons.contactSupport')}
</Button>
</Wrapper>
</OnboardingFooterWrapper>
</Box>
)
}
@ -281,23 +282,22 @@ export const CardTitle = styled(Box).attrs({
pl: 2,
})``
const Wrapper = styled(Box).attrs({
px: 5,
py: 3,
})`
border-top: 2px solid ${p => p.theme.colors.lightGrey};
border-bottom-left-radius: ${radii[1]}px;
border-bottom-right-radius: ${radii[1]}px;
`
const CardWrapper = styled(Card).attrs({
const CardWrapper = styled(Box).attrs({
horizontal: true,
p: 5,
borderRadius: '4px',
justify: 'space-between',
})`
width: 580px;
height: 74px;
transition: all ease-in-out 0.2s;
color: ${p => (p.isDisabled ? p.theme.colors.grey : p.theme.colors.black)};
border: ${p => `1px ${p.isDisabled ? 'dashed' : 'solid'} ${p.theme.colors.fog}`};
pointer-events: ${p => (p.isDisabled ? 'none' : 'auto')};
background-color: ${p => (p.isDisabled ? p.theme.colors.lightGrey : p.theme.colors.white)};
opacity: ${p => (p.isDisabled ? 0.7 : 1)};
&:hover {
cursor: pointer;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.05);
}
`

24
src/components/Onboarding/steps/Init.js

@ -1,7 +1,6 @@
// @flow
import React, { PureComponent } from 'react'
import { shell } from 'electron'
import { connect } from 'react-redux'
import { colors } from 'styles/theme'
@ -22,7 +21,7 @@ const mapDispatchToProps = { flowType }
class Init extends PureComponent<StepProps, *> {
render() {
const { nextStep, t } = this.props
const { t, flowType, jumpStep } = this.props
const optionCards = [
{
@ -30,8 +29,8 @@ class Init extends PureComponent<StepProps, *> {
icon: <IconPlus size={20} />,
title: t('onboarding:init.newDevice.title'),
onClick: () => {
nextStep()
this.props.flowType('newDevice')
jumpStep('selectDevice')
flowType('newDevice')
},
},
{
@ -39,8 +38,8 @@ class Init extends PureComponent<StepProps, *> {
icon: <IconRecover size={20} />,
title: t('onboarding:init.restoreDevice.title'),
onClick: () => {
nextStep()
this.props.flowType('restoreDevice')
jumpStep('selectDevice')
flowType('restoreDevice')
},
},
{
@ -48,8 +47,8 @@ class Init extends PureComponent<StepProps, *> {
icon: <IconCheck size={20} />,
title: t('onboarding:init.initializedDevice.title'),
onClick: () => {
nextStep()
this.props.flowType('initializedDevice')
jumpStep('selectDevice')
flowType('initializedDevice')
},
},
{
@ -57,15 +56,15 @@ class Init extends PureComponent<StepProps, *> {
icon: <IconExternalLink size={20} />,
title: t('onboarding:init.noDevice.title'),
onClick: () => {
shell.openExternal('https://www.ledger.fr/')
this.props.flowType('noDevice')
jumpStep('noDevice')
flowType('noDevice')
},
},
]
return (
<Box sticky pt={130}>
<Box align="center" justifyContent="center">
<Box sticky justifyContent="center">
<Box align="center">
<Box color="wallet">
<IconUser size={36} />
</Box>
@ -117,6 +116,7 @@ const InitCardContainer = styled(Box).attrs({
border: 1px solid ${p => p.theme.colors.fog};
width: 530px;
height: 70px;
transition: all ease-in-out 0.2s;
&:hover {
cursor: pointer;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.05);

71
src/components/Onboarding/steps/NoDevice.js

@ -0,0 +1,71 @@
// @flow
import React, { PureComponent } from 'react'
import { shell } from 'electron'
import Box from 'components/base/Box'
import IconUser from 'icons/User'
import IconCart from 'icons/Cart'
import IconTruck from 'icons/Truck'
import IconInfoCircle from 'icons/InfoCircle'
import Button from '../../base/Button/index'
import { Title, OnboardingFooterWrapper } from '../helperComponents'
import { OptionFlowCard } from './Init'
import type { StepProps } from '..'
class NoDevice extends PureComponent<StepProps, *> {
render() {
const { t, prevStep } = this.props
const optionCards = [
{
key: 'buyNew',
icon: <IconCart size={20} />,
title: t('onboarding:noDevice.buyNew.title'),
onClick: () => {
shell.openExternal('https://www.ledgerwallet.com/')
},
},
{
key: 'trackOrder',
icon: <IconTruck size={20} />,
title: t('onboarding:noDevice.trackOrder.title'),
onClick: () => {
shell.openExternal('http://order.ledgerwallet.com/')
},
},
{
key: 'learnMore',
icon: <IconInfoCircle size={20} />,
title: t('onboarding:noDevice.learnMore.title'),
onClick: () => {
shell.openExternal('https://www.ledgerwallet.com/')
},
},
]
return (
<Box sticky pt={130}>
<Box grow alignItems="center">
<Box color="wallet">
<IconUser size={36} />
</Box>
<Box m={5} style={{ maxWidth: 480 }}>
<Title>{t('onboarding:noDevice.title')}</Title>
</Box>
<Box pt={4} flow={4}>
{optionCards.map(card => <OptionFlowCard key={card.key} card={card} />)}
</Box>
</Box>
<OnboardingFooterWrapper>
<Button padded outlineGrey onClick={() => prevStep()} mr="auto">
{t('app:common.back')}
</Button>
</OnboardingFooterWrapper>
</Box>
)
}
}
export default NoDevice

15
src/components/Onboarding/steps/SelectDevice.js

@ -11,7 +11,7 @@ import Box from 'components/base/Box'
import IconCheckCirle from 'icons/Check'
import IconLedgerNano from 'icons/illustrations/LedgerNano'
import IconLedgerBlue from 'icons/illustrations/LedgerBlue'
import { Title, Inner } from '../helperComponents'
import { Title, Inner, FixedTopContainer } from '../helperComponents'
import OnboardingFooter from '../OnboardingFooter'
import type { StepProps } from '..'
@ -32,11 +32,11 @@ class SelectDevice extends PureComponent<StepProps, {}> {
}
}
render() {
const { t, onboarding, prevStep } = this.props
const { t, onboarding, jumpStep } = this.props
return (
<Box sticky>
<Box grow alignItems="center" justifyContent="center">
<Box m={5}>
<FixedTopContainer>
<Box grow alignItems="center">
<Box mb={5}>
<Title>{t('onboarding:selectDevice.title')}</Title>
</Box>
<Box pt={4}>
@ -66,13 +66,12 @@ class SelectDevice extends PureComponent<StepProps, {}> {
</Box>
<OnboardingFooter
horizontal
flow={2}
t={t}
nextStep={this.handleContinue}
prevStep={prevStep}
prevStep={() => jumpStep('init')}
isContinueDisabled={onboarding.isLedgerNano === null}
/>
</Box>
</FixedTopContainer>
)
}
}

6
src/components/Onboarding/steps/SelectPIN/SelectPINblue.js

@ -23,17 +23,17 @@ class SelectPIN extends PureComponent<Props, *> {
const stepsLedgerBlue = [
{
key: 'step1',
icon: <IconOptionRow>1.</IconOptionRow>,
icon: <IconOptionRow>{'1.'}</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.ledgerBlue.step1'),
},
{
key: 'step2',
icon: <IconOptionRow>2.</IconOptionRow>,
icon: <IconOptionRow>{'2.'}</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.ledgerBlue.step2'),
},
{
key: 'step3',
icon: <IconOptionRow>3.</IconOptionRow>,
icon: <IconOptionRow>{'3.'}</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.ledgerBlue.step3'),
},
]

8
src/components/Onboarding/steps/SelectPIN/SelectPINnano.js

@ -23,22 +23,22 @@ class SelectPINnano extends PureComponent<Props, *> {
const stepsLedgerNano = [
{
key: 'step1',
icon: <IconOptionRow>1.</IconOptionRow>,
icon: <IconOptionRow>{'1.'}</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.ledgerNano.step1'),
},
{
key: 'step2',
icon: <IconOptionRow>2.</IconOptionRow>,
icon: <IconOptionRow>{'2.'}</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.ledgerNano.step2'),
},
{
key: 'step3',
icon: <IconOptionRow>3.</IconOptionRow>,
icon: <IconOptionRow>{'3.'}</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.ledgerNano.step3'),
},
{
key: 'step4',
icon: <IconOptionRow>4.</IconOptionRow>,
icon: <IconOptionRow>{'4.'}</IconOptionRow>,
desc: t('onboarding:selectPIN.instructions.ledgerNano.step4'),
},
]

8
src/components/Onboarding/steps/SelectPIN/index.js

@ -4,7 +4,7 @@ import React from 'react'
import Box from 'components/base/Box'
import { Title } from '../../helperComponents'
import { Title, FixedTopContainer } from '../../helperComponents'
import OnboardingFooter from '../../OnboardingFooter'
import SelectPINnano from './SelectPINnano'
import SelectPINblue from './SelectPINblue'
@ -15,14 +15,14 @@ export default (props: StepProps) => {
const { nextStep, prevStep, t, onboarding } = props
return (
<Box sticky pt={50}>
<Box grow alignItems="center" justifyContent="center">
<FixedTopContainer>
<Box grow alignItems="center">
<Title>{t('onboarding:selectPIN.title')}</Title>
<Box align="center" mt={7}>
{onboarding.isLedgerNano ? <SelectPINnano /> : <SelectPINblue />}
</Box>
</Box>
<OnboardingFooter horizontal flow={2} t={t} nextStep={nextStep} prevStep={prevStep} />
</Box>
</FixedTopContainer>
)
}

34
src/components/Onboarding/steps/SetPassword.js

@ -2,8 +2,7 @@
import React, { PureComponent, Fragment } from 'react'
import bcrypt from 'bcryptjs'
import { colors, radii } from 'styles/theme'
import styled from 'styled-components'
import { colors } from 'styles/theme'
import { setEncryptionKey } from 'helpers/db'
@ -15,7 +14,13 @@ import IconChevronRight from 'icons/ChevronRight'
import PasswordForm from '../../SettingsPage/PasswordForm'
import type { StepProps } from '..'
import { Title, Description, DisclaimerBox } from '../helperComponents'
import {
Title,
Description,
DisclaimerBox,
FixedTopContainer,
OnboardingFooterWrapper,
} from '../helperComponents'
type State = {
currentPassword: string,
@ -90,10 +95,10 @@ class SetPassword extends PureComponent<StepProps, State> {
]
return (
<Box sticky pt={50}>
<Box grow alignItems="center" justify="center">
<FixedTopContainer>
<Box grow alignItems="center">
<Fragment>
<Box mb={3} alignItems="center">
<Box alignItems="center">
<Title>{t('onboarding:setPassword.title')}</Title>
<Description style={{ maxWidth: 620 }}>
{t('onboarding:setPassword.desc')}
@ -117,7 +122,7 @@ class SetPassword extends PureComponent<StepProps, State> {
</Fragment>
</Box>
<CustomFooter>
<OnboardingFooterWrapper>
<Button padded outlineGrey onClick={() => prevStep()}>
{t('app:common.back')}
</Button>
@ -134,21 +139,10 @@ class SetPassword extends PureComponent<StepProps, State> {
{t('app:common.continue')}
</Button>
</Box>
</CustomFooter>
</Box>
</OnboardingFooterWrapper>
</FixedTopContainer>
)
}
}
export default SetPassword
const CustomFooter = styled(Box).attrs({
px: 5,
py: 3,
horizontal: true,
align: 'center',
})`
border-top: 1px solid ${p => p.theme.colors.lightFog};
border-bottom-left-radius: ${radii[1]}px;
border-bottom-right-radius: ${radii[1]}px;
`

6
src/components/Onboarding/steps/WriteSeed/WriteSeedBlue.js

@ -29,17 +29,17 @@ class WriteSeedBlue extends PureComponent<Props, *> {
const steps = [
{
key: 'step1',
icon: <IconOptionRow>1.</IconOptionRow>,
icon: <IconOptionRow>{'1.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.blue.step1'),
},
{
key: 'step2',
icon: <IconOptionRow>2.</IconOptionRow>,
icon: <IconOptionRow>{'2.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.blue.step2'),
},
{
key: 'step3',
icon: <IconOptionRow>3.</IconOptionRow>,
icon: <IconOptionRow>{'3.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.blue.step3'),
},
]

6
src/components/Onboarding/steps/WriteSeed/WriteSeedNano.js

@ -29,17 +29,17 @@ class WriteSeedNano extends PureComponent<Props, *> {
const steps = [
{
key: 'step1',
icon: <IconOptionRow>1.</IconOptionRow>,
icon: <IconOptionRow>{'1.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.nano.step1'),
},
{
key: 'step2',
icon: <IconOptionRow>2.</IconOptionRow>,
icon: <IconOptionRow>{'2.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.nano.step2'),
},
{
key: 'step3',
icon: <IconOptionRow>3.</IconOptionRow>,
icon: <IconOptionRow>{'3.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.nano.step3'),
},
]

8
src/components/Onboarding/steps/WriteSeed/WriteSeedRestore.js

@ -30,22 +30,22 @@ class WriteSeedRestore extends PureComponent<Props, *> {
const steps = [
{
key: 'step1',
icon: <IconOptionRow>1.</IconOptionRow>,
icon: <IconOptionRow>{'1.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.restore.step1'),
},
{
key: 'step2',
icon: <IconOptionRow>2.</IconOptionRow>,
icon: <IconOptionRow>{'2.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.restore.step2'),
},
{
key: 'step3',
icon: <IconOptionRow>3.</IconOptionRow>,
icon: <IconOptionRow>{'3.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.restore.step3'),
},
{
key: 'step4',
icon: <IconOptionRow>4.</IconOptionRow>,
icon: <IconOptionRow>{'4.'}</IconOptionRow>,
desc: t('onboarding:writeSeed.restore.step4'),
},
]

8
src/components/Onboarding/steps/WriteSeed/index.js

@ -9,15 +9,15 @@ import OnboardingFooter from '../../OnboardingFooter'
import WriteSeedNano from './WriteSeedNano'
import WriteSeedBlue from './WriteSeedBlue'
import WriteSeedRestore from './WriteSeedRestore'
import { FixedTopContainer } from '../../helperComponents'
import type { StepProps } from '../..'
export default (props: StepProps) => {
const { nextStep, prevStep, t, onboarding } = props
return (
<Box sticky pt={50}>
<Box grow alignItems="center" justifyContent="center">
<FixedTopContainer>
<Box grow alignItems="center">
{onboarding.flowType === 'restoreDevice' ? (
<WriteSeedRestore />
) : onboarding.isLedgerNano ? (
@ -34,6 +34,6 @@ export default (props: StepProps) => {
nextStep={nextStep}
prevStep={prevStep}
/>
</Box>
</FixedTopContainer>
)
}

2
src/components/RequestAmount/index.js

@ -155,7 +155,7 @@ export class RequestAmount extends PureComponent<Props> {
onChange={this.onLeftChange}
renderRight={<InputRight>{account.unit.code}</InputRight>}
/>
<InputCenter>=</InputCenter>
<InputCenter>{'='}</InputCenter>
<InputCurrency
containerProps={containerProps}
defaultUnit={rightUnit}

10
src/components/SettingsPage/index.js

@ -12,6 +12,7 @@ import type { T } from 'types/common'
import type { SaveSettings } from 'actions/settings'
import { saveSettings } from 'actions/settings'
import { accountsSelector } from 'reducers/accounts'
import Pills from 'components/base/Pills'
import Box from 'components/base/Box'
@ -25,6 +26,7 @@ import SectionAbout from './sections/About'
// maybe even each single settings row should be connected!!
const mapStateToProps = state => ({
settings: state.settings,
accountsCount: accountsSelector(state).length,
})
const mapDispatchToProps = {
@ -38,6 +40,7 @@ type Props = {
match: Match,
saveSettings: SaveSettings,
settings: Settings,
accountsCount: number,
t: T,
}
@ -102,7 +105,7 @@ class SettingsPage extends PureComponent<Props, State> {
}
render() {
const { match, settings, t, i18n, saveSettings } = this.props
const { match, settings, t, i18n, saveSettings, accountsCount } = this.props
const { tab } = this.state
const props = {
t,
@ -112,15 +115,16 @@ class SettingsPage extends PureComponent<Props, State> {
}
const defaultItem = this._items[0]
const items = this._items.filter(item => item.key !== 'currencies' || accountsCount > 0)
return (
<Box>
<Box ff="Museo Sans|Regular" color="dark" fontSize={7} mb={5}>
{t('app:settings.title')}
</Box>
<Pills mb={4} items={this._items} activeKey={tab.key} onChange={this.handleChangeTab} />
<Pills mb={4} items={items} activeKey={tab.key} onChange={this.handleChangeTab} />
<Switch>
{this._items.map(i => (
{items.map(i => (
<Route key={i.key} path={`${match.url}/${i.key}`} render={i.value && i.value(props)} />
))}
<Route render={defaultItem.value && defaultItem.value(props)} />

1
src/components/SettingsPage/sections/Tools.js

@ -1,4 +1,5 @@
// @flow
/* eslint-disable react/jsx-no-literals */ // FIXME
import React, { PureComponent } from 'react'

13
src/components/ThrowBlock.js

@ -5,10 +5,13 @@ import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { shell, remote } from 'electron'
import qs from 'querystring'
import { translate } from 'react-i18next'
import { rgba } from 'styles/helpers'
import db from 'helpers/db'
import type { T } from 'types/common'
import ExportLogsBtn from 'components/ExportLogsBtn'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
@ -16,6 +19,7 @@ import TranslatedError from './TranslatedError'
type Props = {
children: any,
t: T,
}
type State = {
@ -79,6 +83,7 @@ ${error.stack}
render() {
const { error } = this.state
const { t } = this.props
if (error) {
return (
<Container>
@ -87,14 +92,14 @@ ${error.stack}
</Inner>
<Box horizontal flow={2}>
<Button primary onClick={this.handleRestart}>
{'Restart app'}
{t('app:crash.restart')}
</Button>
<Button danger onClick={this.handleReset}>
{'Reset app files'}
{t('app:crash.reset')}
</Button>
<ExportLogsBtn />
<Button primary onClick={this.handleCreateIssue}>
{'Create ticket'}
{t('app:crash.createTicket')}
</Button>
</Box>
</Container>
@ -104,4 +109,4 @@ ${error.stack}
}
}
export default ThrowBlock
export default translate()(ThrowBlock)

6
src/components/base/AccountsList/AccountRow.js

@ -13,6 +13,7 @@ import FormattedVal from 'components/base/FormattedVal'
import Input from 'components/base/Input'
import IconEdit from 'icons/Edit'
import IconCheck from 'icons/Check'
import type { T } from 'types/common'
type Props = {
account: Account,
@ -20,6 +21,7 @@ type Props = {
isDisabled?: boolean,
onClick: Account => void,
onAccountUpdate: Account => void,
t: T,
}
type State = {
@ -71,7 +73,7 @@ export default class AccountRow extends PureComponent<Props, State> {
_input = null
render() {
const { account, isChecked, onClick, isDisabled } = this.props
const { account, isChecked, onClick, isDisabled, t } = this.props
const { isEditing, accountNameCopy } = this.state
return (
@ -100,7 +102,7 @@ export default class AccountRow extends PureComponent<Props, State> {
{!isEditing && (
<Edit onClick={this.handleEditClick}>
<IconEdit size={13} />
<span>{'edit name'}</span>
<span>{t('app:addAccounts.editName')}</span>
</Edit>
)}
<FormattedVal

1
src/components/base/AccountsList/index.js

@ -67,6 +67,7 @@ const AccountsList = ({
isChecked={checkedIds.find(id => id === account.id) !== undefined}
onClick={onToggleAccount}
onAccountUpdate={onUpdateAccount}
t={t}
/>
))}
{isLoading && (

3
src/components/base/InputPassword/index.js

@ -14,6 +14,7 @@ import Box from 'components/base/Box'
import Input from 'components/base/Input'
import IconEye from 'icons/Eye'
import IconEyeOff from 'icons/EyeOff'
const InputRight = styled(Box).attrs({
color: 'grey',
@ -99,7 +100,7 @@ class InputPassword extends PureComponent<Props, State> {
onChange={this.handleChange}
renderRight={
<InputRight onClick={this.toggleInputType}>
<IconEye size={16} />
{inputType === 'password' ? <IconEye size={16} /> : <IconEyeOff size={16} />}
</InputRight>
}
/>

10
src/components/base/LegacySelect/index.js

@ -4,6 +4,9 @@ import React, { PureComponent } from 'react'
import Downshift from 'downshift'
import styled from 'styled-components'
import { space } from 'styled-system'
import { translate } from 'react-i18next'
import type { T } from 'types/common'
import Box from 'components/base/Box'
import GrowScroll from 'components/base/GrowScroll'
@ -33,6 +36,7 @@ type Props = {
value?: *,
disabled: boolean,
small?: boolean,
t: T,
}
const Container = styled(Box).attrs({ relative: true, color: 'graphite' })``
@ -163,7 +167,7 @@ class LegacySelect extends PureComponent<Props> {
_children = {}
renderItems = (items: Array<Object>, selectedItem: any, downshiftProps: Object) => {
const { renderItem, maxHeight, keyProp } = this.props
const { renderItem, maxHeight, keyProp, t } = this.props
const { getItemProps, highlightedIndex } = downshiftProps
const selectedItemIndex = items.indexOf(selectedItem)
@ -228,7 +232,7 @@ class LegacySelect extends PureComponent<Props> {
</GrowScroll>
) : (
<Box>
<Item>{'No results'}</Item>
<Item>{t('app:error.noResults')}</Item>
</Box>
)}
</Dropdown>
@ -340,4 +344,4 @@ class LegacySelect extends PureComponent<Props> {
}
}
export default LegacySelect
export default translate()(LegacySelect)

2
src/components/base/SideBar/SideBarList.js

@ -30,7 +30,7 @@ class SideBarList extends Component<Props> {
<Space of={20} />
</Fragment>
)}
{children ? (
{children && children.length ? (
<ListWrapper flow={2} px={3} fontSize={3} {...props}>
{children}
</ListWrapper>

1
src/components/base/SideBar/SideBarListItem.js

@ -63,6 +63,7 @@ const Container = styled(Tabbable).attrs({
py: 2,
})`
cursor: ${p => (p.disabled || p.isActive ? 'default' : 'pointer')};
pointer-events: ${p => (p.isDisabled ? 'none' : 'auto')};
color: ${p => (p.isActive ? p.theme.colors.dark : p.theme.colors.smoke)};
background: ${p => (p.isActive ? p.theme.colors.lightGrey : '')};
opacity: ${p => (p.disabled ? 0.5 : 1)};

4
src/components/base/StepperNumber/index.js

@ -143,11 +143,11 @@ class StepperNumber extends PureComponent<Props, State> {
return (
<Container>
<Btn onMouseDown={!isMin ? this.handleMouseDown('decrement') : undefined} disabled={isMin}>
-
{'-'}
</Btn>
<Num>{value}</Num>
<Btn onMouseDown={!isMax ? this.handleMouseDown('increment') : undefined} disabled={isMax}>
+
{'+'}
</Btn>
</Container>
)

2
src/components/modals/AddAccounts/index.js

@ -211,7 +211,7 @@ class AddAccounts extends PureComponent<Props, State> {
</ModalContent>
{!hideFooter && (
<ModalFooter horizontal align="center" justify="flex-end" style={{ height: 80 }}>
{StepFooter ? <StepFooter {...stepProps} /> : <Box>footer</Box>}
{StepFooter ? <StepFooter {...stepProps} /> : <Box />}
</ModalFooter>
)}
</ModalBody>

6
src/components/modals/AddAccounts/steps/04-step-finish.js

@ -8,15 +8,15 @@ import IconCheckCircle from 'icons/CheckCircle'
import type { StepProps } from '../index'
function StepFinish({ onCloseModal }: StepProps) {
function StepFinish({ onCloseModal, t }: StepProps) {
return (
<Box align="center" py={6}>
<Box color="positiveGreen" mb={4}>
<IconCheckCircle size={40} />
</Box>
<Box mb={4}>{'Great success!'}</Box>
<Box mb={4}>{t('app:addAccounts.success')}</Box>
<Button primary onClick={onCloseModal}>
{'Close'}
{t('app:common.close')}
</Button>
</Box>
)

1
src/components/modals/Debug.js

@ -1,4 +1,5 @@
// @flow
/* eslint-disable react/jsx-no-literals */
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import last from 'lodash/last'
import React, { Component } from 'react'

19
src/components/modals/ReleaseNotes.js

@ -10,6 +10,7 @@ import Modal, { ModalBody, ModalTitle, ModalContent, ModalFooter } from 'compone
import Button from 'components/base/Button'
import Box from 'components/base/Box'
import GrowScroll from 'components/base/GrowScroll'
import Text from 'components/base/Text'
import Spinner from 'components/base/Spinner'
@ -217,7 +218,12 @@ class ReleaseNotes extends PureComponent<Props, State> {
return (
<ModalBody onClose={onClose}>
<ModalTitle>{t('app:releaseNotes.title')}</ModalTitle>
<ModalContent>{content}</ModalContent>
<ModalContent style={{ height: 400 }} mx={-5} pb={0}>
<GrowScroll px={5} pb={8}>
{content}
</GrowScroll>
<GradientBox />
</ModalContent>
<ModalFooter horizontal justifyContent="flex-end">
<Button onClick={onClose} primary>
{t('app:common.continue')}
@ -231,4 +237,15 @@ class ReleaseNotes extends PureComponent<Props, State> {
}
}
const GradientBox = styled.div`
width: 100%;
height: 60px;
position: absolute;
bottom: 68px;
left: 0;
right: 0;
background: linear-gradient(rgba(255, 255, 255, 0), #ffffff 70%);
z-index: 2;
`
export default translate()(ReleaseNotes)

4
src/components/modals/Send/Footer.js

@ -76,7 +76,7 @@ class Footer extends PureComponent<
/>
<Box horizontal align="center">
<Text ff="Rubik" fontSize={3}>
{'('}
{'(' /* eslint-disable-line react/jsx-no-literals */}
</Text>
<CounterValue
currency={account.currency}
@ -88,7 +88,7 @@ class Footer extends PureComponent<
alwaysShowSign={false}
/>
<Text ff="Rubik" fontSize={3}>
{')'}
{')' /* eslint-disable-line react/jsx-no-literals */}
</Text>
</Box>
</Box>

1
src/config/global-tab.js

@ -5,3 +5,4 @@ let IS_GLOBAL_TAB_ENABLED = false
export const isGlobalTabEnabled = () => IS_GLOBAL_TAB_ENABLED
export const enableGlobalTab = () => (IS_GLOBAL_TAB_ENABLED = true)
export const disableGlobalTab = () => (IS_GLOBAL_TAB_ENABLED = false)

16
src/icons/Cart.js

@ -0,0 +1,16 @@
// @flow
import React from 'react'
const path = (
<path
fill="currentColor"
d="M15.333 2.375H4.008l-.243-1.307A.677.677 0 0 0 3.111.5H.333A.343.343 0 0 0 0 .852v.703c0 .194.15.351.333.351h2.23l1.932 10.421A1.923 1.923 0 0 0 4 13.625c0 1.036.796 1.875 1.778 1.875s1.778-.84 1.778-1.875c0-.33-.082-.653-.239-.938h4.033a1.945 1.945 0 0 0-.239.938c0 1.036.796 1.875 1.778 1.875s1.778-.84 1.778-1.875c0-.531-.21-1.01-.547-1.352l.029-.14c.09-.437-.226-.852-.652-.852H5.66L5.4 9.875h8.677c.314 0 .585-.23.652-.554l1.256-6.094c.09-.438-.227-.852-.652-.852zM5.778 14.328c-.368 0-.667-.315-.667-.703 0-.388.3-.703.667-.703.367 0 .666.315.666.703 0 .388-.299.703-.666.703zm7.11 0c-.367 0-.666-.315-.666-.703 0-.388.3-.703.667-.703.368 0 .667.315.667.703 0 .388-.3.703-.667.703zm.652-5.86H5.138l-.87-4.687h10.238L13.54 8.47z"
/>
)
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 16 16" height={size} width={size} {...p}>
{path}
</svg>
)

4
src/icons/CheckCircle.js

@ -5,11 +5,11 @@ import React from 'react'
const path = (
<path
fill="currentColor"
d="M12 .375C5.58.375.375 5.58.375 12S5.58 23.625 12 23.625 23.625 18.42 23.625 12 18.42.375 12 .375zm0 21.75C6.438 22.125 1.875 17.622 1.875 12 1.875 6.438 6.378 1.875 12 1.875c5.562 0 10.125 4.503 10.125 10.125 0 5.562-4.503 10.125-10.125 10.125zm6.639-12.889l-8.46 8.392a.562.562 0 0 1-.796-.003l-4.025-4.058a.562.562 0 0 1 .003-.795l.4-.397a.562.562 0 0 1 .795.004l3.233 3.259 7.661-7.6a.562.562 0 0 1 .796.003l.396.4a.562.562 0 0 1-.003.795z"
d="M13.917 7.387a.75.75 0 1 1 1.5 0V8a7.417 7.417 0 1 1-4.398-6.778.75.75 0 1 1-.61 1.37A5.917 5.917 0 1 0 13.916 8v-.613zm.22-5.25a.75.75 0 0 1 1.06 1.06L8.531 9.87a.75.75 0 0 1-1.061 0l-2-2a.75.75 0 0 1 1.06-1.06L8 8.28l6.136-6.143z"
/>
)
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 24 24" height={size} width={size} {...p}>
<svg viewBox="0 0 16 16" height={size} width={size} {...p}>
{path}
</svg>
)

16
src/icons/EyeOff.js

@ -0,0 +1,16 @@
// @flow
import React from 'react'
const path = (
<path
fill="currentColor"
d="M2.502 8.393c.335.494.731.99 1.184 1.45 1.267 1.286 2.713 2.046 4.304 2.046a5.462 5.462 0 0 0 3.121-1.03c.257-.187.512-.072.708.172.195.244.256.525 0 .71A6.674 6.674 0 0 1 8 13c-1.972 0-3.698-.907-5.165-2.398a11.477 11.477 0 0 1-1.313-1.606 8.35 8.35 0 0 1-.46-.748.532.532 0 0 1 .007-.51 10.965 10.965 0 0 1 3.112-3.48.603.603 0 0 1 .818.105.538.538 0 0 1-.11.779 9.859 9.859 0 0 0-2.64 2.86c.074.12.158.251.253.391zm10.996-.786c-.335-.494-.731-.99-1.184-1.45-1.267-1.286-2.713-2.046-4.315-2.046-.368 0-.734.04-1.091.119a.585.585 0 0 1-.701-.414.555.555 0 0 1 .435-.668c.446-.1.902-.149 1.358-.148 1.972 0 3.698.907 5.165 2.398.504.512.942 1.059 1.313 1.606.225.33.378.591.46.748a.532.532 0 0 1-.007.51 10.821 10.821 0 0 1-1.328 1.868.604.604 0 0 1-.822.067.538.538 0 0 1-.07-.782c.39-.442.738-.916 1.04-1.416a9.37 9.37 0 0 0-.253-.392zm-4.426 2.29a2.225 2.225 0 0 1-3.38-1.306 2.221 2.221 0 0 1 .3-1.775L1.17 1.996a.583.583 0 1 1 .825-.825l12.833 12.833a.583.583 0 1 1-.825.825L9.072 9.896zm-2.26-2.26a1.11 1.11 0 0 0 1.44 1.44l-1.44-1.44z"
/>
)
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 16 16" height={size} width={size} {...p}>
{path}
</svg>
)

16
src/icons/Truck.js

@ -0,0 +1,16 @@
// @flow
import React from 'react'
const path = (
<path
fill="currentColor"
d="M14.8 1.5h-8c-.663 0-1.2.546-1.2 1.219v1.219H4.497a1.19 1.19 0 0 0-.848.356L1.15 6.831a1.228 1.228 0 0 0-.351.861v3.152H.5c-.166 0-.3.136-.3.304v.61c0 .168.134.305.3.305h1.1C1.6 13.409 2.675 14.5 4 14.5s2.4-1.091 2.4-2.438h3.2c0 1.347 1.075 2.438 2.4 2.438s2.4-1.091 2.4-2.438h.4c.663 0 1.2-.545 1.2-1.218V2.719A1.21 1.21 0 0 0 14.8 1.5zM4 13.281a1.21 1.21 0 0 1-1.2-1.219A1.21 1.21 0 0 1 4 10.845c.662 0 1.2.546 1.2 1.219A1.21 1.21 0 0 1 4 13.28zm1.6-3.035A2.37 2.37 0 0 0 4 9.625c-.835 0-1.57.433-2 1.09V7.692l2.497-2.536H5.6v5.09zM12 13.28a1.21 1.21 0 0 1-1.2-1.219 1.21 1.21 0 0 1 1.2-1.218c.662 0 1.2.546 1.2 1.219A1.21 1.21 0 0 1 12 13.28zm2.8-2.437h-.721A2.393 2.393 0 0 0 12 9.625c-.888 0-1.664.49-2.079 1.219H6.8V2.719h8v8.125zM2.8 8l2-2.031V8h-2z"
/>
)
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 16 16" height={size} width={size} {...p}>
{path}
</svg>
)

9
src/reducers/onboarding.js

@ -57,6 +57,15 @@ const state: OnboardingState = {
showBreadcrumb: false,
},
},
{
name: 'noDevice',
external: true,
options: {
showFooter: false,
showBackground: true,
showBreadcrumb: false,
},
},
{
name: 'selectDevice',
label: 'Select Device',

6
src/renderer/init.js

@ -11,7 +11,7 @@ import moment from 'moment'
import createStore from 'renderer/createStore'
import events from 'renderer/events'
import { enableGlobalTab, isGlobalTabEnabled } from 'config/global-tab'
import { enableGlobalTab, disableGlobalTab, isGlobalTabEnabled } from 'config/global-tab'
import { fetchAccounts } from 'actions/accounts'
import { fetchSettings } from 'actions/settings'
@ -82,6 +82,10 @@ async function init() {
logger.onTabKey(document.activeElement)
}
})
window.addEventListener('click', () => {
if (isGlobalTabEnabled()) disableGlobalTab()
})
}
}

18
static/i18n/en/app.yml

@ -38,6 +38,7 @@ common:
refresh: Refresh
error:
load: Unable to load
noResults: No results
operation:
type:
IN: Receive funds
@ -112,8 +113,9 @@ emptyState:
exchange:
title: Exchange
visitWebsite: Visit website
coinhouse: Lorem ipsum
shapeshift: Ipsum lorem
coinhouse: 'Coinhouse is the trusted platform for individuals and institutional investors looking to analyze, acquire, sell and securely store cryptoassets.'
changelly: 'Changelly is a popular instant cryptocurrency exchange with 100+ coins and tokens listed.'
coinmama: 'Coinmama is a financial service that makes it fast, safe and fun to buy digital currency, anywhere in the world.'
genuinecheck:
modal:
title: Genuine check, bro
@ -128,7 +130,11 @@ addAccounts:
accountToImportSubtitle_plural: 'Accounts to import ({{count}})'
selectAll: Select all
unselectAll: Unselect all
createNewAccount: Create new account
editName: Edit name
success: Great success!
createNewAccount:
title: Create new account
noOperationOnLastAccount: You cannot create a new account because your last account has no operations
retrySync: Retry sync
cta:
create: 'Create account'
@ -212,6 +218,8 @@ send:
message: Leave a message (140)
rippleTag: Tag
ethereumGasLimit: Gas limit
unitPerByte: '{{unit}} per byte'
feePerByte: Fee per byte
connectDevice:
title: Connect device
verification:
@ -345,3 +353,7 @@ password:
update:
newVersionReady: A new update is available.
relaunch: Update now
crash:
restart: Restart app
reset: Reset app files
createTicket: Create ticket

16
static/i18n/en/onboarding.yml

@ -15,6 +15,14 @@ init:
noDevice:
title: Do not have a Ledger device yet?
desc: Please replace it with the final wording once it’s done.
noDevice:
title: Do not have a Ledger device yet?
buyNew:
title: Buy a Ledger device
trackOrder:
title: Track your order
learnMore:
title: Learn about Ledger Live
selectDevice:
title: To get started, select your device
ledgerNanoCard:
@ -44,14 +52,14 @@ writeSeed:
title: Save your recovery phrase
desc: Your recovery phrase is formed by 24 words. They will be displayed only once.
step1: Press the right button to select the length of your recovery phrase. Press both buttons to confirm.
step2: Select the first letters of Word \#1 by pressing the right or left button. Press both buttons to confirm each letter.
step3: Select Word \#1 from the suggested words. Press both buttons to continue.
step2: 'Select the first letters of Word #1 by pressing the right or left button. Press both buttons to confirm each letter.'
step3: 'Select Word #1 from the suggested words. Press both buttons to continue.'
step4: Repeat the process until the last word.
nano:
title: Save your recovery phrase
desc: Your recovery phrase is formed by 24 words. They will be displayed only once.
step1: Copy the first word (Word \#1) in position 1 on the blank Recovery sheet.
step2: Press the right button to display Word \#2 and repeat the process until all 24 words are copied on the Recovery sheet.
step1: 'Copy the first word (Word #1) in position 1 on the blank Recovery sheet.'
step2: 'Press the right button to display Word #2 and repeat the process until all 24 words are copied on the Recovery sheet.'
step3: Confirm your recovery phrase press both buttons to validate each word displayed on the screen.
blue:
title: Save your recovery phrase

13
static/i18n/fr/app.yml

@ -39,6 +39,7 @@ common:
refresh: Refresh
error:
load: Unable to load
noResults: No results
operation:
type:
IN: Receive funds
@ -129,7 +130,11 @@ addAccounts:
accountToImportSubtitle_plural: 'Accounts to import ({{count}})'
selectAll: Select all
unselectAll: Unselect all
createNewAccount: Create new account
editName: Edit name
success: Great success!
createNewAccount:
title: Create new account
noOperationOnLastAccount: You cannot create a new account because your last account has no operations
retrySync: Retry sync
cta:
create: 'Create account'
@ -213,6 +218,8 @@ send:
message: Leave a message (140)
rippleTag: Tag
ethereumGasLimit: Gas limit
unitPerByte: '{{unit}} per byte'
feePerByte: Fee per byte
connectDevice:
title: Connect device
verification:
@ -346,3 +353,7 @@ password:
update:
newVersionReady: A new update is available.
relaunch: Update now
crash:
restart: Restart app
reset: Reset app files
createTicket: Create ticket

16
static/i18n/fr/onboarding.yml

@ -16,6 +16,14 @@ init:
noDevice:
title: Do not have a Ledger device yet?
desc: Please replace it with the final wording once it’s done.
noDevice:
title: Do not have a Ledger device yet?
buyNew:
title: Buy a Ledger device
trackOrder:
title: Track your order
learnMore:
title: Learn about Ledger Live
selectDevice:
title: To get started, select your device
ledgerNanoCard:
@ -45,14 +53,14 @@ writeSeed:
title: Save your recovery phrase
desc: Your recovery phrase is formed by 24 words. They will be displayed only once.
step1: Press the right button to select the length of your recovery phrase. Press both buttons to confirm.
step2: 'Select the first letters of Word \#1 by pressing the right or left button. Press both buttons to confirm each letter.'
step3: 'Select Word \#1 from the suggested words. Press both buttons to continue.'
step2: 'Select the first letters of Word #1 by pressing the right or left button. Press both buttons to confirm each letter.'
step3: 'Select Word #1 from the suggested words. Press both buttons to continue.'
step4: Repeat the process until the last word.
nano:
title: Save your recovery phrase
desc: Your recovery phrase is formed by 24 words. They will be displayed only once.
step1: 'Copy the first word (Word \#1) in position 1 on the blank Recovery sheet.'
step2: 'Press the right button to display Word \#2 and repeat the process until all 24 words are copied on the Recovery sheet.'
step1: 'Copy the first word (Word #1) in position 1 on the blank Recovery sheet.'
step2: 'Press the right button to display Word #2 and repeat the process until all 24 words are copied on the Recovery sheet.'
step3: Confirm your recovery phrase press both buttons to validate each word displayed on the screen.
blue:
title: Save your recovery phrase

BIN
static/images/logos/shapeshift.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Loading…
Cancel
Save