From 2d4f3e823262f43ab69d15644079401395e27df3 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Mon, 1 Oct 2018 13:21:58 +0200 Subject: [PATCH 01/38] fixed edgecase: handle disconnect device in BL/initialized mode OR without features --- src/actions/TrezorConnectActions.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/actions/TrezorConnectActions.js b/src/actions/TrezorConnectActions.js index 7d718906..9ecc92c8 100644 --- a/src/actions/TrezorConnectActions.js +++ b/src/actions/TrezorConnectActions.js @@ -206,7 +206,7 @@ export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispat export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { const selected: ?TrezorDevice = getState().wallet.selectedDevice; - if (device && device.features) { + if (device.features) { if (selected && selected.features && selected.features.device_id === device.features.device_id) { dispatch(DiscoveryActions.stop(selected)); } @@ -218,7 +218,11 @@ export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch device: instances[0], instances, }); + } else { + dispatch(RouterActions.selectFirstAvailableDevice()); } + } else { + dispatch(RouterActions.selectFirstAvailableDevice()); } }; From 0c0f9e7ac8012922e4c50371ba9ae57ef67222d2 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 26 Sep 2018 14:11:37 +0200 Subject: [PATCH 02/38] rewritten actions --- src/actions/SelectedAccountActions.js | 253 +++++++++++++++++++------ src/actions/WalletActions.js | 40 ++-- src/reducers/SelectedAccountReducer.js | 12 +- src/reducers/utils/index.js | 1 + src/services/WalletService.js | 4 +- 5 files changed, 224 insertions(+), 86 deletions(-) diff --git a/src/actions/SelectedAccountActions.js b/src/actions/SelectedAccountActions.js index 4e01c1a5..a5f1a91d 100644 --- a/src/actions/SelectedAccountActions.js +++ b/src/actions/SelectedAccountActions.js @@ -1,12 +1,8 @@ /* @flow */ - -import { LOCATION_CHANGE } from 'react-router-redux'; import * as ACCOUNT from 'actions/constants/account'; -import * as NOTIFICATION from 'actions/constants/notification'; -import * as PENDING from 'actions/constants/pendingTx'; - -import * as stateUtils from 'reducers/utils'; +import * as reducerUtils from 'reducers/utils'; +import { initialState } from 'reducers/SelectedAccountReducer'; import type { AsyncAction, @@ -16,85 +12,216 @@ import type { State, } from 'flowtype'; +type SelectedAccountState = $ElementType; + export type SelectedAccountAction = { type: typeof ACCOUNT.DISPOSE, } | { type: typeof ACCOUNT.UPDATE_SELECTED_ACCOUNT, - payload: $ElementType + payload: SelectedAccountState, }; +type AccountStatus = { + type: string; + title: string; + message?: string; + visible: boolean; +} + export const dispose = (): Action => ({ type: ACCOUNT.DISPOSE, }); -export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const locationChange: boolean = action.type === LOCATION_CHANGE; - const state: State = getState(); - const { location } = state.router; +const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): ?AccountStatus => { + const device = state.wallet.selectedDevice; + if (!device || !device.state) { + return { + type: 'info', + title: 'Loading device...', + visible: false, + }; + } + + const { + account, + discovery, + network, + } = selectedAccount; + + // corner case: accountState didn't finish loading state after LOCATION_CHANGE action + if (!network) { + return { + type: 'info', + title: 'Loading account state...', + visible: false, + }; + } - // handle devices state change (from trezor-connect events or location change) - if (locationChange - || prevState.accounts !== state.accounts - || prevState.discovery !== state.discovery - || prevState.tokens !== state.tokens - || prevState.pending !== state.pending) { - const account = stateUtils.getSelectedAccount(state); - const network = stateUtils.getSelectedNetwork(state); - const discovery = stateUtils.getDiscoveryProcess(state); - const tokens = stateUtils.getAccountTokens(state, account); - const pending = stateUtils.getAccountPendingTx(state.pending, account); - - const payload: $ElementType = { - location: location.pathname, - account, - network, - discovery, - tokens, - pending, + const blockchain = state.blockchain.find(b => b.name === network.network); + if (blockchain && !blockchain.connected) { + return { + type: 'backend', + title: 'Backend is not connected', + visible: false, }; + } - let needUpdate: boolean = false; - Object.keys(payload).forEach((key) => { - if (Array.isArray(payload[key])) { - if (Array.isArray(state.selectedAccount[key]) && payload[key].length !== state.selectedAccount[key].length) { - needUpdate = true; + // account not found (yet). checking why... + if (!account) { + if (!discovery || discovery.waitingForDevice) { + if (device.connected) { + // case 1: device is connected but discovery not started yet (probably waiting for auth) + if (device.available) { + return { + type: 'info', + title: 'Loading accounts...', + visible: false, + }; } - } else if (payload[key] !== state.selectedAccount[key]) { - needUpdate = true; + // case 2: device is unavailable (created with different passphrase settings) account cannot be accessed + return { + type: 'info', + title: `Device ${device.instanceLabel} is unavailable`, + message: 'Change passphrase settings to use this device', + visible: false, + }; } - }); - if (needUpdate) { + // case 3: device is disconnected + return { + type: 'info', + title: `Device ${device.instanceLabel} is disconnected`, + message: 'Connect device to load accounts', + visible: false, + }; + } + + if (discovery.completed) { + // case 4: account not found and discovery is completed + return { + type: 'warning', + title: 'Account does not exist', + visible: false, + }; + } + + // case 6: discovery is not completed yet + return { + type: 'info', + title: 'Loading accounts...', + visible: false, + }; + } + + // Additional status: account does exists and it's visible but shouldn't be active + if (!device.connected) { + return { + type: 'info', + title: `Device ${device.instanceLabel} is disconnected`, + visible: true, + }; + } + if (!device.available) { + return { + type: 'info', + title: `Device ${device.instanceLabel} is unavailable`, + message: 'Change passphrase settings to use this device', + visible: true, + }; + } + + // Additional status: account does exists, but waiting for discovery to complete + if (discovery && !discovery.completed) { + return { + type: 'info', + title: 'Loading accounts...', + visible: true, + }; + } + + return null; +}; + +export const observe = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { + // ignore actions dispatched from this process + if (action.type === ACCOUNT.UPDATE_SELECTED_ACCOUNT) { + return; + } + + const state: State = getState(); + const { location } = state.router; + if (!location.state.account) { + // displayed route is not an account route + if (state.selectedAccount.location.length > 1) { + // reset "selectedAccount" reducer to default dispatch({ type: ACCOUNT.UPDATE_SELECTED_ACCOUNT, - payload, + payload: initialState, }); + } + return; + } + + // get new values for selected account + const account = reducerUtils.getSelectedAccount(state); + const network = reducerUtils.getSelectedNetwork(state); + const discovery = reducerUtils.getDiscoveryProcess(state); + const tokens = reducerUtils.getAccountTokens(state, account); + const pending = reducerUtils.getAccountPendingTx(state.pending, account); + + // prepare new state for "selectedAccount" reducer + const newState: SelectedAccountState = { + location: state.router.location.pathname, + account, + network, + discovery, + tokens, + pending, + notification: null, + visible: false, + }; - if (location.state.send) { - const rejectedTxs = pending.filter(tx => tx.rejected); - rejectedTxs.forEach((tx) => { - dispatch({ - type: NOTIFICATION.ADD, - payload: { - type: 'warning', - title: 'Pending transaction rejected', - message: `Transaction with id: ${tx.id} not found.`, - cancelable: true, - actions: [ - { - label: 'OK', - callback: () => { - dispatch({ - type: PENDING.TX_RESOLVED, - tx, - }); - }, + // get "selectedAccount" status from newState + const status = getAccountStatus(state, newState); + newState.notification = status || null; + newState.visible = status ? status.visible : true; + + // check if newState is different than previous state + const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, ['location', 'account', 'network', 'discovery', 'tokens', 'pending', 'status', 'visible']); + if (stateChanged) { + // update values in reducer + dispatch({ + type: ACCOUNT.UPDATE_SELECTED_ACCOUNT, + payload: newState, + }); + + // TODO: move this to send account actions + /* + if (location.state.send) { + const rejectedTxs = pending.filter(tx => tx.rejected); + rejectedTxs.forEach((tx) => { + dispatch({ + type: NOTIFICATION.ADD, + payload: { + type: 'warning', + title: 'Pending transaction rejected', + message: `Transaction with id: ${tx.id} not found.`, + cancelable: true, + actions: [ + { + label: 'OK', + callback: () => { + dispatch({ + type: PENDING.TX_RESOLVED, + tx, + }); }, - ], - }, - }); + }, + ], + }, }); - } + }); } + */ } }; diff --git a/src/actions/WalletActions.js b/src/actions/WalletActions.js index 9126c541..983b86b2 100644 --- a/src/actions/WalletActions.js +++ b/src/actions/WalletActions.js @@ -1,9 +1,7 @@ /* @flow */ - -import { LOCATION_CHANGE } from 'react-router-redux'; import * as WALLET from 'actions/constants/wallet'; -import * as stateUtils from 'reducers/utils'; +import * as reducerUtils from 'reducers/utils'; import type { Device, @@ -81,25 +79,29 @@ export const clearUnavailableDevicesData = (prevState: State, device: Device): T }; -export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const locationChange: boolean = action.type === LOCATION_CHANGE; +export const observe = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { + // ignore actions dispatched from this process + if (action.type === WALLET.SET_SELECTED_DEVICE || action.type === WALLET.UPDATE_SELECTED_DEVICE) { + return; + } const state: State = getState(); + const locationChanged = reducerUtils.observeChanges(prevState.router.location, state.router.location, ['pathname']); + const device = reducerUtils.getSelectedDevice(state); + const selectedDeviceChanged = reducerUtils.observeChanges(state.wallet.selectedDevice, device); + // handle devices state change (from trezor-connect events or location change) - if (locationChange || prevState.devices !== state.devices) { - const device = stateUtils.getSelectedDevice(state); - if (state.wallet.selectedDevice !== device) { - if (device && stateUtils.isSelectedDevice(state.wallet.selectedDevice, device)) { - dispatch({ - type: WALLET.UPDATE_SELECTED_DEVICE, - device, - }); - } else { - dispatch({ - type: WALLET.SET_SELECTED_DEVICE, - device, - }); - } + if (locationChanged || selectedDeviceChanged) { + if (device && reducerUtils.isSelectedDevice(state.wallet.selectedDevice, device)) { + dispatch({ + type: WALLET.UPDATE_SELECTED_DEVICE, + device, + }); + } else { + dispatch({ + type: WALLET.SET_SELECTED_DEVICE, + device, + }); } } }; \ No newline at end of file diff --git a/src/reducers/SelectedAccountReducer.js b/src/reducers/SelectedAccountReducer.js index 68fb56d6..f64ea582 100644 --- a/src/reducers/SelectedAccountReducer.js +++ b/src/reducers/SelectedAccountReducer.js @@ -11,12 +11,18 @@ import type { } from 'flowtype'; export type State = { - location?: string; + location: string; account: ?Account; network: ?Coin; tokens: Array, pending: Array, - discovery: ?Discovery + discovery: ?Discovery, + notification: ?{ + type: string, + title: string, + message?: string, + }, + visible: boolean, }; export const initialState: State = { @@ -26,6 +32,8 @@ export const initialState: State = { tokens: [], pending: [], discovery: null, + notification: null, + visible: false, }; export default (state: State = initialState, action: Action): State => { diff --git a/src/reducers/utils/index.js b/src/reducers/utils/index.js index 9b5ba7f5..c8b8d56f 100644 --- a/src/reducers/utils/index.js +++ b/src/reducers/utils/index.js @@ -128,6 +128,7 @@ export const observeChanges = (prev: ?(Object | Array), current: ?(Object | if (prev instanceof Object && current instanceof Object) { for (let i = 0; i < fields.length; i++) { const key = fields[i]; + if (Array.isArray(prev[key]) && Array.isArray(current[key])) return prev[key].length !== current[key].length; if (prev[key] !== current[key]) return true; } } diff --git a/src/services/WalletService.js b/src/services/WalletService.js index 9455dfbb..881f293b 100644 --- a/src/services/WalletService.js +++ b/src/services/WalletService.js @@ -92,10 +92,10 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa api.dispatch(SendFormActionActions.observe(prevState, action)); // update common values in WallerReducer - api.dispatch(WalletActions.updateSelectedValues(prevState, action)); + api.dispatch(WalletActions.observe(prevState, action)); // update common values in SelectedAccountReducer - api.dispatch(SelectedAccountActions.updateSelectedValues(prevState, action)); + api.dispatch(SelectedAccountActions.observe(prevState, action)); return action; }; From 25083b5f19ee45c7142c61a58880ba5ba0e0c7b4 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 26 Sep 2018 14:11:54 +0200 Subject: [PATCH 03/38] new components --- src/components/notifications/App/Container.js | 0 src/components/notifications/App/index.js | 0 .../Context/components/Account/index.js | 19 +++++++ .../Context/components/Action/index.js | 20 +++++++ .../Context/components/Static/index.js | 17 ++++++ src/components/notifications/Context/index.js | 55 +++++++++++++++++++ 6 files changed, 111 insertions(+) create mode 100644 src/components/notifications/App/Container.js create mode 100644 src/components/notifications/App/index.js create mode 100644 src/components/notifications/Context/components/Account/index.js create mode 100644 src/components/notifications/Context/components/Action/index.js create mode 100644 src/components/notifications/Context/components/Static/index.js create mode 100644 src/components/notifications/Context/index.js diff --git a/src/components/notifications/App/Container.js b/src/components/notifications/App/Container.js new file mode 100644 index 00000000..e69de29b diff --git a/src/components/notifications/App/index.js b/src/components/notifications/App/index.js new file mode 100644 index 00000000..e69de29b diff --git a/src/components/notifications/Context/components/Account/index.js b/src/components/notifications/Context/components/Account/index.js new file mode 100644 index 00000000..d6ef289a --- /dev/null +++ b/src/components/notifications/Context/components/Account/index.js @@ -0,0 +1,19 @@ +/* @flow */ +import * as React from 'react'; +import { Notification } from 'components/Notification'; + +import type { Props } from '../../index'; + +// There could be only one account notification +export default (props: Props) => { + const { notification } = props.selectedAccount; + if (notification) { + if (notification.type === 'backend') { + // special case: backend is down + // TODO: this is a different component with "auto resolve" button + return (); + } + return (); + } + return null; +}; \ No newline at end of file diff --git a/src/components/notifications/Context/components/Action/index.js b/src/components/notifications/Context/components/Action/index.js new file mode 100644 index 00000000..0bf7f0be --- /dev/null +++ b/src/components/notifications/Context/components/Action/index.js @@ -0,0 +1,20 @@ +/* @flow */ +import * as React from 'react'; +import { Notification } from 'components/Notification'; + +import type { Props } from '../../index'; + +export default (props: Props) => { + const { notifications, close } = props; + return notifications.map(n => ( + + )); +}; \ No newline at end of file diff --git a/src/components/notifications/Context/components/Static/index.js b/src/components/notifications/Context/components/Static/index.js new file mode 100644 index 00000000..73e16893 --- /dev/null +++ b/src/components/notifications/Context/components/Static/index.js @@ -0,0 +1,17 @@ +/* @flow */ +import * as React from 'react'; +import { Notification } from 'components/Notification'; + +import type { Props } from '../../index'; + +export default (props: Props) => { + const { location } = props.router; + if (!location) return null; + + const notifications: Array = []; + if (location.state.device) { + notifications.push(); + } + + return notifications; +}; \ No newline at end of file diff --git a/src/components/notifications/Context/index.js b/src/components/notifications/Context/index.js new file mode 100644 index 00000000..786c6ce1 --- /dev/null +++ b/src/components/notifications/Context/index.js @@ -0,0 +1,55 @@ +/* @flow */ +import * as React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; + +import type { MapStateToProps, MapDispatchToProps } from 'react-redux'; +import type { State, Dispatch } from 'flowtype'; + +import { reconnect } from 'actions/DiscoveryActions'; +import * as NotificationActions from 'actions/NotificationActions'; + +import StaticNotifications from './components/Static'; +import AccountNotifications from './components/Account'; +import ActionNotifications from './components/Action'; + +export type StateProps = { + router: $ElementType; + notifications: $ElementType; + selectedAccount: $ElementType; + wallet: $ElementType; + blockchain: $ElementType; + children?: React.Node; +} + +export type DispatchProps = { + close: typeof NotificationActions.close; + blockchainReconnect: typeof reconnect; +} + +export type Props = StateProps & DispatchProps; + +type OwnProps = {}; + +const Notifications = (props: Props) => ( +
+ + + +
+); + +const mapStateToProps: MapStateToProps = (state: State): StateProps => ({ + router: state.router, + notifications: state.notifications, + selectedAccount: state.selectedAccount, + wallet: state.wallet, + blockchain: state.blockchain, +}); + +const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ + close: bindActionCreators(NotificationActions.close, dispatch), + blockchainReconnect: bindActionCreators(reconnect, dispatch), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Notifications); \ No newline at end of file From 2804275ca9f8b89b1854733a31c0e35bc20d5900 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 26 Sep 2018 14:12:05 +0200 Subject: [PATCH 04/38] update old components --- src/views/Wallet/index.js | 6 +- .../Wallet/views/AccountReceive/index.js | 143 ++++----- src/views/Wallet/views/AccountSend/index.js | 289 +++++++++--------- .../Wallet/views/AccountSummary/index.js | 8 +- 4 files changed, 217 insertions(+), 229 deletions(-) diff --git a/src/views/Wallet/index.js b/src/views/Wallet/index.js index 0bf9849a..c78a3020 100644 --- a/src/views/Wallet/index.js +++ b/src/views/Wallet/index.js @@ -12,13 +12,15 @@ import type { State } from 'flowtype'; import Header from 'components/Header'; import Footer from 'components/Footer'; import ModalContainer from 'components/modals'; -import Notifications from 'components/Notification'; +import ContextNotifications from 'components/notifications/Context'; + import Log from 'components/Log'; import LeftNavigation from './components/LeftNavigation/Container'; import TopNavigationAccount from './components/TopNavigationAccount'; import TopNavigationDeviceSettings from './components/TopNavigationDeviceSettings'; + type WalletContainerProps = { wallet: $ElementType, children?: React.Node @@ -89,7 +91,7 @@ const Wallet = (props: WalletContainerProps) => ( - + { props.children } diff --git a/src/views/Wallet/views/AccountReceive/index.js b/src/views/Wallet/views/AccountReceive/index.js index c3864b60..1a28338a 100644 --- a/src/views/Wallet/views/AccountReceive/index.js +++ b/src/views/Wallet/views/AccountReceive/index.js @@ -154,86 +154,75 @@ const AccountReceive = (props: Props) => { const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified; return ( - - - Receive Ethereum or tokens - - {((addressVerified || addressUnverified) && !isAddressVerifying) && ( - - {addressUnverified ? ( - - Unverified address. -
- {device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.'} -
- ) : ( - - {device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.'} - - )} - - )} - > - props.showAddress(account.addressPath)} - > - - -
- )} - {address} - - {isAddressVerifying && ( - - - - - Check address on your Trezor - - - )} - {/* {isAddressVerifying && ( - {account.network} account #{account.index + 1} - )} */} - {(addressVerified || addressUnverified) && ( - - )} - {!(addressVerified || addressUnverified) && ( - + Receive Ethereum or tokens + + {isAddressVerifying && ( + Confirm address on TREZOR + )} + {((addressVerified || addressUnverified) && !isAddressVerifying) && ( + + {addressUnverified ? ( + + Unverified address. +
+ {device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.'} +
+ ) : ( + + {device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.'} + + )} + + )} + > + props.showAddress(account.addressPath)} - isDisabled={device.connected && !discovery.completed} > - - Show full address -
- )} -
-
-
+ + + )} + {address} + + {isAddressVerifying && ( + {account.network} account #{account.index + 1} + )} + {(addressVerified || addressUnverified) && ( + + )} + {!(addressVerified || addressUnverified) && ( + props.showAddress(account.addressPath)} + isDisabled={device.connected && !discovery.completed} + > + + Show full address + + )} + + ); }; diff --git a/src/views/Wallet/views/AccountSend/index.js b/src/views/Wallet/views/AccountSend/index.js index e634c4aa..6da252b5 100644 --- a/src/views/Wallet/views/AccountSend/index.js +++ b/src/views/Wallet/views/AccountSend/index.js @@ -12,7 +12,6 @@ import { FONT_SIZE, FONT_WEIGHT, TRANSITION } from 'config/variables'; import colors from 'config/colors'; import P from 'components/Paragraph'; import { H2 } from 'components/Heading'; -import SelectedAccount from 'views/Wallet/components/SelectedAccount'; import type { Token } from 'flowtype'; import AdvancedForm from './components/AdvancedForm'; import PendingTransactions from './components/PendingTransactions'; @@ -252,157 +251,155 @@ const AccountSend = (props: Props) => { const isAdvancedSettingsHidden = !advanced; return ( - - - Send Ethereum or tokens - - onAddressChange(event.target.value)} - /> - - - - - Amount - {(isCurrentCurrencyToken && selectedToken) && ( - You have: {selectedTokenBalance} {selectedToken.symbol} + + Send Ethereum or tokens + + onAddressChange(event.target.value)} + /> + + + + + Amount + {(isCurrentCurrencyToken && selectedToken) && ( + You have: {selectedTokenBalance} {selectedToken.symbol} + )} + + )} + value={amount} + onChange={event => onAmountChange(event.target.value)} + bottomText={errors.amount || warnings.amount || infos.amount} + sideAddons={[ + ( + onSetMax()} + isActive={setMax} + > + {!setMax && ( + )} - - )} - value={amount} - onChange={event => onAmountChange(event.target.value)} - bottomText={errors.amount || warnings.amount || infos.amount} - sideAddons={[ - ( - onSetMax()} - isActive={setMax} - > - {!setMax && ( - - )} - {setMax && ( - - )} - Set max - - ), - ( - - ), - ]} - /> - - - - - Fee - {gasPriceNeedsUpdate && ( - - - Recommended fees updated. Click here to use them - - )} - - ( + +

{option.value}

+

{option.label}

+
+ )} + /> +
+ + + + Advanced settings + -
+ - - onSend()} > - Advanced settings - - - - {isAdvancedSettingsHidden && ( - onSend()} - > - {sendButtonText} - - )} - - - {advanced && ( - - onSend()} - > - {sendButtonText} - - + {sendButtonText} + )} + - {props.selectedAccount.pending.length > 0 && ( - - )} -
-
+ {advanced && ( + + onSend()} + > + {sendButtonText} + + + )} + + {props.selectedAccount.pending.length > 0 && ( + + )} + ); }; diff --git a/src/views/Wallet/views/AccountSummary/index.js b/src/views/Wallet/views/AccountSummary/index.js index f5e7b369..d3aa3436 100644 --- a/src/views/Wallet/views/AccountSummary/index.js +++ b/src/views/Wallet/views/AccountSummary/index.js @@ -11,7 +11,6 @@ import Tooltip from 'components/Tooltip'; import CoinLogo from 'components/images/CoinLogo'; import * as stateUtils from 'reducers/utils'; -import SelectedAccount from 'views/Wallet/components/SelectedAccount'; import Link from 'components/Link'; import AccountBalance from './components/AccountBalance'; import AddedToken from './components/AddedToken'; @@ -65,17 +64,18 @@ const AccountSummary = (props: Props) => { network, tokens, pending, + visible, } = props.selectedAccount; // flow - if (!device || !account || !network) return ; + if (!device || !account || !network || !visible) return null; const explorerLink: string = `${network.explorer.address}${account.address}`; const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol); const balance: string = new BigNumber(account.balance).minus(pendingAmount).toString(10); return ( - +
@@ -155,7 +155,7 @@ const AccountSummary = (props: Props) => { /> ))} - +
); }; From 5df872eec10650cf709bbfae46baf8112faff376 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 26 Sep 2018 18:02:39 +0200 Subject: [PATCH 05/38] using React.Fragment --- src/components/notifications/Context/index.js | 4 ++-- src/views/Wallet/views/AccountReceive/index.js | 2 -- src/views/Wallet/views/AccountSummary/index.js | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/notifications/Context/index.js b/src/components/notifications/Context/index.js index 786c6ce1..e28bba0b 100644 --- a/src/components/notifications/Context/index.js +++ b/src/components/notifications/Context/index.js @@ -32,11 +32,11 @@ export type Props = StateProps & DispatchProps; type OwnProps = {}; const Notifications = (props: Props) => ( -
+ -
+ ); const mapStateToProps: MapStateToProps = (state: State): StateProps => ({ diff --git a/src/views/Wallet/views/AccountReceive/index.js b/src/views/Wallet/views/AccountReceive/index.js index 1a28338a..d76a1cab 100644 --- a/src/views/Wallet/views/AccountReceive/index.js +++ b/src/views/Wallet/views/AccountReceive/index.js @@ -10,8 +10,6 @@ import colors from 'config/colors'; import Tooltip from 'components/Tooltip'; import { QRCode } from 'react-qr-svg'; -import SelectedAccount from 'views/Wallet/components/SelectedAccount'; - import { FONT_SIZE, FONT_WEIGHT, FONT_FAMILY } from 'config/variables'; import type { Props } from './Container'; diff --git a/src/views/Wallet/views/AccountSummary/index.js b/src/views/Wallet/views/AccountSummary/index.js index d3aa3436..6e78da1d 100644 --- a/src/views/Wallet/views/AccountSummary/index.js +++ b/src/views/Wallet/views/AccountSummary/index.js @@ -75,7 +75,7 @@ const AccountSummary = (props: Props) => { const balance: string = new BigNumber(account.balance).minus(pendingAmount).toString(10); return ( -
+ @@ -155,7 +155,7 @@ const AccountSummary = (props: Props) => { /> ))} -
+ ); }; From b30c303d5a008bc7903bbd0f2a927bab0d6cbdea Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 26 Sep 2018 18:46:49 +0200 Subject: [PATCH 06/38] rename constant --- src/actions/PendingTxActions.js | 2 +- src/actions/Web3Actions.js | 2 +- src/actions/constants/pendingTx.js | 2 +- src/reducers/PendingTxReducer.js | 2 +- src/services/LocalStorageService.js | 5 ++++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/actions/PendingTxActions.js b/src/actions/PendingTxActions.js index b4f398d3..13eec782 100644 --- a/src/actions/PendingTxActions.js +++ b/src/actions/PendingTxActions.js @@ -15,7 +15,7 @@ export type PendingTxAction = { tx: PendingTx, receipt?: Object, } | { - type: typeof PENDING.TX_NOT_FOUND, + type: typeof PENDING.TX_REJECTED, tx: PendingTx, } | { type: typeof PENDING.TX_TOKEN_ERROR, diff --git a/src/actions/Web3Actions.js b/src/actions/Web3Actions.js index a8d9491c..53c26a3b 100644 --- a/src/actions/Web3Actions.js +++ b/src/actions/Web3Actions.js @@ -139,7 +139,7 @@ export const resolvePendingTransactions = (network: string): PromiseAction const status = await instance.web3.eth.getTransaction(tx.id); if (!status) { dispatch({ - type: PENDING.TX_NOT_FOUND, + type: PENDING.TX_REJECTED, tx, }); } else { diff --git a/src/actions/constants/pendingTx.js b/src/actions/constants/pendingTx.js index 04f8ef91..08376cc4 100644 --- a/src/actions/constants/pendingTx.js +++ b/src/actions/constants/pendingTx.js @@ -4,5 +4,5 @@ export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage'; export const ADD: 'pending__add' = 'pending__add'; export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved'; -export const TX_NOT_FOUND: 'pending__tx_not_found' = 'pending__tx_not_found'; +export const TX_REJECTED: 'pending__tx_rejected' = 'pending__tx_rejected'; export const TX_TOKEN_ERROR: 'pending__tx_token_error' = 'pending__tx_token_error'; \ No newline at end of file diff --git a/src/reducers/PendingTxReducer.js b/src/reducers/PendingTxReducer.js index 9f4ab635..f89243ea 100644 --- a/src/reducers/PendingTxReducer.js +++ b/src/reducers/PendingTxReducer.js @@ -65,7 +65,7 @@ export default function pending(state: State = initialState, action: Action): St // return add(state, action.payload); case PENDING.TX_RESOLVED: return remove(state, action.tx.id); - case PENDING.TX_NOT_FOUND: + case PENDING.TX_REJECTED: return reject(state, action.tx.id); case PENDING.FROM_STORAGE: diff --git a/src/services/LocalStorageService.js b/src/services/LocalStorageService.js index 8bc66798..9a9af858 100644 --- a/src/services/LocalStorageService.js +++ b/src/services/LocalStorageService.js @@ -110,9 +110,12 @@ const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: Middlewar case SEND.TX_COMPLETE: case PENDING.TX_RESOLVED: - case PENDING.TX_NOT_FOUND: + case PENDING.TX_REJECTED: save(api.dispatch, api.getState); break; + + default: + return action; } return action; From 61d9713b0bc1568aeeab6aae190316ded759e31d Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 26 Sep 2018 18:48:45 +0200 Subject: [PATCH 07/38] removed comment --- .../Context/components/Account/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/components/notifications/Context/components/Account/index.js b/src/components/notifications/Context/components/Account/index.js index d6ef289a..9f169a31 100644 --- a/src/components/notifications/Context/components/Account/index.js +++ b/src/components/notifications/Context/components/Account/index.js @@ -11,6 +11,22 @@ export default (props: Props) => { if (notification.type === 'backend') { // special case: backend is down // TODO: this is a different component with "auto resolve" button + /* + return ( + { + await props.blockchainReconnect(network.network); + }, + }] + } + /> + ); + */ return (); } return (); From f0e0b4fee23a60ef4005710a02aa9de574d695f3 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 26 Sep 2018 19:35:36 +0200 Subject: [PATCH 08/38] rename selectedAccount.visible to shouldRender --- src/actions/SelectedAccountActions.js | 65 +++++-------------- src/reducers/SelectedAccountReducer.js | 4 +- .../Wallet/views/AccountReceive/index.js | 3 +- src/views/Wallet/views/AccountSend/index.js | 5 +- .../Wallet/views/AccountSummary/index.js | 4 +- 5 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/actions/SelectedAccountActions.js b/src/actions/SelectedAccountActions.js index a5f1a91d..02826e00 100644 --- a/src/actions/SelectedAccountActions.js +++ b/src/actions/SelectedAccountActions.js @@ -22,10 +22,10 @@ export type SelectedAccountAction = { }; type AccountStatus = { - type: string; - title: string; - message?: string; - visible: boolean; + type: string; // notification type + title: string; // notification title + message?: string; // notification message + shouldRender: boolean; // should render account page } export const dispose = (): Action => ({ @@ -38,7 +38,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return { type: 'info', title: 'Loading device...', - visible: false, + shouldRender: false, }; } @@ -53,7 +53,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return { type: 'info', title: 'Loading account state...', - visible: false, + shouldRender: false, }; } @@ -62,7 +62,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return { type: 'backend', title: 'Backend is not connected', - visible: false, + shouldRender: false, }; } @@ -75,7 +75,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return { type: 'info', title: 'Loading accounts...', - visible: false, + shouldRender: false, }; } // case 2: device is unavailable (created with different passphrase settings) account cannot be accessed @@ -83,7 +83,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): type: 'info', title: `Device ${device.instanceLabel} is unavailable`, message: 'Change passphrase settings to use this device', - visible: false, + shouldRender: false, }; } @@ -92,7 +92,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): type: 'info', title: `Device ${device.instanceLabel} is disconnected`, message: 'Connect device to load accounts', - visible: false, + shouldRender: false, }; } @@ -101,7 +101,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return { type: 'warning', title: 'Account does not exist', - visible: false, + shouldRender: false, }; } @@ -109,7 +109,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return { type: 'info', title: 'Loading accounts...', - visible: false, + shouldRender: false, }; } @@ -118,7 +118,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return { type: 'info', title: `Device ${device.instanceLabel} is disconnected`, - visible: true, + shouldRender: true, }; } if (!device.available) { @@ -126,7 +126,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): type: 'info', title: `Device ${device.instanceLabel} is unavailable`, message: 'Change passphrase settings to use this device', - visible: true, + shouldRender: true, }; } @@ -135,7 +135,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return { type: 'info', title: 'Loading accounts...', - visible: true, + shouldRender: true, }; } @@ -178,50 +178,21 @@ export const observe = (prevState: State, action: Action): AsyncAction => async tokens, pending, notification: null, - visible: false, + shouldRender: false, }; // get "selectedAccount" status from newState const status = getAccountStatus(state, newState); newState.notification = status || null; - newState.visible = status ? status.visible : true; + newState.shouldRender = status ? status.shouldRender : true; // check if newState is different than previous state - const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, ['location', 'account', 'network', 'discovery', 'tokens', 'pending', 'status', 'visible']); + const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, ['location', 'account', 'network', 'discovery', 'tokens', 'pending', 'notification', 'shouldRender']); if (stateChanged) { // update values in reducer dispatch({ type: ACCOUNT.UPDATE_SELECTED_ACCOUNT, payload: newState, }); - - // TODO: move this to send account actions - /* - if (location.state.send) { - const rejectedTxs = pending.filter(tx => tx.rejected); - rejectedTxs.forEach((tx) => { - dispatch({ - type: NOTIFICATION.ADD, - payload: { - type: 'warning', - title: 'Pending transaction rejected', - message: `Transaction with id: ${tx.id} not found.`, - cancelable: true, - actions: [ - { - label: 'OK', - callback: () => { - dispatch({ - type: PENDING.TX_RESOLVED, - tx, - }); - }, - }, - ], - }, - }); - }); - } - */ } }; diff --git a/src/reducers/SelectedAccountReducer.js b/src/reducers/SelectedAccountReducer.js index f64ea582..1b400668 100644 --- a/src/reducers/SelectedAccountReducer.js +++ b/src/reducers/SelectedAccountReducer.js @@ -22,7 +22,7 @@ export type State = { title: string, message?: string, }, - visible: boolean, + shouldRender: boolean, }; export const initialState: State = { @@ -33,7 +33,7 @@ export const initialState: State = { pending: [], discovery: null, notification: null, - visible: false, + shouldRender: false, }; export default (state: State = initialState, action: Action): State => { diff --git a/src/views/Wallet/views/AccountReceive/index.js b/src/views/Wallet/views/AccountReceive/index.js index d76a1cab..9e80b195 100644 --- a/src/views/Wallet/views/AccountReceive/index.js +++ b/src/views/Wallet/views/AccountReceive/index.js @@ -133,9 +133,10 @@ const AccountReceive = (props: Props) => { const { account, discovery, + shouldRender, } = props.selectedAccount; - if (!device || !account || !discovery) return null; + if (!device || !account || !discovery || !shouldRender) return null; const { addressVerified, diff --git a/src/views/Wallet/views/AccountSend/index.js b/src/views/Wallet/views/AccountSend/index.js index 6da252b5..7007d92d 100644 --- a/src/views/Wallet/views/AccountSend/index.js +++ b/src/views/Wallet/views/AccountSend/index.js @@ -190,6 +190,7 @@ const AccountSend = (props: Props) => { network, discovery, tokens, + shouldRender, } = props.selectedAccount; const { address, @@ -218,6 +219,8 @@ const AccountSend = (props: Props) => { onSend, } = props.sendFormActions; + if (!device || !account || !discovery || !network || !shouldRender) return null; + const isCurrentCurrencyToken = networkSymbol !== currency; let selectedTokenBalance = 0; @@ -226,8 +229,6 @@ const AccountSend = (props: Props) => { selectedTokenBalance = selectedToken.balance; } - if (!device || !account || !discovery || !network) return null; - let isSendButtonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending; let sendButtonText: string = 'Send'; if (networkSymbol !== currency && amount.length > 0 && !errors.amount) { diff --git a/src/views/Wallet/views/AccountSummary/index.js b/src/views/Wallet/views/AccountSummary/index.js index 6e78da1d..405428b9 100644 --- a/src/views/Wallet/views/AccountSummary/index.js +++ b/src/views/Wallet/views/AccountSummary/index.js @@ -64,11 +64,11 @@ const AccountSummary = (props: Props) => { network, tokens, pending, - visible, + shouldRender, } = props.selectedAccount; // flow - if (!device || !account || !network || !visible) return null; + if (!device || !account || !network || !shouldRender) return null; const explorerLink: string = `${network.explorer.address}${account.address}`; const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol); From e3816be8d2f2ca5600e7fabeead3023a78cb9aaa Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 26 Sep 2018 19:45:22 +0200 Subject: [PATCH 09/38] fix for reducerUtils.observeChanges --- src/actions/SelectedAccountActions.js | 1 - src/reducers/utils/index.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/actions/SelectedAccountActions.js b/src/actions/SelectedAccountActions.js index 02826e00..f10d1414 100644 --- a/src/actions/SelectedAccountActions.js +++ b/src/actions/SelectedAccountActions.js @@ -185,7 +185,6 @@ export const observe = (prevState: State, action: Action): AsyncAction => async const status = getAccountStatus(state, newState); newState.notification = status || null; newState.shouldRender = status ? status.shouldRender : true; - // check if newState is different than previous state const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, ['location', 'account', 'network', 'discovery', 'tokens', 'pending', 'notification', 'shouldRender']); if (stateChanged) { diff --git a/src/reducers/utils/index.js b/src/reducers/utils/index.js index c8b8d56f..8a3ded63 100644 --- a/src/reducers/utils/index.js +++ b/src/reducers/utils/index.js @@ -128,7 +128,7 @@ export const observeChanges = (prev: ?(Object | Array), current: ?(Object | if (prev instanceof Object && current instanceof Object) { for (let i = 0; i < fields.length; i++) { const key = fields[i]; - if (Array.isArray(prev[key]) && Array.isArray(current[key])) return prev[key].length !== current[key].length; + if (Array.isArray(prev[key]) && Array.isArray(current[key]) && prev[key].length !== current[key].length) return true; if (prev[key] !== current[key]) return true; } } From 0fbbdf7b7181567002bafccd49b4b62f24a3d9fb Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Mon, 1 Oct 2018 11:59:31 +0200 Subject: [PATCH 10/38] changed "observeChanges" utility method --- src/actions/SelectedAccountActions.js | 55 ++++++++++++++--------- src/actions/SendFormActions.js | 27 ++++++----- src/actions/WalletActions.js | 28 ++++++++---- src/reducers/utils/index.js | 64 +++++++++++++++++++++------ src/services/WalletService.js | 18 ++++---- 5 files changed, 129 insertions(+), 63 deletions(-) diff --git a/src/actions/SelectedAccountActions.js b/src/actions/SelectedAccountActions.js index f10d1414..53e8b62f 100644 --- a/src/actions/SelectedAccountActions.js +++ b/src/actions/SelectedAccountActions.js @@ -1,11 +1,15 @@ /* @flow */ - +import { LOCATION_CHANGE } from 'react-router-redux'; +import * as WALLET from 'actions/constants/wallet'; import * as ACCOUNT from 'actions/constants/account'; +import * as DISCOVERY from 'actions/constants/discovery'; +import * as TOKEN from 'actions/constants/token'; +import * as PENDING from 'actions/constants/pendingTx'; + import * as reducerUtils from 'reducers/utils'; -import { initialState } from 'reducers/SelectedAccountReducer'; import type { - AsyncAction, + PayloadAction, Action, GetState, Dispatch, @@ -61,7 +65,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): if (blockchain && !blockchain.connected) { return { type: 'backend', - title: 'Backend is not connected', + title: `${network.name} backend is not connected`, shouldRender: false, }; } @@ -142,25 +146,29 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState): return null; }; -export const observe = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { - // ignore actions dispatched from this process - if (action.type === ACCOUNT.UPDATE_SELECTED_ACCOUNT) { - return; - } +// list of all actions which has influence on "selectedAccount" reducer +// other actions will be ignored +const actions = [ + LOCATION_CHANGE, + WALLET.SET_SELECTED_DEVICE, + WALLET.UPDATE_SELECTED_DEVICE, + ...Object.values(ACCOUNT).filter(v => typeof v === 'string' && v !== ACCOUNT.UPDATE_SELECTED_ACCOUNT), // exported values got unwanted "__esModule: true" as first element + ...Object.values(DISCOVERY).filter(v => typeof v === 'string'), + ...Object.values(TOKEN).filter(v => typeof v === 'string'), + ...Object.values(PENDING).filter(v => typeof v === 'string'), +]; + +/* +* Called from WalletService +*/ +export const observe = (prevState: State, action: Action): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { + // ignore not listed actions + if (actions.indexOf(action.type) < 0) return false; const state: State = getState(); const { location } = state.router; - if (!location.state.account) { - // displayed route is not an account route - if (state.selectedAccount.location.length > 1) { - // reset "selectedAccount" reducer to default - dispatch({ - type: ACCOUNT.UPDATE_SELECTED_ACCOUNT, - payload: initialState, - }); - } - return; - } + // displayed route is not an account route + if (!location.state.account) return false; // get new values for selected account const account = reducerUtils.getSelectedAccount(state); @@ -186,7 +194,11 @@ export const observe = (prevState: State, action: Action): AsyncAction => async newState.notification = status || null; newState.shouldRender = status ? status.shouldRender : true; // check if newState is different than previous state - const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, ['location', 'account', 'network', 'discovery', 'tokens', 'pending', 'notification', 'shouldRender']); + const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, { + account: ['balance', 'nonce'], + discovery: ['accountIndex', 'interrupted', 'completed', 'waitingForBlockchain', 'waitingForDevice'], + }); + if (stateChanged) { // update values in reducer dispatch({ @@ -194,4 +206,5 @@ export const observe = (prevState: State, action: Action): AsyncAction => async payload: newState, }); } + return stateChanged; }; diff --git a/src/actions/SendFormActions.js b/src/actions/SendFormActions.js index 1302d611..f26664f3 100644 --- a/src/actions/SendFormActions.js +++ b/src/actions/SendFormActions.js @@ -2,6 +2,7 @@ import TrezorConnect from 'trezor-connect'; import BigNumber from 'bignumber.js'; +import * as ACCOUNT from 'actions/constants/account'; import * as NOTIFICATION from 'actions/constants/notification'; import * as SEND from 'actions/constants/send'; import * as WEB3 from 'actions/constants/web3'; @@ -45,10 +46,21 @@ export type SendFormAction = { type: typeof SEND.TOGGLE_ADVANCED | typeof SEND.TX_SENDING | typeof SEND.TX_ERROR, } | SendTxAction; +// list of all actions which has influence on "sendForm" reducer +// other actions will be ignored +const actions = [ + ACCOUNT.UPDATE_SELECTED_ACCOUNT, + WEB3.GAS_PRICE_UPDATED, + ...Object.values(SEND).filter(v => typeof v === 'string'), +]; + /* -* Called from WalletService on EACH action +* Called from WalletService */ export const observe = (prevState: ReducersState, action: Action): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + // ignore not listed actions + if (actions.indexOf(action.type) < 0) return; + const currentState = getState(); // do not proceed if it's not "send" url if (!currentState.router.location.state.send) return; @@ -75,16 +87,9 @@ export const observe = (prevState: ReducersState, action: Action): ThunkAction = let shouldUpdate: boolean = false; // check if "selectedAccount" reducer changed - const selectedAccountChanged = reducerUtils.observeChanges(prevState.selectedAccount, currentState.selectedAccount, ['account', 'tokens', 'pending']); - if (selectedAccountChanged) { - // double check - // there are only few fields that we are interested in - // check them to avoid unnecessary calculation and validation - const accountChanged = reducerUtils.observeChanges(prevState.selectedAccount.account, currentState.selectedAccount.account, ['balance', 'nonce']); - const tokensChanged = reducerUtils.observeChanges(prevState.selectedAccount.tokens, currentState.selectedAccount.tokens); - const pendingChanged = reducerUtils.observeChanges(prevState.selectedAccount.pending, currentState.selectedAccount.pending); - shouldUpdate = accountChanged || tokensChanged || pendingChanged; - } + shouldUpdate = reducerUtils.observeChanges(prevState.selectedAccount, currentState.selectedAccount, { + account: ['balance', 'nonce'], + }); // check if "sendForm" reducer changed if (!shouldUpdate) { diff --git a/src/actions/WalletActions.js b/src/actions/WalletActions.js index 983b86b2..0d572f4f 100644 --- a/src/actions/WalletActions.js +++ b/src/actions/WalletActions.js @@ -1,5 +1,7 @@ /* @flow */ +import { LOCATION_CHANGE } from 'react-router-redux'; +import { DEVICE } from 'trezor-connect'; import * as WALLET from 'actions/constants/wallet'; import * as reducerUtils from 'reducers/utils'; @@ -8,7 +10,7 @@ import type { TrezorDevice, RouterLocationState, ThunkAction, - AsyncAction, + PayloadAction, Action, Dispatch, GetState, @@ -78,18 +80,26 @@ export const clearUnavailableDevicesData = (prevState: State, device: Device): T } }; +// list of all actions which has influence on "selectedDevice" field in "wallet" reducer +// other actions will be ignored +const actions = [ + LOCATION_CHANGE, + ...Object.values(DEVICE).filter(v => typeof v === 'string'), +]; + +/* +* Called from WalletService +*/ +export const observe = (prevState: State, action: Action): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { + // ignore not listed actions + if (actions.indexOf(action.type) < 0) return false; -export const observe = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { - // ignore actions dispatched from this process - if (action.type === WALLET.SET_SELECTED_DEVICE || action.type === WALLET.UPDATE_SELECTED_DEVICE) { - return; - } const state: State = getState(); - const locationChanged = reducerUtils.observeChanges(prevState.router.location, state.router.location, ['pathname']); + const locationChanged = reducerUtils.observeChanges(prevState.router.location, state.router.location); const device = reducerUtils.getSelectedDevice(state); const selectedDeviceChanged = reducerUtils.observeChanges(state.wallet.selectedDevice, device); - + // handle devices state change (from trezor-connect events or location change) if (locationChanged || selectedDeviceChanged) { if (device && reducerUtils.isSelectedDevice(state.wallet.selectedDevice, device)) { @@ -103,5 +113,7 @@ export const observe = (prevState: State, action: Action): AsyncAction => async device, }); } + return true; } + return false; }; \ No newline at end of file diff --git a/src/reducers/utils/index.js b/src/reducers/utils/index.js index 8a3ded63..d3a8e740 100644 --- a/src/reducers/utils/index.js +++ b/src/reducers/utils/index.js @@ -116,22 +116,58 @@ export const getWeb3 = (state: State): ?Web3Instance => { return state.web3.find(w3 => w3.network === locationState.network); }; -export const observeChanges = (prev: ?(Object | Array), current: ?(Object | Array), fields?: Array): boolean => { - if (prev !== current) { - // 1. one of the objects is null/undefined - if (!prev || !current) return true; - // 2. object are Arrays and they have different length - if (Array.isArray(prev) && Array.isArray(current)) return prev.length !== current.length; - // 3. no nested field to check - if (!Array.isArray(fields)) return true; - // 4. validate nested field - if (prev instanceof Object && current instanceof Object) { - for (let i = 0; i < fields.length; i++) { - const key = fields[i]; - if (Array.isArray(prev[key]) && Array.isArray(current[key]) && prev[key].length !== current[key].length) return true; - if (prev[key] !== current[key]) return true; +export const observeChanges = (prev: ?Object, current: ?Object, filter?: {[k: string]: Array}): boolean => { + // 1. both objects are the same (solves simple types like string, boolean and number) + if (prev === current) return false; + // 2. one of the objects is null/undefined + if (!prev || !current) return true; + + const prevType = Object.prototype.toString.call(current); + const currentType = Object.prototype.toString.call(current); + // 3. one of the objects has different type then other + if (prevType !== currentType) return true; + + if (currentType === '[object Array]') { + // 4. Array length is different + if (prev.length !== current.length) return true; + // observe array recursive + for (let i = 0; i < current.length; i++) { + if (observeChanges(prev[i], current[i], filter)) return true; + } + } else if (currentType === '[object Object]') { + const prevKeys = Object.keys(prev); + const currentKeys = Object.keys(prev); + // 5. simple validation of keys length + if (prevKeys.length !== currentKeys.length) return true; + + // 6. "prev" has keys which "current" doesn't have + const prevDifference = prevKeys.find(k => currentKeys.indexOf(k) < 0); + if (prevDifference) return true; + + // 7. "current" has keys which "prev" doesn't have + const currentDifference = currentKeys.find(k => prevKeys.indexOf(k) < 0); + if (currentDifference) return true; + + // 8. observe every key recursive + for (let i = 0; i < currentKeys.length; i++) { + const key = currentKeys[i]; + if (filter && filter.hasOwnProperty(key) && prev[key] && current[key]) { + const prevFiltered = {}; + const currentFiltered = {}; + for (let i2 = 0; i2 < filter[key].length; i2++) { + const field = filter[key][i2]; + prevFiltered[field] = prev[key][field]; + currentFiltered[field] = current[key][field]; + } + if (observeChanges(prevFiltered, currentFiltered)) return true; + } else if (observeChanges(prev[key], current[key])) { + return true; } } + } else if (prev !== current) { + // solve simple types like string, boolean and number + return true; } + return false; }; diff --git a/src/services/WalletService.js b/src/services/WalletService.js index 881f293b..76db7638 100644 --- a/src/services/WalletService.js +++ b/src/services/WalletService.js @@ -21,7 +21,7 @@ import type { /** * Middleware */ -const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => (action: Action): Action => { +const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => async (action: Action): Promise => { const prevState = api.getState(); // Application live cycle starts HERE! @@ -88,14 +88,14 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa api.dispatch(NotificationActions.clear(prevLocation.state, currentLocation.state)); } - // observe send form props changes - api.dispatch(SendFormActionActions.observe(prevState, action)); - - // update common values in WallerReducer - api.dispatch(WalletActions.observe(prevState, action)); - - // update common values in SelectedAccountReducer - api.dispatch(SelectedAccountActions.observe(prevState, action)); + // observe common values in WallerReducer + if (!await api.dispatch(WalletActions.observe(prevState, action))) { + // if "selectedDevice" didn't change observe common values in SelectedAccountReducer + if (!await api.dispatch(SelectedAccountActions.observe(prevState, action))) { + // if "selectedAccount" didn't change observe send form props changes + api.dispatch(SendFormActionActions.observe(prevState, action)); + } + } return action; }; From 9fcd81924e5300c2c0602cce54fd9cfa1fc590db Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Mon, 1 Oct 2018 12:00:00 +0200 Subject: [PATCH 11/38] comment sticy notification example --- .../notifications/Context/components/Static/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/notifications/Context/components/Static/index.js b/src/components/notifications/Context/components/Static/index.js index 73e16893..47d30b24 100644 --- a/src/components/notifications/Context/components/Static/index.js +++ b/src/components/notifications/Context/components/Static/index.js @@ -9,9 +9,9 @@ export default (props: Props) => { if (!location) return null; const notifications: Array = []; - if (location.state.device) { - notifications.push(); - } + // if (location.state.device) { + // notifications.push(); + // } return notifications; }; \ No newline at end of file From 7375f70a18a0cc5750861dd4e4e13bb68d97706b Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Mon, 1 Oct 2018 12:00:28 +0200 Subject: [PATCH 12/38] added backend notification --- .../Context/components/Account/index.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/notifications/Context/components/Account/index.js b/src/components/notifications/Context/components/Account/index.js index 9f169a31..fe42bbd1 100644 --- a/src/components/notifications/Context/components/Account/index.js +++ b/src/components/notifications/Context/components/Account/index.js @@ -11,23 +11,24 @@ export default (props: Props) => { if (notification.type === 'backend') { // special case: backend is down // TODO: this is a different component with "auto resolve" button - /* + return ( { - await props.blockchainReconnect(network.network); + await props.blockchainReconnect('trop'); }, }] } /> ); - */ - return (); + + // return (); } return (); } From 7083c6e0d1269b0c29987ab13d807186458ab679 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Mon, 1 Oct 2018 12:02:54 +0200 Subject: [PATCH 13/38] add "reject" method to PendingTxActions (not implemented yet) --- src/actions/PendingTxActions.js | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/actions/PendingTxActions.js b/src/actions/PendingTxActions.js index 13eec782..a6377171 100644 --- a/src/actions/PendingTxActions.js +++ b/src/actions/PendingTxActions.js @@ -2,8 +2,13 @@ import * as PENDING from 'actions/constants/pendingTx'; + +import type { + Action, ThunkAction, GetState, Dispatch, +} from 'flowtype'; import type { State, PendingTx } from 'reducers/PendingTxReducer'; + export type PendingTxAction = { type: typeof PENDING.FROM_STORAGE, payload: State @@ -20,4 +25,29 @@ export type PendingTxAction = { } | { type: typeof PENDING.TX_TOKEN_ERROR, tx: PendingTx, -} \ No newline at end of file +} + +export const reject = (tx: PendingTx): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + /* + dispatch({ + type: NOTIFICATION.ADD, + payload: { + type: 'warning', + title: 'Pending transaction rejected', + message: `Transaction with id: ${tx.id} not found.`, + cancelable: true, + actions: [ + { + label: 'OK', + callback: () => { + dispatch({ + type: PENDING.TX_RESOLVED, + tx, + }); + }, + }, + ], + }, + }); + */ +}; \ No newline at end of file From 740e7ef473dbcc7ecd7772144681f0a93cf63772 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Mon, 1 Oct 2018 15:44:05 +0200 Subject: [PATCH 14/38] fixed conflicts in AccountReceive component --- .../Wallet/views/AccountReceive/index.js | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/views/Wallet/views/AccountReceive/index.js b/src/views/Wallet/views/AccountReceive/index.js index 9e80b195..202ceee1 100644 --- a/src/views/Wallet/views/AccountReceive/index.js +++ b/src/views/Wallet/views/AccountReceive/index.js @@ -133,10 +133,9 @@ const AccountReceive = (props: Props) => { const { account, discovery, - shouldRender, } = props.selectedAccount; - if (!device || !account || !discovery || !shouldRender) return null; + if (!device || !account || !discovery) return null; const { addressVerified, @@ -158,9 +157,6 @@ const AccountReceive = (props: Props) => { - {isAddressVerifying && ( - Confirm address on TREZOR - )} {((addressVerified || addressUnverified) && !isAddressVerifying) && ( { >{address} {isAddressVerifying && ( - {account.network} account #{account.index + 1} + + + + + Check address on your Trezor + + )} + {/* {isAddressVerifying && ( + {account.network} account #{account.index + 1} + )} */} {(addressVerified || addressUnverified) && ( { ); }; -export default AccountReceive; +export default AccountReceive; \ No newline at end of file From b7c1ef59625e1aac926c03dcae1ea53b3035528a Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Mon, 1 Oct 2018 15:12:54 +0200 Subject: [PATCH 15/38] Added gitlab config --- .gitlab-ci.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..5c0f6536 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,20 @@ +image: node:8 + +before_script: + - yarn install + +cache: + paths: + - node_modules/ + +flow: + script: + - yarn run flow + +test:lint: + script: + - yarn run lint + +test:unit: + script: + - yarn run test From 6fbb76b165a9dfc80361c6f4cebd2184fadca1b5 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Mon, 1 Oct 2018 15:27:05 +0200 Subject: [PATCH 16/38] Fixed notification style without button --- src/components/Notification/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Notification/index.js b/src/components/Notification/index.js index 49953fff..4951e479 100644 --- a/src/components/Notification/index.js +++ b/src/components/Notification/index.js @@ -37,6 +37,7 @@ const Wrapper = styled.div` background: ${colors.TEXT_SECONDARY}; padding: 24px 48px 24px 24px; display: flex; + min-height: 90px; flex-direction: row; text-align: left; From 2c2fdf4913b7439b1e7c6358ff4292e9379dc6df Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Mon, 1 Oct 2018 15:54:35 +0200 Subject: [PATCH 17/38] Better style for notifications --- src/components/Notification/index.js | 47 +++++++++++++++++++--------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/components/Notification/index.js b/src/components/Notification/index.js index 4951e479..4ffa29d1 100644 --- a/src/components/Notification/index.js +++ b/src/components/Notification/index.js @@ -1,6 +1,7 @@ /* @flow */ import React from 'react'; +import media from 'styled-media-query'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import styled, { css } from 'styled-components'; @@ -60,11 +61,19 @@ const Wrapper = styled.div` color: ${colors.ERROR_PRIMARY}; background: ${colors.ERROR_SECONDARY}; `} + + ${media.lessThan('610px')` + flex-direction: column; + `} `; const Body = styled.div` display: flex; - margin-right: 40px; + width: 60%; + + ${media.lessThan('610px')` + width: 100%; + `} `; const Title = styled.div` @@ -86,28 +95,36 @@ const Message = styled.div` const StyledIcon = styled(Icon)` position: relative; top: -7px; + min-width: 20px; `; -const MessageContent = styled.div` - height: 20px; - display: flex; +const IconWrapper = styled.div` + min-width: 20px; `; const Texts = styled.div` display: flex; flex-direction: column; + flex: 1; `; const AdditionalContent = styled.div` - flex: 1; display: flex; justify-content: flex-end; align-items: flex-end; + flex: 1; `; const ActionContent = styled.div` + display: flex; justify-content: right; align-items: flex-end; + + ${media.lessThan('610px')` + width: 100%; + padding: 10px 0 0 30px; + align-items: flex-start; + `} `; export const Notification = (props: NProps): React$Element => { @@ -127,20 +144,20 @@ export const Notification = (props: NProps): React$Element => { )} - + - - { props.title } - { props.message && ( - -

- - ) } - - + + + { props.title } + { props.message && ( + +

+ + ) } + {props.actions && props.actions.length > 0 && ( From 4bf5f83d2ae909a2f8ee976405345c264e5ff893 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Mon, 1 Oct 2018 15:55:51 +0200 Subject: [PATCH 18/38] Revert "Added gitlab config" This reverts commit 2073125805162517c7b5a7e3ab3c96f35172c5f4. --- .gitlab-ci.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 5c0f6536..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,20 +0,0 @@ -image: node:8 - -before_script: - - yarn install - -cache: - paths: - - node_modules/ - -flow: - script: - - yarn run flow - -test:lint: - script: - - yarn run lint - -test:unit: - script: - - yarn run test From 2e4acf3ee82bddb6d14d10d9e05387b9db0edadf Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Tue, 2 Oct 2018 10:05:51 +0200 Subject: [PATCH 19/38] renamed "acquiring" to "acquiringDevice" in connect reducer --- src/actions/TrezorConnectActions.js | 12 ++--- src/reducers/DevicesReducer.js | 2 - src/reducers/TrezorConnectReducer.js | 53 +++++++++---------- src/views/Wallet/views/Acquire/index.js | 2 +- .../Wallet/views/UnreadableDevice/index.js | 9 +--- 5 files changed, 32 insertions(+), 46 deletions(-) diff --git a/src/actions/TrezorConnectActions.js b/src/actions/TrezorConnectActions.js index 9ecc92c8..d3813a22 100644 --- a/src/actions/TrezorConnectActions.js +++ b/src/actions/TrezorConnectActions.js @@ -71,9 +71,7 @@ export type TrezorConnectAction = { type: typeof CONNECT.DEVICE_FROM_STORAGE, payload: Array } | { - type: typeof CONNECT.START_ACQUIRING, -} | { - type: typeof CONNECT.STOP_ACQUIRING, + type: typeof CONNECT.START_ACQUIRING | typeof CONNECT.STOP_ACQUIRING, }; export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { @@ -128,10 +126,10 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS pendingTransportEvent: (getState().devices.length < 1), }); } catch (error) { - // dispatch({ - // type: CONNECT.INITIALIZATION_ERROR, - // error - // }) + dispatch({ + type: CONNECT.INITIALIZATION_ERROR, + error, + }); } }; diff --git a/src/reducers/DevicesReducer.js b/src/reducers/DevicesReducer.js index f75d991d..0b3a8d64 100644 --- a/src/reducers/DevicesReducer.js +++ b/src/reducers/DevicesReducer.js @@ -94,7 +94,6 @@ const addDevice = (state: State, device: Device): State => { const newDevice: TrezorDevice = device.type === 'acquired' ? { ...device, - // acquiring: false, ...extended, } : { ...device, @@ -154,7 +153,6 @@ const duplicate = (state: State, device: TrezorDevice): State => { const newDevice: TrezorDevice = { ...device, - // acquiring: false, remember: false, state: null, // instance, (instance is already part of device - added in modal) diff --git a/src/reducers/TrezorConnectReducer.js b/src/reducers/TrezorConnectReducer.js index f6e48630..f8adf4dd 100644 --- a/src/reducers/TrezorConnectReducer.js +++ b/src/reducers/TrezorConnectReducer.js @@ -10,9 +10,7 @@ export type SelectedDevice = { } export type State = { - // devices: Array; - // selectedDevice: ?SelectedDevice; - discoveryComplete: boolean; + initialized: boolean; error: ?string; transport: ?{ type: string; @@ -26,54 +24,42 @@ export type State = { // mobile: boolean; // } | {}; browserState: any; - acquiring: boolean; + acquiringDevice: boolean; } - const initialState: State = { - // devices: [], - //selectedDevice: null, - discoveryComplete: false, + initialized: false, error: null, transport: null, browserState: {}, - acquiring: false, + acquiringDevice: false, }; export default function connect(state: State = initialState, action: Action): State { switch (action.type) { - case UI.IFRAME_HANDSHAKE: - return { - ...state, - browserState: action.payload.browser, - }; - - case CONNECT.START_ACQUIRING: - return { - ...state, - acquiring: true, - }; - - case CONNECT.STOP_ACQUIRING: + // trezor-connect iframe didn't loaded properly + case CONNECT.INITIALIZATION_ERROR: return { ...state, - acquiring: false, + error: action.error, }; - - case CONNECT.INITIALIZATION_ERROR: + // trezor-connect iframe loaded + case UI.IFRAME_HANDSHAKE: return { ...state, - error: action.error, + initialized: true, + browserState: action.payload.browser, }; - + // trezor-connect (trezor-link) initialized case TRANSPORT.START: return { ...state, transport: action.payload, error: null, }; - + // trezor-connect (trezor-link) + // will be called continuously in interval until connection (bridge/webusb) will be established case TRANSPORT.ERROR: return { ...state, @@ -82,6 +68,17 @@ export default function connect(state: State = initialState, action: Action): St transport: null, }; + case CONNECT.START_ACQUIRING: + return { + ...state, + acquiringDevice: true, + }; + + case CONNECT.STOP_ACQUIRING: + return { + ...state, + acquiringDevice: false, + }; default: return state; diff --git a/src/views/Wallet/views/Acquire/index.js b/src/views/Wallet/views/Acquire/index.js index 618a7a1f..b514b9c7 100644 --- a/src/views/Wallet/views/Acquire/index.js +++ b/src/views/Wallet/views/Acquire/index.js @@ -46,7 +46,7 @@ const Acquire = (props: Props) => { export default connect( (state: State) => ({ - acquiring: state.connect.acquiring, + acquiring: state.connect.acquiringDevice, }), (dispatch: Dispatch) => ({ acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch), diff --git a/src/views/Wallet/views/UnreadableDevice/index.js b/src/views/Wallet/views/UnreadableDevice/index.js index a9f0273b..f0bf3dff 100644 --- a/src/views/Wallet/views/UnreadableDevice/index.js +++ b/src/views/Wallet/views/UnreadableDevice/index.js @@ -23,11 +23,4 @@ const UnreadableDevice = () => ( ); -export default connect( - (state: State) => ({ - acquiring: state.connect.acquiring, - }), - (dispatch: Dispatch) => ({ - acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch), - }), -)(UnreadableDevice); +export default connect(null, null)(UnreadableDevice); From f59b9c31b1948481d8909d4319a5fd34f32651cd Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Tue, 2 Oct 2018 10:21:22 +0200 Subject: [PATCH 20/38] trezor-connect iframe loading error --- .../components/InitializationError/index.js | 23 +++++++++++++++++++ src/views/Landing/index.js | 10 ++++---- .../Wallet/views/UnreadableDevice/index.js | 8 +------ 3 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 src/views/Landing/components/InitializationError/index.js diff --git a/src/views/Landing/components/InitializationError/index.js b/src/views/Landing/components/InitializationError/index.js new file mode 100644 index 00000000..2db65aee --- /dev/null +++ b/src/views/Landing/components/InitializationError/index.js @@ -0,0 +1,23 @@ +/* @flow */ + +import React from 'react'; +import styled from 'styled-components'; +import { Notification } from 'components/Notification'; + +const Wrapper = styled.div` + min-width: 720px; + width: 100%; +`; + +const InitializationError = (props: { error: ?string }) => ( + + + +); + +export default InitializationError; \ No newline at end of file diff --git a/src/views/Landing/index.js b/src/views/Landing/index.js index 398b9ef4..13ec9331 100644 --- a/src/views/Landing/index.js +++ b/src/views/Landing/index.js @@ -14,6 +14,7 @@ import { H2 } from 'components/Heading'; import { isWebUSB } from 'utils/device'; import { FONT_SIZE } from 'config/variables'; +import InitializationError from './components/InitializationError'; import BrowserNotSupported from './components/BrowserNotSupported'; import ConnectDevice from './components/ConnectDevice'; import InstallBridge from './components/InstallBridge'; @@ -77,13 +78,13 @@ export default (props: Props) => { const bridgeRoute: boolean = props.router.location.state.hasOwnProperty('bridge'); const deviceLabel = props.wallet.disconnectRequest ? props.wallet.disconnectRequest.label : ''; - const shouldShowInstallBridge = connectError || bridgeRoute; + const shouldShowInitializationError = connectError && !props.connect.initialized; + const shouldShowInstallBridge = props.connect.initialized && (connectError || bridgeRoute); const shouldShowConnectDevice = props.wallet.ready && devices.length < 1; const shouldShowDisconnectDevice = !!props.wallet.disconnectRequest; const shouldShowUnsupportedBrowser = browserState.supported === false; - const isLoading = !shouldShowInstallBridge && !shouldShowConnectDevice && !shouldShowUnsupportedBrowser && !localStorageError; - + const isLoading = !shouldShowInitializationError && !shouldShowInstallBridge && !shouldShowConnectDevice && !shouldShowUnsupportedBrowser && !localStorageError; return ( {isLoading && } @@ -98,10 +99,9 @@ export default (props: Props) => { /> )} + {shouldShowInitializationError && } - - {shouldShowUnsupportedBrowser && } {shouldShowInstallBridge && } diff --git a/src/views/Wallet/views/UnreadableDevice/index.js b/src/views/Wallet/views/UnreadableDevice/index.js index f0bf3dff..15e99283 100644 --- a/src/views/Wallet/views/UnreadableDevice/index.js +++ b/src/views/Wallet/views/UnreadableDevice/index.js @@ -1,14 +1,8 @@ /* @flow */ - import React from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; import styled from 'styled-components'; import { Notification } from 'components/Notification'; -import * as TrezorConnectActions from 'actions/TrezorConnectActions'; - -import type { State, Dispatch } from 'flowtype'; const Wrapper = styled.div``; @@ -23,4 +17,4 @@ const UnreadableDevice = () => ( ); -export default connect(null, null)(UnreadableDevice); +export default UnreadableDevice; From 479486e4c99ed964abb145cb22833969fe9380fc Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Tue, 2 Oct 2018 10:57:17 +0200 Subject: [PATCH 21/38] eslint fixes --- src/reducers/DevicesReducer.js | 40 +++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/reducers/DevicesReducer.js b/src/reducers/DevicesReducer.js index 0b3a8d64..538c096a 100644 --- a/src/reducers/DevicesReducer.js +++ b/src/reducers/DevicesReducer.js @@ -131,8 +131,8 @@ const addDevice = (state: State, device: Device): State => { const changedDevices: Array = affectedDevices.filter(d => d.features && device.features && d.features.passphrase_protection === device.features.passphrase_protection).map((d) => { - const extended: Object = { connected: true, available: true }; - return mergeDevices(d, { ...device, ...extended }); + const extended2: Object = { connected: true, available: true }; + return mergeDevices(d, { ...device, ...extended2 }); }); if (changedDevices.length !== affectedDevices.length) { changedDevices.push(newDevice); @@ -197,7 +197,7 @@ const authDevice = (state: State, device: TrezorDevice, deviceState: string): St // Transform JSON form local storage into State -const devicesFromStorage = (devices: Array): State => devices.map((device: TrezorDevice) => { +const devicesFromStorage = ($devices: Array): State => $devices.map((device: TrezorDevice) => { const extended = { connected: false, available: false, @@ -231,14 +231,14 @@ const disconnectDevice = (state: State, device: Device): State => { const otherDevices: State = state.filter(d => affectedDevices.indexOf(d) === -1); if (affectedDevices.length > 0) { - const acquiredDevices = affectedDevices.filter(d => d.features && d.state).map((d) => { - if (d.type === 'acquired') { - d.connected = false; - d.available = false; - d.status = 'available'; - d.path = ''; - } - return d; + const acquiredDevices = affectedDevices.filter(d => d.features && d.state).map((d) => { // eslint-disable-line arrow-body-style + return d.type === 'acquired' ? { + ...d, + connected: false, + available: false, + status: 'available', + path: '', + } : d; }); return otherDevices.concat(acquiredDevices); } @@ -247,12 +247,18 @@ const disconnectDevice = (state: State, device: Device): State => { }; const onSelectedDevice = (state: State, device: ?TrezorDevice): State => { - if (device) { - const otherDevices: Array = state.filter(d => d !== device); - device.ts = new Date().getTime(); - return otherDevices.concat([device]); - } - return state; + if (!device) return state; + + const otherDevices: Array = state.filter(d => d !== device); + const extended = device.type === 'acquired' ? { + ...device, + ts: new Date().getTime(), + } : { + ...device, + features: null, + ts: new Date().getTime(), + }; + return otherDevices.concat([extended]); }; export default function devices(state: State = initialState, action: Action): State { From bfea1b225cc58e6472345e7f743819b0a83eaeff Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Tue, 2 Oct 2018 13:17:43 +0200 Subject: [PATCH 22/38] fix for LeftNavigation render - removed componentDidMount which resets state applied in contructor - added animationType in constructor if location has network --- src/views/Wallet/components/LeftNavigation/index.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/views/Wallet/components/LeftNavigation/index.js b/src/views/Wallet/components/LeftNavigation/index.js index b5402d22..6f3bf158 100644 --- a/src/views/Wallet/components/LeftNavigation/index.js +++ b/src/views/Wallet/components/LeftNavigation/index.js @@ -69,7 +69,7 @@ type TransitionMenuProps = { children?: React.Node; } -// TransitionMenu needs to dispatch window.resize even +// TransitionMenu needs to dispatch window.resize event // in order to StickyContainer be recalculated const TransitionMenu = (props: TransitionMenuProps): React$Element => ( @@ -98,19 +98,14 @@ type State = { class LeftNavigation extends React.PureComponent { constructor(props: Props) { super(props); + const { location } = this.props.router; + const hasNetwork = location && location.state && location.state.network; this.state = { - animationType: null, + animationType: hasNetwork ? 'slide-left' : null, shouldRenderDeviceSelection: false, }; } - componentDidMount() { - this.setState({ - animationType: null, - shouldRenderDeviceSelection: false, - }); - } - componentWillReceiveProps(nextProps: Props) { const { deviceDropdownOpened } = nextProps; const { selectedDevice } = nextProps.wallet; From e21cde44928f49eb6646971bd44cb1f3e39c8bad Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 16:29:47 +0200 Subject: [PATCH 23/38] Update README.md --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae2b91c2..1bc667f3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,19 @@ -# TREZOR Wallet +``` + + _ +| |_ _ __ ___ _______ _ __ +| __| '__/ _ |_ / _ \| '__| +| |_| | | __// | (_) | | + \__|_| \___/___\___/|_| + + _ _ _ _ _ + ___| |_| |__ ___ _ __ ___ _ _ _ __ ___ __ ____ _| | | ___| |_ + / _ | __| '_ \ / _ | '__/ _ | | | | '_ ` _ \ \ \ /\ / / _` | | |/ _ | __| +| __| |_| | | | __| | | __| |_| | | | | | | \ V V | (_| | | | __| |_ + \___|\__|_| |_|\___|_| \___|\__,_|_| |_| |_| \_/\_/ \__,_|_|_|\___|\__| + + +``` To install dependencies run `npm install` or `yarn` From e86bf6f81b384499a5a4e242d1cc4b030c311b65 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 16:30:11 +0200 Subject: [PATCH 24/38] Update README.md --- README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/README.md b/README.md index 1bc667f3..8045d434 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,3 @@ -``` - - _ -| |_ _ __ ___ _______ _ __ -| __| '__/ _ |_ / _ \| '__| -| |_| | | __// | (_) | | - \__|_| \___/___\___/|_| - - _ _ _ _ _ - ___| |_| |__ ___ _ __ ___ _ _ _ __ ___ __ ____ _| | | ___| |_ - / _ | __| '_ \ / _ | '__/ _ | | | | '_ ` _ \ \ \ /\ / / _` | | |/ _ | __| -| __| |_| | | | __| | | __| |_| | | | | | | \ V V | (_| | | | __| |_ - \___|\__|_| |_|\___|_| \___|\__,_|_| |_| |_| \_/\_/ \__,_|_|_|\___|\__| - - -``` - To install dependencies run `npm install` or `yarn` To start locally run `npm run dev` or `yarn run dev` From 998882b50a8ecce5fcf3e9d2f829db0ce847355c Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 16:32:46 +0200 Subject: [PATCH 25/38] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8045d434..dcd55a02 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# Trezor Ethereum wallet + To install dependencies run `npm install` or `yarn` To start locally run `npm run dev` or `yarn run dev` From 5eca2bf4f1dea69fbc737d190e74074466aea6f3 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 16:43:09 +0200 Subject: [PATCH 26/38] Added clickable logo - redirects to / --- src/components/Header/index.js | 40 +++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/components/Header/index.js b/src/components/Header/index.js index b64301c5..75a9862b 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -1,7 +1,7 @@ /* @flow */ import React from 'react'; import styled from 'styled-components'; - +import { NavLink } from 'react-router-dom'; import colors from 'config/colors'; const Wrapper = styled.header` @@ -13,12 +13,9 @@ const Wrapper = styled.header` fill: ${colors.WHITE}; height: 28px; width: 100px; - flex: 1; } `; -const LinkWrapper = styled.div``; - const LayoutWrapper = styled.div` width: 100%; height: 100%; @@ -26,12 +23,21 @@ const LayoutWrapper = styled.div` margin: 0 auto; display: flex; align-items: center; + justify-content: space-between; @media screen and (max-width: 1170px) { padding: 0 25px; } `; +const Left = styled.div` + flex: 1; + display: flex; + justify-content: flex-start; +`; + +const Right = styled.div``; + const A = styled.a` color: ${colors.WHITE}; margin-left: 24px; @@ -54,21 +60,25 @@ const A = styled.a` const Header = (): React$Element => ( - - - - - - - - - - + + + + + + + + + + + + + + TREZOR Docs Blog Support - + ); From 25f41bd1639b35731cbe133d0786acccdce9e4e2 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Tue, 2 Oct 2018 17:33:49 +0200 Subject: [PATCH 27/38] fix router actions --- src/actions/RouterActions.js | 68 +++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/actions/RouterActions.js b/src/actions/RouterActions.js index 2dee9f5f..d513611f 100644 --- a/src/actions/RouterActions.js +++ b/src/actions/RouterActions.js @@ -152,7 +152,7 @@ export const getValidUrl = (action: RouterAction): PayloadAction => (dis // Disallow displaying landing page // redirect to previous url if (!shouldBeLandingPage && landingPageUrl) { - return location.pathname; + return dispatch(getFirstAvailableDeviceUrl()) || location.pathname; } // Regular url change during application live cycle @@ -170,22 +170,10 @@ export const getValidUrl = (action: RouterAction): PayloadAction => (dis return composedUrl || location.pathname; }; - /* -* Utility used in "selectDevice" and "selectFirstAvailableDevice" -* sorting device array by "ts" (timestamp) field +* Compose url from requested device object and returns url */ -const sortDevices = (devices: Array): Array => devices.sort((a, b) => { - if (!a.ts || !b.ts) { - return -1; - } - return a.ts > b.ts ? -1 : 1; -}); - -/* -* Compose url from given device object and redirect -*/ -export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +const getDeviceUrl = (device: TrezorDevice | Device): PayloadAction => (dispatch: Dispatch, getState: GetState): ?string => { let url: ?string; if (!device.features) { url = `/device/${device.path}/${device.type === 'unreadable' ? 'unreadable' : 'acquire'}`; @@ -208,12 +196,7 @@ export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dis } } } - - const currentParams: RouterLocationState = getState().router.location.state; - const requestedParams = dispatch(pathToParams(url)); - if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) { - dispatch(goto(url)); - } + return url; }; /* @@ -223,17 +206,54 @@ export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dis * 3. Saved with latest timestamp * OR redirect to landing page */ -export const selectFirstAvailableDevice = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +export const getFirstAvailableDeviceUrl = (): PayloadAction => (dispatch: Dispatch, getState: GetState): ?string => { const { devices } = getState(); + let url: ?string; if (devices.length > 0) { const unacquired = devices.find(d => !d.features); if (unacquired) { - dispatch(selectDevice(unacquired)); + url = dispatch(getDeviceUrl(unacquired)); } else { const latest: Array = sortDevices(devices); const firstConnected: ?TrezorDevice = latest.find(d => d.connected); - dispatch(selectDevice(firstConnected || latest[0])); + url = dispatch(getDeviceUrl(firstConnected || latest[0])); } + } + return url; +}; + +/* +* Utility used in "getDeviceUrl" and "getFirstAvailableDeviceUrl" +* sorting device array by "ts" (timestamp) field +*/ +const sortDevices = (devices: Array): Array => devices.sort((a, b) => { + if (!a.ts || !b.ts) { + return -1; + } + return a.ts > b.ts ? -1 : 1; +}); + +/* +* Redirect to requested device +*/ +export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + const url: ?string = dispatch(getDeviceUrl(device)); + if (!url) return; + + const currentParams: RouterLocationState = getState().router.location.state; + const requestedParams = dispatch(pathToParams(url)); + if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) { + dispatch(goto(url)); + } +}; + +/* +* Redirect to first device or landing page +*/ +export const selectFirstAvailableDevice = (): ThunkAction => (dispatch: Dispatch): void => { + const url = dispatch(getFirstAvailableDeviceUrl()); + if (url) { + dispatch(goto(url)); } else { dispatch(gotoLandingPage()); } From 147cf400a43d531e9da85cf9a29791170d5b7295 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 19:55:22 +0200 Subject: [PATCH 28/38] Added basic tests --- jest.config.js | 1 + .../__snapshots__/index.test.js.snap | 37 +++++++ src/reducers/utils/__tests__/index.test.js | 99 +++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/reducers/utils/__tests__/__snapshots__/index.test.js.snap create mode 100644 src/reducers/utils/__tests__/index.test.js diff --git a/jest.config.js b/jest.config.js index d9c12451..3f20eda3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,6 +12,7 @@ module.exports = { ], collectCoverageFrom: [ 'utils/**.js', + 'reducers/utils/**.js', ], setupFiles: [ './support/setupJest.js', diff --git a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap new file mode 100644 index 00000000..f1703259 --- /dev/null +++ b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`reducers utils observeChanges shoud be the same - returns false 1`] = `false`; + +exports[`reducers utils observeChanges shoud be the same - returns false 2`] = `false`; + +exports[`reducers utils observeChanges shoud be the same - returns false 3`] = `false`; + +exports[`reducers utils observeChanges shoud be the same - returns false 4`] = `false`; + +exports[`reducers utils observeChanges shoud be the same - returns false 5`] = `false`; + +exports[`reducers utils observeChanges shoud be the same - returns false 6`] = `false`; + +exports[`reducers utils observeChanges shoud be the same - returns false 7`] = `false`; + +exports[`reducers utils observeChanges shoud be the same - returns false 8`] = `false`; + +exports[`reducers utils observeChanges shoud be the same - returns false 9`] = `false`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 1`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 2`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 3`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 4`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 5`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 6`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 7`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 8`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 9`] = `true`; diff --git a/src/reducers/utils/__tests__/index.test.js b/src/reducers/utils/__tests__/index.test.js new file mode 100644 index 00000000..dc937181 --- /dev/null +++ b/src/reducers/utils/__tests__/index.test.js @@ -0,0 +1,99 @@ +import * as reducerUtils from '../index'; + +describe('reducers utils', () => { + it('observeChanges shoud be the same - returns false', () => { + const data = [ + // example of same data (false) + { + pervious: {}, + current: {}, + }, + { + pervious: 1, + current: 1, + }, + { + pervious: [], + current: [], + }, + { + pervious: 'a', + current: 'a', + }, + { + pervious: { test: 1 }, + current: { test: 1 }, + }, + { + pervious: { test: { test: 1 } }, + current: { test: { test: 1 } }, + }, + { + pervious: { test: { test: [1, 2, 3] } }, + current: { test: { test: [1, 2, 3] } }, + }, + { + pervious: { test: { test: [1, { test: 1 }, 3] } }, + current: { test: { test: [1, { test: 1 }, 3] } }, + }, + { + pervious: { test: { test: [1, { test: 1 }, { test: 3, test1: { test: 3 } }] } }, + current: { test: { test: [1, { test: 1 }, { test: 3, test1: { test: 3 } }] } }, + }, + ]; + + data.forEach((item) => { + expect(reducerUtils.observeChanges( + item.pervious, item.current, + )).toMatchSnapshot(); + }); + }); + + it('observeChanges shoul NOT be the same - returns true', () => { + const data = [ + // example of different data (true) + { + pervious: { test: 1 }, + current: {}, + }, + { + pervious: [{}], + current: [], + }, + { + pervious: 'a', + current: 'b', + }, + { + pervious: 1, + current: '1', + }, + { + pervious: { test: 1 }, + current: { test: 2 }, + }, + { + pervious: { test: { test: 1 } }, + current: { test: { test: 2 } }, + }, + { + pervious: { test: { test: [1, 2, 3] } }, + current: { test: { test: [1, 1, 3] } }, + }, + { + pervious: { test: { test: [1, { test: 1 }, 3] } }, + current: { test: { test: [1, { test: 2 }, 3] } }, + }, + { + pervious: { test: { test: [1, { test: 1 }, { test: 3, test1: { test: 3 } }] } }, + current: { test: { test: [1, { test: 1 }, { test: 3, test1: { test: 1 } }] } }, + }, + ]; + + data.forEach((item) => { + expect(reducerUtils.observeChanges( + item.pervious, item.current, + )).toMatchSnapshot(); + }); + }); +}); From 9771b21093b86254db255b26e2805e2f64b62cb4 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 19:57:13 +0200 Subject: [PATCH 29/38] Changed coverage report path --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 3f20eda3..10729508 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ module.exports = { rootDir: './src', automock: false, - coverageDirectory: 'coverage/', + coverageDirectory: '../coverage/', collectCoverage: true, testURL: 'http://localhost', modulePathIgnorePatterns: [ From 802e8f8e3259dbfd83d9d101ec297074bb59787e Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 20:06:59 +0200 Subject: [PATCH 30/38] Fixed bug in function, add more tests --- .../utils/__tests__/__snapshots__/index.test.js.snap | 4 ++++ src/reducers/utils/__tests__/index.test.js | 10 +++++++++- src/reducers/utils/index.js | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap index f1703259..dc206e2a 100644 --- a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap +++ b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap @@ -18,6 +18,8 @@ exports[`reducers utils observeChanges shoud be the same - returns false 8`] = ` exports[`reducers utils observeChanges shoud be the same - returns false 9`] = `false`; +exports[`reducers utils observeChanges shoud be the same - returns false 10`] = `false`; + exports[`reducers utils observeChanges shoul NOT be the same - returns true 1`] = `true`; exports[`reducers utils observeChanges shoul NOT be the same - returns true 2`] = `true`; @@ -35,3 +37,5 @@ exports[`reducers utils observeChanges shoul NOT be the same - returns true 7`] exports[`reducers utils observeChanges shoul NOT be the same - returns true 8`] = `true`; exports[`reducers utils observeChanges shoul NOT be the same - returns true 9`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 10`] = `true`; diff --git a/src/reducers/utils/__tests__/index.test.js b/src/reducers/utils/__tests__/index.test.js index dc937181..d9d2f913 100644 --- a/src/reducers/utils/__tests__/index.test.js +++ b/src/reducers/utils/__tests__/index.test.js @@ -16,6 +16,10 @@ describe('reducers utils', () => { pervious: [], current: [], }, + { + pervious: [1, 1, 1], + current: [1, 1, 1], + }, { pervious: 'a', current: 'a', @@ -57,9 +61,13 @@ describe('reducers utils', () => { current: {}, }, { - pervious: [{}], + pervious: [{}, {}], current: [], }, + { + pervious: [1, 1, 1], + current: [1, 1], + }, { pervious: 'a', current: 'b', diff --git a/src/reducers/utils/index.js b/src/reducers/utils/index.js index d3a8e740..03662042 100644 --- a/src/reducers/utils/index.js +++ b/src/reducers/utils/index.js @@ -122,7 +122,7 @@ export const observeChanges = (prev: ?Object, current: ?Object, filter?: {[k: st // 2. one of the objects is null/undefined if (!prev || !current) return true; - const prevType = Object.prototype.toString.call(current); + const prevType = Object.prototype.toString.call(prev); const currentType = Object.prototype.toString.call(current); // 3. one of the objects has different type then other if (prevType !== currentType) return true; From 583a0f2cf6beccd64af7141d5cf12954974967c2 Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 20:26:24 +0200 Subject: [PATCH 31/38] Make tests more readable --- .../__snapshots__/index.test.js.snap | 6 +++ src/reducers/utils/__tests__/index.test.js | 54 +++++++++++-------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap index dc206e2a..0f2d33bf 100644 --- a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap +++ b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap @@ -39,3 +39,9 @@ exports[`reducers utils observeChanges shoul NOT be the same - returns true 8`] exports[`reducers utils observeChanges shoul NOT be the same - returns true 9`] = `true`; exports[`reducers utils observeChanges shoul NOT be the same - returns true 10`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 11`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 12`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 13`] = `true`; diff --git a/src/reducers/utils/__tests__/index.test.js b/src/reducers/utils/__tests__/index.test.js index d9d2f913..391ac767 100644 --- a/src/reducers/utils/__tests__/index.test.js +++ b/src/reducers/utils/__tests__/index.test.js @@ -25,24 +25,24 @@ describe('reducers utils', () => { current: 'a', }, { - pervious: { test: 1 }, - current: { test: 1 }, + pervious: { one: 1 }, + current: { one: 1 }, }, { - pervious: { test: { test: 1 } }, - current: { test: { test: 1 } }, + pervious: { one: { two: 1 } }, + current: { one: { two: 1 } }, }, { - pervious: { test: { test: [1, 2, 3] } }, - current: { test: { test: [1, 2, 3] } }, + pervious: { one: { two: [1, 2, 3] } }, + current: { one: { two: [1, 2, 3] } }, }, { - pervious: { test: { test: [1, { test: 1 }, 3] } }, - current: { test: { test: [1, { test: 1 }, 3] } }, + pervious: { one: { two: [1, { three: 1 }, 3] } }, + current: { one: { two: [1, { three: 1 }, 3] } }, }, { - pervious: { test: { test: [1, { test: 1 }, { test: 3, test1: { test: 3 } }] } }, - current: { test: { test: [1, { test: 1 }, { test: 3, test1: { test: 3 } }] } }, + pervious: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 3 } }] } }, + current: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 3 } }] } }, }, ]; @@ -57,9 +57,13 @@ describe('reducers utils', () => { const data = [ // example of different data (true) { - pervious: { test: 1 }, + pervious: { one: 1 }, current: {}, }, + { + pervious: { one: 1 }, + current: { one: 1, two: 2 }, + }, { pervious: [{}, {}], current: [], @@ -77,24 +81,32 @@ describe('reducers utils', () => { current: '1', }, { - pervious: { test: 1 }, - current: { test: 2 }, + pervious: { one: 1 }, + current: { one: 2 }, + }, + { + pervious: { one: { two: 1 } }, + current: { one: { two: 2 } }, + }, + { + pervious: { one: { two: 1 } }, + current: { one: { two: 2 } }, }, { - pervious: { test: { test: 1 } }, - current: { test: { test: 2 } }, + pervious: { one: { two: [1, 2, 3] } }, + current: { one: { two: [1, 1, 3] } }, }, { - pervious: { test: { test: [1, 2, 3] } }, - current: { test: { test: [1, 1, 3] } }, + pervious: { one: { two: [1, { three: 1 }, 3] } }, + current: { one: { two: [1, { three: 2 }, 3] } }, }, { - pervious: { test: { test: [1, { test: 1 }, 3] } }, - current: { test: { test: [1, { test: 2 }, 3] } }, + pervious: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 3 } }] } }, + current: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 1 } }] } }, }, { - pervious: { test: { test: [1, { test: 1 }, { test: 3, test1: { test: 3 } }] } }, - current: { test: { test: [1, { test: 1 }, { test: 3, test1: { test: 1 } }] } }, + pervious: { one: { two: [1, { three: 1 }, { four: 3, five: { sixxx: 3 } }] } }, + current: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 1 } }] } }, }, ]; From 29e0eb928ac0ee9c60611f925be27d4f4ed91439 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 3 Oct 2018 10:39:54 +0200 Subject: [PATCH 32/38] fix reducer utility --- src/reducers/utils/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reducers/utils/index.js b/src/reducers/utils/index.js index 03662042..98b89ea3 100644 --- a/src/reducers/utils/index.js +++ b/src/reducers/utils/index.js @@ -34,7 +34,7 @@ export const getSelectedDevice = (state: State): ?TrezorDevice => { export const isSelectedDevice = (current: ?TrezorDevice, device: ?TrezorDevice): boolean => !!((current && device && (current.path === device.path && current.instance === device.instance))); // find device by id and state -export const findDevice = (devices: Array, deviceId: string, deviceState: string, instance: ?number): ?TrezorDevice => devices.find((d) => { +export const findDevice = (devices: Array, deviceId: string, deviceState: string /*, instance: ?number*/): ?TrezorDevice => devices.find((d) => { // TODO: && (instance && d.instance === instance) if (d.features && d.features.device_id === deviceId && d.state === deviceState) { return true; @@ -136,7 +136,7 @@ export const observeChanges = (prev: ?Object, current: ?Object, filter?: {[k: st } } else if (currentType === '[object Object]') { const prevKeys = Object.keys(prev); - const currentKeys = Object.keys(prev); + const currentKeys = Object.keys(current); // 5. simple validation of keys length if (prevKeys.length !== currentKeys.length) return true; From a03a8287e8a25174cb29dc5f84433b0ae177555a Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Wed, 3 Oct 2018 11:45:08 +0200 Subject: [PATCH 33/38] Add more test cases --- .../__snapshots__/index.test.js.snap | 8 ++ src/reducers/utils/__tests__/index.test.js | 75 ++++++++++++------- 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap index 0f2d33bf..8414ce19 100644 --- a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap +++ b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap @@ -45,3 +45,11 @@ exports[`reducers utils observeChanges shoul NOT be the same - returns true 11`] exports[`reducers utils observeChanges shoul NOT be the same - returns true 12`] = `true`; exports[`reducers utils observeChanges shoul NOT be the same - returns true 13`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 14`] = `true`; + +exports[`reducers utils observeChanges shoul NOT be the same - returns true 15`] = `true`; + +exports[`reducers utils observeChanges test filter 1`] = `true`; + +exports[`reducers utils observeChanges test filter 2`] = `false`; diff --git a/src/reducers/utils/__tests__/index.test.js b/src/reducers/utils/__tests__/index.test.js index 391ac767..e7b57b7c 100644 --- a/src/reducers/utils/__tests__/index.test.js +++ b/src/reducers/utils/__tests__/index.test.js @@ -5,50 +5,50 @@ describe('reducers utils', () => { const data = [ // example of same data (false) { - pervious: {}, + previous: {}, current: {}, }, { - pervious: 1, + previous: 1, current: 1, }, { - pervious: [], + previous: [], current: [], }, { - pervious: [1, 1, 1], + previous: [1, 1, 1], current: [1, 1, 1], }, { - pervious: 'a', + previous: 'a', current: 'a', }, { - pervious: { one: 1 }, + previous: { one: 1 }, current: { one: 1 }, }, { - pervious: { one: { two: 1 } }, + previous: { one: { two: 1 } }, current: { one: { two: 1 } }, }, { - pervious: { one: { two: [1, 2, 3] } }, + previous: { one: { two: [1, 2, 3] } }, current: { one: { two: [1, 2, 3] } }, }, { - pervious: { one: { two: [1, { three: 1 }, 3] } }, + previous: { one: { two: [1, { three: 1 }, 3] } }, current: { one: { two: [1, { three: 1 }, 3] } }, }, { - pervious: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 3 } }] } }, + previous: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 3 } }] } }, current: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 3 } }] } }, }, ]; data.forEach((item) => { expect(reducerUtils.observeChanges( - item.pervious, item.current, + item.previous, item.current, )).toMatchSnapshot(); }); }); @@ -57,62 +57,87 @@ describe('reducers utils', () => { const data = [ // example of different data (true) { - pervious: { one: 1 }, + previous: null, current: {}, }, { - pervious: { one: 1 }, + previous: { one: 1 }, + current: {}, + }, + { + previous: { one: 1 }, current: { one: 1, two: 2 }, }, { - pervious: [{}, {}], + previous: [{}, {}], current: [], }, { - pervious: [1, 1, 1], + previous: [1, 1, 1], current: [1, 1], }, { - pervious: 'a', + previous: 'a', current: 'b', }, { - pervious: 1, + previous: 1, current: '1', }, { - pervious: { one: 1 }, + previous: { one: 1 }, current: { one: 2 }, }, { - pervious: { one: { two: 1 } }, + previous: { one: { two: 1 } }, current: { one: { two: 2 } }, }, { - pervious: { one: { two: 1 } }, + previous: { one: { two: 1 } }, current: { one: { two: 2 } }, }, { - pervious: { one: { two: [1, 2, 3] } }, + previous: { one: { two: [1, 2, 3] } }, current: { one: { two: [1, 1, 3] } }, }, { - pervious: { one: { two: [1, { three: 1 }, 3] } }, + previous: { one: { two: [1, { three: 1 }, 3] } }, current: { one: { two: [1, { three: 2 }, 3] } }, }, { - pervious: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 3 } }] } }, + previous: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 3 } }] } }, current: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 1 } }] } }, }, { - pervious: { one: { two: [1, { three: 1 }, { four: 3, five: { sixxx: 3 } }] } }, + previous: { one: { two: [1, { three: 1 }, { four: 3, five: { sixxx: 3 } }] } }, current: { one: { two: [1, { three: 1 }, { four: 3, five: { six: 1 } }] } }, }, ]; data.forEach((item) => { expect(reducerUtils.observeChanges( - item.pervious, item.current, + item.previous, item.current, + )).toMatchSnapshot(); + }); + }); + + it('observeChanges test filter', () => { + const data = [ + { + previous: { one: { two: 2, three: 3 } }, + current: { one: { two: 2 } }, + filter: { one: ['two'] }, + }, + { + previous: { one: { two: 2, three: 3 } }, + current: { one: { two: 1 } }, + filter: { one: ['two'] }, + }, + ]; + + data.forEach((item) => { + expect(reducerUtils.observeChanges( + item.previous, item.current, item.filter, )).toMatchSnapshot(); }); }); From 8be738bf2ee0383e6ad1dab59e9c52acb35199bc Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Wed, 3 Oct 2018 12:02:08 +0200 Subject: [PATCH 34/38] Removed unnecesary case, add more tests --- .../utils/__tests__/__snapshots__/index.test.js.snap | 4 ++-- src/reducers/utils/__tests__/index.test.js | 10 +++++++--- src/reducers/utils/index.js | 4 ---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap index 8414ce19..5efb9857 100644 --- a/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap +++ b/src/reducers/utils/__tests__/__snapshots__/index.test.js.snap @@ -50,6 +50,6 @@ exports[`reducers utils observeChanges shoul NOT be the same - returns true 14`] exports[`reducers utils observeChanges shoul NOT be the same - returns true 15`] = `true`; -exports[`reducers utils observeChanges test filter 1`] = `true`; +exports[`reducers utils observeChanges test filter 1`] = `false`; -exports[`reducers utils observeChanges test filter 2`] = `false`; +exports[`reducers utils observeChanges test filter 2`] = `true`; diff --git a/src/reducers/utils/__tests__/index.test.js b/src/reducers/utils/__tests__/index.test.js index e7b57b7c..48c5d93b 100644 --- a/src/reducers/utils/__tests__/index.test.js +++ b/src/reducers/utils/__tests__/index.test.js @@ -65,7 +65,7 @@ describe('reducers utils', () => { current: {}, }, { - previous: { one: 1 }, + previous: { one: 1, three: 3 }, current: { one: 1, two: 2 }, }, { @@ -80,6 +80,10 @@ describe('reducers utils', () => { previous: 'a', current: 'b', }, + { + previous: ['a'], + current: ['b'], + }, { previous: 1, current: '1', @@ -125,12 +129,12 @@ describe('reducers utils', () => { const data = [ { previous: { one: { two: 2, three: 3 } }, - current: { one: { two: 2 } }, + current: { one: { two: 2, three: 4 } }, filter: { one: ['two'] }, }, { previous: { one: { two: 2, three: 3 } }, - current: { one: { two: 1 } }, + current: { one: { two: 1, three: 3 } }, filter: { one: ['two'] }, }, ]; diff --git a/src/reducers/utils/index.js b/src/reducers/utils/index.js index 98b89ea3..3d6a8de4 100644 --- a/src/reducers/utils/index.js +++ b/src/reducers/utils/index.js @@ -144,10 +144,6 @@ export const observeChanges = (prev: ?Object, current: ?Object, filter?: {[k: st const prevDifference = prevKeys.find(k => currentKeys.indexOf(k) < 0); if (prevDifference) return true; - // 7. "current" has keys which "prev" doesn't have - const currentDifference = currentKeys.find(k => prevKeys.indexOf(k) < 0); - if (currentDifference) return true; - // 8. observe every key recursive for (let i = 0; i < currentKeys.length; i++) { const key = currentKeys[i]; From 2981c3bdd238753a6e9992ca19ea30e1c6289adc Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Mon, 1 Oct 2018 16:54:15 +0200 Subject: [PATCH 35/38] Connect actions notification with grouped notifications --- .../Notification/NotificationGroups/index.js | 56 ------------ .../components/Group/index.js | 6 +- .../components/NotificationsGroups/index.js | 87 +++++++++++++++++++ .../Context/components/Action/index.js | 15 ++-- 4 files changed, 97 insertions(+), 67 deletions(-) delete mode 100644 src/components/Notification/NotificationGroups/index.js rename src/components/{Notification/NotificationGroups => notifications/Context/components/Action/components/NotificationsGroups}/components/Group/index.js (91%) create mode 100644 src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js diff --git a/src/components/Notification/NotificationGroups/index.js b/src/components/Notification/NotificationGroups/index.js deleted file mode 100644 index 715b1da3..00000000 --- a/src/components/Notification/NotificationGroups/index.js +++ /dev/null @@ -1,56 +0,0 @@ -import React, { Component } from 'react'; -import styled from 'styled-components'; - -import { PRIORITY } from 'constants/notifications'; -import Group from './components/Group'; - -const Wrapper = styled.div``; - -class NotificationsGroup extends Component { - constructor() { - super(); - this.notifications = [ - { type: 'warning', title: 'adddaa', message: 'aaaa' }, - { type: 'error', title: 'aaddda', message: 'aaaa' }, - { type: 'info', title: 'aafffa', message: 'aaaa' }, - { type: 'error', title: 'aggaa', message: 'aaaa' }, - { type: 'warning', title: 'aasssa', message: 'aaaa' }, - { type: 'success', title: 'afaa', message: 'aaaa' }, - { type: 'error', title: 'aada', message: 'aaaa' }, - { type: 'error', title: 'aafffa', message: 'aaaa' }, - ]; - } - - groupNotifications = notifications => notifications - .reduce((acc, obj) => { - const key = obj.type; - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(obj); - return acc; - }, {}); - - sortByPriority(notifications) { - return notifications; - } - - render() { - const { notifications } = this; - const notificationGroups = this.groupNotifications(notifications); - const sortedNotifications = this.sortByPriority(notificationGroups); - - return ( - - {Object.keys(sortedNotifications).map(group => ( - - ))} - - ); - } -} - -export default NotificationsGroup; \ No newline at end of file diff --git a/src/components/Notification/NotificationGroups/components/Group/index.js b/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js similarity index 91% rename from src/components/Notification/NotificationGroups/components/Group/index.js rename to src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js index c3915b77..298a77df 100644 --- a/src/components/Notification/NotificationGroups/components/Group/index.js +++ b/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js @@ -56,7 +56,7 @@ class Group extends Component { } render() { - const { type, groupNotifications } = this.props; + const { type, groupNotifications, close } = this.props; const color = getColor(type); return ( @@ -92,6 +92,9 @@ class Group extends Component { type={notification.type} title={notification.title} message={notification.message} + cancelable={notification.cancelable} + actions={notification.actions} + close={close} /> ))} @@ -102,6 +105,7 @@ class Group extends Component { Group.propTypes = { type: PropTypes.string, + close: PropTypes.func.isRequired, groupNotifications: PropTypes.arrayOf({ key: PropTypes.string, type: PropTypes.string, diff --git a/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js b/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js new file mode 100644 index 00000000..28592017 --- /dev/null +++ b/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js @@ -0,0 +1,87 @@ +import React, { Component } from 'react'; +import styled from 'styled-components'; +import PropTypes from 'prop-types'; + +import { PRIORITY } from 'constants/notifications'; +import Group from './components/Group'; + +const Wrapper = styled.div``; + +class NotificationsGroup extends Component { + groupNotifications = notifications => notifications + .reduce((acc, obj) => { + const key = obj.type; + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(obj); + return acc; + }, {}); + + sortByPriority(notifications) { + return notifications; + } + + render() { + const { close, notifications } = this.props; + // const notifications = [ + // { + // key: '1', + // title: 'this is a title of error notification', + // type: 'error', + // message: 'this is a message of error notification', + // }, + // { + // key: '2', + // title: 'this is a title of warning notification', + // type: 'warning', + // message: 'this is a message of warning notification', + // }, + // { + // key: '3', + // title: 'this is a title of warning notification', + // type: 'warning', + // message: 'this is a message of warning notification', + // }, + // { + // key: '4', + // title: 'this is a title of warning notification', + // type: 'warning', + // message: 'this is a message of warning notification', + // }, + // { + // key: '5', + // title: 'this is a title of warning notification', + // type: 'info', + // message: 'this is a message of warning notification', + // }, + // { + // key: '6', + // title: 'this is a title of info notification', + // type: 'info', + // message: 'this is a message of info notification', + // }, + // ]; + const notificationGroups = this.groupNotifications(notifications); + const sortedNotifications = this.sortByPriority(notificationGroups); + + return ( + + {Object.keys(sortedNotifications).map(group => ( + + ))} + + ); + } +} + +NotificationsGroup.propTypes = { + notifications: PropTypes.array.isRequired, + close: PropTypes.func.isRequired, +}; + +export default NotificationsGroup; \ No newline at end of file diff --git a/src/components/notifications/Context/components/Action/index.js b/src/components/notifications/Context/components/Action/index.js index 0bf7f0be..3050e159 100644 --- a/src/components/notifications/Context/components/Action/index.js +++ b/src/components/notifications/Context/components/Action/index.js @@ -1,20 +1,15 @@ /* @flow */ import * as React from 'react'; -import { Notification } from 'components/Notification'; +import NotificationsGroups from './components/NotificationsGroups'; import type { Props } from '../../index'; export default (props: Props) => { const { notifications, close } = props; - return notifications.map(n => ( - - )); + ); }; \ No newline at end of file From 6c481e954d0e2e23ce39f00c77a6891f0659e0db Mon Sep 17 00:00:00 2001 From: Vladimir Volek Date: Tue, 2 Oct 2018 17:58:37 +0200 Subject: [PATCH 36/38] Fixed eslint warning --- .../components/Action/components/NotificationsGroups/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js b/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js index 28592017..169a689c 100644 --- a/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js +++ b/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js @@ -69,6 +69,7 @@ class NotificationsGroup extends Component { {Object.keys(sortedNotifications).map(group => ( Date: Tue, 2 Oct 2018 18:05:10 +0200 Subject: [PATCH 37/38] Switched behavior --- .../components/Group/index.js | 6 +- .../components/NotificationsGroups/index.js | 78 +++++++++---------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js b/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js index 298a77df..0edceb1d 100644 --- a/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js +++ b/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js @@ -45,12 +45,12 @@ class Group extends Component { if (this.state.visible) { this.setState({ visible: false, - visibleCount: 0, + visibleCount: this.props.groupNotifications.length, }); } else { this.setState({ visible: true, - visibleCount: this.props.groupNotifications.length, + visibleCount: 0, }); } } @@ -88,7 +88,7 @@ class Group extends Component { .slice(0, this.state.visibleCount) .map(notification => ( Date: Tue, 2 Oct 2018 19:09:57 +0200 Subject: [PATCH 38/38] Fixed proptypes --- .../components/Group/index.js | 16 ++-- .../components/NotificationsGroups/index.js | 78 +++++++++---------- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js b/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js index 0edceb1d..3edec964 100644 --- a/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js +++ b/src/components/notifications/Context/components/Action/components/NotificationsGroups/components/Group/index.js @@ -77,7 +77,7 @@ class Group extends Component { icon={ICONS.ARROW_DOWN} color={colors.TEXT_SECONDARY} size={24} - isActive={this.state.visible} + isActive={!this.state.visible} canAnimate /> @@ -106,12 +106,14 @@ class Group extends Component { Group.propTypes = { type: PropTypes.string, close: PropTypes.func.isRequired, - groupNotifications: PropTypes.arrayOf({ - key: PropTypes.string, - type: PropTypes.string, - title: PropTypes.string, - message: PropTypes.string, - }), + groupNotifications: PropTypes.arrayOf( + PropTypes.shape({ + key: PropTypes.number, + type: PropTypes.string, + title: PropTypes.string, + message: PropTypes.string, + }), + ), }; export default Group; \ No newline at end of file diff --git a/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js b/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js index d100cb92..db61988c 100644 --- a/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js +++ b/src/components/notifications/Context/components/Action/components/NotificationsGroups/index.js @@ -23,45 +23,45 @@ class NotificationsGroup extends Component { } render() { - const { close } = this.props; - const notifications = [ - { - key: 1, - title: 'this is a title of error notification', - type: 'error', - message: 'this is a message of error notification', - }, - { - key: 2, - title: 'this is a title of warning notification', - type: 'warning', - message: 'this is a message of warning notification', - }, - { - key: 3, - title: 'this is a title of warning notification', - type: 'warning', - message: 'this is a message of warning notification', - }, - { - key: 4, - title: 'this is a title of warning notification sds d', - type: 'warning', - message: 'this is a message of warning notification', - }, - { - key: 5, - title: 'this is a title of warning notification as', - type: 'info', - message: 'this is a message of warning notification', - }, - { - key: 6, - title: 'this is a title of info notification s ', - type: 'info', - message: 'this is a message of info notification', - }, - ]; + const { close, notifications } = this.props; + // const notifications = [ + // { + // key: 1, + // title: 'this is a title of error notification', + // type: 'error', + // message: 'this is a message of error notification', + // }, + // { + // key: 2, + // title: 'this is a title of warning notification', + // type: 'warning', + // message: 'this is a message of warning notification', + // }, + // { + // key: 3, + // title: 'this is a title of warning notification', + // type: 'warning', + // message: 'this is a message of warning notification', + // }, + // { + // key: 4, + // title: 'this is a title of warning notification sds d', + // type: 'warning', + // message: 'this is a message of warning notification', + // }, + // { + // key: 5, + // title: 'this is a title of warning notification as', + // type: 'info', + // message: 'this is a message of warning notification', + // }, + // { + // key: 6, + // title: 'this is a title of info notification s ', + // type: 'info', + // message: 'this is a message of info notification', + // }, + // ]; const notificationGroups = this.groupNotifications(notifications); const sortedNotifications = this.sortByPriority(notificationGroups);