mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-30 20:28:09 +00:00
split loader, notification and exceptionPage to isolated variables
and pass them as they are to views/Wallet/components/Content
This commit is contained in:
parent
f34992dd68
commit
2aa6a82911
@ -8,6 +8,7 @@ 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 {
|
||||
PayloadAction,
|
||||
@ -17,7 +18,12 @@ import type {
|
||||
State,
|
||||
} from 'flowtype';
|
||||
|
||||
type SelectedAccountState = $ElementType<State, 'selectedAccount'>;
|
||||
import type {
|
||||
State as SelectedAccountState,
|
||||
Loader,
|
||||
Notification,
|
||||
ExceptionPage,
|
||||
} from 'reducers/SelectedAccountReducer';
|
||||
|
||||
export type SelectedAccountAction = {
|
||||
type: typeof ACCOUNT.DISPOSE,
|
||||
@ -26,18 +32,38 @@ export type SelectedAccountAction = {
|
||||
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 => ({
|
||||
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) {
|
||||
return {
|
||||
type: 'info',
|
||||
title: `Device ${device.instanceLabel} firmware is outdated`,
|
||||
message: 'TODO: update firmware explanation',
|
||||
shortcut: network.shortcut,
|
||||
};
|
||||
}
|
||||
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 {
|
||||
account,
|
||||
@ -53,11 +79,11 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
||||
};
|
||||
}
|
||||
|
||||
// corner case: accountState didn't finish loading state after LOCATION_CHANGE action
|
||||
// corner case: SelectedAccountState didn't change after LOCATION_CHANGE action
|
||||
if (!network) {
|
||||
return {
|
||||
type: 'progress',
|
||||
title: 'Loading account state...',
|
||||
title: 'Loading account',
|
||||
shouldRender: false,
|
||||
};
|
||||
}
|
||||
@ -66,24 +92,6 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
||||
if (account) return null;
|
||||
// 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: 'fwNotSupported',
|
||||
title: `${network.name} is not supported with Trezor ${(device.features || {}).model}`,
|
||||
message: 'Find more information on Trezor Wiki.',
|
||||
shouldRender: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (!discovery || (discovery.waitingForDevice || discovery.interrupted)) {
|
||||
if (device.connected) {
|
||||
// case 1: device is connected but discovery not started yet (probably waiting for auth)
|
||||
@ -130,7 +138,8 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState):
|
||||
};
|
||||
};
|
||||
|
||||
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 { account, network, discovery } = selectedAccount;
|
||||
if (!device || !network) return null;
|
||||
@ -198,16 +207,6 @@ export const observe = (prevState: State, action: Action): PayloadAction<boolean
|
||||
// ignore not listed actions
|
||||
if (actions.indexOf(action.type) < 0) return false;
|
||||
const state: State = getState();
|
||||
const notification = {
|
||||
type: null,
|
||||
message: null,
|
||||
title: null,
|
||||
};
|
||||
const loader = {
|
||||
type: null,
|
||||
message: null,
|
||||
title: null,
|
||||
};
|
||||
|
||||
const { location } = state.router;
|
||||
// displayed route is not an account route
|
||||
@ -222,25 +221,29 @@ export const observe = (prevState: State, action: Action): PayloadAction<boolean
|
||||
|
||||
// prepare new state for "selectedAccount" reducer
|
||||
const newState: SelectedAccountState = {
|
||||
...initialState,
|
||||
location: state.router.location.pathname,
|
||||
account,
|
||||
network,
|
||||
discovery,
|
||||
tokens,
|
||||
pending,
|
||||
notification,
|
||||
loader,
|
||||
shouldRender: false,
|
||||
};
|
||||
|
||||
// get "selectedAccount" status from newState
|
||||
const statusNotification = getAccountNotification(state, newState);
|
||||
const statusLoader = getAccountLoader(state, newState);
|
||||
const shouldRender = (statusNotification && statusLoader) ? (statusNotification.shouldRender || statusLoader.shouldRender) : true;
|
||||
const exceptionPage = getExceptionPage(state, newState);
|
||||
const loader = getAccountLoader(state, newState);
|
||||
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
|
||||
const stateChanged = reducerUtils.observeChanges(prevState.selectedAccount, newState, {
|
||||
account: ['balance', 'nonce'],
|
||||
|
@ -7,34 +7,32 @@ import type { Props } from '../../index';
|
||||
// There could be only one account notification
|
||||
export default (props: Props) => {
|
||||
const { network, notification } = props.selectedAccount;
|
||||
if (network && notification.type && notification.title) {
|
||||
if (notification.type === 'backend') {
|
||||
// special case: backend is down
|
||||
// TODO: this is a different component with "auto resolve" button
|
||||
return (
|
||||
<Notification
|
||||
type="error"
|
||||
title={notification.title}
|
||||
message={notification.message}
|
||||
actions={
|
||||
[{
|
||||
label: 'Connect',
|
||||
callback: async () => {
|
||||
await props.blockchainReconnect(network.shortcut);
|
||||
},
|
||||
}]
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (!network || !notification) return null;
|
||||
|
||||
if (notification.type === 'backend') {
|
||||
// special case: backend is down
|
||||
// TODO: this is a different component with "auto resolve" button
|
||||
return (
|
||||
<Notification
|
||||
type={notification.type}
|
||||
type="error"
|
||||
title={notification.title}
|
||||
message={notification.message}
|
||||
actions={
|
||||
[{
|
||||
label: 'Connect',
|
||||
callback: async () => {
|
||||
await props.blockchainReconnect(network.shortcut);
|
||||
},
|
||||
}]
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Notification
|
||||
type={notification.type}
|
||||
title={notification.title}
|
||||
message={notification.message}
|
||||
/>
|
||||
);
|
||||
};
|
@ -10,23 +10,35 @@ import type {
|
||||
Discovery,
|
||||
} 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 = {
|
||||
location: string;
|
||||
account: ?Account;
|
||||
network: ?Network;
|
||||
location: string,
|
||||
account: ?Account,
|
||||
network: ?Network,
|
||||
tokens: Array<Token>,
|
||||
pending: Array<PendingTx>,
|
||||
discovery: ?Discovery,
|
||||
notification: {
|
||||
type: ?string,
|
||||
title: ?string,
|
||||
message: ?string,
|
||||
},
|
||||
loader: {
|
||||
type: ?string,
|
||||
title: ?string,
|
||||
message: ?string,
|
||||
},
|
||||
loader: ?Loader,
|
||||
notification: ?Notification,
|
||||
exceptionPage: ?ExceptionPage,
|
||||
shouldRender: boolean,
|
||||
};
|
||||
|
||||
@ -37,16 +49,9 @@ export const initialState: State = {
|
||||
tokens: [],
|
||||
pending: [],
|
||||
discovery: null,
|
||||
notification: {
|
||||
type: null,
|
||||
title: null,
|
||||
message: null,
|
||||
},
|
||||
loader: {
|
||||
type: null,
|
||||
title: null,
|
||||
message: null,
|
||||
},
|
||||
loader: null,
|
||||
notification: null,
|
||||
exceptionPage: null,
|
||||
shouldRender: false,
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,76 @@
|
||||
/* @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;
|
||||
`;
|
||||
|
||||
// eslint-disable-next-line arrow-body-style
|
||||
const FirmwareOutdated = (props: Props) => {
|
||||
return (
|
||||
<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 FirmwareOutdated;
|
@ -1,11 +1,24 @@
|
||||
import React from 'react';
|
||||
/* @flow */
|
||||
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import Loader from 'components/Loader';
|
||||
import { FONT_SIZE } from 'config/variables';
|
||||
import colors from 'config/colors';
|
||||
|
||||
import type { State } from 'flowtype';
|
||||
|
||||
import FirmwareOutdated from './components/FirmwareOutdated';
|
||||
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`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
@ -37,38 +50,43 @@ const Row = styled.div`
|
||||
flex-direction: row;
|
||||
`;
|
||||
|
||||
const getExceptionPage = (exceptionPage) => {
|
||||
const { title, message, shortcut } = exceptionPage;
|
||||
switch (exceptionPage.type) {
|
||||
case 'fwOutdated':
|
||||
return <FirmwareOutdated title={title} message={message} networkShortcut={shortcut} />;
|
||||
case 'fwNotSupported':
|
||||
return <FirmwareUnsupported title={title} message={message} networkShortcut={shortcut} />;
|
||||
default: return null;
|
||||
}
|
||||
};
|
||||
|
||||
const Content = ({
|
||||
children,
|
||||
title,
|
||||
message,
|
||||
type,
|
||||
networkShortcut,
|
||||
isLoading = false,
|
||||
}) => (
|
||||
loader,
|
||||
exceptionPage,
|
||||
}: Props) => (
|
||||
<Wrapper>
|
||||
{(!isLoading) && children}
|
||||
{isLoading && (type === 'progress' || type === 'info') && (
|
||||
{isLoading && exceptionPage && getExceptionPage(exceptionPage)}
|
||||
{isLoading && loader && (
|
||||
<Loading>
|
||||
<Row>
|
||||
{type === 'progress' && <Loader size={30} />}
|
||||
<Text>{title || 'Initializing accounts'}</Text>
|
||||
{loader.type === 'progress' && <Loader size={30} />}
|
||||
<Text>{loader.title || 'Initializing accounts'}</Text>
|
||||
</Row>
|
||||
{message && <Message>{message}</Message>}
|
||||
{loader.message && <Message>{loader.message}</Message>}
|
||||
</Loading>
|
||||
)}
|
||||
{isLoading && (type === 'fwNotSupported') && (
|
||||
<FirmwareUnsupported title={title} message={message} networkShortcut={networkShortcut} />
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
Content.propTypes = {
|
||||
children: PropTypes.oneOfType([PropTypes.element, PropTypes.array]),
|
||||
isLoading: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
message: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
networkShortcut: PropTypes.string,
|
||||
loader: PropTypes.object,
|
||||
exceptionPage: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Content;
|
||||
|
@ -96,11 +96,12 @@ const AccountReceive = (props: Props) => {
|
||||
account,
|
||||
discovery,
|
||||
shouldRender,
|
||||
loader,
|
||||
network,
|
||||
} = props.selectedAccount;
|
||||
const { type, title, message } = loader;
|
||||
if (!device || !account || !discovery || !shouldRender) return <Content type={type} title={title} message={message} networkShortcut={(network || {}).shortcut} isLoading />;
|
||||
|
||||
if (!device || !account || !discovery || !shouldRender) {
|
||||
const { loader, exceptionPage } = props.selectedAccount;
|
||||
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||
}
|
||||
|
||||
const {
|
||||
addressVerified,
|
||||
|
@ -96,11 +96,12 @@ const AccountReceive = (props: Props) => {
|
||||
account,
|
||||
discovery,
|
||||
shouldRender,
|
||||
loader,
|
||||
network,
|
||||
} = props.selectedAccount;
|
||||
const { type, title, message } = loader;
|
||||
if (!device || !account || !discovery || !shouldRender) return <Content type={type} title={title} message={message} networkShortcut={(network || {}).shortcut} isLoading />;
|
||||
|
||||
if (!device || !account || !discovery || !shouldRender) {
|
||||
const { loader, exceptionPage } = props.selectedAccount;
|
||||
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||
}
|
||||
|
||||
const {
|
||||
addressVerified,
|
||||
|
@ -184,7 +184,6 @@ const AccountSend = (props: Props) => {
|
||||
discovery,
|
||||
tokens,
|
||||
shouldRender,
|
||||
loader,
|
||||
} = props.selectedAccount;
|
||||
const {
|
||||
address,
|
||||
@ -213,8 +212,11 @@ const AccountSend = (props: Props) => {
|
||||
updateFeeLevels,
|
||||
onSend,
|
||||
} = props.sendFormActions;
|
||||
const { type, title, message } = loader;
|
||||
if (!device || !account || !discovery || !network || !shouldRender) return <Content type={type} title={title} message={message} networkShortcut={(network || {}).shortcut} isLoading />;
|
||||
|
||||
if (!device || !account || !discovery || !network || !shouldRender) {
|
||||
const { loader, exceptionPage } = props.selectedAccount;
|
||||
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||
}
|
||||
|
||||
const isCurrentCurrencyToken = networkSymbol !== currency;
|
||||
|
||||
|
@ -122,7 +122,6 @@ const AccountSend = (props: Props) => {
|
||||
network,
|
||||
discovery,
|
||||
shouldRender,
|
||||
loader,
|
||||
} = props.selectedAccount;
|
||||
const {
|
||||
address,
|
||||
@ -142,8 +141,11 @@ const AccountSend = (props: Props) => {
|
||||
onSetMax,
|
||||
onSend,
|
||||
} = props.sendFormActions;
|
||||
const { type, title, message } = loader;
|
||||
if (!device || !account || !discovery || !network || !shouldRender) return <Content type={type} title={title} message={message} networkShortcut={(network || {}).shortcut} 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 sendButtonText: string = ` ${total} ${network.symbol}`;
|
||||
|
@ -57,10 +57,14 @@ class SignVerify extends Component <Props> {
|
||||
render() {
|
||||
const device = this.props.wallet.selectedDevice;
|
||||
const {
|
||||
account, discovery, shouldRender, notification, network,
|
||||
account, discovery, shouldRender,
|
||||
} = this.props.selectedAccount;
|
||||
const { type, title, message } = notification;
|
||||
if (!device || !account || !discovery || !shouldRender) return <Content type={type} title={title} message={message} networkShortcut={(network || {}).shortcut} isLoading />;
|
||||
|
||||
if (!device || !account || !discovery || !shouldRender) {
|
||||
const { loader, exceptionPage } = this.props.selectedAccount;
|
||||
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||
}
|
||||
|
||||
const {
|
||||
signVerifyActions,
|
||||
signVerify: {
|
||||
|
@ -76,13 +76,13 @@ const AccountSummary = (props: Props) => {
|
||||
network,
|
||||
tokens,
|
||||
pending,
|
||||
loader,
|
||||
shouldRender,
|
||||
} = props.selectedAccount;
|
||||
|
||||
const { type, title, message } = loader;
|
||||
|
||||
if (!device || !account || !network || !shouldRender) return <Content type={type} title={title} message={message} networkShortcut={(network || {}).shortcut} isLoading />;
|
||||
if (!device || !account || !network || !shouldRender) {
|
||||
const { loader, exceptionPage } = props.selectedAccount;
|
||||
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||
}
|
||||
|
||||
const explorerLink: string = `${network.explorer.address}${account.address}`;
|
||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
|
||||
|
@ -66,13 +66,13 @@ const AccountSummary = (props: Props) => {
|
||||
account,
|
||||
network,
|
||||
pending,
|
||||
loader,
|
||||
shouldRender,
|
||||
} = props.selectedAccount;
|
||||
|
||||
const { type, title, message } = loader;
|
||||
|
||||
if (!device || !account || !network || !shouldRender) return <Content type={type} title={title} message={message} networkShortcut={(network || {}).shortcut} isLoading />;
|
||||
if (!device || !account || !network || !shouldRender) {
|
||||
const { loader, exceptionPage } = props.selectedAccount;
|
||||
return <Content loader={loader} exceptionPage={exceptionPage} isLoading />;
|
||||
}
|
||||
|
||||
const explorerLink: string = `${network.explorer.address}${account.address}`;
|
||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
|
||||
|
Loading…
Reference in New Issue
Block a user