From 66c29613181b0eb46334dae7e5339ee47c1d3c10 Mon Sep 17 00:00:00 2001 From: dasilvarosa Date: Wed, 25 Jul 2018 12:43:49 +0200 Subject: [PATCH 1/9] More information on clear cache modal --- static/i18n/en/app.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/i18n/en/app.json b/static/i18n/en/app.json index cbed0f97..331c6b5b 100644 --- a/static/i18n/en/app.json +++ b/static/i18n/en/app.json @@ -399,7 +399,7 @@ }, "softResetModal": { "title": "Clear cache", - "desc": "Clearing the Ledger Live cache forces network resynchronization" + "desc": "Clearing the Ledger Live cache forces network resynchronization. Your settings and accounts are not affected. The private keys to access your crypto assets in the blockchain remain secure on your Ledger device and on your Recovery sheet." }, "removeAccountModal": { "title": "Remove account", @@ -459,4 +459,4 @@ "desc_2": "Please beware that Ledger does not provide financial, tax, or legal advice. You should take such decisions on your own or consult with reliable experts.", "cta": "Got it" } -} \ No newline at end of file +} From 8d1b7865152720008b27ddcb99a9732adef74a74 Mon Sep 17 00:00:00 2001 From: Anastasia Poupeney Date: Thu, 26 Jul 2018 18:35:11 +0200 Subject: [PATCH 2/9] wording --- static/i18n/en/app.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/i18n/en/app.json b/static/i18n/en/app.json index cbed0f97..da867665 100644 --- a/static/i18n/en/app.json +++ b/static/i18n/en/app.json @@ -196,7 +196,7 @@ "successDescription_plural": "Your accounts have been created.", "createNewAccount": { "title": "Add a new account", - "noOperationOnLastAccount": "No transactions found on your last new account <1><0>{{accountName}}. You can add a new account after you've started transacting on that account.", + "noOperationOnLastAccount": "There are no transactions on your last created <1><0>{{accountName}} account. You must first receive a transaction on that account before you can add a new one.", "noAccountToCreate": "No <1><0>{{currencyName}} account was found to create" }, "cta": { @@ -459,4 +459,4 @@ "desc_2": "Please beware that Ledger does not provide financial, tax, or legal advice. You should take such decisions on your own or consult with reliable experts.", "cta": "Got it" } -} \ No newline at end of file +} From 3e71c489170b151d66ac2dac232356b90d458544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Mon, 30 Jul 2018 12:25:51 +0200 Subject: [PATCH 3/9] Add more timeout on the sync to avoid issues --- src/config/constants.js | 2 +- src/config/errors.js | 1 + src/helpers/libcore.js | 62 +++++++++++++++++++++++++++----------- src/helpers/promise.js | 18 +++++++++++ static/i18n/en/errors.json | 4 +++ 5 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/config/constants.js b/src/config/constants.js index aefe35b3..023626ef 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -46,7 +46,7 @@ export const SYNC_ALL_INTERVAL = 120 * 1000 export const SYNC_BOOT_DELAY = 2 * 1000 export const SYNC_PENDING_INTERVAL = 10 * 1000 export const SYNC_MAX_CONCURRENT = intFromEnv('LEDGER_SYNC_MAX_CONCURRENT', 1) -export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 30 * 1000) +export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 60 * 1000) // Endpoints... diff --git a/src/config/errors.js b/src/config/errors.js index fa6508bd..d6140d6c 100644 --- a/src/config/errors.js +++ b/src/config/errors.js @@ -12,6 +12,7 @@ export const UserRefusedAddress = createCustomErrorClass('UserRefusedAddress') export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount') export const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine') export const DeviceGenuineSocketEarlyClose = createCustomErrorClass('DeviceGenuineSocketEarlyClose') +export const TimeoutTagged = createCustomErrorClass('TimeoutTagged') // db stuff, no need to translate export const NoDBPathGiven = createCustomErrorClass('NoDBPathGiven') diff --git a/src/helpers/libcore.js b/src/helpers/libcore.js index a9ed673d..630c0c14 100644 --- a/src/helpers/libcore.js +++ b/src/helpers/libcore.js @@ -16,6 +16,7 @@ import { isSegwitPath, isUnsplitPath } from 'helpers/bip32' import * as accountIdHelper from 'helpers/accountId' import { createCustomErrorClass, deserializeError } from './errors' import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName' +import { timeoutTagged } from './promise' const NoAddressesFound = createCustomErrorClass('NoAddressesFound') @@ -202,6 +203,7 @@ const coreSyncAccount = (core, account) => new Promise((resolve, reject) => { const eventReceiver = createEventReceiver(core, e => { const code = e.getCode() + logger.debug(`syncAccountEvent ${code}`, { type: 'libcore-sync' }) if (code === core.EVENT_CODE.UNDEFINED || code === core.EVENT_CODE.SYNCHRONIZATION_FAILED) { const payload = e.getPayload() const message = ( @@ -270,7 +272,7 @@ async function scanNextAccount(props: { const shouldSyncAccount = true // TODO: let's sync everytime. maybe in the future we can optimize. if (shouldSyncAccount) { - await coreSyncAccount(core, njsAccount) + await timeoutTagged('coreSyncAccount', 30000, coreSyncAccount(core, njsAccount)) } if (isUnsubscribed()) return [] @@ -325,10 +327,10 @@ async function getOrCreateWallet( ): NJSWallet { const pool = core.getPoolInstance() try { - const wallet = await pool.getWallet(WALLET_IDENTIFIER) + const wallet = await timeoutTagged('getWallet', 5000, pool.getWallet(WALLET_IDENTIFIER)) return wallet } catch (err) { - const currency = await pool.getCurrency(currencyId) + const currency = await timeoutTagged('getCurrency', 5000, pool.getCurrency(currencyId)) const splitConfig = isUnsplit ? SPLITTED_CURRENCIES[currencyId] || null : null const coinType = splitConfig ? splitConfig.coinType : '' const walletConfig = isSegwit @@ -342,9 +344,11 @@ async function getOrCreateWallet( } : undefined const njsWalletConfig = createWalletConfig(core, walletConfig) - const wallet = await core - .getPoolInstance() - .createWallet(WALLET_IDENTIFIER, currency, njsWalletConfig) + const wallet = await timeoutTagged( + 'createWallet', + 10000, + core.getPoolInstance().createWallet(WALLET_IDENTIFIER, currency, njsWalletConfig), + ) return wallet } } @@ -368,21 +372,33 @@ async function buildAccountRaw({ core: *, ops: NJSOperation[], }): Promise { - const njsBalance = await njsAccount.getBalance() + const njsBalance = await timeoutTagged('getBalance', 10000, njsAccount.getBalance()) const balance = njsBalance.toLong() const jsCurrency = getCryptoCurrencyById(currencyId) - const { derivations } = await wallet.getAccountCreationInfo(accountIndex) + const { derivations } = await timeoutTagged( + 'getAccountCreationInfo', + 10000, + wallet.getAccountCreationInfo(accountIndex), + ) const [walletPath, accountPath] = derivations // retrieve xpub const xpub = njsAccount.getRestoreKey() // blockHeight - const { height: blockHeight } = await njsAccount.getLastBlock() + const { height: blockHeight } = await timeoutTagged( + 'getLastBlock', + 30000, + njsAccount.getLastBlock(), + ) // get a bunch of fresh addresses - const rawAddresses = await njsAccount.getFreshPublicAddresses() + const rawAddresses = await timeoutTagged( + 'getFreshPublicAddresses', + 10000, + njsAccount.getFreshPublicAddresses(), + ) const addresses = rawAddresses.map(njsAddress => ({ str: njsAddress.toString(), @@ -500,7 +516,11 @@ export async function syncAccount({ const isUnsplit = isUnsplitPath(freshAddressPath, SPLITTED_CURRENCIES[currencyId]) let njsWallet try { - njsWallet = await core.getPoolInstance().getWallet(decodedAccountId.walletName) + njsWallet = await timeoutTagged( + 'getWallet', + 10000, + core.getPoolInstance().getWallet(decodedAccountId.walletName), + ) } catch (e) { logger.warn(`Have to reimport the account... (${e})`) njsWallet = await getOrCreateWallet( @@ -514,20 +534,28 @@ export async function syncAccount({ let njsAccount try { - njsAccount = await njsWallet.getAccount(index) + njsAccount = await timeoutTagged('getAccount', 10000, njsWallet.getAccount(index)) } catch (e) { logger.warn(`Have to recreate the account... (${e.message})`) - const extendedInfos = await njsWallet.getExtendedKeyAccountCreationInfo(index) + const extendedInfos = await timeoutTagged( + 'getEKACI', + 10000, + njsWallet.getExtendedKeyAccountCreationInfo(index), + ) extendedInfos.extendedKeys.push(decodedAccountId.xpub) - njsAccount = await njsWallet.newAccountWithExtendedKeyInfo(extendedInfos) + njsAccount = await timeoutTagged( + 'newAWEKI', + 10000, + njsWallet.newAccountWithExtendedKeyInfo(extendedInfos), + ) } - const unsub = await coreSyncAccount(core, njsAccount) + const unsub = await timeoutTagged('coreSyncAccount', 30000, coreSyncAccount(core, njsAccount)) unsub() const query = njsAccount.queryOperations() - const ops = await query.complete().execute() - const njsBalance = await njsAccount.getBalance() + const ops = await timeoutTagged('ops', 30000, query.complete().execute()) + const njsBalance = await timeoutTagged('getBalance', 10000, njsAccount.getBalance()) const syncedRawAccount = await buildAccountRaw({ njsAccount, diff --git a/src/helpers/promise.js b/src/helpers/promise.js index ad494a16..0ae2d09f 100644 --- a/src/helpers/promise.js +++ b/src/helpers/promise.js @@ -2,6 +2,7 @@ // small utilities for Promises import logger from 'logger' +import { TimeoutTagged } from 'config/errors' export const delay = (ms: number): Promise => new Promise(f => setTimeout(f, ms)) @@ -65,6 +66,23 @@ export function createCancelablePolling( return { unsubscribe, promise } } +export const timeoutTagged = (tag: string, delay: number, promise: Promise): Promise => + new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new TimeoutTagged('timeout', { tag })) + }, delay) + promise.then( + r => { + clearTimeout(timeout) + resolve(r) + }, + e => { + clearTimeout(timeout) + reject(e) + }, + ) + }) + export const promisify = (fn: any) => (...args: any) => new Promise((resolve, reject) => fn(...args, (err: Error, res: any) => { diff --git a/static/i18n/en/errors.json b/static/i18n/en/errors.json index 2249bec7..154b6da5 100644 --- a/static/i18n/en/errors.json +++ b/static/i18n/en/errors.json @@ -115,6 +115,10 @@ "title": "Oops, a time out occurred", "description": "It took too long for the server to respond." }, + "TimeoutTagged": { + "title": "Oops, a time out occurred ({{tag}})", + "description": "It took too long for the server to respond." + }, "TransportError": { "title": "Something went wrong. Please reconnect your device.", "description": "{{message}}" From c1572d58f829ef1cf9de779ae3fed480da0e2bde Mon Sep 17 00:00:00 2001 From: amougel Date: Mon, 30 Jul 2018 17:55:03 +0200 Subject: [PATCH 4/9] Remove LEDGER_RESET_ALL --- README.md | 1 - src/config/constants.js | 1 - src/renderer/init.js | 6 ------ 3 files changed, 8 deletions(-) diff --git a/README.md b/README.md index fd4d0465..98bdda85 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,6 @@ DEBUG_ACTION=1 DEBUG_TAB_KEY=1 DEBUG_LIBCORE=1 DEBUG_WS=1 -LEDGER_RESET_ALL=1 LEDGER_DEBUG_ALL_LANGS=1 SKIP_GENUINE=1 SKIP_ONBOARDING=1 diff --git a/src/config/constants.js b/src/config/constants.js index aefe35b3..2734edff 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -80,7 +80,6 @@ export const DEBUG_TAB_KEY = boolFromEnv('DEBUG_TAB_KEY') export const DEBUG_LIBCORE = boolFromEnv('DEBUG_LIBCORE') export const DEBUG_WS = boolFromEnv('DEBUG_WS') export const DEBUG_SYNC = boolFromEnv('DEBUG_SYNC') -export const LEDGER_RESET_ALL = boolFromEnv('LEDGER_RESET_ALL') export const LEDGER_DEBUG_ALL_LANGS = boolFromEnv('LEDGER_DEBUG_ALL_LANGS') export const SKIP_GENUINE = boolFromEnv('SKIP_GENUINE') export const SKIP_ONBOARDING = boolFromEnv('SKIP_ONBOARDING') diff --git a/src/renderer/init.js b/src/renderer/init.js index 52fd35f7..4f7278df 100644 --- a/src/renderer/init.js +++ b/src/renderer/init.js @@ -12,7 +12,6 @@ import { runMigrations } from 'migrations' import createStore from 'renderer/createStore' import events from 'renderer/events' -import { LEDGER_RESET_ALL } from 'config/constants' import { enableGlobalTab, disableGlobalTab, isGlobalTabEnabled } from 'config/global-tab' import { fetchAccounts } from 'actions/accounts' @@ -25,7 +24,6 @@ import resolveUserDataDirectory from 'helpers/resolveUserDataDirectory' import db from 'helpers/db' import dbMiddleware from 'middlewares/db' import CounterValues from 'helpers/countervalues' -import hardReset from 'helpers/hardReset' import { decodeAccountsModel, encodeAccountsModel } from 'reducers/accounts' @@ -43,10 +41,6 @@ const TAB_KEY = 9 db.init(userDataDirectory) async function init() { - if (LEDGER_RESET_ALL) { - await hardReset() - } - await runMigrations() db.init(userDataDirectory) db.registerTransform('app', 'accounts', { get: decodeAccountsModel, set: encodeAccountsModel }) From ddce9589c52acd2bd08761a996409c0f2f79ece1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Mon, 30 Jul 2018 21:05:29 +0200 Subject: [PATCH 5/9] More modular portfolio split into components --- .../DashboardPage/AccountCardList.js | 66 +++++++ .../DashboardPage/AccountCardListHeader.js | 33 ++++ .../DashboardPage/AccountCardPlaceholder.js | 64 ++++++ .../DashboardPage/CurrentGreetings.js | 31 +++ src/components/DashboardPage/SummaryDesc.js | 22 +++ src/components/DashboardPage/index.js | 186 ++++-------------- 6 files changed, 258 insertions(+), 144 deletions(-) create mode 100644 src/components/DashboardPage/AccountCardList.js create mode 100644 src/components/DashboardPage/AccountCardListHeader.js create mode 100644 src/components/DashboardPage/AccountCardPlaceholder.js create mode 100644 src/components/DashboardPage/CurrentGreetings.js create mode 100644 src/components/DashboardPage/SummaryDesc.js diff --git a/src/components/DashboardPage/AccountCardList.js b/src/components/DashboardPage/AccountCardList.js new file mode 100644 index 00000000..eee7321e --- /dev/null +++ b/src/components/DashboardPage/AccountCardList.js @@ -0,0 +1,66 @@ +// @flow + +import React, { Component } from 'react' +import type { Account, Currency } from '@ledgerhq/live-common/lib/types' + +import Box from 'components/base/Box' +import AccountCard from './AccountCard' +import AccountCardListHeader from './AccountCardListHeader' +import AccountCardPlaceholder from './AccountCardPlaceholder' + +type Props = { + accounts: Account[], + onAccountClick: Account => void, + counterValue: Currency, + daysCount: number, +} + +class AccountCardList extends Component { + render() { + const { accounts, counterValue, daysCount, onAccountClick } = this.props + + return ( + + + + {accounts + .map(account => ({ + key: account.id, + account, + })) + .concat( + Array(3 - (accounts.length % 3)) + .fill(null) + .map((_, i) => ({ + key: `placeholder_${i}`, + withPlaceholder: i === 0, + })), + ) + .map(item => ( + + {item.account ? ( + + ) : item.withPlaceholder ? ( + + ) : null} + + ))} + + + ) + } +} + +export default AccountCardList diff --git a/src/components/DashboardPage/AccountCardListHeader.js b/src/components/DashboardPage/AccountCardListHeader.js new file mode 100644 index 00000000..9da0fde5 --- /dev/null +++ b/src/components/DashboardPage/AccountCardListHeader.js @@ -0,0 +1,33 @@ +// @flow + +import React, { PureComponent } from 'react' +import { translate } from 'react-i18next' +import type { T } from 'types/common' + +import Box from 'components/base/Box' +import Text from 'components/base/Text' +import AccountsOrder from './AccountsOrder' + +type Props = { + t: T, + accountsLength: number, +} + +class AccountCardListHeader extends PureComponent { + render() { + const { accountsLength, t } = this.props + + return ( + + + {t('app:dashboard.accounts.title', { count: accountsLength })} + + + + + + ) + } +} + +export default translate()(AccountCardListHeader) diff --git a/src/components/DashboardPage/AccountCardPlaceholder.js b/src/components/DashboardPage/AccountCardPlaceholder.js new file mode 100644 index 00000000..1936fe4d --- /dev/null +++ b/src/components/DashboardPage/AccountCardPlaceholder.js @@ -0,0 +1,64 @@ +// @flow + +import React, { PureComponent } from 'react' +import { connect } from 'react-redux' +import { translate } from 'react-i18next' +import styled from 'styled-components' + +import { openModal } from 'reducers/modals' +import { MODAL_ADD_ACCOUNTS } from 'config/constants' +import type { T } from 'types/common' +import { i } from 'helpers/staticPath' +import Box from 'components/base/Box' +import Button from 'components/base/Button' + +const Wrapper = styled(Box).attrs({ + p: 4, + flex: 1, + alignItems: 'center', +})` + border: 1px dashed ${p => p.theme.colors.fog}; + border-radius: 4px; + height: 215px; +` + +class AccountCardPlaceholder extends PureComponent<{ + t: T, + openModal: string => void, +}> { + onAddAccounts = () => this.props.openModal(MODAL_ADD_ACCOUNTS) + + render() { + const { t } = this.props + return ( + + + + + + {t('app:dashboard.emptyAccountTile.desc')} + + + + ) + } +} + +export default translate()( + connect( + null, + { + openModal, + }, + )(AccountCardPlaceholder), +) diff --git a/src/components/DashboardPage/CurrentGreetings.js b/src/components/DashboardPage/CurrentGreetings.js new file mode 100644 index 00000000..da4896b8 --- /dev/null +++ b/src/components/DashboardPage/CurrentGreetings.js @@ -0,0 +1,31 @@ +// @flow +import React, { PureComponent } from 'react' +import { translate } from 'react-i18next' +import type { T } from 'types/common' + +import Text from 'components/base/Text' + +const getCurrentGreetings = () => { + const localTimeHour = new Date().getHours() + const afternoon_breakpoint = 12 + const evening_breakpoint = 17 + if (localTimeHour >= afternoon_breakpoint && localTimeHour < evening_breakpoint) { + return 'app:dashboard.greeting.afternoon' + } else if (localTimeHour >= evening_breakpoint) { + return 'app:dashboard.greeting.evening' + } + return 'app:dashboard.greeting.morning' +} + +class CurrentGettings extends PureComponent<{ t: T }> { + render() { + const { t } = this.props + return ( + + {t(getCurrentGreetings())} + + ) + } +} + +export default translate()(CurrentGettings) diff --git a/src/components/DashboardPage/SummaryDesc.js b/src/components/DashboardPage/SummaryDesc.js new file mode 100644 index 00000000..ff8c5f3e --- /dev/null +++ b/src/components/DashboardPage/SummaryDesc.js @@ -0,0 +1,22 @@ +// @flow + +import React, { PureComponent } from 'react' +import { translate } from 'react-i18next' +import type { T } from 'types/common' +import Text from 'components/base/Text' + +class SummaryDesc extends PureComponent<{ + t: T, + totalAccounts: number, +}> { + render() { + const { totalAccounts, t } = this.props + return ( + + {t('app:dashboard.summary', { count: totalAccounts })} + + ) + } +} + +export default translate()(SummaryDesc) diff --git a/src/components/DashboardPage/index.js b/src/components/DashboardPage/index.js index 384f6d71..2b3b6340 100644 --- a/src/components/DashboardPage/index.js +++ b/src/components/DashboardPage/index.js @@ -5,7 +5,6 @@ import uniq from 'lodash/uniq' import { compose } from 'redux' import { translate } from 'react-i18next' import { connect } from 'react-redux' -import styled from 'styled-components' import { push } from 'react-router-redux' import { createStructuredSelector } from 'reselect' import type { Account, Currency } from '@ledgerhq/live-common/lib/types' @@ -14,11 +13,8 @@ import type { T } from 'types/common' import { colors } from 'styles/theme' import { accountsSelector } from 'reducers/accounts' -import { openModal } from 'reducers/modals' -import { MODAL_ADD_ACCOUNTS } from 'config/constants' import { counterValueCurrencySelector, - localeSelector, selectedTimeRangeSelector, timeRangeDaysByKey, } from 'reducers/settings' @@ -32,27 +28,23 @@ import UpdateNotifier from 'components/UpdateNotifier' import BalanceInfos from 'components/BalanceSummary/BalanceInfos' import BalanceSummary from 'components/BalanceSummary' import Box from 'components/base/Box' -import { i } from 'helpers/staticPath' import PillsDaysCount from 'components/PillsDaysCount' -import Text from 'components/base/Text' import OperationsList from 'components/OperationsList' import StickyBackToTop from 'components/StickyBackToTop' -import Button from 'components/base/Button' -import AccountCard from './AccountCard' -import AccountsOrder from './AccountsOrder' import EmptyState from './EmptyState' +import CurrentGreetings from './CurrentGreetings' +import SummaryDesc from './SummaryDesc' +import AccountCardList from './AccountCardList' const mapStateToProps = createStructuredSelector({ accounts: accountsSelector, counterValue: counterValueCurrencySelector, - locale: localeSelector, selectedTimeRange: selectedTimeRangeSelector, }) const mapDispatchToProps = { push, saveSettings, - openModal, } type Props = { @@ -62,41 +54,33 @@ type Props = { counterValue: Currency, selectedTimeRange: TimeRange, saveSettings: ({ selectedTimeRange: TimeRange }) => *, - openModal: string => void, } class DashboardPage extends PureComponent { onAccountClick = account => this.props.push(`/account/${account.id}`) - handleGreeting = () => { - const localTimeHour = new Date().getHours() - const afternoon_breakpoint = 12 - const evening_breakpoint = 17 - - if (localTimeHour >= afternoon_breakpoint && localTimeHour < evening_breakpoint) { - return 'app:dashboard.greeting.afternoon' - } else if (localTimeHour >= evening_breakpoint) { - return 'app:dashboard.greeting.evening' - } - return 'app:dashboard.greeting.morning' - } - handleChangeSelectedTime = item => { this.props.saveSettings({ selectedTimeRange: item.key }) } - _cacheBalance = null + renderHeader = ({ isAvailable, totalBalance, selectedTimeRange, sinceBalance, refBalance }) => ( + + ) render() { - const { accounts, t, counterValue, selectedTimeRange, openModal } = this.props + const { accounts, t, counterValue, selectedTimeRange } = this.props const daysCount = timeRangeDaysByKey[selectedTimeRange] - const timeFrame = this.handleGreeting() - const imagePath = i('empty-account-tile.svg') const totalAccounts = accounts.length const totalCurrencies = uniq(accounts.map(a => a.currency.id)).length const totalOperations = accounts.reduce((sum, a) => sum + a.operations.length, 0) - const displayOperationsHelper = (account: Account) => account.operations.length > 0 - const displayOperations = accounts.some(displayOperationsHelper) return ( @@ -113,12 +97,8 @@ class DashboardPage extends PureComponent { - - {t(timeFrame)} - - - {t('app:dashboard.summary', { count: totalAccounts })} - + + { /> - - + + + + {totalOperations > 0 && ( + ( - - )} + title={t('app:dashboard.recentActivity')} + withAccount /> - - - - {t('app:dashboard.accounts.title', { count: accounts.length })} - - - - - - - {accounts - .concat( - Array(3 - (accounts.length % 3)) - .fill(null) - .map((_, i) => i === 0), - ) - .map((account, i) => ( - - {account ? ( - typeof account === 'object' ? ( - - ) : ( - - - - - - {t('app:dashboard.emptyAccountTile.desc')} - - - - ) - ) : null} - - ))} - - - {displayOperations && ( - - )} - - + )} + ) : ( @@ -243,13 +151,3 @@ export default compose( ), translate(), )(DashboardPage) - -const Wrapper = styled(Box).attrs({ - p: 4, - flex: 1, - alignItems: 'center', -})` - border: 1px dashed ${p => p.theme.colors.fog}; - border-radius: 4px; - height: 215px; -` From 24915b72318cd07fac11bcc47bb2fa859b2b89a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Tue, 31 Jul 2018 08:36:10 +0200 Subject: [PATCH 6/9] Split AccountCard as well --- src/components/DashboardPage/AccountCard.js | 103 ------------------ .../DashboardPage/AccountCard/Header.js | 33 ++++++ .../DashboardPage/AccountCard/index.js | 94 ++++++++++++++++ 3 files changed, 127 insertions(+), 103 deletions(-) delete mode 100644 src/components/DashboardPage/AccountCard.js create mode 100644 src/components/DashboardPage/AccountCard/Header.js create mode 100644 src/components/DashboardPage/AccountCard/index.js diff --git a/src/components/DashboardPage/AccountCard.js b/src/components/DashboardPage/AccountCard.js deleted file mode 100644 index a9881a2f..00000000 --- a/src/components/DashboardPage/AccountCard.js +++ /dev/null @@ -1,103 +0,0 @@ -// @flow - -import React, { PureComponent } from 'react' -import styled from 'styled-components' - -import type { Account, Currency } from '@ledgerhq/live-common/lib/types' - -import Chart from 'components/base/Chart' -import Bar from 'components/base/Bar' -import Box, { Card } from 'components/base/Box' -import CalculateBalance from 'components/CalculateBalance' -import FormattedVal from 'components/base/FormattedVal' -import Ellipsis from 'components/base/Ellipsis' -import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon' -import DeltaChange from '../DeltaChange' - -const Wrapper = styled(Card).attrs({ - p: 4, - flex: 1, -})` - cursor: ${p => (p.onClick ? 'pointer' : 'default')}; -` - -class AccountCard extends PureComponent<{ - counterValue: Currency, - account: Account, - onClick?: Account => void, - daysCount: number, -}> { - render() { - const { counterValue, account, onClick, daysCount, ...props } = this.props - return ( - onClick(account) : null} {...props}> - - - - - - - - {account.currency.name} - - - {account.name} - - - - - - - - - - {({ isAvailable, balanceHistory, balanceStart, balanceEnd }) => ( - - - - {isAvailable ? ( - - ) : null} - - - {isAvailable && !balanceStart.isZero() ? ( - - ) : null} - - - - - )} - - - ) - } -} - -export default AccountCard diff --git a/src/components/DashboardPage/AccountCard/Header.js b/src/components/DashboardPage/AccountCard/Header.js new file mode 100644 index 00000000..642ec56e --- /dev/null +++ b/src/components/DashboardPage/AccountCard/Header.js @@ -0,0 +1,33 @@ +// @flow + +import React, { PureComponent } from 'react' +import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' +import Box from 'components/base/Box' +import Ellipsis from 'components/base/Ellipsis' +import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon' + +class AccountCardHeader extends PureComponent<{ + currency: CryptoCurrency, + accountName: string, +}> { + render() { + const { currency, accountName } = this.props + return ( + + + + + + + {currency.name} + + + {accountName} + + + + ) + } +} + +export default AccountCardHeader diff --git a/src/components/DashboardPage/AccountCard/index.js b/src/components/DashboardPage/AccountCard/index.js new file mode 100644 index 00000000..25dc16d4 --- /dev/null +++ b/src/components/DashboardPage/AccountCard/index.js @@ -0,0 +1,94 @@ +// @flow + +import React, { PureComponent } from 'react' +import styled from 'styled-components' + +import type { Account, Currency } from '@ledgerhq/live-common/lib/types' + +import Chart from 'components/base/Chart' +import Bar from 'components/base/Bar' +import Box, { Card } from 'components/base/Box' +import CalculateBalance from 'components/CalculateBalance' +import FormattedVal from 'components/base/FormattedVal' +import DeltaChange from 'components/DeltaChange' +import AccountCardHeader from './Header' + +const Wrapper = styled(Card).attrs({ + p: 4, + flex: 1, +})` + cursor: ${p => (p.onClick ? 'pointer' : 'default')}; +` + +class AccountCard extends PureComponent<{ + counterValue: Currency, + account: Account, + onClick: Account => void, + daysCount: number, +}> { + renderBody = ({ isAvailable, balanceHistory, balanceStart, balanceEnd }: *) => { + const { counterValue, account } = this.props + return ( + + + + {isAvailable ? ( + + ) : null} + + + {isAvailable && !balanceStart.isZero() ? ( + + ) : null} + + + + + ) + } + onClick = () => { + const { account, onClick } = this.props + onClick(account) + } + render() { + const { counterValue, account, onClick, daysCount, ...props } = this.props + return ( + + + + + + + + + + {this.renderBody} + + + ) + } +} + +export default AccountCard From ddf5d548dc47b28488b49873da7fb021ffa06c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Tue, 31 Jul 2018 08:37:14 +0200 Subject: [PATCH 7/9] update wording --- static/i18n/en/app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/i18n/en/app.json b/static/i18n/en/app.json index da867665..4c84564d 100644 --- a/static/i18n/en/app.json +++ b/static/i18n/en/app.json @@ -196,7 +196,7 @@ "successDescription_plural": "Your accounts have been created.", "createNewAccount": { "title": "Add a new account", - "noOperationOnLastAccount": "There are no transactions on your last created <1><0>{{accountName}} account. You must first receive a transaction on that account before you can add a new one.", + "noOperationOnLastAccount": "There are no transactions on your last created <1><0>{{accountName}} account. You must first receive crypto assets on that account before you can add a new one.", "noAccountToCreate": "No <1><0>{{currencyName}} account was found to create" }, "cta": { From 9bb448a3b0444969520a86327931101e6922e8a3 Mon Sep 17 00:00:00 2001 From: meriadec Date: Tue, 31 Jul 2018 11:12:45 +0200 Subject: [PATCH 8/9] Fix outline button active style --- src/components/base/Button/index.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/components/base/Button/index.js b/src/components/base/Button/index.js index 4bf4839e..1d7a0444 100644 --- a/src/components/base/Button/index.js +++ b/src/components/base/Button/index.js @@ -93,16 +93,22 @@ const buttonStyles: { [_: string]: Style } = { background: ${rgba(c, 0.1)}; ` }, - active: p => ` - color: ${darken( - p.outlineColor ? p.theme.colors[p.outlineColor] || p.outlineColor : p.theme.colors.wallet, - 0.1, - )}; - border-color: ${darken( - p.outlineColor ? p.theme.colors[p.outlineColor] || p.outlineColor : p.theme.colors.wallet, - 0.1, - )}; - `, + active: p => { + const c = p.outlineColor + ? p.theme.colors[p.outlineColor] || p.outlineColor + : p.theme.colors.wallet + return ` + background: ${rgba(c, 0.15)}; + color: ${darken( + p.outlineColor ? p.theme.colors[p.outlineColor] || p.outlineColor : p.theme.colors.wallet, + 0.1, + )}; + border-color: ${darken( + p.outlineColor ? p.theme.colors[p.outlineColor] || p.outlineColor : p.theme.colors.wallet, + 0.1, + )}; + ` + }, }, outlineGrey: { default: p => ` From a348cd1d206b740b84e627f847587f9afdafe68b Mon Sep 17 00:00:00 2001 From: meriadec Date: Tue, 31 Jul 2018 11:12:03 +0200 Subject: [PATCH 9/9] Fix storybook --- src/helpers/linking.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/helpers/linking.js b/src/helpers/linking.js index e810f123..36855a21 100644 --- a/src/helpers/linking.js +++ b/src/helpers/linking.js @@ -1,12 +1,17 @@ // @flow -import { shell } from 'electron' import { track } from 'analytics/segment' +let shell +if (!process.env.STORYBOOK_ENV) { + const electron = require('electron') + shell = electron.shell +} + export const openURL = ( url: string, customEventName: string = 'OpenURL', extraParams: Object = {}, ) => { track(customEventName, { ...extraParams, url }) - shell.openExternal(url) + shell && shell.openExternal(url) }