Browse Source

Merge branch 'master' into migrate-live-common

master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
bdc476ef8e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 60
      src/components/IsUnlocked.js
  2. 4
      src/components/OperationsList/Operation.js
  3. 7
      src/components/SettingsPage/DisablePasswordModal.js
  4. 28
      src/components/SettingsPage/sections/Display.js
  5. 53
      src/components/WarnBox/index.js
  6. 18
      src/components/WarnBox/stories.js
  7. 2
      src/components/base/Input/index.js
  8. 7
      src/components/base/Select/index.js
  9. 53
      src/components/modals/Send/03-step-verification.js
  10. 57
      src/components/modals/Send/04-step-confirmation.js
  11. 67
      src/components/modals/Send/Footer.js
  12. 64
      src/components/modals/Send/index.js
  13. 245
      src/helpers/countries.json
  14. 12
      src/icons/CheckCircle.js
  15. 12
      src/icons/ExclamationCircleThin.js
  16. 42
      src/icons/LockScreen.js
  17. 2
      src/reducers/settings.js
  18. 5
      src/styles/helpers.js
  19. 5
      src/types/common.js
  20. 5
      static/i18n/en/common.yml
  21. 15
      static/i18n/en/send.yml
  22. 3
      static/i18n/en/settings.yml
  23. 4
      static/i18n/fr/manager.yml
  24. 1
      static/i18n/fr/password.yml
  25. 5
      static/i18n/fr/settings.yml
  26. 8
      yarn.lock

60
src/components/IsUnlocked.js

@ -4,10 +4,14 @@ import bcrypt from 'bcryptjs'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { compose } from 'redux'
import styled from 'styled-components'
import { translate } from 'react-i18next'
import type { Account } from '@ledgerhq/live-common/lib/types'
import type { Settings, T } from 'types/common'
import IconLockScreen from 'icons/LockScreen'
import { ErrorMessageInput } from 'components/base/Input'
import get from 'lodash/get'
@ -43,6 +47,7 @@ type Props = {
}
type State = {
inputValue: InputValue,
incorrectPassword: boolean,
}
const mapStateToProps = state => ({
@ -61,8 +66,27 @@ const defaultState = {
inputValue: {
password: '',
},
incorrectPassword: false,
}
export const PageTitle = styled(Box).attrs({
width: 152,
height: 27,
ff: 'Museo Sans|Regular',
fontSize: 7,
color: 'dark',
})``
export const LockScreenDesc = styled(Box).attrs({
width: 340,
height: 36,
ff: 'Open Sans|Regular',
fontSize: 4,
textAlign: 'center',
color: 'smoke',
})`
margin: 10px auto 25px;
`
class IsUnlocked extends Component<Props, State> {
state = {
...defaultState,
@ -121,6 +145,8 @@ class IsUnlocked extends Component<Props, State> {
this.setState({
...defaultState,
})
} else {
this.setState({ incorrectPassword: true })
}
}
@ -133,22 +159,36 @@ class IsUnlocked extends Component<Props, State> {
_input: ?HTMLInputElement
render() {
const { inputValue } = this.state
const { inputValue, incorrectPassword } = this.state
const { isLocked, t } = this.props
if (isLocked) {
return (
<Box sticky alignItems="center" justifyContent="center" onClick={this.handleFocusInput}>
<form onSubmit={this.handleSubmit}>
<Box>
<InputPassword
autoFocus
innerRef={(n: any) => (this._input = n)}
placeholder={t('common:password')}
type="password"
onChange={this.handleChangeInput('password')}
value={inputValue.password}
/>
<Box align="center">
<IconLockScreen size={136} />
<PageTitle>{t('common:lockScreen.title')}</PageTitle>
<LockScreenDesc>
{t('common:lockScreen.subTitle')}
<br />
{t('common:lockScreen.description')}
</LockScreenDesc>
<Box style={{ minWidth: 230 }}>
<InputPassword
autoFocus
innerRef={(n: any) => (this._input = n)}
placeholder={t('common:lockScreen.inputPlaceholder')}
type="password"
onChange={this.handleChangeInput('password')}
value={inputValue.password}
/>
{incorrectPassword && (
<ErrorMessageInput>
{t('password:errorMessageIncorrectPassword')}
</ErrorMessageInput>
)}
</Box>
</Box>
</form>
</Box>

4
src/components/OperationsList/Operation.js

@ -49,6 +49,10 @@ const OperationRaw = styled(Box).attrs({
`
const Address = ({ value }: { value: string }) => {
if (!value) {
return <Box />
}
const addrSize = value.length / 2
const left = value.slice(0, 10)

7
src/components/SettingsPage/DisablePasswordModal.js

@ -6,6 +6,7 @@ import bcrypt from 'bcryptjs'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import InputPassword from 'components/base/InputPassword'
import Label from 'components/base/Label'
import { ErrorMessageInput } from 'components/base/Input'
import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal'
@ -72,14 +73,14 @@ class DisablePasswordModal extends PureComponent<Props, State> {
<ModalBody onClose={onClose}>
<ModalTitle>{t('settings:profile.disablePasswordModalTitle')}</ModalTitle>
<ModalContent>
<Box ff="Museo Sans|Regular" color="dark" textAlign="center" mb={2} mt={3}>
{t('settings:profile.disablePasswordModalSubtitle')}
</Box>
<Box ff="Open Sans" color="smoke" fontSize={4} textAlign="center" px={4}>
{t('settings:profile.disablePasswordModalDesc')}
<Box px={7} mt={4} flow={3}>
{isPasswordEnabled && (
<Box flow={1}>
<Label htmlFor="password">
{t('settings:profile.disablePasswordModalInput')}
</Label>
<InputPassword
autoFocus
type="password"

28
src/components/SettingsPage/sections/Display.js

@ -8,9 +8,10 @@ import type { Settings, T } from 'types/common'
import Select from 'components/base/Select'
import RadioGroup from 'components/base/RadioGroup'
import IconDisplay from 'icons/Display'
import COUNTRIES from 'helpers/countries.json'
import {
SettingsSection as Section,
SettingsSectionHeader as Header,
@ -27,20 +28,6 @@ const fiats = listFiatCurrencies()
name: `${fiat.name} - ${fiat.code}${fiat.symbol ? ` (${fiat.symbol})` : ''}`,
}))
/* temporary subset of countries */
const COUNTRIES = [
{ name: 'China', key: 'CN' },
{ name: 'France', key: 'FR' },
{ name: 'India', key: 'IN' },
{ name: 'Italy', key: 'IT' },
{ name: 'Japan', key: 'JP' },
{ name: 'Russian Federation', key: 'RU' },
{ name: 'Singapore', key: 'SG' },
{ name: 'Switzerland', key: 'CH' },
{ name: 'United Kingdom', key: 'GB' },
{ name: 'United States', key: 'US' },
]
type Props = {
t: T,
settings: Settings,
@ -52,7 +39,7 @@ type State = {
cachedMarketIndicator: string,
cachedLanguageKey: string,
cachedCounterValue: ?Object,
cachedRegion: Object,
cachedRegion: string,
}
class TabProfile extends PureComponent<Props, State> {
@ -102,7 +89,7 @@ class TabProfile extends PureComponent<Props, State> {
})
}
handleChangeRegion = (region: Object) => {
handleChangeRegion = (region: string) => {
const { saveSettings } = this.props
this.setState({ cachedRegion: region })
window.requestIdleCallback(() => {
@ -131,7 +118,7 @@ class TabProfile extends PureComponent<Props, State> {
} = this.state
const { languages } = this.getDatas()
const currentLanguage = languages.find(l => l.key === cachedLanguageKey)
const currentRegion = COUNTRIES.find(r => r.key === cachedRegion.key)
const currentRegion = COUNTRIES.find(r => r.key === cachedRegion)
return (
<Section>
@ -171,9 +158,8 @@ class TabProfile extends PureComponent<Props, State> {
<Select
searchable
fuseOptions={{ keys: ['name'] }}
style={{ minWidth: 130 }}
small
onChange={item => this.handleChangeRegion(item)}
maxHeight={200}
onChange={item => this.handleChangeRegion(item.key)}
renderSelected={item => item && item.name}
value={currentRegion}
items={COUNTRIES}

53
src/components/WarnBox/index.js

@ -0,0 +1,53 @@
// @flow
import * as React from 'react'
import styled from 'styled-components'
import Box from 'components/base/Box'
const Container = styled(Box).attrs({
color: 'graphite',
borderRadius: 1,
px: 4,
py: 3,
horizontal: true,
ff: 'Open Sans',
fontSize: 4,
flow: 4,
})`
border: solid 1px;
border-color: ${p => p.theme.colors.fog};
align-items: center;
`
const svg = (
<svg width="36" height="43">
<g fill="none">
<path
fill="#4B84FF"
fillOpacity=".08"
d="M18.177 2C18.06 24.126 18 37.184 18 41.174h.354C27.74 38.96 35 29.762 35 19.884V9.154L18.177 2z"
/>
<path
stroke="#142533"
strokeWidth="2"
d="M18 2L1 9.153v10.73c0 9.88 7.158 19.077 16.821 21.29h.358C27.663 38.96 35 29.764 35 19.884V9.154L18 2z"
/>
<path
fill="#4B84FF"
d="M23.733 15.036c-.568 0-1.03.448-1.03 1.001l-.019 4.488s.002.313-.308.313c-.316 0-.307-.313-.307-.313v-6.474c0-.553-.456-.982-1.024-.982-.57 0-.974.43-.974.982v6.474s-.035.316-.34.316c-.303 0-.327-.316-.327-.316v-7.553c0-.552-.428-.972-.996-.972-.569 0-1 .42-1 .972v7.553s-.016.303-.344.303c-.321 0-.323-.303-.323-.303v-5.611c0-.553-.445-.9-1.013-.9-.57 0-.985.347-.985.9v8.2s-.056.365-.594-.237c-1.378-1.543-2.097-1.85-2.097-1.85s-1.31-.622-1.889.503c-.42.816.025.86.712 1.861.607.888 2.529 3.221 2.529 3.221s2.28 3.126 5.355 3.126c0 0 6.024.25 6.024-5.544l-.021-8.157c0-.553-.46-1.001-1.03-1.001"
/>
</g>
</svg>
)
type Props = {
children: React.Node,
}
export default (props: Props) => (
<Container>
<Box mx={1}>{svg}</Box>
<Box shrink>{props.children}</Box>
</Container>
)

18
src/components/WarnBox/stories.js

@ -0,0 +1,18 @@
// @flow
import React from 'react'
import { storiesOf } from '@storybook/react'
import { text } from '@storybook/addon-knobs'
import WarnBox from 'components/WarnBox'
const stories = storiesOf('Components', module)
stories.add('WarnBox', () => (
<WarnBox>
{text(
'children',
'Nulla ornare ligula nec velit fermentum accumsan. Mauris sagittis iaculis pretium. Maecenas tincidunt tortor ullamcorper neque scelerisque lacinia sit amet sit amet elit. Quisque vulputate at tellus ut fringilla. Sed varius neque accumsan nunc consequat semper. In interdum euismod velit, sed pulvinar justo finibus ac. Nullam euismod felis non pellentesque fermentum. Nullam sed libero eu ligula porta accumsan eget et neque. Sed varius lobortis vestibulum. Morbi efficitur leo at augue venenatis, vitae faucibus ante lobortis. Nunc tincidunt, sem eget ultricies convallis, dolor est gravida sem, non vestibulum urna lorem a justo. Quisque ultrices feugiat arcu, sit amet tristique tellus maximus in. Phasellus ultricies mattis erat vitae laoreet. Fusce ac dignissim dui. Etiam semper purus nisi, eu semper tortor mollis nec.',
)}
</WarnBox>
))

2
src/components/base/Input/index.js

@ -38,7 +38,7 @@ const Base = styled.input.attrs({
`
export const ErrorMessageInput = styled(Box).attrs({
alignItems: 'flex-end',
alignItems: 'flex-start',
color: 'alertRed',
ff: 'Open Sans|SemiBold',
fontSize: 3,

7
src/components/base/Select/index.js

@ -320,8 +320,8 @@ class Select extends PureComponent<Props> {
<AngleDown mr={-1} />
</TriggerBtn>
)}
{isOpen &&
(searchable ? (
<div hidden={!isOpen}>
{searchable ? (
<Search
value={inputValue}
items={items}
@ -332,7 +332,8 @@ class Select extends PureComponent<Props> {
/>
) : (
this.renderItems(items, selectedItem, downshiftProps)
))}
)}
</div>
</Container>
)
}}

53
src/components/modals/Send/03-step-verification.js

@ -1,7 +1,54 @@
// @flow
import React from 'react'
import styled from 'styled-components'
import Box from 'components/base/Box'
import WarnBox from 'components/WarnBox'
import { multiline } from 'styles/helpers'
import DeviceCheckAddress from 'components/DeviceCheckAddress'
import DeviceConfirm from 'components/DeviceConfirm'
import type { Account } from '@ledgerhq/wallet-common/lib/types'
import type { Device, T } from 'types/common'
const Container = styled(Box).attrs({
alignItems: 'center',
fontSize: 4,
pb: 4,
})``
const Info = styled(Box).attrs({
ff: 'Open Sans|SemiBold',
color: 'dark',
mt: 6,
mb: 4,
px: 5,
})`
text-align: center;
`
function StepVerification() {
return <div>step verification</div>
type Props = {
account: ?Account,
device: ?Device,
onValidate: Function,
t: T,
}
export default StepVerification
export default (props: Props) => (
<Container>
<WarnBox>{multiline(props.t('send:steps.verification.warning'))}</WarnBox>
<Info>{props.t('send:steps.verification.body')}</Info>
{// TODO: Actually create a tx
// DeviceCheckAddress used as a placeholder in the meantime
props.account &&
props.device && (
<DeviceCheckAddress
account={props.account}
device={props.device}
onCheck={props.onValidate}
render={({ isVerified }) => <DeviceConfirm notValid={isVerified === false} />}
/>
)}
</Container>
)

57
src/components/modals/Send/04-step-confirmation.js

@ -1,7 +1,60 @@
// @flow
import React from 'react'
import styled from 'styled-components'
function StepConfirmation() {
return <div>step confirmation</div>
import IconCheckCircle from 'icons/CheckCircle'
import IconExclamationCircleThin from 'icons/ExclamationCircleThin'
import Box from 'components/base/Box'
import { multiline } from 'styles/helpers'
import { colors } from 'styles/theme'
import type { T } from 'types/common'
const Container = styled(Box).attrs({
alignItems: 'center',
justifyContent: 'center',
grow: true,
color: 'dark',
})`
height: 220px;
`
const Title = styled(Box).attrs({
ff: 'Museo Sans',
fontSize: 5,
mt: 4,
})`
text-align: center;
`
const Text = styled(Box).attrs({
ff: 'Open Sans',
fontSize: 4,
mt: 2,
})`
text-align: center;
`
type Props = {
txValidated: boolean,
t: T,
}
function StepConfirmation(props: Props) {
const { t, txValidated } = props
const Icon = txValidated ? IconCheckCircle : IconExclamationCircleThin
const iconColor = txValidated ? colors.positiveGreen : colors.alertRed
const tPrefix = txValidated ? 'send:steps.confirmation.success' : 'send:steps.confirmation.error'
return (
<Container>
<span style={{ color: iconColor }}>
<Icon size={43} />
</span>
<Title>{t(`${tPrefix}.title`)}</Title>
<Text>{multiline(t(`${tPrefix}.text`))}</Text>
</Container>
)
}
export default StepConfirmation

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

@ -20,42 +20,47 @@ type Props = {
fees: number,
onNext: Function,
canNext: boolean,
showTotal: boolean,
}
function Footer({ account, amount, fees, t, onNext, canNext }: Props) {
function Footer({ account, amount, fees, t, onNext, canNext, showTotal }: Props) {
return (
<ModalFooter horizontal alignItems="center">
<Box grow>
<Label>{t('send:totalSpent')}</Label>
<Box horizontal flow={2} align="center">
<FormattedVal
disableRounding
color="dark"
val={amount + fees}
unit={account.unit}
showCode
/>
<Box horizontal align="center">
<Text ff="Rubik" fontSize={3}>
{'('}
</Text>
<CounterValue
ticker={account.currency.units[0].code}
value={amount + fees}
disableRounding
color="grey"
fontSize={3}
showCode
/>
<Text ff="Rubik" fontSize={3}>
{')'}
</Text>
<ModalFooter>
<Box horizontal alignItems="center" justifyContent="flex-end" flow={2}>
{showTotal && (
<Box grow>
<Label>{t('send:totalSpent')}</Label>
<Box horizontal flow={2} align="center">
<FormattedVal
disableRounding
color="dark"
val={amount + fees}
unit={account.unit}
showCode
/>
<Box horizontal align="center">
<Text ff="Rubik" fontSize={3}>
{'('}
</Text>
<CounterValue
ticker={account.currency.units[0].code}
value={amount + fees}
disableRounding
color="grey"
fontSize={3}
showCode
/>
<Text ff="Rubik" fontSize={3}>
{')'}
</Text>
</Box>
</Box>
</Box>
</Box>
)}
<Button primary onClick={onNext} disabled={!canNext}>
{'Next'}
</Button>
</Box>
<Button primary onClick={onNext} disabled={!canNext}>
{'Next'}
</Button>
</ModalFooter>
)
}

64
src/components/modals/Send/index.js

@ -37,6 +37,7 @@ type State = {
fees: number,
isRBF: boolean,
recipientAddress: string,
txValidated: null | boolean,
stepIndex: number,
}
@ -59,6 +60,7 @@ const INITIAL_STATE = {
fees: 0,
isRBF: false,
recipientAddress: '',
txValidated: null,
stepIndex: 0,
}
@ -136,6 +138,15 @@ class SendModal extends PureComponent<Props, State> {
})
}
handleValidate = isValidated => {
const { stepIndex } = this.state
this.setState({
txValidated: isValidated,
stepIndex: stepIndex + 1,
})
}
createChangeHandler = key => value => {
const patch = { [key]: value }
// ensure max is always restecped when changing fees
@ -158,34 +169,47 @@ class SendModal extends PureComponent<Props, State> {
renderStep = () => {
const { t } = this.props
const { stepIndex, amount, deviceSelected, ...otherState } = this.state
const { stepIndex, deviceSelected, txValidated, ...otherState } = this.state
const step = this._steps[stepIndex]
if (!step) {
return null
}
const { Comp } = step
const props = (predicate, props) => (predicate ? props : {})
const stepProps = {
...otherState,
t,
amount,
account: this._account,
...props(stepIndex === 1, {
accountName: this._account ? this._account.name : undefined,
deviceSelected,
onChangeDevice: this.handleChangeDevice,
onStatusChange: this.handleChangeStatus,
}),
}
return <Comp onChange={this.createChangeHandler} {...stepProps} {...this.props} />
switch (stepIndex) {
case 1:
stepProps.accountName = this._account ? this._account.name : undefined
stepProps.deviceSelected = deviceSelected
stepProps.onChangeDevice = this.handleChangeDevice
stepProps.onStatusChange = this.handleChangeStatus
break
case 2:
stepProps.device = deviceSelected
stepProps.onValidate = this.handleValidate
break
case 3:
if (txValidated !== null) {
stepProps.txValidated = txValidated
}
break
default:
break
}
return <Comp onChange={this.createChangeHandler} {...stepProps} />
}
render() {
const { accounts, t } = this.props
const { stepIndex, amount, account, fees } = this.state
const { stepIndex, amount, account, fees, txValidated } = this.state
const canNext = this.canNext()
const canPrev = this.canPrev()
@ -210,20 +234,30 @@ class SendModal extends PureComponent<Props, State> {
{this.renderStep()}
</ModalContent>
{this._account &&
stepIndex !== 3 && (
stepIndex < 2 && (
<Footer
canNext={canNext}
onNext={this.handleNextStep}
account={this._account}
amount={amount}
fees={fees}
showTotal={stepIndex === 0}
t={t}
/>
)}
{stepIndex === 3 && (
<ModalFooter horizontal alignItems="center" justifyContent="flex-end" flow={2}>
<Button onClick={onClose}>{t('common:close')}</Button>
<Button primary>{t('send:steps.confirmation.cta')}</Button>
{txValidated ? (
// TODO: actually go to operations details
<Button onClick={onClose} primary>
{t('send:steps.confirmation.success.cta')}
</Button>
) : (
<Button onClick={() => this.setState({ stepIndex: 0 })} primary>
{t('send:steps.confirmation.error.cta')}
</Button>
)}
</ModalFooter>
)}
</ModalBody>

245
src/helpers/countries.json

@ -0,0 +1,245 @@
[
{ "name": "Afghanistan", "key": "AF" },
{ "name": "Åland Islands", "key": "AX" },
{ "name": "Albania", "key": "AL" },
{ "name": "Algeria", "key": "DZ" },
{ "name": "American Samoa", "key": "AS" },
{ "name": "AndorrA", "key": "AD" },
{ "name": "Angola", "key": "AO" },
{ "name": "Anguilla", "key": "AI" },
{ "name": "Antarctica", "key": "AQ" },
{ "name": "Antigua and Barbuda", "key": "AG" },
{ "name": "Argentina", "key": "AR" },
{ "name": "Armenia", "key": "AM" },
{ "name": "Aruba", "key": "AW" },
{ "name": "Australia", "key": "AU" },
{ "name": "Austria", "key": "AT" },
{ "name": "Azerbaijan", "key": "AZ" },
{ "name": "Bahamas", "key": "BS" },
{ "name": "Bahrain", "key": "BH" },
{ "name": "Bangladesh", "key": "BD" },
{ "name": "Barbados", "key": "BB" },
{ "name": "Belarus", "key": "BY" },
{ "name": "Belgium", "key": "BE" },
{ "name": "Belize", "key": "BZ" },
{ "name": "Benin", "key": "BJ" },
{ "name": "Bermuda", "key": "BM" },
{ "name": "Bhutan", "key": "BT" },
{ "name": "Bolivia", "key": "BO" },
{ "name": "Bosnia and Herzegovina", "key": "BA" },
{ "name": "Botswana", "key": "BW" },
{ "name": "Bouvet Island", "key": "BV" },
{ "name": "Brazil", "key": "BR" },
{ "name": "British Indian Ocean Territory", "key": "IO" },
{ "name": "Brunei Darussalam", "key": "BN" },
{ "name": "Bulgaria", "key": "BG" },
{ "name": "Burkina Faso", "key": "BF" },
{ "name": "Burundi", "key": "BI" },
{ "name": "Cambodia", "key": "KH" },
{ "name": "Cameroon", "key": "CM" },
{ "name": "Canada", "key": "CA" },
{ "name": "Cape Verde", "key": "CV" },
{ "name": "Cayman Islands", "key": "KY" },
{ "name": "Central African Republic", "key": "CF" },
{ "name": "Chad", "key": "TD" },
{ "name": "Chile", "key": "CL" },
{ "name": "China", "key": "CN" },
{ "name": "Christmas Island", "key": "CX" },
{ "name": "Cocos (Keeling) Islands", "key": "CC" },
{ "name": "Colombia", "key": "CO" },
{ "name": "Comoros", "key": "KM" },
{ "name": "Congo", "key": "CG" },
{ "name": "Congo, The Democratic Republic of the", "key": "CD" },
{ "name": "Cook Islands", "key": "CK" },
{ "name": "Costa Rica", "key": "CR" },
{ "name": "Cote D'Ivoire", "key": "CI" },
{ "name": "Croatia", "key": "HR" },
{ "name": "Cuba", "key": "CU" },
{ "name": "Cyprus", "key": "CY" },
{ "name": "Czech Republic", "key": "CZ" },
{ "name": "Denmark", "key": "DK" },
{ "name": "Djibouti", "key": "DJ" },
{ "name": "Dominica", "key": "DM" },
{ "name": "Dominican Republic", "key": "DO" },
{ "name": "Ecuador", "key": "EC" },
{ "name": "Egypt", "key": "EG" },
{ "name": "El Salvador", "key": "SV" },
{ "name": "Equatorial Guinea", "key": "GQ" },
{ "name": "Eritrea", "key": "ER" },
{ "name": "Estonia", "key": "EE" },
{ "name": "Ethiopia", "key": "ET" },
{ "name": "Falkland Islands (Malvinas)", "key": "FK" },
{ "name": "Faroe Islands", "key": "FO" },
{ "name": "Fiji", "key": "FJ" },
{ "name": "Finland", "key": "FI" },
{ "name": "France", "key": "FR" },
{ "name": "French Guiana", "key": "GF" },
{ "name": "French Polynesia", "key": "PF" },
{ "name": "French Southern Territories", "key": "TF" },
{ "name": "Gabon", "key": "GA" },
{ "name": "Gambia", "key": "GM" },
{ "name": "Georgia", "key": "GE" },
{ "name": "Germany", "key": "DE" },
{ "name": "Ghana", "key": "GH" },
{ "name": "Gibraltar", "key": "GI" },
{ "name": "Greece", "key": "GR" },
{ "name": "Greenland", "key": "GL" },
{ "name": "Grenada", "key": "GD" },
{ "name": "Guadeloupe", "key": "GP" },
{ "name": "Guam", "key": "GU" },
{ "name": "Guatemala", "key": "GT" },
{ "name": "Guernsey", "key": "GG" },
{ "name": "Guinea", "key": "GN" },
{ "name": "Guinea-Bissau", "key": "GW" },
{ "name": "Guyana", "key": "GY" },
{ "name": "Haiti", "key": "HT" },
{ "name": "Heard Island and Mcdonald Islands", "key": "HM" },
{ "name": "Holy See (Vatican City State)", "key": "VA" },
{ "name": "Honduras", "key": "HN" },
{ "name": "Hong Kong", "key": "HK" },
{ "name": "Hungary", "key": "HU" },
{ "name": "Iceland", "key": "IS" },
{ "name": "India", "key": "IN" },
{ "name": "Indonesia", "key": "ID" },
{ "name": "Iran, Islamic Republic Of", "key": "IR" },
{ "name": "Iraq", "key": "IQ" },
{ "name": "Ireland", "key": "IE" },
{ "name": "Isle of Man", "key": "IM" },
{ "name": "Israel", "key": "IL" },
{ "name": "Italy", "key": "IT" },
{ "name": "Jamaica", "key": "JM" },
{ "name": "Japan", "key": "JP" },
{ "name": "Jersey", "key": "JE" },
{ "name": "Jordan", "key": "JO" },
{ "name": "Kazakhstan", "key": "KZ" },
{ "name": "Kenya", "key": "KE" },
{ "name": "Kiribati", "key": "KI" },
{ "name": "Korea, Democratic People'S Republic of", "key": "KP" },
{ "name": "Korea, Republic of", "key": "KR" },
{ "name": "Kuwait", "key": "KW" },
{ "name": "Kyrgyzstan", "key": "KG" },
{ "name": "Lao People'S Democratic Republic", "key": "LA" },
{ "name": "Latvia", "key": "LV" },
{ "name": "Lebanon", "key": "LB" },
{ "name": "Lesotho", "key": "LS" },
{ "name": "Liberia", "key": "LR" },
{ "name": "Libyan Arab Jamahiriya", "key": "LY" },
{ "name": "Liechtenstein", "key": "LI" },
{ "name": "Lithuania", "key": "LT" },
{ "name": "Luxembourg", "key": "LU" },
{ "name": "Macao", "key": "MO" },
{ "name": "Macedonia, The Former Yugoslav Republic of", "key": "MK" },
{ "name": "Madagascar", "key": "MG" },
{ "name": "Malawi", "key": "MW" },
{ "name": "Malaysia", "key": "MY" },
{ "name": "Maldives", "key": "MV" },
{ "name": "Mali", "key": "ML" },
{ "name": "Malta", "key": "MT" },
{ "name": "Marshall Islands", "key": "MH" },
{ "name": "Martinique", "key": "MQ" },
{ "name": "Mauritania", "key": "MR" },
{ "name": "Mauritius", "key": "MU" },
{ "name": "Mayotte", "key": "YT" },
{ "name": "Mexico", "key": "MX" },
{ "name": "Micronesia, Federated States of", "key": "FM" },
{ "name": "Moldova, Republic of", "key": "MD" },
{ "name": "Monaco", "key": "MC" },
{ "name": "Mongolia", "key": "MN" },
{ "name": "Montserrat", "key": "MS" },
{ "name": "Morocco", "key": "MA" },
{ "name": "Mozambique", "key": "MZ" },
{ "name": "Myanmar", "key": "MM" },
{ "name": "Namibia", "key": "NA" },
{ "name": "Nauru", "key": "NR" },
{ "name": "Nepal", "key": "NP" },
{ "name": "Netherlands", "key": "NL" },
{ "name": "Netherlands Antilles", "key": "AN" },
{ "name": "New Caledonia", "key": "NC" },
{ "name": "New Zealand", "key": "NZ" },
{ "name": "Nicaragua", "key": "NI" },
{ "name": "Niger", "key": "NE" },
{ "name": "Nigeria", "key": "NG" },
{ "name": "Niue", "key": "NU" },
{ "name": "Norfolk Island", "key": "NF" },
{ "name": "Northern Mariana Islands", "key": "MP" },
{ "name": "Norway", "key": "NO" },
{ "name": "Oman", "key": "OM" },
{ "name": "Pakistan", "key": "PK" },
{ "name": "Palau", "key": "PW" },
{ "name": "Palestinian Territory, Occupied", "key": "PS" },
{ "name": "Panama", "key": "PA" },
{ "name": "Papua New Guinea", "key": "PG" },
{ "name": "Paraguay", "key": "PY" },
{ "name": "Peru", "key": "PE" },
{ "name": "Philippines", "key": "PH" },
{ "name": "Pitcairn", "key": "PN" },
{ "name": "Poland", "key": "PL" },
{ "name": "Portugal", "key": "PT" },
{ "name": "Puerto Rico", "key": "PR" },
{ "name": "Qatar", "key": "QA" },
{ "name": "Reunion", "key": "RE" },
{ "name": "Romania", "key": "RO" },
{ "name": "Russian Federation", "key": "RU" },
{ "name": "RWANDA", "key": "RW" },
{ "name": "Saint Helena", "key": "SH" },
{ "name": "Saint Kitts and Nevis", "key": "KN" },
{ "name": "Saint Lucia", "key": "LC" },
{ "name": "Saint Pierre and Miquelon", "key": "PM" },
{ "name": "Saint Vincent and the Grenadines", "key": "VC" },
{ "name": "Samoa", "key": "WS" },
{ "name": "San Marino", "key": "SM" },
{ "name": "Sao Tome and Principe", "key": "ST" },
{ "name": "Saudi Arabia", "key": "SA" },
{ "name": "Senegal", "key": "SN" },
{ "name": "Serbia and Montenegro", "key": "CS" },
{ "name": "Seychelles", "key": "SC" },
{ "name": "Sierra Leone", "key": "SL" },
{ "name": "Singapore", "key": "SG" },
{ "name": "Slovakia", "key": "SK" },
{ "name": "Slovenia", "key": "SI" },
{ "name": "Solomon Islands", "key": "SB" },
{ "name": "Somalia", "key": "SO" },
{ "name": "South Africa", "key": "ZA" },
{ "name": "South Georgia and the South Sandwich Islands", "key": "GS" },
{ "name": "Spain", "key": "ES" },
{ "name": "Sri Lanka", "key": "LK" },
{ "name": "Sudan", "key": "SD" },
{ "name": "Suriname", "key": "SR" },
{ "name": "Svalbard and Jan Mayen", "key": "SJ" },
{ "name": "Swaziland", "key": "SZ" },
{ "name": "Sweden", "key": "SE" },
{ "name": "Switzerland", "key": "CH" },
{ "name": "Syrian Arab Republic", "key": "SY" },
{ "name": "Taiwan, Province of China", "key": "TW" },
{ "name": "Tajikistan", "key": "TJ" },
{ "name": "Tanzania, United Republic of", "key": "TZ" },
{ "name": "Thailand", "key": "TH" },
{ "name": "Timor-Leste", "key": "TL" },
{ "name": "Togo", "key": "TG" },
{ "name": "Tokelau", "key": "TK" },
{ "name": "Tonga", "key": "TO" },
{ "name": "Trinidad and Tobago", "key": "TT" },
{ "name": "Tunisia", "key": "TN" },
{ "name": "Turkey", "key": "TR" },
{ "name": "Turkmenistan", "key": "TM" },
{ "name": "Turks and Caicos Islands", "key": "TC" },
{ "name": "Tuvalu", "key": "TV" },
{ "name": "Uganda", "key": "UG" },
{ "name": "Ukraine", "key": "UA" },
{ "name": "United Arab Emirates", "key": "AE" },
{ "name": "United Kingdom", "key": "GB" },
{ "name": "United States", "key": "US" },
{ "name": "United States Minor Outlying Islands", "key": "UM" },
{ "name": "Uruguay", "key": "UY" },
{ "name": "Uzbekistan", "key": "UZ" },
{ "name": "Vanuatu", "key": "VU" },
{ "name": "Venezuela", "key": "VE" },
{ "name": "Viet Nam", "key": "VN" },
{ "name": "Virgin Islands, British", "key": "VG" },
{ "name": "Virgin Islands, U.S.", "key": "VI" },
{ "name": "Wallis and Futuna", "key": "WF" },
{ "name": "Western Sahara", "key": "EH" },
{ "name": "Yemen", "key": "YE" },
{ "name": "Zambia", "key": "ZM" },
{ "name": "Zimbabwe", "key": "ZW" }
]

12
src/icons/CheckCircle.js

@ -0,0 +1,12 @@
// @flow
import React from 'react'
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 24 24" height={size} width={size} {...p}>
<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"
/>
</svg>
)

12
src/icons/ExclamationCircleThin.js

@ -0,0 +1,12 @@
// @flow
import React from 'react'
export default ({ size, ...p }: { size: number }) => (
<svg viewBox="0 0 24 24" height={size} width={size} {...p}>
<path
fill="currentColor"
d="M12 1.875c5.56 0 10.125 4.504 10.125 10.125A10.122 10.122 0 0 1 12 22.125C6.41 22.125 1.875 17.599 1.875 12 1.875 6.412 6.403 1.875 12 1.875zm0-1.5C5.58.375.375 5.582.375 12 .375 18.422 5.58 23.625 12 23.625S23.625 18.422 23.625 12C23.625 5.582 18.42.375 12 .375zM11.461 6h1.078c.32 0 .575.266.562.586l-.329 7.875a.562.562 0 0 1-.562.539h-.42a.563.563 0 0 1-.563-.54L10.9 6.587A.563.563 0 0 1 11.461 6zM12 15.938a1.312 1.312 0 1 0 0 2.624 1.312 1.312 0 0 0 0-2.625z"
/>
</svg>
)

42
src/icons/LockScreen.js

@ -0,0 +1,42 @@
// @flow
/* this icon is a placeholder for now */
import React from 'react'
export default ({ size, ...p }: { size: number }) => (
<svg width={size} height={size} {...p}>
<defs>
<filter
id="a"
width="178.9%"
height="178.9%"
x="-39.4%"
y="-37.2%"
filterUnits="objectBoundingBox"
>
<feOffset dy="2" in="SourceAlpha" result="shadowOffsetOuter1" />
<feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="11.5" />
<feColorMatrix
in="shadowBlurOuter1"
result="shadowMatrixOuter1"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0787760417 0"
/>
<feMerge>
<feMergeNode in="shadowMatrixOuter1" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<g fill="none" fillRule="evenodd" filter="url(#a)" transform="translate(23 21)">
<rect width="90" height="90" fill="#FFF" rx="20" />
<path fill="#6490F1" d="M45 13.36c-17.475 0-31.64 14.165-31.64 31.64S27.524 76.64 45 76.64" />
<path fill="#142533" fillOpacity=".1" d="M13.36 45c0 17.475 14.165 31.64 31.64 31.64V45" />
<path
fill="#142533"
fillOpacity=".1"
d="M22.845 67.59c5.708 5.598 13.528 9.05 22.155 9.05V45"
/>
</g>
</svg>
)

2
src/reducers/settings.js

@ -36,7 +36,7 @@ const defaultState: SettingsState = {
},
marketIndicator: 'western',
currenciesSettings: {},
region: { key: 'US', name: 'United States' },
region: 'US',
}
const CURRENCY_DEFAULTS_SETTINGS: CurrencySettings = {

5
src/styles/helpers.js

@ -1,6 +1,8 @@
// @flow
import React from 'react'
import Color from 'color'
import uniqueId from 'lodash/uniqueId'
import staticPath from 'helpers/staticPath'
import { colors, fontFamilies } from 'styles/theme'
@ -53,6 +55,9 @@ export const fontFace = ({
}
`
export const multiline = (str: string) =>
str.split('\n').map(line => <p key={uniqueId()}>{line}</p>)
export function getMarketColor({
marketIndicator,
isNegative,

5
src/types/common.js

@ -40,10 +40,7 @@ export type Settings = {
},
marketIndicator: 'eastern' | 'western',
currenciesSettings: CurrenciesSettings,
region: {
key: string,
name: string,
},
region: string,
}
export type T = (?string, ?Object) => string

5
static/i18n/en/common.yml

@ -18,3 +18,8 @@ retry: Retry
close: Close
eastern: Eastern
western: Western
lockScreen:
title: Welcome Back
subTitle: Your application is locked
description: Please enter your password to continue
inputPlaceholder: Type your password

15
static/i18n/en/send.yml

@ -15,6 +15,19 @@ steps:
title: Connect device
verification:
title: Verification
warning: |
You are about to validate a transaction.
Be careful, we strongly recommand you to verify that the informations on your Ledger device are correct.
body: Once you have checked everything is ok, you can validate securely the transaction on your device.
confirmation:
title: Confirmation
cta: View operation details
success:
title: Transaction successfully completed
text: You may have to wait few confirmations unitl the transaction appear
cta: View operation details
error:
title: Transaction aborted
text: |
The transaction have been aborted on your device.
You can try again the operation.
cta: Retry operation

3
static/i18n/en/settings.yml

@ -36,9 +36,8 @@ profile:
passwordModalRepeatPasswordInput: Repeat password
passwordModalSave: Save
disablePasswordModalTitle: Disable Password
disablePasswordModalSubtitle: Type your password
disablePasswordModalDesc: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh diam.
disablePasswordModalInput: Type your password
disablePasswordModalInput: Current password
disablePasswordModalSave: Save
sync: Sync accounts
syncDesc: Lorem ipsum dolor sit amet

4
static/i18n/fr/manager.yml

@ -4,3 +4,7 @@ tabs:
device: Mon appareil
install: Installer
allApps: Toutes les applications
plugYourDevice:
title: Plug your device
desc: Lorem Ipsum is simply dummy text of the printing and typesetting industry’s standard dummy text
cta: Plug my device

1
static/i18n/fr/password.yml

@ -4,3 +4,4 @@ warning_1: Warning 1
warning_2: Warning 2
warning_3: Warning 3
warning_4: Warning 4
errorMessageIncorrectPassword: The password you entered is incorrect.

5
static/i18n/fr/settings.yml

@ -36,6 +36,11 @@ profile:
passwordModalNewPasswordInput: Nouveau mot de passe
passwordModalRepeatPasswordInput: Répéter mot de passe
passwordModalSave: Sauvegarder
disablePasswordModalTitle: Disable Password
disablePasswordModalSubtitle: Type your password
disablePasswordModalDesc: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh diam.
disablePasswordModalInput: Type your password
disablePasswordModalSave: Save
sync: Synchroniser les comptes
syncDesc: Lorem ipsum dolor sit amet
export: Exporter le journal

8
yarn.lock

@ -5018,6 +5018,14 @@ electron@1.8.4, electron@^1.8.2:
electron-download "^3.0.1"
extract-zip "^1.0.3"
electron@^1.8.2:
version "1.8.6"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.8.6.tgz#6d45e377b321a35b78a794b64e40b2cf8face6ae"
dependencies:
"@types/node" "^8.0.24"
electron-download "^3.0.1"
extract-zip "^1.0.3"
elegant-spinner@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"

Loading…
Cancel
Save