26 changed files with 1012 additions and 613 deletions
@ -0,0 +1,26 @@ |
|||||
|
// @flow
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import { shell } from 'electron' |
||||
|
import IconExternalLink from 'icons/ExternalLink' |
||||
|
import { Tabbable } from 'components/base/Box' |
||||
|
import { SettingsSectionRow } from './SettingsSection' |
||||
|
|
||||
|
export default class AboutRowItem extends PureComponent<{ |
||||
|
url: string, |
||||
|
title: string, |
||||
|
desc: string, |
||||
|
url: string, |
||||
|
}> { |
||||
|
onClick = () => shell.openExternal(this.props.url) |
||||
|
|
||||
|
render() { |
||||
|
const { title, desc } = this.props |
||||
|
return ( |
||||
|
<SettingsSectionRow title={title} desc={desc}> |
||||
|
<Tabbable p={2} borderRadius={1} onClick={this.onClick}> |
||||
|
<IconExternalLink style={{ cursor: 'pointer' }} size={16} /> |
||||
|
</Tabbable> |
||||
|
</SettingsSectionRow> |
||||
|
) |
||||
|
} |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { translate } from 'react-i18next' |
||||
|
import type { T } from 'types/common' |
||||
|
import { remote } from 'electron' |
||||
|
import { cleanAccountsCache } from 'actions/accounts' |
||||
|
import db from 'helpers/db' |
||||
|
import { delay } from 'helpers/promise' |
||||
|
import Button from 'components/base/Button' |
||||
|
import { ConfirmModal } from 'components/base/Modal' |
||||
|
|
||||
|
const mapDispatchToProps = { |
||||
|
cleanAccountsCache, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
cleanAccountsCache: () => *, |
||||
|
} |
||||
|
|
||||
|
type State = { |
||||
|
opened: boolean, |
||||
|
} |
||||
|
|
||||
|
class CleanButton extends PureComponent<Props, State> { |
||||
|
state = { |
||||
|
opened: false, |
||||
|
} |
||||
|
|
||||
|
open = () => this.setState({ opened: true }) |
||||
|
|
||||
|
close = () => this.setState({ opened: false }) |
||||
|
|
||||
|
action = async () => { |
||||
|
this.props.cleanAccountsCache() |
||||
|
await delay(500) |
||||
|
db.cleanCache() |
||||
|
remote.getCurrentWindow().webContents.reload() |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { t } = this.props |
||||
|
const { opened } = this.state |
||||
|
return ( |
||||
|
<Fragment> |
||||
|
<Button primary onClick={this.open} event="ClearCacheIntent"> |
||||
|
{t('app:settings.profile.softReset')} |
||||
|
</Button> |
||||
|
|
||||
|
<ConfirmModal |
||||
|
isDanger |
||||
|
isOpened={opened} |
||||
|
onClose={this.close} |
||||
|
onReject={this.close} |
||||
|
onConfirm={this.action} |
||||
|
title={t('app:settings.softResetModal.title')} |
||||
|
subTitle={t('app:settings.softResetModal.subTitle')} |
||||
|
desc={t('app:settings.softResetModal.desc')} |
||||
|
/> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default translate()( |
||||
|
connect( |
||||
|
null, |
||||
|
mapDispatchToProps, |
||||
|
)(CleanButton), |
||||
|
) |
@ -0,0 +1,53 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { createStructuredSelector } from 'reselect' |
||||
|
import { |
||||
|
counterValueCurrencySelector, |
||||
|
counterValueExchangeSelector, |
||||
|
intermediaryCurrency, |
||||
|
} from 'reducers/settings' |
||||
|
import { setCounterValueExchange } from 'actions/settings' |
||||
|
import type { Currency } from '@ledgerhq/live-common/lib/types' |
||||
|
import SelectExchange from 'components/SelectExchange' |
||||
|
|
||||
|
type Props = { |
||||
|
counterValueCurrency: Currency, |
||||
|
counterValueExchange: ?string, |
||||
|
setCounterValueExchange: (?string) => void, |
||||
|
} |
||||
|
|
||||
|
class CounterValueExchangeSelect extends PureComponent<Props> { |
||||
|
handleChangeExchange = (exchange: *) => |
||||
|
this.props.setCounterValueExchange(exchange ? exchange.id : null) |
||||
|
|
||||
|
render() { |
||||
|
const { counterValueCurrency, counterValueExchange } = this.props |
||||
|
|
||||
|
return ( |
||||
|
<Fragment> |
||||
|
{counterValueCurrency ? ( |
||||
|
<SelectExchange |
||||
|
small |
||||
|
from={intermediaryCurrency} |
||||
|
to={counterValueCurrency} |
||||
|
exchangeId={counterValueExchange} |
||||
|
onChange={this.handleChangeExchange} |
||||
|
minWidth={200} |
||||
|
/> |
||||
|
) : null} |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect( |
||||
|
createStructuredSelector({ |
||||
|
counterValueCurrency: counterValueCurrencySelector, |
||||
|
counterValueExchange: counterValueExchangeSelector, |
||||
|
}), |
||||
|
{ |
||||
|
setCounterValueExchange, |
||||
|
}, |
||||
|
)(CounterValueExchangeSelect) |
@ -0,0 +1,58 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { createStructuredSelector } from 'reselect' |
||||
|
import { listFiatCurrencies } from '@ledgerhq/live-common/lib/helpers/currencies' |
||||
|
import type { Currency } from '@ledgerhq/live-common/lib/types' |
||||
|
import { setCounterValue } from 'actions/settings' |
||||
|
import { counterValueCurrencySelector } from 'reducers/settings' |
||||
|
import Select from 'components/base/Select' |
||||
|
|
||||
|
const fiats = listFiatCurrencies() |
||||
|
.map(f => f.units[0]) |
||||
|
// For now we take first unit, in the future we'll need to figure out something else
|
||||
|
.map(fiat => ({ |
||||
|
value: fiat.code, |
||||
|
label: `${fiat.name} - ${fiat.code}${fiat.symbol ? ` (${fiat.symbol})` : ''}`, |
||||
|
fiat, |
||||
|
})) |
||||
|
|
||||
|
type Props = { |
||||
|
counterValueCurrency: Currency, |
||||
|
setCounterValue: string => void, |
||||
|
} |
||||
|
|
||||
|
class CounterValueSelect extends PureComponent<Props> { |
||||
|
handleChangeCounterValue = (item: Object) => { |
||||
|
const { setCounterValue } = this.props |
||||
|
setCounterValue(item.fiat.code) |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { counterValueCurrency } = this.props |
||||
|
const cvOption = fiats.find(f => f.value === counterValueCurrency.ticker) |
||||
|
|
||||
|
return ( |
||||
|
<Fragment> |
||||
|
{/* TODO Track */} |
||||
|
<Select |
||||
|
small |
||||
|
minWidth={250} |
||||
|
onChange={this.handleChangeCounterValue} |
||||
|
itemToString={item => (item ? item.name : '')} |
||||
|
renderSelected={item => item && item.name} |
||||
|
options={fiats} |
||||
|
value={cvOption} |
||||
|
/> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect( |
||||
|
createStructuredSelector({ |
||||
|
counterValueCurrency: counterValueCurrencySelector, |
||||
|
}), |
||||
|
{ setCounterValue }, |
||||
|
)(CounterValueSelect) |
@ -0,0 +1,39 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { createStructuredSelector } from 'reselect' |
||||
|
import { setDeveloperMode } from 'actions/settings' |
||||
|
import { developerModeSelector } from 'reducers/settings' |
||||
|
import Track from 'analytics/Track' |
||||
|
import CheckBox from 'components/base/CheckBox' |
||||
|
|
||||
|
const mapStateToProps = createStructuredSelector({ |
||||
|
developerMode: developerModeSelector, |
||||
|
}) |
||||
|
|
||||
|
const mapDispatchToProps = { |
||||
|
setDeveloperMode, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
developerMode: boolean, |
||||
|
setDeveloperMode: boolean => void, |
||||
|
} |
||||
|
|
||||
|
class DevModeButton extends PureComponent<Props> { |
||||
|
render() { |
||||
|
const { developerMode, setDeveloperMode } = this.props |
||||
|
return ( |
||||
|
<Fragment> |
||||
|
<Track onUpdate event={developerMode ? 'DevModeEnabled' : 'DevModeDisabled'} /> |
||||
|
<CheckBox isChecked={developerMode} onChange={setDeveloperMode} /> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect( |
||||
|
mapStateToProps, |
||||
|
mapDispatchToProps, |
||||
|
)(DevModeButton) |
@ -0,0 +1,133 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { translate } from 'react-i18next' |
||||
|
import type { T } from 'types/common' |
||||
|
import { createStructuredSelector } from 'reselect' |
||||
|
import bcrypt from 'bcryptjs' |
||||
|
import { setEncryptionKey } from 'helpers/db' |
||||
|
import { cleanAccountsCache } from 'actions/accounts' |
||||
|
import { saveSettings } from 'actions/settings' |
||||
|
import { storeSelector } from 'reducers/settings' |
||||
|
import type { SettingsState } from 'reducers/settings' |
||||
|
import { unlock } from 'reducers/application' // FIXME should be in actions
|
||||
|
import Track from 'analytics/Track' |
||||
|
import CheckBox from 'components/base/CheckBox' |
||||
|
import Box from 'components/base/Box' |
||||
|
import Button from 'components/base/Button' |
||||
|
import PasswordModal from './PasswordModal' |
||||
|
import DisablePasswordModal from './DisablePasswordModal' |
||||
|
|
||||
|
const mapStateToProps = createStructuredSelector({ |
||||
|
// FIXME in future we should use dedicated password selector and a savePassword action (you don't know the shape of settings)
|
||||
|
settings: storeSelector, |
||||
|
}) |
||||
|
|
||||
|
const mapDispatchToProps = { |
||||
|
unlock, |
||||
|
cleanAccountsCache, |
||||
|
saveSettings, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
unlock: () => void, |
||||
|
settings: SettingsState, |
||||
|
saveSettings: Function, |
||||
|
} |
||||
|
|
||||
|
type State = { |
||||
|
isPasswordModalOpened: boolean, |
||||
|
isDisablePasswordModalOpened: boolean, |
||||
|
} |
||||
|
|
||||
|
class DisablePasswordButton extends PureComponent<Props, State> { |
||||
|
state = { |
||||
|
isPasswordModalOpened: false, |
||||
|
isDisablePasswordModalOpened: 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() |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
handleOpenPasswordModal = () => this.setState({ isPasswordModalOpened: true }) |
||||
|
handleClosePasswordModal = () => this.setState({ isPasswordModalOpened: false }) |
||||
|
handleDisablePassowrd = () => this.setState({ isDisablePasswordModalOpened: true }) |
||||
|
handleCloseDisablePasswordModal = () => this.setState({ isDisablePasswordModalOpened: false }) |
||||
|
|
||||
|
handleChangePasswordCheck = isChecked => { |
||||
|
if (isChecked) { |
||||
|
this.handleOpenPasswordModal() |
||||
|
} else { |
||||
|
this.handleDisablePassowrd() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
handleChangePassword = (password: ?string) => { |
||||
|
if (password) { |
||||
|
this.setPassword(password) |
||||
|
this.handleClosePasswordModal() |
||||
|
} else { |
||||
|
this.setPassword(undefined) |
||||
|
this.handleCloseDisablePasswordModal() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { t, settings } = this.props |
||||
|
const { isDisablePasswordModalOpened, isPasswordModalOpened } = this.state |
||||
|
const isPasswordEnabled = settings.password.isEnabled === true |
||||
|
return ( |
||||
|
<Fragment> |
||||
|
<Track onUpdate event={isPasswordEnabled ? 'PasswordEnabled' : 'PasswordDisabled'} /> |
||||
|
|
||||
|
<Box horizontal flow={2} align="center"> |
||||
|
{isPasswordEnabled && ( |
||||
|
<Button onClick={this.handleOpenPasswordModal}> |
||||
|
{t('app:settings.profile.changePassword')} |
||||
|
</Button> |
||||
|
)} |
||||
|
<CheckBox isChecked={isPasswordEnabled} onChange={this.handleChangePasswordCheck} /> |
||||
|
</Box> |
||||
|
|
||||
|
<PasswordModal |
||||
|
t={t} |
||||
|
isOpened={isPasswordModalOpened} |
||||
|
onClose={this.handleClosePasswordModal} |
||||
|
onChangePassword={this.handleChangePassword} |
||||
|
isPasswordEnabled={isPasswordEnabled} |
||||
|
currentPasswordHash={settings.password.value} |
||||
|
/> |
||||
|
|
||||
|
<DisablePasswordModal |
||||
|
t={t} |
||||
|
isOpened={isDisablePasswordModalOpened} |
||||
|
onClose={this.handleCloseDisablePasswordModal} |
||||
|
onChangePassword={this.handleChangePassword} |
||||
|
isPasswordEnabled={isPasswordEnabled} |
||||
|
currentPasswordHash={settings.password.value} |
||||
|
/> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default translate()( |
||||
|
connect( |
||||
|
mapStateToProps, |
||||
|
mapDispatchToProps, |
||||
|
)(DisablePasswordButton), |
||||
|
) |
@ -0,0 +1,61 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import moment from 'moment' |
||||
|
import { translate } from 'react-i18next' |
||||
|
import type { T } from 'types/common' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { setLanguage } from 'actions/settings' |
||||
|
import { langAndRegionSelector } from 'reducers/settings' |
||||
|
import languageKeys from 'config/languages' |
||||
|
import Select from 'components/base/Select' |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
useSystem: boolean, |
||||
|
language: string, |
||||
|
setLanguage: (?string) => void, |
||||
|
i18n: Object, |
||||
|
} |
||||
|
|
||||
|
class LanguageSelect extends PureComponent<Props> { |
||||
|
handleChangeLanguage = ({ value: languageKey }: *) => { |
||||
|
const { i18n, setLanguage } = this.props |
||||
|
i18n.changeLanguage(languageKey) |
||||
|
moment.locale(languageKey) |
||||
|
setLanguage(languageKey) |
||||
|
} |
||||
|
|
||||
|
languages = [{ value: null, label: this.props.t(`language:system`) }].concat( |
||||
|
languageKeys.map(key => ({ value: key, label: this.props.t(`language:${key}`) })), |
||||
|
) |
||||
|
|
||||
|
render() { |
||||
|
const { language, useSystem } = this.props |
||||
|
const currentLanguage = useSystem |
||||
|
? this.languages[0] |
||||
|
: this.languages.find(l => l.value === language) |
||||
|
return ( |
||||
|
<Fragment> |
||||
|
<Select |
||||
|
small |
||||
|
minWidth={250} |
||||
|
isSearchable={false} |
||||
|
onChange={this.handleChangeLanguage} |
||||
|
renderSelected={item => item && item.name} |
||||
|
value={currentLanguage} |
||||
|
options={this.languages} |
||||
|
/> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default translate()( |
||||
|
connect( |
||||
|
langAndRegionSelector, |
||||
|
{ |
||||
|
setLanguage, |
||||
|
}, |
||||
|
)(LanguageSelect), |
||||
|
) |
@ -0,0 +1,48 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import { translate } from 'react-i18next' |
||||
|
import type { T } from 'types/common' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { createStructuredSelector } from 'reselect' |
||||
|
import { setMarketIndicator } from 'actions/settings' |
||||
|
import { marketIndicatorSelector } from 'reducers/settings' |
||||
|
import RadioGroup from 'components/base/RadioGroup' |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
setMarketIndicator: (*) => *, |
||||
|
marketIndicator: *, |
||||
|
} |
||||
|
|
||||
|
class MarketIndicatorRadio extends PureComponent<Props> { |
||||
|
indicators = [ |
||||
|
{ |
||||
|
label: this.props.t('app:common.eastern'), |
||||
|
key: 'eastern', |
||||
|
}, |
||||
|
{ |
||||
|
label: this.props.t('app:common.western'), |
||||
|
key: 'western', |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
onChange = (item: Object) => { |
||||
|
const { setMarketIndicator } = this.props |
||||
|
setMarketIndicator(item.key) |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { marketIndicator } = this.props |
||||
|
return ( |
||||
|
<RadioGroup items={this.indicators} activeKey={marketIndicator} onChange={this.onChange} /> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default translate()( |
||||
|
connect( |
||||
|
createStructuredSelector({ marketIndicator: marketIndicatorSelector }), |
||||
|
{ setMarketIndicator }, |
||||
|
)(MarketIndicatorRadio), |
||||
|
) |
@ -0,0 +1,67 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { createSelector } from 'reselect' |
||||
|
import { setRegion } from 'actions/settings' |
||||
|
import { langAndRegionSelector, counterValueCurrencySelector } from 'reducers/settings' |
||||
|
import type { Currency } from '@ledgerhq/live-common/lib/types' |
||||
|
import type { T } from 'types/common' |
||||
|
|
||||
|
import regionsByKey from 'helpers/regions.json' |
||||
|
import Select from 'components/base/Select' |
||||
|
|
||||
|
const regions = Object.keys(regionsByKey).map(key => { |
||||
|
const [language, region] = key.split('-') |
||||
|
return { value: key, language, region, label: regionsByKey[key] } |
||||
|
}) |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
counterValueCurrency: Currency, |
||||
|
useSystem: boolean, |
||||
|
language: string, |
||||
|
region: ?string, |
||||
|
setRegion: (?string) => void, |
||||
|
} |
||||
|
|
||||
|
class RegionSelect extends PureComponent<Props> { |
||||
|
handleChangeRegion = ({ region }: *) => { |
||||
|
const { setRegion } = this.props |
||||
|
setRegion(region) |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { language, region } = this.props |
||||
|
|
||||
|
const regionsFiltered = regions.filter(item => language === item.language) |
||||
|
const currentRegion = regionsFiltered.find(item => item.region === region) || regionsFiltered[0] |
||||
|
|
||||
|
return ( |
||||
|
<Fragment> |
||||
|
<Select |
||||
|
small |
||||
|
minWidth={250} |
||||
|
onChange={this.handleChangeRegion} |
||||
|
renderSelected={item => item && item.name} |
||||
|
value={currentRegion} |
||||
|
options={regionsFiltered} |
||||
|
/> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect( |
||||
|
createSelector( |
||||
|
langAndRegionSelector, |
||||
|
counterValueCurrencySelector, |
||||
|
(langAndRegion, counterValueCurrency) => ({ |
||||
|
...langAndRegion, |
||||
|
counterValueCurrency, |
||||
|
}), |
||||
|
), |
||||
|
{ |
||||
|
setRegion, |
||||
|
}, |
||||
|
)(RegionSelect) |
@ -0,0 +1,45 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { PureComponent } from 'react' |
||||
|
import { translate } from 'react-i18next' |
||||
|
import type { T } from 'types/common' |
||||
|
import { shell } from 'electron' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { openModal } from 'reducers/modals' |
||||
|
import { MODAL_RELEASES_NOTES } from 'config/constants' |
||||
|
import Button from 'components/base/Button' |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
openModal: Function, |
||||
|
} |
||||
|
|
||||
|
const mapDispatchToProps = { |
||||
|
openModal, |
||||
|
} |
||||
|
|
||||
|
class ReleaseNotesButton extends PureComponent<Props> { |
||||
|
handleOpenLink = (url: string) => shell.openExternal(url) |
||||
|
|
||||
|
render() { |
||||
|
const { t, openModal } = this.props |
||||
|
const version = __APP_VERSION__ |
||||
|
return ( |
||||
|
<Button |
||||
|
primary |
||||
|
onClick={() => { |
||||
|
openModal(MODAL_RELEASES_NOTES, version) |
||||
|
}} |
||||
|
> |
||||
|
{t('app:settings.about.releaseNotesBtn')} |
||||
|
</Button> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default translate()( |
||||
|
connect( |
||||
|
null, |
||||
|
mapDispatchToProps, |
||||
|
)(ReleaseNotesButton), |
||||
|
) |
@ -0,0 +1,82 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import styled from 'styled-components' |
||||
|
import { remote } from 'electron' |
||||
|
import { translate } from 'react-i18next' |
||||
|
import type { T } from 'types/common' |
||||
|
import hardReset from 'helpers/hardReset' |
||||
|
import Box from 'components/base/Box' |
||||
|
import Button from 'components/base/Button' |
||||
|
import { ConfirmModal } from 'components/base/Modal' |
||||
|
import IconTriangleWarning from 'icons/TriangleWarning' |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
} |
||||
|
|
||||
|
type State = { |
||||
|
opened: boolean, |
||||
|
pending: boolean, |
||||
|
} |
||||
|
|
||||
|
class ResetButton extends PureComponent<Props, State> { |
||||
|
state = { |
||||
|
opened: false, |
||||
|
pending: false, |
||||
|
} |
||||
|
|
||||
|
open = () => this.setState({ opened: true }) |
||||
|
close = () => this.setState({ opened: false }) |
||||
|
|
||||
|
action = async () => { |
||||
|
this.setState({ pending: true }) |
||||
|
try { |
||||
|
await hardReset() |
||||
|
remote.getCurrentWindow().webContents.reloadIgnoringCache() |
||||
|
} catch (err) { |
||||
|
this.setState({ pending: false }) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { t } = this.props |
||||
|
const { opened, pending } = this.state |
||||
|
return ( |
||||
|
<Fragment> |
||||
|
<Button danger onClick={this.open} event="HardResetIntent"> |
||||
|
{t('app:settings.profile.hardReset')} |
||||
|
</Button> |
||||
|
|
||||
|
<ConfirmModal |
||||
|
isDanger |
||||
|
isLoading={pending} |
||||
|
isOpened={opened} |
||||
|
onClose={this.close} |
||||
|
onReject={this.close} |
||||
|
onConfirm={this.action} |
||||
|
confirmText={t('app:common.reset')} |
||||
|
title={t('app:settings.hardResetModal.title')} |
||||
|
desc={t('app:settings.hardResetModal.desc')} |
||||
|
renderIcon={() => ( |
||||
|
// FIXME why not pass in directly the DOM 🤷🏻
|
||||
|
<IconWrapperCircle color="alertRed"> |
||||
|
<IconTriangleWarning width={23} height={21} /> |
||||
|
</IconWrapperCircle> |
||||
|
)} |
||||
|
/> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export const IconWrapperCircle = styled(Box).attrs({})` |
||||
|
width: 50px; |
||||
|
height: 50px; |
||||
|
border-radius: 50%; |
||||
|
background: #ea2e4919; |
||||
|
text-align: -webkit-center; |
||||
|
justify-content: center; |
||||
|
` |
||||
|
|
||||
|
export default translate()(ResetButton) |
@ -0,0 +1,39 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { createStructuredSelector } from 'reselect' |
||||
|
import { setSentryLogs } from 'actions/settings' |
||||
|
import { sentryLogsSelector } from 'reducers/settings' |
||||
|
import Track from 'analytics/Track' |
||||
|
import CheckBox from 'components/base/CheckBox' |
||||
|
|
||||
|
const mapStateToProps = createStructuredSelector({ |
||||
|
sentryLogs: sentryLogsSelector, |
||||
|
}) |
||||
|
|
||||
|
const mapDispatchToProps = { |
||||
|
setSentryLogs, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
sentryLogs: boolean, |
||||
|
setSentryLogs: boolean => void, |
||||
|
} |
||||
|
|
||||
|
class SentryLogsButton extends PureComponent<Props> { |
||||
|
render() { |
||||
|
const { sentryLogs, setSentryLogs } = this.props |
||||
|
return ( |
||||
|
<Fragment> |
||||
|
<Track onUpdate event={sentryLogs ? 'SentryEnabled' : 'SentryDisabled'} /> |
||||
|
<CheckBox isChecked={sentryLogs} onChange={setSentryLogs} /> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect( |
||||
|
mapStateToProps, |
||||
|
mapDispatchToProps, |
||||
|
)(SentryLogsButton) |
@ -0,0 +1,38 @@ |
|||||
|
// @flow
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { createStructuredSelector } from 'reselect' |
||||
|
import { setShareAnalytics } from 'actions/settings' |
||||
|
import { shareAnalyticsSelector } from 'reducers/settings' |
||||
|
import Track from 'analytics/Track' |
||||
|
import CheckBox from 'components/base/CheckBox' |
||||
|
|
||||
|
const mapStateToProps = createStructuredSelector({ |
||||
|
shareAnalytics: shareAnalyticsSelector, |
||||
|
}) |
||||
|
|
||||
|
const mapDispatchToProps = { |
||||
|
setShareAnalytics, |
||||
|
} |
||||
|
|
||||
|
type Props = { |
||||
|
shareAnalytics: boolean, |
||||
|
setShareAnalytics: boolean => void, |
||||
|
} |
||||
|
|
||||
|
class ShareAnalytics extends PureComponent<Props> { |
||||
|
render() { |
||||
|
const { shareAnalytics, setShareAnalytics } = this.props |
||||
|
return ( |
||||
|
<Fragment> |
||||
|
<Track onUpdate event={shareAnalytics ? 'AnalyticsEnabled' : 'AnalyticsDisabled'} /> |
||||
|
<CheckBox isChecked={shareAnalytics} onChange={setShareAnalytics} /> |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default connect( |
||||
|
mapStateToProps, |
||||
|
mapDispatchToProps, |
||||
|
)(ShareAnalytics) |
@ -0,0 +1,114 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import React, { Fragment, PureComponent } from 'react' |
||||
|
import { translate } from 'react-i18next' |
||||
|
import { connect } from 'react-redux' |
||||
|
import { createStructuredSelector } from 'reselect' |
||||
|
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' |
||||
|
import type { T } from 'types/common' |
||||
|
import { saveSettings } from 'actions/settings' |
||||
|
import { intermediaryCurrency, currencySettingsSelector, storeSelector } from 'reducers/settings' |
||||
|
import type { SettingsState, CurrencySettings } from 'reducers/settings' |
||||
|
import { currencySettingsDefaults } from 'helpers/SettingsDefaults' |
||||
|
import StepperNumber from 'components/base/StepperNumber' |
||||
|
import ExchangeSelect from 'components/SelectExchange' |
||||
|
|
||||
|
import { SettingsSectionRow as Row } from '../SettingsSection' |
||||
|
|
||||
|
type Props = { |
||||
|
t: T, |
||||
|
currency: CryptoCurrency, |
||||
|
currencySettings: CurrencySettings, |
||||
|
// FIXME: the stuff bellow to be to be gone!
|
||||
|
settings: SettingsState, |
||||
|
saveSettings: ($Shape<SettingsState>) => void, |
||||
|
} |
||||
|
|
||||
|
class CurrencyRows extends PureComponent<Props> { |
||||
|
handleChangeConfirmationsToSpend = (nb: number) => |
||||
|
this.updateCurrencySettings('confirmationsToSpend', nb) |
||||
|
|
||||
|
handleChangeConfirmationsNb = (nb: number) => this.updateCurrencySettings('confirmationsNb', nb) |
||||
|
|
||||
|
handleChangeExchange = (exchange: *) => |
||||
|
this.updateCurrencySettings('exchange', exchange ? exchange.id : null) |
||||
|
|
||||
|
updateCurrencySettings = (key: string, val: *) => { |
||||
|
// FIXME this really should be a dedicated action
|
||||
|
const { settings, saveSettings, currency } = this.props |
||||
|
const currencySettings = settings.currenciesSettings[currency.id] |
||||
|
let newCurrenciesSettings = [] |
||||
|
if (!currencySettings) { |
||||
|
newCurrenciesSettings = { |
||||
|
...settings.currenciesSettings, |
||||
|
[currency.id]: { |
||||
|
[key]: val, |
||||
|
}, |
||||
|
} |
||||
|
} else { |
||||
|
newCurrenciesSettings = { |
||||
|
...settings.currenciesSettings, |
||||
|
[currency.id]: { |
||||
|
...currencySettings, |
||||
|
[key]: val, |
||||
|
}, |
||||
|
} |
||||
|
} |
||||
|
saveSettings({ currenciesSettings: newCurrenciesSettings }) |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { currency, t, currencySettings } = this.props |
||||
|
const { confirmationsNb, exchange } = currencySettings |
||||
|
const defaults = currencySettingsDefaults(currency) |
||||
|
return ( |
||||
|
<Fragment> |
||||
|
{currency !== intermediaryCurrency ? ( |
||||
|
<Row |
||||
|
title={t('app:settings.currencies.exchange', { |
||||
|
ticker: currency.ticker, |
||||
|
})} |
||||
|
desc={t('app:settings.currencies.exchangeDesc', { |
||||
|
currencyName: currency.name, |
||||
|
})} |
||||
|
> |
||||
|
<ExchangeSelect |
||||
|
small |
||||
|
from={currency} |
||||
|
to={intermediaryCurrency} |
||||
|
exchangeId={exchange} |
||||
|
onChange={this.handleChangeExchange} |
||||
|
minWidth={200} |
||||
|
/> |
||||
|
</Row> |
||||
|
) : null} |
||||
|
{defaults.confirmationsNb ? ( |
||||
|
<Row |
||||
|
title={t('app:settings.currencies.confirmationsNb')} |
||||
|
desc={t('app:settings.currencies.confirmationsNbDesc')} |
||||
|
> |
||||
|
<StepperNumber |
||||
|
min={defaults.confirmationsNb.min} |
||||
|
max={defaults.confirmationsNb.max} |
||||
|
step={1} |
||||
|
onChange={this.handleChangeConfirmationsNb} |
||||
|
value={confirmationsNb} |
||||
|
/> |
||||
|
</Row> |
||||
|
) : null} |
||||
|
</Fragment> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default translate()( |
||||
|
connect( |
||||
|
createStructuredSelector({ |
||||
|
currencySettings: currencySettingsSelector, |
||||
|
settings: storeSelector, |
||||
|
}), |
||||
|
{ |
||||
|
saveSettings, |
||||
|
}, |
||||
|
)(CurrencyRows), |
||||
|
) |
Loading…
Reference in new issue