mirror of
https://github.com/trezor/trezor-wallet
synced 2024-12-25 00:18:07 +00:00
Merge branch 'master' into fonts-refactoring
This commit is contained in:
commit
859b1ca6db
@ -1,4 +1,9 @@
|
|||||||
## feature/ripple
|
## 1.1.2-beta
|
||||||
|
|
||||||
|
__added__
|
||||||
|
- Stellar and Cardano external wallets
|
||||||
|
|
||||||
|
# new feature/ripple
|
||||||
__changed__
|
__changed__
|
||||||
- Split code to coin specific components. actions and reducers
|
- Split code to coin specific components. actions and reducers
|
||||||
- Use TrezorConnect to communicate with trezor-blockchain-link
|
- Use TrezorConnect to communicate with trezor-blockchain-link
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
"rc-tooltip": "^3.7.0",
|
"rc-tooltip": "^3.7.0",
|
||||||
"react": "^16.6.3",
|
"react": "^16.6.3",
|
||||||
"react-dom": "^16.6.3",
|
"react-dom": "^16.6.3",
|
||||||
"react-hot-loader": "^4.3.4",
|
"react-hot-loader": "^4.6.2",
|
||||||
"react-json-view": "^1.19.1",
|
"react-json-view": "^1.19.1",
|
||||||
"react-qr-svg": "^2.1.0",
|
"react-qr-svg": "^2.1.0",
|
||||||
"react-redux": "^5.0.7",
|
"react-redux": "^5.0.7",
|
||||||
|
@ -8,6 +8,7 @@ import * as TOKEN from 'actions/constants/token';
|
|||||||
import * as PENDING from 'actions/constants/pendingTx';
|
import * as PENDING from 'actions/constants/pendingTx';
|
||||||
|
|
||||||
import * as reducerUtils from 'reducers/utils';
|
import * as reducerUtils from 'reducers/utils';
|
||||||
|
import { initialState } from 'reducers/SelectedAccountReducer';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
PayloadAction,
|
PayloadAction,
|
||||||
@ -17,7 +18,12 @@ import type {
|
|||||||
State,
|
State,
|
||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
|
|
||||||
type SelectedAccountState = $ElementType<State, 'selectedAccount'>;
|
import type {
|
||||||
|
State as SelectedAccountState,
|
||||||
|
Loader,
|
||||||
|
Notification,
|
||||||
|
ExceptionPage,
|
||||||
|
} from 'reducers/SelectedAccountReducer';
|
||||||
|
|
||||||
export type SelectedAccountAction = {
|
export type SelectedAccountAction = {
|
||||||
type: typeof ACCOUNT.DISPOSE,
|
type: typeof ACCOUNT.DISPOSE,
|
||||||
@ -26,18 +32,39 @@ export type SelectedAccountAction = {
|
|||||||
payload: SelectedAccountState,
|
payload: SelectedAccountState,
|
||||||
};
|
};
|
||||||
|
|
||||||
type AccountStatus = {
|
|
||||||
type: ?string; // notification type
|
|
||||||
title: ?string; // notification title
|
|
||||||
message?: ?string; // notification message
|
|
||||||
shouldRender: boolean; // should render account page
|
|
||||||
}
|
|
||||||
|
|
||||||
export const dispose = (): Action => ({
|
export const dispose = (): Action => ({
|
||||||
type: ACCOUNT.DISPOSE,
|
type: ACCOUNT.DISPOSE,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getAccountLoader = (state: State, selectedAccount: SelectedAccountState): ?AccountStatus => {
|
// display exception page instead of component body
|
||||||
|
const getExceptionPage = (state: State, selectedAccount: SelectedAccountState): ?ExceptionPage => {
|
||||||
|
const device = state.wallet.selectedDevice;
|
||||||
|
const { discovery, network } = selectedAccount;
|
||||||
|
if (!device || !device.features || !network || !discovery) return null;
|
||||||
|
|
||||||
|
if (discovery.fwOutdated) {
|
||||||
|
// those values are not used because in this case views/Wallet/views/FirmwareUpdate component will be displayed and it already has text content
|
||||||
|
return {
|
||||||
|
type: 'fwOutdated',
|
||||||
|
title: 'not-used',
|
||||||
|
message: 'not-used',
|
||||||
|
shortcut: 'not-used',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (discovery.fwNotSupported) {
|
||||||
|
return {
|
||||||
|
type: 'fwNotSupported',
|
||||||
|
title: `${network.name} is not supported with Trezor ${device.features.model}`,
|
||||||
|
message: 'Find more information on Trezor Wiki.',
|
||||||
|
shortcut: network.shortcut,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// display loader instead of component body
|
||||||
|
const getAccountLoader = (state: State, selectedAccount: SelectedAccountState): ?Loader => {
|
||||||
const device = state.wallet.selectedDevice;
|
const device = state.wallet.selectedDevice;
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
@ -49,16 +76,14 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
|||||||
return {
|
return {
|
||||||
type: 'progress',
|
type: 'progress',
|
||||||
title: 'Loading device...',
|
title: 'Loading device...',
|
||||||
shouldRender: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// corner case: accountState didn't finish loading state after LOCATION_CHANGE action
|
// corner case: SelectedAccountState didn't change after LOCATION_CHANGE action
|
||||||
if (!network) {
|
if (!network) {
|
||||||
return {
|
return {
|
||||||
type: 'progress',
|
type: 'progress',
|
||||||
title: 'Loading account state...',
|
title: 'Loading account',
|
||||||
shouldRender: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,24 +91,6 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
|||||||
if (account) return null;
|
if (account) return null;
|
||||||
// account not found (yet). checking why...
|
// account not found (yet). checking why...
|
||||||
|
|
||||||
if (discovery && discovery.fwOutdated) {
|
|
||||||
return {
|
|
||||||
type: 'info',
|
|
||||||
title: `Device ${device.instanceLabel} firmware is outdated`,
|
|
||||||
message: 'TODO: update firmware explanation',
|
|
||||||
shouldRender: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (discovery && discovery.fwNotSupported) {
|
|
||||||
return {
|
|
||||||
type: 'info',
|
|
||||||
title: `Device ${device.instanceLabel} is not supported`,
|
|
||||||
message: 'TODO: model T1 not supported explanation',
|
|
||||||
shouldRender: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!discovery || (discovery.waitingForDevice || discovery.interrupted)) {
|
if (!discovery || (discovery.waitingForDevice || discovery.interrupted)) {
|
||||||
if (device.connected) {
|
if (device.connected) {
|
||||||
// case 1: device is connected but discovery not started yet (probably waiting for auth)
|
// case 1: device is connected but discovery not started yet (probably waiting for auth)
|
||||||
@ -91,7 +98,6 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
|||||||
return {
|
return {
|
||||||
type: 'progress',
|
type: 'progress',
|
||||||
title: 'Authenticating device...',
|
title: 'Authenticating device...',
|
||||||
shouldRender: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
|
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
|
||||||
@ -100,7 +106,6 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
|||||||
type: 'info',
|
type: 'info',
|
||||||
title: `Device ${device.instanceLabel} is unavailable`,
|
title: `Device ${device.instanceLabel} is unavailable`,
|
||||||
message: 'Change passphrase settings to use this device',
|
message: 'Change passphrase settings to use this device',
|
||||||
shouldRender: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +114,6 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
|||||||
type: 'info',
|
type: 'info',
|
||||||
title: `Device ${device.instanceLabel} is disconnected`,
|
title: `Device ${device.instanceLabel} is disconnected`,
|
||||||
message: 'Connect device to load accounts',
|
message: 'Connect device to load accounts',
|
||||||
shouldRender: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +122,6 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
|||||||
return {
|
return {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
title: 'Account does not exist',
|
title: 'Account does not exist',
|
||||||
shouldRender: false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,11 +129,11 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
|||||||
return {
|
return {
|
||||||
type: 'progress',
|
type: 'progress',
|
||||||
title: 'Loading account',
|
title: 'Loading account',
|
||||||
shouldRender: false,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAccountNotification = (state: State, selectedAccount: SelectedAccountState): ?AccountStatus => {
|
// display notification above the component, with or without component body
|
||||||
|
const getAccountNotification = (state: State, selectedAccount: SelectedAccountState): ?(Notification & { shouldRender: boolean }) => {
|
||||||
const device = state.wallet.selectedDevice;
|
const device = state.wallet.selectedDevice;
|
||||||
const { account, network, discovery } = selectedAccount;
|
const { account, network, discovery } = selectedAccount;
|
||||||
if (!device || !network) return null;
|
if (!device || !network) return null;
|
||||||
@ -198,16 +201,6 @@ export const observe = (prevState: State, action: Action): PayloadAction<boolean
|
|||||||
// ignore not listed actions
|
// ignore not listed actions
|
||||||
if (actions.indexOf(action.type) < 0) return false;
|
if (actions.indexOf(action.type) < 0) return false;
|
||||||
const state: State = getState();
|
const state: State = getState();
|
||||||
const notification = {
|
|
||||||
type: null,
|
|
||||||
message: null,
|
|
||||||
title: null,
|
|
||||||
};
|
|
||||||
const loader = {
|
|
||||||
type: null,
|
|
||||||
message: null,
|
|
||||||
title: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { location } = state.router;
|
const { location } = state.router;
|
||||||
// displayed route is not an account route
|
// displayed route is not an account route
|
||||||
@ -222,25 +215,29 @@ export const observe = (prevState: State, action: Action): PayloadAction<boolean
|
|||||||
|
|
||||||
// prepare new state for "selectedAccount" reducer
|
// prepare new state for "selectedAccount" reducer
|
||||||
const newState: SelectedAccountState = {
|
const newState: SelectedAccountState = {
|
||||||
|
...initialState,
|
||||||
location: state.router.location.pathname,
|
location: state.router.location.pathname,
|
||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
discovery,
|
discovery,
|
||||||
tokens,
|
tokens,
|
||||||
pending,
|
pending,
|
||||||
notification,
|
|
||||||
loader,
|
|
||||||
shouldRender: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// get "selectedAccount" status from newState
|
// get "selectedAccount" status from newState
|
||||||
const statusNotification = getAccountNotification(state, newState);
|
const exceptionPage = getExceptionPage(state, newState);
|
||||||
const statusLoader = getAccountLoader(state, newState);
|
const loader = getAccountLoader(state, newState);
|
||||||
const shouldRender = (statusNotification && statusLoader) ? (statusNotification.shouldRender || statusLoader.shouldRender) : true;
|
const notification = getAccountNotification(state, newState);
|
||||||
|
|
||||||
|
if (exceptionPage) {
|
||||||
|
newState.exceptionPage = exceptionPage;
|
||||||
|
} else {
|
||||||
|
newState.loader = loader;
|
||||||
|
newState.notification = notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
newState.shouldRender = !(loader || exceptionPage || (notification && !notification.shouldRender));
|
||||||
|
|
||||||
newState.notification = statusNotification || notification;
|
|
||||||
newState.shouldRender = shouldRender;
|
|
||||||
newState.loader = statusLoader || loader;
|
|
||||||
// check if newState is different than previous state
|
// check if newState is different than previous state
|
||||||
const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, {
|
const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, {
|
||||||
account: ['balance', 'nonce'],
|
account: ['balance', 'nonce'],
|
||||||
|
@ -101,6 +101,7 @@ const DeviceHeader = ({
|
|||||||
isAccessible = true,
|
isAccessible = true,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
isSelected = false,
|
isSelected = false,
|
||||||
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
const status = getStatus(device);
|
const status = getStatus(device);
|
||||||
return (
|
return (
|
||||||
@ -109,6 +110,7 @@ const DeviceHeader = ({
|
|||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
isHoverable={isHoverable}
|
isHoverable={isHoverable}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
className={className}
|
||||||
>
|
>
|
||||||
<ClickWrapper
|
<ClickWrapper
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@ -139,6 +141,7 @@ DeviceHeader.propTypes = {
|
|||||||
isOpen: PropTypes.bool,
|
isOpen: PropTypes.bool,
|
||||||
isSelected: PropTypes.bool,
|
isSelected: PropTypes.bool,
|
||||||
onClickWrapper: PropTypes.func.isRequired,
|
onClickWrapper: PropTypes.func.isRequired,
|
||||||
|
className: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeviceHeader;
|
export default DeviceHeader;
|
||||||
|
@ -28,13 +28,23 @@ class CoinLogo extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { network } = this.props;
|
const { network, className, standalone } = this.props;
|
||||||
return (
|
|
||||||
<Wrapper>
|
const logo = (
|
||||||
<Logo
|
<Logo
|
||||||
|
className={className}
|
||||||
hasLongIcon={this.hasLongIcon(network)}
|
hasLongIcon={this.hasLongIcon(network)}
|
||||||
src={require(`./images/${network}.png`)} // eslint-disable-line
|
src={require(`./images/${network}.png`)} // eslint-disable-line
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (standalone) {
|
||||||
|
return logo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper className={className}>
|
||||||
|
{logo}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -42,6 +52,8 @@ class CoinLogo extends PureComponent {
|
|||||||
|
|
||||||
CoinLogo.propTypes = {
|
CoinLogo.propTypes = {
|
||||||
network: PropTypes.string,
|
network: PropTypes.string,
|
||||||
|
className: PropTypes.string,
|
||||||
|
standalone: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CoinLogo;
|
export default CoinLogo;
|
||||||
|
@ -7,7 +7,8 @@ import type { Props } from '../../index';
|
|||||||
// There could be only one account notification
|
// There could be only one account notification
|
||||||
export default (props: Props) => {
|
export default (props: Props) => {
|
||||||
const { network, notification } = props.selectedAccount;
|
const { network, notification } = props.selectedAccount;
|
||||||
if (network && notification.type && notification.title) {
|
if (!network || !notification) return null;
|
||||||
|
|
||||||
if (notification.type === 'backend') {
|
if (notification.type === 'backend') {
|
||||||
// special case: backend is down
|
// special case: backend is down
|
||||||
// TODO: this is a different component with "auto resolve" button
|
// TODO: this is a different component with "auto resolve" button
|
||||||
@ -34,7 +35,4 @@ export default (props: Props) => {
|
|||||||
message={notification.message}
|
message={notification.message}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
@ -10,23 +10,35 @@ import type {
|
|||||||
Discovery,
|
Discovery,
|
||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
|
|
||||||
|
export type Loader = {
|
||||||
|
type: string,
|
||||||
|
title: string,
|
||||||
|
message?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Notification = {
|
||||||
|
type: string,
|
||||||
|
title: string,
|
||||||
|
message?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExceptionPage = {
|
||||||
|
type: ?string,
|
||||||
|
title: ?string,
|
||||||
|
message: ?string,
|
||||||
|
shortcut: string,
|
||||||
|
}
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
location: string;
|
location: string,
|
||||||
account: ?Account;
|
account: ?Account,
|
||||||
network: ?Network;
|
network: ?Network,
|
||||||
tokens: Array<Token>,
|
tokens: Array<Token>,
|
||||||
pending: Array<PendingTx>,
|
pending: Array<PendingTx>,
|
||||||
discovery: ?Discovery,
|
discovery: ?Discovery,
|
||||||
notification: {
|
loader: ?Loader,
|
||||||
type: ?string,
|
notification: ?Notification,
|
||||||
title: ?string,
|
exceptionPage: ?ExceptionPage,
|
||||||
message: ?string,
|
|
||||||
},
|
|
||||||
loader: {
|
|
||||||
type: ?string,
|
|
||||||
title: ?string,
|
|
||||||
message: ?string,
|
|
||||||
},
|
|
||||||
shouldRender: boolean,
|
shouldRender: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,16 +49,9 @@ export const initialState: State = {
|
|||||||
tokens: [],
|
tokens: [],
|
||||||
pending: [],
|
pending: [],
|
||||||
discovery: null,
|
discovery: null,
|
||||||
notification: {
|
loader: null,
|
||||||
type: null,
|
notification: null,
|
||||||
title: null,
|
exceptionPage: null,
|
||||||
message: null,
|
|
||||||
},
|
|
||||||
loader: {
|
|
||||||
type: null,
|
|
||||||
title: null,
|
|
||||||
message: null,
|
|
||||||
},
|
|
||||||
shouldRender: false,
|
shouldRender: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,9 +47,11 @@ if (buildUtils.isDev()) {
|
|||||||
collapsed: true,
|
collapsed: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window && typeof window.devToolsExtension === 'function') {
|
/* eslint-disable no-underscore-dangle */
|
||||||
enhancers.push(window.devToolsExtension());
|
if (window && typeof window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ === 'function') {
|
||||||
|
enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__());
|
||||||
}
|
}
|
||||||
|
/* eslint-enable */
|
||||||
|
|
||||||
composedEnhancers = compose(
|
composedEnhancers = compose(
|
||||||
applyMiddleware(logger, ...middlewares, ...services),
|
applyMiddleware(logger, ...middlewares, ...services),
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
/* @flow */
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import colors from 'config/colors';
|
||||||
|
|
||||||
|
import { FONT_SIZE } from 'config/variables';
|
||||||
|
import { H2 } from 'components/Heading';
|
||||||
|
import Button from 'components/Button';
|
||||||
|
import Link from 'components/Link';
|
||||||
|
import CoinLogo from 'components/images/CoinLogo';
|
||||||
|
|
||||||
|
const getInfoUrl = (networkShortcut: ?string) => {
|
||||||
|
const urls = {
|
||||||
|
default: 'https://wiki.trezor.io',
|
||||||
|
xrp: 'https://wiki.trezor.io/Ripple_(XRP)',
|
||||||
|
};
|
||||||
|
|
||||||
|
return networkShortcut ? urls[networkShortcut] : urls.default;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
networkShortcut: ?string,
|
||||||
|
title: ?string,
|
||||||
|
message: ?string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
background: ${colors.WHITE};
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CoinLogoWrapper = styled.div`
|
||||||
|
margin: 10px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledCoinLogo = styled(CoinLogo)`
|
||||||
|
width: 32px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledLink = styled(Link)`
|
||||||
|
padding-top: 24px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Row = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Message = styled.div`
|
||||||
|
font-size: ${FONT_SIZE.SMALL};
|
||||||
|
color: ${colors.TEXT_SECONDARY};
|
||||||
|
text-align: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FirmwareUnsupported = (props: Props) => (
|
||||||
|
<Wrapper>
|
||||||
|
<Row>
|
||||||
|
{props.networkShortcut && <CoinLogoWrapper><StyledCoinLogo standalone network={props.networkShortcut} /></CoinLogoWrapper>}
|
||||||
|
<H2>{props.title}</H2>
|
||||||
|
<Message>{props.message}</Message>
|
||||||
|
<StyledLink href={getInfoUrl(props.networkShortcut)}>
|
||||||
|
<Button>Find out more info</Button>
|
||||||
|
</StyledLink>
|
||||||
|
</Row>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FirmwareUnsupported;
|
@ -1,10 +1,24 @@
|
|||||||
import React from 'react';
|
/* @flow */
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
import { FONT_SIZE } from 'config/variables';
|
import { FONT_SIZE } from 'config/variables';
|
||||||
import colors from 'config/colors';
|
import colors from 'config/colors';
|
||||||
|
|
||||||
|
import type { State } from 'flowtype';
|
||||||
|
|
||||||
|
import FirmwareUpdate from 'views/Wallet/views/FirmwareUpdate';
|
||||||
|
import FirmwareUnsupported from './components/FirmwareUnsupported';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children?: React.Node,
|
||||||
|
isLoading?: boolean,
|
||||||
|
loader?: $ElementType<$ElementType<State, 'selectedAccount'>, 'loader'>,
|
||||||
|
exceptionPage?: $ElementType<$ElementType<State, 'selectedAccount'>, 'exceptionPage'>,
|
||||||
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -36,22 +50,33 @@ const Row = styled.div`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const getExceptionPage = (exceptionPage) => {
|
||||||
|
const { title, message, shortcut } = exceptionPage;
|
||||||
|
switch (exceptionPage.type) {
|
||||||
|
case 'fwOutdated':
|
||||||
|
return <FirmwareUpdate />;
|
||||||
|
case 'fwNotSupported':
|
||||||
|
return <FirmwareUnsupported title={title} message={message} networkShortcut={shortcut} />;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const Content = ({
|
const Content = ({
|
||||||
children,
|
children,
|
||||||
title,
|
|
||||||
message,
|
|
||||||
type,
|
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
}) => (
|
loader,
|
||||||
|
exceptionPage,
|
||||||
|
}: Props) => (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{(!isLoading) && children}
|
{(!isLoading) && children}
|
||||||
{isLoading && (type === 'progress' || type === 'info') && (
|
{isLoading && exceptionPage && getExceptionPage(exceptionPage)}
|
||||||
|
{isLoading && loader && (
|
||||||
<Loading>
|
<Loading>
|
||||||
<Row>
|
<Row>
|
||||||
{type === 'progress' && <Loader size={30} />}
|
{loader.type === 'progress' && <Loader size={30} />}
|
||||||
<Text>{title || 'Initializing accounts'}</Text>
|
<Text>{loader.title || 'Initializing accounts'}</Text>
|
||||||
</Row>
|
</Row>
|
||||||
{message && <Message>{message}</Message>}
|
{loader.message && <Message>{loader.message}</Message>}
|
||||||
</Loading>
|
</Loading>
|
||||||
)}
|
)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
@ -60,9 +85,8 @@ const Content = ({
|
|||||||
Content.propTypes = {
|
Content.propTypes = {
|
||||||
children: PropTypes.oneOfType([PropTypes.element, PropTypes.array]),
|
children: PropTypes.oneOfType([PropTypes.element, PropTypes.array]),
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
title: PropTypes.string,
|
loader: PropTypes.object,
|
||||||
message: PropTypes.string,
|
exceptionPage: PropTypes.object,
|
||||||
type: PropTypes.string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Content;
|
export default Content;
|
||||||
|
@ -19,8 +19,10 @@ type State = {
|
|||||||
footerFixed: boolean,
|
footerFixed: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AsideWrapper = styled.aside`
|
const AsideWrapper = styled.aside.attrs(props => ({
|
||||||
min-height: ${props => props.minHeight}px;
|
style: { minHeight: props.minHeight },
|
||||||
|
}))`
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
width: 320px;
|
width: 320px;
|
||||||
@ -30,10 +32,13 @@ const AsideWrapper = styled.aside`
|
|||||||
border-right: 1px solid ${colors.DIVIDER};
|
border-right: 1px solid ${colors.DIVIDER};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StickyContainerWrapper = styled.div`
|
const StickyContainerWrapper = styled.div.attrs(props => ({
|
||||||
top: ${props => props.top}px;
|
style: {
|
||||||
left: ${props => props.left}px;
|
top: props.top,
|
||||||
padding-bottom: ${props => props.paddingBottom}px;
|
left: props.left,
|
||||||
|
paddingBottom: props.paddingBottom,
|
||||||
|
},
|
||||||
|
}))`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
border-right: 1px solid ${colors.DIVIDER};
|
border-right: 1px solid ${colors.DIVIDER};
|
||||||
width: 320px;
|
width: 320px;
|
||||||
|
@ -45,8 +45,9 @@ const TransitionContentWrapper = styled.div`
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Footer = styled.div`
|
const Footer = styled.div.attrs(props => ({
|
||||||
position: ${props => props.position};
|
style: { position: props.position },
|
||||||
|
}))`
|
||||||
width: 320px;
|
width: 320px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: ${colors.MAIN};
|
background: ${colors.MAIN};
|
||||||
|
@ -96,10 +96,12 @@ const AccountReceive = (props: Props) => {
|
|||||||
account,
|
account,
|
||||||
discovery,
|
discovery,
|
||||||
shouldRender,
|
shouldRender,
|
||||||
loader,
|
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
const { type, title, message } = loader;
|
|
||||||
if (!device || !account || !discovery || !shouldRender) return <Content type={type} title={title} message={message} isLoading />;
|
if (!device || !account || !discovery || !shouldRender) {
|
||||||
|
const { loader, exceptionPage } = props.selectedAccount;
|
||||||
|
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
addressVerified,
|
addressVerified,
|
||||||
|
@ -96,10 +96,12 @@ const AccountReceive = (props: Props) => {
|
|||||||
account,
|
account,
|
||||||
discovery,
|
discovery,
|
||||||
shouldRender,
|
shouldRender,
|
||||||
loader,
|
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
const { type, title, message } = loader;
|
|
||||||
if (!device || !account || !discovery || !shouldRender) return <Content type={type} title={title} message={message} isLoading />;
|
if (!device || !account || !discovery || !shouldRender) {
|
||||||
|
const { loader, exceptionPage } = props.selectedAccount;
|
||||||
|
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
addressVerified,
|
addressVerified,
|
||||||
|
@ -184,7 +184,6 @@ const AccountSend = (props: Props) => {
|
|||||||
discovery,
|
discovery,
|
||||||
tokens,
|
tokens,
|
||||||
shouldRender,
|
shouldRender,
|
||||||
loader,
|
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
const {
|
const {
|
||||||
address,
|
address,
|
||||||
@ -213,8 +212,11 @@ const AccountSend = (props: Props) => {
|
|||||||
updateFeeLevels,
|
updateFeeLevels,
|
||||||
onSend,
|
onSend,
|
||||||
} = props.sendFormActions;
|
} = props.sendFormActions;
|
||||||
const { type, title, message } = loader;
|
|
||||||
if (!device || !account || !discovery || !network || !shouldRender) return <Content type={type} title={title} message={message} isLoading />;
|
if (!device || !account || !discovery || !network || !shouldRender) {
|
||||||
|
const { loader, exceptionPage } = props.selectedAccount;
|
||||||
|
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
const isCurrentCurrencyToken = networkSymbol !== currency;
|
const isCurrentCurrencyToken = networkSymbol !== currency;
|
||||||
|
|
||||||
|
@ -122,7 +122,6 @@ const AccountSend = (props: Props) => {
|
|||||||
network,
|
network,
|
||||||
discovery,
|
discovery,
|
||||||
shouldRender,
|
shouldRender,
|
||||||
loader,
|
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
const {
|
const {
|
||||||
address,
|
address,
|
||||||
@ -142,8 +141,11 @@ const AccountSend = (props: Props) => {
|
|||||||
onSetMax,
|
onSetMax,
|
||||||
onSend,
|
onSend,
|
||||||
} = props.sendFormActions;
|
} = props.sendFormActions;
|
||||||
const { type, title, message } = loader;
|
|
||||||
if (!device || !account || !discovery || !network || !shouldRender) return <Content type={type} title={title} message={message} isLoading />;
|
if (!device || !account || !discovery || !network || !shouldRender) {
|
||||||
|
const { loader, exceptionPage } = props.selectedAccount;
|
||||||
|
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
let isSendButtonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending;
|
let isSendButtonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending;
|
||||||
let sendButtonText: string = ` ${total} ${network.symbol}`;
|
let sendButtonText: string = ` ${total} ${network.symbol}`;
|
||||||
|
@ -57,10 +57,14 @@ class SignVerify extends Component <Props> {
|
|||||||
render() {
|
render() {
|
||||||
const device = this.props.wallet.selectedDevice;
|
const device = this.props.wallet.selectedDevice;
|
||||||
const {
|
const {
|
||||||
account, discovery, shouldRender, notification,
|
account, discovery, shouldRender,
|
||||||
} = this.props.selectedAccount;
|
} = this.props.selectedAccount;
|
||||||
const { type, title, message } = notification;
|
|
||||||
if (!device || !account || !discovery || !shouldRender) return <Content type={type} title={title} message={message} isLoading />;
|
if (!device || !account || !discovery || !shouldRender) {
|
||||||
|
const { loader, exceptionPage } = this.props.selectedAccount;
|
||||||
|
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
signVerifyActions,
|
signVerifyActions,
|
||||||
signVerify: {
|
signVerify: {
|
||||||
|
@ -76,13 +76,13 @@ const AccountSummary = (props: Props) => {
|
|||||||
network,
|
network,
|
||||||
tokens,
|
tokens,
|
||||||
pending,
|
pending,
|
||||||
loader,
|
|
||||||
shouldRender,
|
shouldRender,
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
|
|
||||||
const { type, title, message } = loader;
|
if (!device || !account || !network || !shouldRender) {
|
||||||
|
const { loader, exceptionPage } = props.selectedAccount;
|
||||||
if (!device || !account || !network || !shouldRender) return <Content type={type} title={title} message={message} isLoading />;
|
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
const explorerLink: string = `${network.explorer.address}${account.address}`;
|
const explorerLink: string = `${network.explorer.address}${account.address}`;
|
||||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
|
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
|
||||||
|
@ -66,13 +66,13 @@ const AccountSummary = (props: Props) => {
|
|||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
pending,
|
pending,
|
||||||
loader,
|
|
||||||
shouldRender,
|
shouldRender,
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
|
|
||||||
const { type, title, message } = loader;
|
if (!device || !account || !network || !shouldRender) {
|
||||||
|
const { loader, exceptionPage } = props.selectedAccount;
|
||||||
if (!device || !account || !network || !shouldRender) return <Content type={type} title={title} message={message} isLoading />;
|
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
const explorerLink: string = `${network.explorer.address}${account.address}`;
|
const explorerLink: string = `${network.explorer.address}${account.address}`;
|
||||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
|
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
|
||||||
|
@ -9,16 +9,14 @@ import DashboardImg from 'images/dashboard.png';
|
|||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Row = styled.div`
|
const Row = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0px 48px;
|
padding: 100px 48px;
|
||||||
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
26
yarn.lock
26
yarn.lock
@ -7249,6 +7249,19 @@ lodash.keys@^3.1.2:
|
|||||||
lodash.isarguments "^3.0.0"
|
lodash.isarguments "^3.0.0"
|
||||||
lodash.isarray "^3.0.0"
|
lodash.isarray "^3.0.0"
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
lodash.memoize@^4.1.2:
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||||
|
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||||
|
|
||||||
|
lodash.merge@^4.6.1:
|
||||||
|
version "4.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54"
|
||||||
|
integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==
|
||||||
|
|
||||||
|
>>>>>>> master
|
||||||
lodash.sortby@^4.7.0:
|
lodash.sortby@^4.7.0:
|
||||||
version "4.7.0"
|
version "4.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||||
@ -9283,17 +9296,20 @@ react-dom@^16.6.3:
|
|||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
scheduler "^0.11.2"
|
scheduler "^0.11.2"
|
||||||
|
|
||||||
react-hot-loader@^4.3.4:
|
react-hot-loader@^4.6.2:
|
||||||
version "4.3.4"
|
version "4.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.3.4.tgz#4f9bdd55bb20d77a6ae8931fa1c187e5f0ce6279"
|
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.6.2.tgz#9844b76a7bf4b6fdd45dd91f7e757ddf3aad5289"
|
||||||
integrity sha512-LlKjtHq+RhDq9xm6crXojbkzrEvli5F4/RaeJ//XtDWrwwsAHDjEqKfZZiPCxv7gWV2cxE3YE8TXeE9BDzLqOA==
|
integrity sha512-9XxH/t9jblu4vUDgxHcMLwvm4aOhaoxazzTP9vwjTVzOgU987T4rDYA85XrlmbDan9WkD+h/iVHOK8F8UnQsDg==
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-levenshtein "^2.0.6"
|
fast-levenshtein "^2.0.6"
|
||||||
global "^4.3.0"
|
global "^4.3.0"
|
||||||
hoist-non-react-statics "^2.5.0"
|
hoist-non-react-statics "^2.5.0"
|
||||||
|
loader-utils "^1.1.0"
|
||||||
|
lodash.merge "^4.6.1"
|
||||||
prop-types "^15.6.1"
|
prop-types "^15.6.1"
|
||||||
react-lifecycles-compat "^3.0.4"
|
react-lifecycles-compat "^3.0.4"
|
||||||
shallowequal "^1.0.2"
|
shallowequal "^1.0.2"
|
||||||
|
source-map "^0.7.3"
|
||||||
|
|
||||||
react-input-autosize@^2.2.1:
|
react-input-autosize@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
@ -10606,7 +10622,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
|
||||||
source-map@^0.7.2:
|
source-map@^0.7.2, source-map@^0.7.3:
|
||||||
version "0.7.3"
|
version "0.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||||
|
Loading…
Reference in New Issue
Block a user