diff --git a/src/bridge/BridgeSyncContext.js b/src/bridge/BridgeSyncContext.js index 977ffdfb..7a4228c8 100644 --- a/src/bridge/BridgeSyncContext.js +++ b/src/bridge/BridgeSyncContext.js @@ -16,7 +16,9 @@ import { setAccountSyncState } from 'actions/bridgeSync' import { bridgeSyncSelector, syncStateLocalSelector } from 'reducers/bridgeSync' import type { BridgeSyncState } from 'reducers/bridgeSync' import { accountsSelector, isUpToDateSelector } from 'reducers/accounts' +import { currenciesStatusSelector, getIsCurrencyDown } from 'reducers/currenciesStatus' import { SYNC_MAX_CONCURRENT, SYNC_TIMEOUT } from 'config/constants' +import type { CurrencyStatus } from 'reducers/currenciesStatus' import { getBridgeForCurrency } from '.' type BridgeSyncProviderProps = { @@ -29,6 +31,7 @@ type BridgeSyncProviderOwnProps = BridgeSyncProviderProps & { isUpToDate: boolean, updateAccountWithUpdater: (string, (Account) => Account) => void, setAccountSyncState: (string, AsyncState) => *, + currenciesStatus: CurrencyStatus[], } type AsyncState = { @@ -48,6 +51,7 @@ export type Sync = (action: BehaviorAction) => void const BridgeSyncContext = React.createContext((_: BehaviorAction) => {}) const mapStateToProps = createStructuredSelector({ + currenciesStatus: currenciesStatusSelector, accounts: accountsSelector, bridgeSync: bridgeSyncSelector, isUpToDate: isUpToDateSelector, @@ -73,6 +77,11 @@ class Provider extends Component { return } + if (getIsCurrencyDown(this.props.currenciesStatus, account.currency)) { + next() + return + } + const bridge = getBridgeForCurrency(account.currency) this.props.setAccountSyncState(accountId, { pending: true, error: null }) diff --git a/src/components/CurrenciesStatusBanner.js b/src/components/CurrenciesStatusBanner.js index 656786f2..df9af8f2 100644 --- a/src/components/CurrenciesStatusBanner.js +++ b/src/components/CurrenciesStatusBanner.js @@ -8,40 +8,33 @@ import { createStructuredSelector } from 'reselect' import styled from 'styled-components' import type { Currency } from '@ledgerhq/live-common/lib/types' -import logger from 'logger' import { colors } from 'styles/theme' import { openURL } from 'helpers/linking' -import { urls } from 'config/urls' import { CHECK_CUR_STATUS_INTERVAL } from 'config/constants' -import network from 'api/network' import IconCross from 'icons/Cross' import IconTriangleWarning from 'icons/TriangleWarning' import IconChevronRight from 'icons/ChevronRight' import { dismissedBannersSelector } from 'reducers/settings' +import { currenciesStatusSelector, fetchCurrenciesStatus } from 'reducers/currenciesStatus' import { currenciesSelector } from 'reducers/accounts' import { dismissBanner } from 'actions/settings' +import type { CurrencyStatus } from 'reducers/currenciesStatus' import Box from 'components/base/Box' -type ResultItem = { - id: string, - status: string, - message: string, - link: string, - nonce: number, -} - const mapStateToProps = createStructuredSelector({ dismissedBanners: dismissedBannersSelector, accountsCurrencies: currenciesSelector, + currenciesStatus: currenciesStatusSelector, }) const mapDispatchToProps = { dismissBanner, + fetchCurrenciesStatus, } -const getItemKey = (item: ResultItem) => `${item.id}_${item.nonce}` +const getItemKey = (item: CurrencyStatus) => `${item.id}_${item.nonce}` const CloseIconContainer = styled.div` position: absolute; @@ -69,18 +62,12 @@ type Props = { accountsCurrencies: Currency[], dismissedBanners: string[], dismissBanner: string => void, + currenciesStatus: CurrencyStatus[], + fetchCurrenciesStatus: () => Promise, t: *, } -type State = { - result: ResultItem[], -} - -class CurrenciesStatusBanner extends PureComponent { - state = { - result: [], - } - +class CurrenciesStatusBanner extends PureComponent { componentDidMount() { this.pollStatus() } @@ -95,32 +82,17 @@ class CurrenciesStatusBanner extends PureComponent { unmounted = false timeout: * - pollStatus = () => { - this.fetchStatus() + pollStatus = async () => { + await this.props.fetchCurrenciesStatus() + if (this.unmounted) return this.timeout = setTimeout(this.pollStatus, CHECK_CUR_STATUS_INTERVAL) } - fetchStatus = async () => { - try { - const baseUrl = process.env.LL_STATUS_API || urls.currenciesStatus - const { data } = await network({ - method: 'GET', - url: `${baseUrl}/currencies-status`, - }) - if (this.unmounted) return - this.setState({ result: data }) - } catch (err) { - logger.error(err) - } - } - dismiss = item => this.props.dismissBanner(getItemKey(item)) render() { - const { dismissedBanners, accountsCurrencies, t } = this.props - const { result } = this.state - if (!result) return null - const filtered = result.filter( + const { dismissedBanners, accountsCurrencies, currenciesStatus, t } = this.props + const filtered = currenciesStatus.filter( item => accountsCurrencies.find(cur => cur.id === item.id) && dismissedBanners.indexOf(getItemKey(item)) === -1, @@ -135,8 +107,8 @@ class CurrenciesStatusBanner extends PureComponent { } class BannerItem extends PureComponent<{ - item: ResultItem, - onItemDismiss: ResultItem => void, + item: CurrencyStatus, + onItemDismiss: CurrencyStatus => void, t: *, }> { onLinkClick = () => openURL(this.props.item.link) diff --git a/src/reducers/currenciesStatus.js b/src/reducers/currenciesStatus.js new file mode 100644 index 00000000..47211b28 --- /dev/null +++ b/src/reducers/currenciesStatus.js @@ -0,0 +1,59 @@ +// @flow + +import { handleActions, createAction } from 'redux-actions' +import type { Currency } from '@ledgerhq/live-common/lib/types' + +import network from 'api/network' +import { urls } from 'config/urls' +import logger from 'logger' + +import type { State } from './index' + +export type CurrencyStatus = { + id: string, // the currency id + status: 'KO' | 'OK', + message: string, + link: string, + nonce: number, +} + +export type CurrenciesStatusState = CurrencyStatus[] + +const state: CurrenciesStatusState = [] + +const handlers = { + CURRENCIES_STATUS_SET: ( + state: CurrenciesStatusState, + { payload }: { payload: CurrenciesStatusState }, + ) => payload, +} + +// Actions + +const setCurrenciesStatus = createAction('CURRENCIES_STATUS_SET') +export const fetchCurrenciesStatus = () => async (dispatch: *) => { + try { + const baseUrl = process.env.LL_STATUS_API || urls.currenciesStatus + const { data } = await network({ + method: 'GET', + url: `${baseUrl}/currencies-status`, + }) + dispatch(setCurrenciesStatus(data)) + } catch (err) { + logger.error(err) + } +} + +// Selectors + +export const currenciesStatusSelector = (state: State) => state.currenciesStatus + +// It's not a *real* selector, but it's better than having this logic inside component +export const getIsCurrencyDown = (currenciesStatus: CurrenciesStatusState, currency: Currency) => { + const item = currenciesStatus.find(c => c.id === currency.id) + return !!item && item.status === 'KO' +} + +// Exporting reducer + +export default handleActions(handlers, state) diff --git a/src/reducers/index.js b/src/reducers/index.js index f2e2f76e..a84f2d3e 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -9,6 +9,7 @@ import type { CounterValuesState } from '@ledgerhq/live-common/lib/countervalues import CounterValues from 'helpers/countervalues' import accounts from './accounts' import application from './application' +import currenciesStatus from './currenciesStatus' import devices from './devices' import modals from './modals' import settings from './settings' @@ -24,11 +25,13 @@ import type { SettingsState } from './settings' import type { UpdateState } from './update' import type { OnboardingState } from './onboarding' import type { BridgeSyncState } from './bridgeSync' +import type { CurrenciesStatusState } from './currenciesStatus' export type State = { accounts: AccountsState, application: ApplicationState, countervalues: CounterValuesState, + currenciesStatus: CurrenciesStatusState, devices: DevicesState, modals: ModalsState, router: LocationShape, @@ -42,6 +45,7 @@ export default combineReducers({ accounts, application, countervalues: CounterValues.reducer, + currenciesStatus, devices, modals, router,