Meriadec Pillet
7 years ago
committed by
GitHub
52 changed files with 1589 additions and 799 deletions
@ -0,0 +1,60 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React from 'react' |
||||
|
import { translate } from 'react-i18next' |
||||
|
import { getIconByCoinType } from '@ledgerhq/currencies/react' |
||||
|
import { listCurrencies } from '@ledgerhq/currencies' |
||||
|
|
||||
|
import noop from 'lodash/noop' |
||||
|
|
||||
|
import type { Currency } from '@ledgerhq/currencies' |
||||
|
import type { T } from 'types/common' |
||||
|
|
||||
|
import Select from 'components/base/Select' |
||||
|
import Box from 'components/base/Box' |
||||
|
|
||||
|
const renderItem = a => { |
||||
|
const { color, name, coinType } = a |
||||
|
const Icon = getIconByCoinType(coinType) |
||||
|
return ( |
||||
|
<Box grow horizontal alignItems="center" flow={2}> |
||||
|
{Icon && ( |
||||
|
<Box style={{ width: 16, height: 16, color }}> |
||||
|
<Icon size={16} /> |
||||
|
</Box> |
||||
|
)} |
||||
|
<Box grow ff="Open Sans|SemiBold" color="dark" fontSize={4}> |
||||
|
{name} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
const currencies = listCurrencies() |
||||
|
|
||||
|
type Props = { |
||||
|
onChange: Function, |
||||
|
value?: Currency, |
||||
|
t: T, |
||||
|
} |
||||
|
|
||||
|
const SelectCurrency = ({ onChange, value, t, ...props }: Props) => ( |
||||
|
<Select |
||||
|
{...props} |
||||
|
value={value} |
||||
|
renderSelected={renderItem} |
||||
|
renderItem={renderItem} |
||||
|
keyProp="coinType" |
||||
|
items={currencies.sort((a, b) => (a.name < b.name ? -1 : 1))} |
||||
|
placeholder={t('common:selectCurrency')} |
||||
|
fontSize={4} |
||||
|
onChange={onChange} |
||||
|
/> |
||||
|
) |
||||
|
|
||||
|
SelectCurrency.defaultProps = { |
||||
|
onChange: noop, |
||||
|
value: undefined, |
||||
|
} |
||||
|
|
||||
|
export default translate()(SelectCurrency) |
@ -0,0 +1,31 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Component } from 'react' |
||||
|
import { storiesOf } from '@storybook/react' |
||||
|
import { action } from '@storybook/addon-actions' |
||||
|
|
||||
|
import SelectCurrency from 'components/SelectCurrency' |
||||
|
|
||||
|
const stories = storiesOf('Components', module) |
||||
|
|
||||
|
class Wrapper extends Component<any, any> { |
||||
|
state = { |
||||
|
value: '', |
||||
|
} |
||||
|
|
||||
|
handleChange = item => { |
||||
|
this.setState({ value: item }) |
||||
|
action('onChange')(item) |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { render } = this.props |
||||
|
const { value } = this.state |
||||
|
|
||||
|
return render({ onChange: this.handleChange, value }) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
stories.add('SelectCurrency', () => ( |
||||
|
<Wrapper render={({ onChange, value }) => <SelectCurrency onChange={onChange} value={value} />} /> |
||||
|
)) |
@ -1,98 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import React, { PureComponent } from 'react' |
|
||||
|
|
||||
import type { SettingsDisplay, T } from 'types/common' |
|
||||
|
|
||||
import Box, { Card } from 'components/base/Box' |
|
||||
import Button from 'components/base/Button' |
|
||||
import Label from 'components/base/Label' |
|
||||
import Select from 'components/base/Select' |
|
||||
|
|
||||
type InputValue = SettingsDisplay |
|
||||
|
|
||||
type Props = { |
|
||||
t: T, |
|
||||
settings: SettingsDisplay, |
|
||||
onSaveSettings: Function, |
|
||||
} |
|
||||
|
|
||||
type State = { |
|
||||
inputValue: InputValue, |
|
||||
} |
|
||||
|
|
||||
class TabProfile extends PureComponent<Props, State> { |
|
||||
state = { |
|
||||
inputValue: { |
|
||||
language: this.props.settings.language, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
getDatas() { |
|
||||
const { t } = this.props |
|
||||
|
|
||||
return { |
|
||||
languages: [ |
|
||||
{ |
|
||||
key: 'en', |
|
||||
name: t('language:en'), |
|
||||
}, |
|
||||
{ |
|
||||
key: 'fr', |
|
||||
name: t('language:fr'), |
|
||||
}, |
|
||||
], |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) => |
|
||||
this.setState(prev => ({ |
|
||||
inputValue: { |
|
||||
...prev.inputValue, |
|
||||
[key]: value, |
|
||||
}, |
|
||||
})) |
|
||||
|
|
||||
handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
const { onSaveSettings } = this.props |
|
||||
const { inputValue } = this.state |
|
||||
|
|
||||
onSaveSettings({ |
|
||||
...inputValue, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
render() { |
|
||||
const { t } = this.props |
|
||||
const { inputValue } = this.state |
|
||||
|
|
||||
const { languages } = this.getDatas() |
|
||||
|
|
||||
const currentLanguage = languages.find(l => l.key === inputValue.language) |
|
||||
|
|
||||
return ( |
|
||||
<form onSubmit={this.handleSubmit}> |
|
||||
<Card flow={3}> |
|
||||
<Box flow={1}> |
|
||||
<Label>{t('settings:display.language')}</Label> |
|
||||
<Select |
|
||||
onChange={item => this.handleChangeInput('language')(item.key)} |
|
||||
renderSelected={item => item && item.name} |
|
||||
value={currentLanguage} |
|
||||
items={languages} |
|
||||
/> |
|
||||
</Box> |
|
||||
<Box horizontal justifyContent="flex-end"> |
|
||||
<Button primary type="submit"> |
|
||||
{t('common:save')} |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Card> |
|
||||
</form> |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export default TabProfile |
|
@ -1,88 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import React, { PureComponent } from 'react' |
|
||||
import { getFiatUnit } from '@ledgerhq/currencies' |
|
||||
|
|
||||
import type { SettingsMoney, T } from 'types/common' |
|
||||
|
|
||||
import Box, { Card } from 'components/base/Box' |
|
||||
import Button from 'components/base/Button' |
|
||||
import Label from 'components/base/Label' |
|
||||
import Select from 'components/base/Select' |
|
||||
|
|
||||
const counterValues = ['USD', 'EUR', 'JPY', 'GBP'].sort().map(c => { |
|
||||
const { name } = getFiatUnit(c) |
|
||||
return { |
|
||||
key: c, |
|
||||
name, |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
type InputValue = SettingsMoney |
|
||||
|
|
||||
type Props = { |
|
||||
t: T, |
|
||||
settings: SettingsMoney, |
|
||||
onSaveSettings: Function, |
|
||||
} |
|
||||
|
|
||||
type State = { |
|
||||
inputValue: InputValue, |
|
||||
} |
|
||||
|
|
||||
class TabProfile extends PureComponent<Props, State> { |
|
||||
state = { |
|
||||
inputValue: { |
|
||||
counterValue: this.props.settings.counterValue, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
handleChangeInput = (key: $Keys<InputValue>) => (value: $Values<InputValue>) => |
|
||||
this.setState(prev => ({ |
|
||||
inputValue: { |
|
||||
...prev.inputValue, |
|
||||
[key]: value, |
|
||||
}, |
|
||||
})) |
|
||||
|
|
||||
handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
const { onSaveSettings } = this.props |
|
||||
const { inputValue } = this.state |
|
||||
|
|
||||
onSaveSettings({ |
|
||||
...inputValue, |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
render() { |
|
||||
const { t } = this.props |
|
||||
const { inputValue } = this.state |
|
||||
|
|
||||
const currentCounterValues = counterValues.find(l => l.key === inputValue.counterValue) |
|
||||
|
|
||||
return ( |
|
||||
<form onSubmit={this.handleSubmit}> |
|
||||
<Card flow={3}> |
|
||||
<Box flow={1}> |
|
||||
<Label>{t('settings:display.counterValue')}</Label> |
|
||||
<Select |
|
||||
onChange={item => this.handleChangeInput('counterValue')(item.key)} |
|
||||
renderSelected={item => item && item.name} |
|
||||
value={currentCounterValues} |
|
||||
items={counterValues} |
|
||||
/> |
|
||||
</Box> |
|
||||
<Box horizontal justifyContent="flex-end"> |
|
||||
<Button primary type="submit"> |
|
||||
{t('common:save')} |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Card> |
|
||||
</form> |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export default TabProfile |
|
@ -0,0 +1,137 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import bcrypt from 'bcryptjs' |
||||
|
|
||||
|
import { unlock } from 'reducers/application' |
||||
|
|
||||
|
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 { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from 'components/base/Modal' |
||||
|
|
||||
|
import type { T } from 'types/common' |
||||
|
|
||||
|
const mapDispatchToProps = { |
||||
|
unlock, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
onClose: Function, |
||||
|
unlock: Function, |
||||
|
isPasswordEnabled: boolean, |
||||
|
currentPasswordHash: string, |
||||
|
onChangePassword: Function, |
||||
|
} |
||||
|
|
||||
|
type State = { |
||||
|
currentPassword: string, |
||||
|
newPassword: string, |
||||
|
} |
||||
|
|
||||
|
const INITIAL_STATE = { |
||||
|
currentPassword: '', |
||||
|
newPassword: '', |
||||
|
} |
||||
|
|
||||
|
class PasswordModal extends PureComponent<Props, State> { |
||||
|
state = INITIAL_STATE |
||||
|
|
||||
|
handleSave = (e: SyntheticEvent<HTMLFormElement>) => { |
||||
|
if (e) { |
||||
|
e.preventDefault() |
||||
|
} |
||||
|
if (!this.isValid()) { |
||||
|
return |
||||
|
} |
||||
|
const { currentPassword, newPassword } = this.state |
||||
|
const { isPasswordEnabled, currentPasswordHash, onChangePassword } = this.props |
||||
|
if (isPasswordEnabled) { |
||||
|
if (!bcrypt.compareSync(currentPassword, currentPasswordHash)) { |
||||
|
return |
||||
|
} |
||||
|
onChangePassword(newPassword) |
||||
|
} else { |
||||
|
onChangePassword(newPassword) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
handleInputChange = key => value => this.setState({ [key]: value }) |
||||
|
|
||||
|
handleReset = () => this.setState(INITIAL_STATE) |
||||
|
|
||||
|
isValid = () => { |
||||
|
const { newPassword } = this.state |
||||
|
return newPassword |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { t, isPasswordEnabled, onClose, ...props } = this.props |
||||
|
const { currentPassword, newPassword } = this.state |
||||
|
const isValid = this.isValid() |
||||
|
return ( |
||||
|
<Modal |
||||
|
{...props} |
||||
|
onHide={this.handleReset} |
||||
|
onClose={onClose} |
||||
|
render={({ onClose }) => ( |
||||
|
<form onSubmit={this.handleSave}> |
||||
|
<ModalBody onClose={onClose}> |
||||
|
<ModalTitle>{t('settings:profile.passwordModalTitle')}</ModalTitle> |
||||
|
<ModalContent> |
||||
|
<Box ff="Museo Sans|Regular" color="dark" textAlign="center" mb={2} mt={3}> |
||||
|
{t('settings:profile.passwordModalSubtitle')} |
||||
|
</Box> |
||||
|
<Box ff="Open Sans" color="smoke" fontSize={4} textAlign="center" px={4}> |
||||
|
{t('settings:profile.passwordModalDesc')} |
||||
|
<Box px={7} mt={4} flow={3}> |
||||
|
{isPasswordEnabled && ( |
||||
|
<Box flow={1}> |
||||
|
<Label htmlFor="password"> |
||||
|
{t('settings:profile.passwordModalPasswordInput')} |
||||
|
</Label> |
||||
|
<InputPassword |
||||
|
type="password" |
||||
|
placeholder={t('settings:profile.passwordModalPasswordInput')} |
||||
|
autoFocus |
||||
|
id="password" |
||||
|
onChange={this.handleInputChange('currentPassword')} |
||||
|
value={currentPassword} |
||||
|
/> |
||||
|
</Box> |
||||
|
)} |
||||
|
<Box flow={1}> |
||||
|
{isPasswordEnabled && ( |
||||
|
<Label htmlFor="newPassword"> |
||||
|
{t('settings:profile.passwordModalNewPasswordInput')} |
||||
|
</Label> |
||||
|
)} |
||||
|
<InputPassword |
||||
|
placeholder={t('settings:profile.passwordModalNewPasswordInput')} |
||||
|
id="newPassword" |
||||
|
onChange={this.handleInputChange('newPassword')} |
||||
|
value={newPassword} |
||||
|
withStrength |
||||
|
/> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</Box> |
||||
|
</ModalContent> |
||||
|
<ModalFooter horizontal align="center" justify="flex-end" flow={2}> |
||||
|
<Button onClick={onClose}>{t('common:cancel')}</Button> |
||||
|
<Button primary onClick={this.handleSave} disabled={!isValid}> |
||||
|
{t('settings:profile.passwordModalSave')} |
||||
|
</Button> |
||||
|
</ModalFooter> |
||||
|
</ModalBody> |
||||
|
</form> |
||||
|
)} |
||||
|
/> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect(null, mapDispatchToProps)(PasswordModal) |
@ -1,124 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import React, { PureComponent } from 'react' |
|
||||
import { connect } from 'react-redux' |
|
||||
import bcrypt from 'bcryptjs' |
|
||||
|
|
||||
import get from 'lodash/get' |
|
||||
import set from 'lodash/set' |
|
||||
|
|
||||
import { setEncryptionKey } from 'helpers/db' |
|
||||
|
|
||||
import type { SettingsProfile, T } from 'types/common' |
|
||||
|
|
||||
import { unlock } from 'reducers/application' |
|
||||
|
|
||||
import Box, { Card } from 'components/base/Box' |
|
||||
import Input from 'components/base/Input' |
|
||||
import CheckBox from 'components/base/CheckBox' |
|
||||
import Button from 'components/base/Button' |
|
||||
import Label from 'components/base/Label' |
|
||||
|
|
||||
type InputValue = SettingsProfile |
|
||||
|
|
||||
type Props = { |
|
||||
t: T, |
|
||||
settings: SettingsProfile, |
|
||||
onSaveSettings: Function, |
|
||||
unlock: Function, |
|
||||
} |
|
||||
type State = { |
|
||||
inputValue: InputValue, |
|
||||
} |
|
||||
|
|
||||
const mapDispatchToProps = { |
|
||||
unlock, |
|
||||
} |
|
||||
|
|
||||
class TabProfile extends PureComponent<Props, State> { |
|
||||
state = { |
|
||||
inputValue: { |
|
||||
password: { |
|
||||
...this.props.settings.password, |
|
||||
value: undefined, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
handleChangeInput = (key: string) => (value: $Values<InputValue>) => |
|
||||
this.setState(prev => ({ |
|
||||
inputValue: { |
|
||||
...set(prev.inputValue, key, value), |
|
||||
}, |
|
||||
})) |
|
||||
|
|
||||
handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => { |
|
||||
e.preventDefault() |
|
||||
|
|
||||
const { onSaveSettings, unlock } = this.props |
|
||||
const { inputValue } = this.state |
|
||||
|
|
||||
const settings = { |
|
||||
...inputValue, |
|
||||
password: { |
|
||||
...inputValue.password, |
|
||||
value: '', |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
const password = get(inputValue, 'password', {}) |
|
||||
|
|
||||
if (password.state === true && password.value.trim() !== '') { |
|
||||
settings.password.value = bcrypt.hashSync(password.value, 8) |
|
||||
setEncryptionKey('accounts', password.value) |
|
||||
} else { |
|
||||
setEncryptionKey('accounts', undefined) |
|
||||
} |
|
||||
|
|
||||
unlock() |
|
||||
|
|
||||
onSaveSettings(settings) |
|
||||
} |
|
||||
|
|
||||
render() { |
|
||||
const { t } = this.props |
|
||||
const { inputValue } = this.state |
|
||||
|
|
||||
const isPasswordChecked = get(inputValue, 'password.state', false) |
|
||||
return ( |
|
||||
<form onSubmit={this.handleSubmit}> |
|
||||
<Card flow={3}> |
|
||||
<label> |
|
||||
<Box |
|
||||
horizontal |
|
||||
alignItems="center" |
|
||||
flow={2} |
|
||||
style={{ cursor: 'pointer' }} |
|
||||
onClick={() => this.handleChangeInput('password.state')(!isPasswordChecked)} |
|
||||
> |
|
||||
<CheckBox isChecked={isPasswordChecked} /> |
|
||||
<div>{t('settings:profile.protectWithPassword')}</div> |
|
||||
</Box> |
|
||||
</label> |
|
||||
{get(inputValue, 'password.state') === true && ( |
|
||||
<Box flow={1}> |
|
||||
<Label>{t('settings:profile.password')}</Label> |
|
||||
<Input |
|
||||
value={get(inputValue, 'password.value', '')} |
|
||||
onChange={this.handleChangeInput('password.value')} |
|
||||
type="password" |
|
||||
/> |
|
||||
</Box> |
|
||||
)} |
|
||||
<Box horizontal justifyContent="flex-end"> |
|
||||
<Button primary type="submit"> |
|
||||
{t('common:save')} |
|
||||
</Button> |
|
||||
</Box> |
|
||||
</Card> |
|
||||
</form> |
|
||||
) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export default connect(null, mapDispatchToProps)(TabProfile) |
|
@ -0,0 +1,120 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React from 'react' |
||||
|
import styled from 'styled-components' |
||||
|
|
||||
|
import { rgba } from 'styles/helpers' |
||||
|
|
||||
|
import Box, { Card } from 'components/base/Box' |
||||
|
|
||||
|
export const SettingsSection = styled(Card).attrs({ p: 0 })`` |
||||
|
|
||||
|
const SettingsSectionHeaderContainer = styled(Box).attrs({ |
||||
|
p: 4, |
||||
|
horizontal: true, |
||||
|
align: 'center', |
||||
|
})` |
||||
|
border-bottom: 1px solid ${p => p.theme.colors.lightFog}; |
||||
|
line-height: normal; |
||||
|
` |
||||
|
|
||||
|
const RoundIconContainer = styled(Box).attrs({ |
||||
|
align: 'center', |
||||
|
justify: 'center', |
||||
|
bg: p => rgba(p.theme.colors.wallet, 0.2), |
||||
|
color: 'wallet', |
||||
|
})` |
||||
|
height: 30px; |
||||
|
width: 30px; |
||||
|
border-radius: 50%; |
||||
|
` |
||||
|
|
||||
|
export const SettingsSectionBody = styled(Box)` |
||||
|
> * + * { |
||||
|
&:after { |
||||
|
background: ${p => p.theme.colors.lightFog}; |
||||
|
content: ''; |
||||
|
display: block; |
||||
|
height: 1px; |
||||
|
left: ${p => p.theme.space[4]}px; |
||||
|
position: absolute; |
||||
|
right: ${p => p.theme.space[4]}px; |
||||
|
top: 0; |
||||
|
} |
||||
|
} |
||||
|
` |
||||
|
|
||||
|
export function SettingsSectionHeader({ |
||||
|
title, |
||||
|
desc, |
||||
|
icon, |
||||
|
renderRight, |
||||
|
}: { |
||||
|
title: string, |
||||
|
desc: string, |
||||
|
icon: any, |
||||
|
renderRight?: any, |
||||
|
}) { |
||||
|
return ( |
||||
|
<SettingsSectionHeaderContainer> |
||||
|
<RoundIconContainer mr={3}>{icon}</RoundIconContainer> |
||||
|
<Box grow> |
||||
|
<Box ff="Museo Sans|Regular" color="dark"> |
||||
|
{title} |
||||
|
</Box> |
||||
|
<Box ff="Open Sans" fontSize={3}> |
||||
|
{desc} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
{renderRight && ( |
||||
|
<Box alignItems="center" justifyContent="flex-end"> |
||||
|
{renderRight} |
||||
|
</Box> |
||||
|
)} |
||||
|
</SettingsSectionHeaderContainer> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
SettingsSectionHeader.defaultProps = { |
||||
|
renderRight: undefined, |
||||
|
} |
||||
|
|
||||
|
const SettingsSectionRowContainer = styled(Box).attrs({ |
||||
|
p: 4, |
||||
|
horizontal: true, |
||||
|
align: 'center', |
||||
|
relative: true, |
||||
|
})` |
||||
|
cursor: ${p => (p.onClick ? 'pointer' : '')}; |
||||
|
` |
||||
|
|
||||
|
export function SettingsSectionRow({ |
||||
|
title, |
||||
|
desc, |
||||
|
children, |
||||
|
onClick, |
||||
|
}: { |
||||
|
title: string, |
||||
|
desc: string, |
||||
|
children?: any, |
||||
|
onClick?: ?Function, |
||||
|
}) { |
||||
|
return ( |
||||
|
<SettingsSectionRowContainer onClick={onClick}> |
||||
|
<Box grow> |
||||
|
<Box ff="Open Sans|SemiBold" color="dark" fontSize={4}> |
||||
|
{title} |
||||
|
</Box> |
||||
|
<Box ff="Open Sans" fontSize={3} color="grey"> |
||||
|
{desc} |
||||
|
</Box> |
||||
|
</Box> |
||||
|
<Box>{children}</Box> |
||||
|
</SettingsSectionRowContainer> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
SettingsSectionRow.defaultProps = { |
||||
|
children: null, |
||||
|
onClick: null, |
||||
|
} |
@ -0,0 +1,62 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import { shell } from 'electron' |
||||
|
|
||||
|
import type { T } from 'types/common' |
||||
|
|
||||
|
import IconHelp from 'icons/Help' |
||||
|
import IconChevronRight from 'icons/ChevronRight' |
||||
|
|
||||
|
import { |
||||
|
SettingsSection as Section, |
||||
|
SettingsSectionHeader as Header, |
||||
|
SettingsSectionBody as Body, |
||||
|
SettingsSectionRow as Row, |
||||
|
} from '../SettingsSection' |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
} |
||||
|
|
||||
|
class SectionAbout extends PureComponent<Props> { |
||||
|
handleOpenLink = (url: string) => () => shell.openExternal(url) |
||||
|
|
||||
|
render() { |
||||
|
const { t } = this.props |
||||
|
return ( |
||||
|
<Section> |
||||
|
<Header |
||||
|
icon={<IconHelp size={16} />} |
||||
|
title={t('settings:tabs.about')} |
||||
|
desc="Lorem ipsum dolor sit amet" |
||||
|
/> |
||||
|
<Body> |
||||
|
<Row |
||||
|
onClick={this.handleOpenLink('http://google.com')} |
||||
|
title={t('settings:about.faq')} |
||||
|
desc={t('settings:about.faqDesc')} |
||||
|
> |
||||
|
<IconChevronRight size={16} /> |
||||
|
</Row> |
||||
|
<Row |
||||
|
onClick={this.handleOpenLink('http://google.com')} |
||||
|
title={t('settings:about.contactUs')} |
||||
|
desc={t('settings:about.contactUsDesc')} |
||||
|
> |
||||
|
<IconChevronRight size={16} /> |
||||
|
</Row> |
||||
|
<Row |
||||
|
onClick={this.handleOpenLink('http://google.com')} |
||||
|
title={t('settings:about.terms')} |
||||
|
desc={t('settings:about.termsDesc')} |
||||
|
> |
||||
|
<IconChevronRight size={16} /> |
||||
|
</Row> |
||||
|
</Body> |
||||
|
</Section> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default SectionAbout |
@ -0,0 +1,72 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
|
||||
|
import { listCurrencies } from '@ledgerhq/currencies' |
||||
|
|
||||
|
import type { Currency } from '@ledgerhq/currencies' |
||||
|
import type { T } from 'types/common' |
||||
|
|
||||
|
import SelectCurrency from 'components/SelectCurrency' |
||||
|
|
||||
|
import IconCurrencies from 'icons/Currencies' |
||||
|
|
||||
|
import { |
||||
|
SettingsSection as Section, |
||||
|
SettingsSectionHeader as Header, |
||||
|
SettingsSectionBody as Body, |
||||
|
SettingsSectionRow as Row, |
||||
|
} from '../SettingsSection' |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
} |
||||
|
|
||||
|
type State = { |
||||
|
currency: Currency, |
||||
|
} |
||||
|
|
||||
|
class TabCurrencies extends PureComponent<Props, State> { |
||||
|
state = { |
||||
|
currency: listCurrencies()[0], |
||||
|
} |
||||
|
|
||||
|
handleChangeCurrency = (currency: Currency) => this.setState({ currency }) |
||||
|
|
||||
|
render() { |
||||
|
const { t } = this.props |
||||
|
const { currency } = this.state |
||||
|
return ( |
||||
|
<Section> |
||||
|
<Header |
||||
|
icon={<IconCurrencies size={16} />} |
||||
|
title={t('settings:tabs.currencies')} |
||||
|
desc="Lorem ipsum dolor sit amet" |
||||
|
renderRight={ |
||||
|
<SelectCurrency small value={currency} onChange={this.handleChangeCurrency} /> |
||||
|
} |
||||
|
/> |
||||
|
<Body> |
||||
|
<Row |
||||
|
title={t('settings:currencies.confirmationsToSpend')} |
||||
|
desc={t('settings:currencies.confirmationsToSpendDesc')} |
||||
|
/> |
||||
|
<Row |
||||
|
title={t('settings:currencies.confirmationsNb')} |
||||
|
desc={t('settings:currencies.confirmationsNbDesc')} |
||||
|
/> |
||||
|
<Row |
||||
|
title={t('settings:currencies.transactionsFees')} |
||||
|
desc={t('settings:currencies.transactionsFeesDesc')} |
||||
|
/> |
||||
|
<Row |
||||
|
title={t('settings:currencies.explorer')} |
||||
|
desc={t('settings:currencies.explorerDesc')} |
||||
|
/> |
||||
|
</Body> |
||||
|
</Section> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default TabCurrencies |
@ -0,0 +1,120 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import moment from 'moment' |
||||
|
import { listFiats } from '@ledgerhq/currencies' |
||||
|
|
||||
|
import type { Settings, T } from 'types/common' |
||||
|
|
||||
|
import Select from 'components/base/Select' |
||||
|
import IconDisplay from 'icons/Display' |
||||
|
|
||||
|
import { |
||||
|
SettingsSection as Section, |
||||
|
SettingsSectionHeader as Header, |
||||
|
SettingsSectionBody as Body, |
||||
|
SettingsSectionRow as Row, |
||||
|
} from '../SettingsSection' |
||||
|
|
||||
|
const fiats = listFiats().map(fiat => ({ |
||||
|
key: fiat.code, |
||||
|
fiat, |
||||
|
name: `${fiat.name} - ${fiat.code}${fiat.symbol ? ` (${fiat.symbol})` : ''}`, |
||||
|
})) |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
settings: Settings, |
||||
|
saveSettings: Function, |
||||
|
i18n: Object, |
||||
|
} |
||||
|
|
||||
|
type State = { |
||||
|
cachedLanguageKey: string, |
||||
|
cachedCounterValue: ?Object, |
||||
|
} |
||||
|
|
||||
|
class TabProfile extends PureComponent<Props, State> { |
||||
|
state = { |
||||
|
cachedLanguageKey: this.props.settings.language, |
||||
|
cachedCounterValue: fiats.find(fiat => fiat.fiat.code === this.props.settings.counterValue), |
||||
|
} |
||||
|
|
||||
|
getDatas() { |
||||
|
const { t } = this.props |
||||
|
return { |
||||
|
languages: [{ key: 'en', name: t('language:en') }, { key: 'fr', name: t('language:fr') }], |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
handleChangeCounterValue = (item: Object) => { |
||||
|
const { saveSettings } = this.props |
||||
|
this.setState({ cachedCounterValue: item.fiat }) |
||||
|
window.requestIdleCallback(() => { |
||||
|
saveSettings({ counterValue: item.fiat.code }) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
handleChangeLanguage = (languageKey: string) => { |
||||
|
const { i18n, saveSettings } = this.props |
||||
|
this.setState({ cachedLanguageKey: languageKey }) |
||||
|
window.requestIdleCallback(() => { |
||||
|
i18n.changeLanguage(languageKey) |
||||
|
moment.locale(languageKey) |
||||
|
saveSettings({ language: languageKey }) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { t } = this.props |
||||
|
const { cachedLanguageKey, cachedCounterValue } = this.state |
||||
|
const { languages } = this.getDatas() |
||||
|
const currentLanguage = languages.find(l => l.key === cachedLanguageKey) |
||||
|
|
||||
|
return ( |
||||
|
<Section> |
||||
|
<Header |
||||
|
icon={<IconDisplay size={16} />} |
||||
|
title={t('settings:tabs.display')} |
||||
|
desc="Lorem ipsum dolor sit amet" |
||||
|
/> |
||||
|
<Body> |
||||
|
<Row |
||||
|
title={t('settings:display.counterValue')} |
||||
|
desc={t('settings:display.counterValueDesc')} |
||||
|
> |
||||
|
<Select |
||||
|
searchable |
||||
|
fuseOptions={{ keys: ['name'] }} |
||||
|
style={{ minWidth: 250 }} |
||||
|
small |
||||
|
onChange={item => this.handleChangeCounterValue(item)} |
||||
|
itemToString={item => (item ? item.name : '')} |
||||
|
renderSelected={item => item && item.name} |
||||
|
items={fiats} |
||||
|
value={cachedCounterValue} |
||||
|
/> |
||||
|
</Row> |
||||
|
<Row title={t('settings:display.language')} desc={t('settings:display.languageDesc')}> |
||||
|
<Select |
||||
|
style={{ minWidth: 130 }} |
||||
|
small |
||||
|
onChange={item => this.handleChangeLanguage(item.key)} |
||||
|
renderSelected={item => item && item.name} |
||||
|
value={currentLanguage} |
||||
|
items={languages} |
||||
|
/> |
||||
|
</Row> |
||||
|
<Row title={t('settings:display.region')} desc={t('settings:display.regionDesc')}> |
||||
|
{'-'} |
||||
|
</Row> |
||||
|
<Row title={t('settings:display.stock')} desc={t('settings:display.stockDesc')}> |
||||
|
{'-'} |
||||
|
</Row> |
||||
|
</Body> |
||||
|
</Section> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default TabProfile |
@ -0,0 +1,172 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { remote } from 'electron' |
||||
|
import bcrypt from 'bcryptjs' |
||||
|
|
||||
|
import type { Settings, T } from 'types/common' |
||||
|
|
||||
|
import debounce from 'lodash/debounce' |
||||
|
|
||||
|
import { unlock } from 'reducers/application' |
||||
|
import db, { setEncryptionKey } from 'helpers/db' |
||||
|
|
||||
|
import Input from 'components/base/Input' |
||||
|
import CheckBox from 'components/base/CheckBox' |
||||
|
import Box from 'components/base/Box' |
||||
|
import Button from 'components/base/Button' |
||||
|
import { ConfirmModal } from 'components/base/Modal' |
||||
|
import IconUser from 'icons/User' |
||||
|
import PasswordModal from '../PasswordModal' |
||||
|
|
||||
|
import { |
||||
|
SettingsSection as Section, |
||||
|
SettingsSectionHeader as Header, |
||||
|
SettingsSectionBody as Body, |
||||
|
SettingsSectionRow as Row, |
||||
|
} from '../SettingsSection' |
||||
|
|
||||
|
const mapDispatchToProps = { |
||||
|
unlock, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
settings: Settings, |
||||
|
unlock: Function, |
||||
|
saveSettings: Function, |
||||
|
} |
||||
|
|
||||
|
type State = { |
||||
|
isHardResetModalOpened: boolean, |
||||
|
isPasswordModalOpened: boolean, |
||||
|
username: string, |
||||
|
} |
||||
|
|
||||
|
class TabProfile extends PureComponent<Props, State> { |
||||
|
state = { |
||||
|
username: this.props.settings.username, |
||||
|
isHardResetModalOpened: false, |
||||
|
isPasswordModalOpened: false, |
||||
|
} |
||||
|
|
||||
|
setPassword = password => { |
||||
|
const { saveSettings, unlock } = this.props |
||||
|
window.requestIdleCallback(() => { |
||||
|
setEncryptionKey('accounts', password) |
||||
|
const hash = password ? bcrypt.hashSync(password, 8) : undefined |
||||
|
saveSettings({ |
||||
|
password: { |
||||
|
isEnabled: hash !== undefined, |
||||
|
value: hash, |
||||
|
}, |
||||
|
}) |
||||
|
unlock() |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
debounceSaveUsername = debounce( |
||||
|
v => this.props.saveSettings({ username: v.trim() || 'Anonymous' }), |
||||
|
250, |
||||
|
) |
||||
|
|
||||
|
handleChangeUsername = username => { |
||||
|
this.setState({ username }) |
||||
|
this.debounceSaveUsername(username) |
||||
|
} |
||||
|
|
||||
|
handleOpenHardResetModal = () => this.setState({ isHardResetModalOpened: true }) |
||||
|
handleCloseHardResetModal = () => this.setState({ isHardResetModalOpened: false }) |
||||
|
handleOpenPasswordModal = () => this.setState({ isPasswordModalOpened: true }) |
||||
|
handleClosePasswordModal = () => this.setState({ isPasswordModalOpened: false }) |
||||
|
|
||||
|
handleHardReset = () => { |
||||
|
db.resetAll() |
||||
|
remote.app.relaunch() |
||||
|
remote.app.exit() |
||||
|
} |
||||
|
|
||||
|
handleChangePasswordCheck = isChecked => { |
||||
|
if (isChecked) { |
||||
|
this.handleOpenPasswordModal() |
||||
|
} else { |
||||
|
this.setPassword(undefined) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
handleChangePassword = (password: ?string) => { |
||||
|
if (password) { |
||||
|
this.setPassword(password) |
||||
|
this.handleClosePasswordModal() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { t, settings } = this.props |
||||
|
const { username, isHardResetModalOpened, isPasswordModalOpened } = this.state |
||||
|
const isPasswordEnabled = settings.password.isEnabled === true |
||||
|
return ( |
||||
|
<Section> |
||||
|
<Header |
||||
|
icon={<IconUser size={16} />} |
||||
|
title={t('settings:tabs.profile')} |
||||
|
desc="Lorem ipsum dolor sit amet" |
||||
|
/> |
||||
|
<Body> |
||||
|
<Row title={t('settings:profile.username')} desc={t('settings:profile.usernameDesc')}> |
||||
|
<Input |
||||
|
small |
||||
|
placeholder={t('settings:profile.username')} |
||||
|
onChange={this.handleChangeUsername} |
||||
|
value={username} |
||||
|
/> |
||||
|
</Row> |
||||
|
<Row title={t('settings:profile.password')} desc={t('settings:profile.passwordDesc')}> |
||||
|
<Box horizontal flow={2} align="center"> |
||||
|
{isPasswordEnabled && ( |
||||
|
<Button onClick={this.handleOpenPasswordModal}> |
||||
|
{t('settings:profile.changePassword')} |
||||
|
</Button> |
||||
|
)} |
||||
|
<CheckBox isChecked={isPasswordEnabled} onChange={this.handleChangePasswordCheck} /> |
||||
|
</Box> |
||||
|
</Row> |
||||
|
<Row title={t('settings:profile.sync')} desc={t('settings:profile.syncDesc')}> |
||||
|
<Button primary>{t('settings:profile.sync')}</Button> |
||||
|
</Row> |
||||
|
<Row title={t('settings:profile.export')} desc={t('settings:profile.exportDesc')}> |
||||
|
<Button primary>{t('settings:profile.export')}</Button> |
||||
|
</Row> |
||||
|
<Row title={t('settings:profile.reset')} desc={t('settings:profile.resetDesc')}> |
||||
|
<Button danger onClick={this.handleOpenHardResetModal}> |
||||
|
{t('settings:profile.resetButton')} |
||||
|
</Button> |
||||
|
</Row> |
||||
|
</Body> |
||||
|
|
||||
|
<ConfirmModal |
||||
|
isDanger |
||||
|
isOpened={isHardResetModalOpened} |
||||
|
onClose={this.handleCloseHardResetModal} |
||||
|
onReject={this.handleCloseHardResetModal} |
||||
|
onConfirm={this.handleHardReset} |
||||
|
title={t('settings:hardResetModal.title')} |
||||
|
subTitle={t('settings:hardResetModal.subTitle')} |
||||
|
desc={t('settings:hardResetModal.desc')} |
||||
|
/> |
||||
|
|
||||
|
<PasswordModal |
||||
|
t={t} |
||||
|
isOpened={isPasswordModalOpened} |
||||
|
onClose={this.handleClosePasswordModal} |
||||
|
onChangePassword={this.handleChangePassword} |
||||
|
isPasswordEnabled={isPasswordEnabled} |
||||
|
currentPasswordHash={settings.password.value} |
||||
|
/> |
||||
|
</Section> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect(null, mapDispatchToProps)(TabProfile) |
@ -0,0 +1,74 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import { translate } from 'react-i18next' |
||||
|
|
||||
|
import type { T } from 'types/common' |
||||
|
|
||||
|
import Button from 'components/base/Button' |
||||
|
import Box from 'components/base/Box' |
||||
|
|
||||
|
import { Modal, ModalContent, ModalBody, ModalTitle, ModalFooter } from './index' |
||||
|
|
||||
|
type Props = { |
||||
|
isOpened: boolean, |
||||
|
isDanger: boolean, |
||||
|
title: string, |
||||
|
subTitle: string, |
||||
|
desc: string, |
||||
|
confirmText: string, |
||||
|
cancelText: string, |
||||
|
onReject: Function, |
||||
|
onConfirm: Function, |
||||
|
t: T, |
||||
|
} |
||||
|
|
||||
|
class ConfirmModal extends PureComponent<Props> { |
||||
|
render() { |
||||
|
const { |
||||
|
isOpened, |
||||
|
title, |
||||
|
subTitle, |
||||
|
desc, |
||||
|
confirmText, |
||||
|
cancelText, |
||||
|
isDanger, |
||||
|
onReject, |
||||
|
onConfirm, |
||||
|
t, |
||||
|
...props |
||||
|
} = this.props |
||||
|
|
||||
|
const realConfirmText = confirmText || t('common:confirm') |
||||
|
const realCancelText = cancelText || t('common:cancel') |
||||
|
return ( |
||||
|
<Modal |
||||
|
isOpened={isOpened} |
||||
|
{...props} |
||||
|
render={({ onClose }) => ( |
||||
|
<ModalBody onClose={onClose}> |
||||
|
<ModalTitle>{title}</ModalTitle> |
||||
|
<ModalContent> |
||||
|
{subTitle && ( |
||||
|
<Box ff="Museo Sans|Regular" color="dark" textAlign="center" mb={2} mt={3}> |
||||
|
{subTitle} |
||||
|
</Box> |
||||
|
)} |
||||
|
<Box ff="Open Sans" color="smoke" fontSize={4} textAlign="center"> |
||||
|
{desc} |
||||
|
</Box> |
||||
|
</ModalContent> |
||||
|
<ModalFooter horizontal align="center" justify="flex-end" flow={2}> |
||||
|
<Button onClick={onReject}>{realCancelText}</Button> |
||||
|
<Button onClick={onConfirm} primary={!isDanger} danger={isDanger}> |
||||
|
{realConfirmText} |
||||
|
</Button> |
||||
|
</ModalFooter> |
||||
|
</ModalBody> |
||||
|
)} |
||||
|
/> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default translate()(ConfirmModal) |
@ -1,68 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import React, { Fragment } from 'react' |
|
||||
import styled from 'styled-components' |
|
||||
|
|
||||
import type { Element } from 'react' |
|
||||
|
|
||||
import Box, { Tabbable } from 'components/base/Box' |
|
||||
|
|
||||
const WrapperTab = styled(Box).attrs({ |
|
||||
horizontal: true, |
|
||||
})` |
|
||||
border-bottom: 1px solid ${p => p.theme.colors.fog}; |
|
||||
` |
|
||||
|
|
||||
const Tab = styled(Tabbable).attrs({ |
|
||||
flex: 1, |
|
||||
pb: 2, |
|
||||
alignItems: 'center', |
|
||||
justifyContent: 'center', |
|
||||
fontSize: 3, |
|
||||
})` |
|
||||
border-bottom: 2px solid transparent; |
|
||||
border-bottom-color: ${p => (p.isActive ? p.theme.colors.wallet : '')}; |
|
||||
color: ${p => |
|
||||
p.isActive |
|
||||
? p.theme.colors.wallet |
|
||||
: p.isDisabled |
|
||||
? p.theme.colors.grey |
|
||||
: p.theme.colors.graphite}; |
|
||||
margin-bottom: -1px; |
|
||||
outline: none; |
|
||||
cursor: ${p => (p.isActive ? 'default' : p.isDisabled ? 'not-allowed' : 'pointer')}; |
|
||||
max-width: 200px; |
|
||||
` |
|
||||
|
|
||||
type Item = { |
|
||||
key: string | number, |
|
||||
isDisabled?: boolean, |
|
||||
title: string | Element<any>, |
|
||||
render: () => Element<any>, |
|
||||
} |
|
||||
|
|
||||
type Props = { |
|
||||
items: Array<Item>, |
|
||||
index: number, |
|
||||
onTabClick: number => void, |
|
||||
} |
|
||||
|
|
||||
const Tabs = ({ items, index, onTabClick }: Props) => ( |
|
||||
<Fragment> |
|
||||
<WrapperTab> |
|
||||
{items.map((item, i) => ( |
|
||||
<Tab |
|
||||
key={item.key} |
|
||||
isDisabled={item.isDisabled} |
|
||||
isActive={index === i} |
|
||||
onClick={item.isDisabled ? void 0 : () => onTabClick(i)} |
|
||||
> |
|
||||
{item.title} |
|
||||
</Tab> |
|
||||
))} |
|
||||
</WrapperTab> |
|
||||
{items[index] && items[index].render()} |
|
||||
</Fragment> |
|
||||
) |
|
||||
|
|
||||
export default Tabs |
|
@ -1,31 +0,0 @@ |
|||||
import React from 'react' |
|
||||
|
|
||||
import { number } from '@storybook/addon-knobs' |
|
||||
import { action } from '@storybook/addon-actions' |
|
||||
import { storiesOf } from '@storybook/react' |
|
||||
|
|
||||
import Tabs from 'components/base/Tabs' |
|
||||
|
|
||||
const stories = storiesOf('Components/base', module) |
|
||||
|
|
||||
stories.add('Tabs', () => ( |
|
||||
<Tabs |
|
||||
index={number('index', 0, { |
|
||||
min: 0, |
|
||||
max: 1, |
|
||||
})} |
|
||||
onTabClick={action('onTabClick')} |
|
||||
items={[ |
|
||||
{ |
|
||||
key: 'first', |
|
||||
title: 'first tab', |
|
||||
render: () => <div>{'first tab content'}</div>, |
|
||||
}, |
|
||||
{ |
|
||||
key: 'second', |
|
||||
title: 'second tab', |
|
||||
render: () => <div>{'second tab content'}</div>, |
|
||||
}, |
|
||||
]} |
|
||||
/> |
|
||||
)) |
|
@ -0,0 +1,12 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React from 'react' |
||||
|
|
||||
|
export default ({ size, ...p }: { size: number }) => ( |
||||
|
<svg viewBox="0 0 16 16" height={size} width={size} {...p}> |
||||
|
<path |
||||
|
fill="currentColor" |
||||
|
d="M8 5.581c-3.865 0-7-1.083-7-2.79C1 1.083 4.135 0 8 0s7 1.083 7 2.79c0 1.708-3.135 2.791-7 2.791zm-7-2.79c0-.309.241-.558.538-.558.298 0 .539.25.539.558v10.418c0 .28.516.704 1.517 1.05 1.142.396 2.714.625 4.406.625 1.692 0 3.264-.23 4.406-.625 1.001-.346 1.517-.77 1.517-1.05V2.791c0-.309.241-.558.539-.558.297 0 .538.25.538.558v10.418C15 14.921 11.88 16 8 16s-7-1.08-7-2.79V2.79zM13.923 8c0-.308.241-.558.539-.558.297 0 .538.25.538.558 0 1.711-3.12 2.79-7 2.79S1 9.712 1 8c0-.308.241-.558.538-.558.298 0 .539.25.539.558 0 .28.516.704 1.517 1.05 1.142.395 2.714.624 4.406.624 1.692 0 3.264-.229 4.406-.624 1.001-.346 1.517-.77 1.517-1.05zM8 4.465c1.682 0 3.254-.23 4.399-.625 1.004-.347 1.524-.772 1.524-1.05 0-.277-.52-.702-1.524-1.048-1.145-.396-2.717-.626-4.399-.626s-3.254.23-4.399.626c-1.004.346-1.524.771-1.524 1.049 0 .277.52.702 1.524 1.049 1.145.395 2.717.625 4.399.625z" |
||||
|
/> |
||||
|
</svg> |
||||
|
) |
@ -0,0 +1,12 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React from 'react' |
||||
|
|
||||
|
export default ({ size, ...p }: { size: number }) => ( |
||||
|
<svg viewBox="0 0 16 16" width={size} height={size} {...p}> |
||||
|
<path |
||||
|
fill="currentColor" |
||||
|
d="M3.2 2.26c-.552 0-1 .471-1 1.051v6.547c0 .58.448 1.05 1 1.05h9.6c.552 0 1-.47 1-1.05V3.31c0-.58-.448-1.05-1-1.05H3.2zm5.4 9.909v1.57h1.96c.331 0 .6.283.6.63 0 .349-.269.631-.6.631H5.44c-.331 0-.6-.282-.6-.63 0-.348.269-.63.6-.63H7.4v-1.571H3.2c-1.215 0-2.2-1.035-2.2-2.311V3.31C1 2.035 1.985 1 3.2 1h9.6C14.015 1 15 2.035 15 3.311v6.547c0 1.276-.985 2.311-2.2 2.311H8.6z" |
||||
|
/> |
||||
|
</svg> |
||||
|
) |
@ -0,0 +1,12 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React from 'react' |
||||
|
|
||||
|
export default ({ size, ...p }: { size: number }) => ( |
||||
|
<svg viewBox="0 0 16 16" height={size} width={size} {...p}> |
||||
|
<path |
||||
|
fill="currentColor" |
||||
|
d="M3.3 4.007a6.167 6.167 0 1 0 .707-.707l2.135 2.135a3.167 3.167 0 1 1-.707.707L3.3 4.007zM8 15.167A7.167 7.167 0 1 1 8 .833a7.167 7.167 0 0 1 0 14.334zm0-5a2.167 2.167 0 1 0 0-4.334 2.167 2.167 0 0 0 0 4.334zm1.387-4.054c0-.128.048-.256.146-.353l2.353-2.354a.5.5 0 0 1 .708.708L10.24 6.467a.5.5 0 0 1-.707-.707l2.827-2.827a.5.5 0 0 1 .707.707L10.24 6.467a.5.5 0 0 1-.853-.354zm.146 4.127a.5.5 0 0 1 .707-.707l2.827 2.827a.5.5 0 1 1-.707.707L9.533 10.24zM3.64 13.067a.5.5 0 1 1-.707-.707L5.76 9.533a.5.5 0 1 1 .707.707L3.64 13.067z" |
||||
|
/> |
||||
|
</svg> |
||||
|
) |
@ -1 +1,2 @@ |
|||||
label: Current address |
label: Current address |
||||
|
labelFrom: Address from <1><0>{{accountName}}</0></1> |
||||
|
@ -1,16 +1,55 @@ |
|||||
title: Settings |
title: Settings |
||||
tabs: |
tabs: |
||||
display: Display |
display: Display |
||||
money: Money |
currencies: Currencies |
||||
material: Material |
|
||||
app: App (beta) |
|
||||
tools: Tools |
|
||||
blockchain: Blockchain |
|
||||
profile: Profile |
profile: Profile |
||||
|
about: About |
||||
display: |
display: |
||||
language: Language |
language: Interface language |
||||
counterValue: Counter Value |
languageDesc: Lorem ipsum dolor sit amet |
||||
orderAccounts: Order accounts |
counterValue: Countervalue |
||||
|
counterValueDesc: Lorem ipsum dolor sit amet |
||||
|
region: Region |
||||
|
regionDesc: Lorem ipsum dolor sit amet |
||||
|
stock: Stock market indicators |
||||
|
stockDesc: Lorem ipsum dolor sit amet |
||||
|
currencies: |
||||
|
confirmationsToSpend: Confirmations to spend |
||||
|
confirmationsToSpendDesc: Lorem ipsum dolor sit amet |
||||
|
confirmationsNb: Number of confirmations |
||||
|
confirmationsNbDesc: Lorem ipsum dolor sit amet |
||||
|
transactionsFees: Transactions fees |
||||
|
transactionsFeesDesc: Lorem ipsum dolor sit amet |
||||
|
explorer: Blockchain explorer |
||||
|
explorerDesc: Lorem ipsum dolor sit amet |
||||
profile: |
profile: |
||||
protectWithPassword: Protect local data with a password |
username: Username |
||||
|
usernameDesc: Lorem ipsum dolor sit amet |
||||
password: Password |
password: Password |
||||
|
passwordDesc: Lorem ipsum dolor sit amet |
||||
|
changePassword: Change password |
||||
|
passwordModalTitle: Password |
||||
|
passwordModalSubtitle: Set a password to lock your application |
||||
|
passwordModalDesc: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer non nibh diam. |
||||
|
passwordModalPasswordInput: Current password |
||||
|
passwordModalNewPasswordInput: New password |
||||
|
passwordModalRepeatPasswordInput: Repeat password |
||||
|
passwordModalSave: Save |
||||
|
sync: Sync accounts |
||||
|
syncDesc: Lorem ipsum dolor sit amet |
||||
|
export: Export logs |
||||
|
exportDesc: Lorem ipsum dolor sit amet |
||||
|
reset: Reset application |
||||
|
resetDesc: Lorem ipsum dolor sit amet |
||||
|
resetButton: Hard reset |
||||
|
about: |
||||
|
faq: FAQ |
||||
|
faqDesc: Lorem ipsum dolor sit amet |
||||
|
contactUs: Contact us |
||||
|
contactUsDesc: Lorem ipsum dolor sit amet |
||||
|
terms: Terms and Privacy policy |
||||
|
termsDesc: Lorem ipsum dolor sit amet |
||||
|
hardResetModal: |
||||
|
title: Hard reset |
||||
|
subTitle: Are you sure houston? |
||||
|
desc: Lorem ipsum dolor sit amet |
||||
|
Loading…
Reference in new issue