Merge pull request #63 from andrerfneves/feature/catch-error-middleware
Feature/catch error middleware
This commit is contained in:
commit
07a9e817ed
|
@ -0,0 +1,94 @@
|
||||||
|
// @flow
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { TextComponent } from './text';
|
||||||
|
import { Button } from './button';
|
||||||
|
|
||||||
|
import ErrorIcon from '../assets/images/error_icon.png';
|
||||||
|
|
||||||
|
const ModalWrapper = styled.div`
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ChildrenWrapper = styled.div`
|
||||||
|
width: 350px;
|
||||||
|
background-color: ${props => props.theme.colors.background};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0px 0px 30px 0px black;
|
||||||
|
position: relative;
|
||||||
|
z-index: 90;
|
||||||
|
min-height: 400px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Message = styled(TextComponent)`
|
||||||
|
margin: 15px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ErrorImage = styled.img`
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isVisible: boolean,
|
||||||
|
message: string,
|
||||||
|
onRequestClose: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
const modalRoot = document.getElementById('modal-root');
|
||||||
|
|
||||||
|
export class ErrorModalComponent extends PureComponent<Props> {
|
||||||
|
element = document.createElement('div');
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { isVisible } = this.props;
|
||||||
|
|
||||||
|
if (isVisible) {
|
||||||
|
if (modalRoot) modalRoot.appendChild(this.element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate = (prevProps: Props) => {
|
||||||
|
const { isVisible } = this.props;
|
||||||
|
|
||||||
|
if (!prevProps.isVisible && isVisible) {
|
||||||
|
if (modalRoot) modalRoot.appendChild(this.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevProps.isVisible && !isVisible) {
|
||||||
|
if (modalRoot) modalRoot.removeChild(this.element);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isVisible, message, onRequestClose } = this.props;
|
||||||
|
|
||||||
|
return isVisible
|
||||||
|
? createPortal(
|
||||||
|
<ModalWrapper id='error-modal-portal-wrapper'>
|
||||||
|
<ChildrenWrapper>
|
||||||
|
<ErrorImage src={ErrorIcon} alt='Error Icon' />
|
||||||
|
<Message value={message} />
|
||||||
|
<Button label='Ok!' onClick={onRequestClose} />
|
||||||
|
</ChildrenWrapper>
|
||||||
|
</ModalWrapper>,
|
||||||
|
this.element,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React, { type Element } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { ErrorModalComponent } from './error-modal';
|
||||||
|
|
||||||
const Layout = styled.div`
|
const Layout = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -14,12 +16,25 @@ const Layout = styled.div`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
chidren: any, // eslint-disable-line
|
children: Element<*>,
|
||||||
|
closeErrorModal: () => void,
|
||||||
|
isErrorModalVisible: boolean,
|
||||||
|
error: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LayoutComponent = (props: Props) => {
|
export const LayoutComponent = (props: Props) => {
|
||||||
// $FlowFixMe
|
const {
|
||||||
const { children } = props; // eslint-disable-line
|
children, error, isErrorModalVisible, closeErrorModal,
|
||||||
|
} = props;
|
||||||
|
|
||||||
return <Layout id='layout'>{children}</Layout>;
|
return (
|
||||||
|
<Layout id='layout'>
|
||||||
|
{children}
|
||||||
|
<ErrorModalComponent
|
||||||
|
message={error}
|
||||||
|
isVisible={isErrorModalVisible}
|
||||||
|
onRequestClose={closeErrorModal}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
import styled, { keyframes } from 'styled-components';
|
import styled, { keyframes } from 'styled-components';
|
||||||
|
import eres from 'eres';
|
||||||
|
|
||||||
import { TextComponent } from './text';
|
import { TextComponent } from './text';
|
||||||
|
|
||||||
|
import rpc from '../../services/api';
|
||||||
|
|
||||||
import readyIcon from '../assets/images/green_check.png';
|
import readyIcon from '../assets/images/green_check.png';
|
||||||
import syncIcon from '../assets/images/sync_icon.png';
|
import syncIcon from '../assets/images/sync_icon.png';
|
||||||
import errorIcon from '../assets/images/error_icon.png';
|
import errorIcon from '../assets/images/error_icon.png';
|
||||||
|
@ -42,39 +45,70 @@ const StatusPillLabel = styled(TextComponent)`
|
||||||
user-select: none;
|
user-select: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = {
|
type Props = {};
|
||||||
type: 'syncing' | 'ready' | 'error',
|
|
||||||
progress: number,
|
|
||||||
};
|
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
withError: boolean,
|
type: string,
|
||||||
|
icon: string,
|
||||||
|
progress: number,
|
||||||
|
isSyncing: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class StatusPill extends React.PureComponent<Props, State> {
|
export class StatusPill extends Component<Props, State> {
|
||||||
|
timer: ?IntervalID = null;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
withError: false,
|
type: 'syncing',
|
||||||
|
icon: syncIcon,
|
||||||
|
progress: 0,
|
||||||
|
isSyncing: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { type } = this.props;
|
this.timer = setInterval(() => {
|
||||||
if (type === 'error') {
|
this.getBlockchainStatus();
|
||||||
this.setState(() => ({ withError: false }));
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.timer) {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
this.timer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBlockchainStatus = async () => {
|
||||||
|
const [blockchainErr, blockchaininfo] = await eres(rpc.getblockchaininfo());
|
||||||
|
|
||||||
|
if (blockchainErr || !blockchaininfo) return;
|
||||||
|
|
||||||
|
const newProgress = blockchaininfo.verificationprogress * 100;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
progress: newProgress,
|
||||||
|
...(newProgress > 99.99
|
||||||
|
? {
|
||||||
|
type: 'ready',
|
||||||
|
icon: readyIcon,
|
||||||
|
isSyncing: false,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (blockchainErr) {
|
||||||
|
this.setState(() => ({ type: 'error', icon: errorIcon }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { type, progress } = this.props;
|
const {
|
||||||
const { withError } = this.state;
|
type, icon, progress, isSyncing,
|
||||||
|
} = this.state;
|
||||||
const isSyncing = type === 'syncing';
|
|
||||||
|
|
||||||
const icon = isSyncing ? syncIcon : readyIcon;
|
|
||||||
const showPercent = isSyncing ? `(${progress.toFixed(2)}%)` : '';
|
const showPercent = isSyncing ? `(${progress.toFixed(2)}%)` : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper data-testid='StatusPill'>
|
<Wrapper id='status-pill'>
|
||||||
<Icon src={withError ? errorIcon : icon} animated={isSyncing} />
|
<Icon src={icon} animated={isSyncing} />
|
||||||
<StatusPillLabel value={`${type} ${showPercent}`} />
|
<StatusPillLabel value={`${type} ${showPercent}`} />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import React, { Component } from 'react';
|
||||||
import React, { PureComponent } from 'react';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Transition, animated } from 'react-spring';
|
|
||||||
|
|
||||||
|
import { ColumnComponent } from './column';
|
||||||
import { Button } from './button';
|
import { Button } from './button';
|
||||||
import { QRCode } from './qrcode';
|
import { QRCode } from './qrcode';
|
||||||
|
|
||||||
|
@ -18,7 +17,6 @@ const AddressWrapper = styled.div`
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 7px 13px;
|
padding: 7px 13px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 5px;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Input = styled.input`
|
const Input = styled.input`
|
||||||
|
@ -36,123 +34,70 @@ const Input = styled.input`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
|
||||||
const Btn = styled(Button)`
|
|
||||||
border-width: 1px;
|
|
||||||
font-weight: ${props => props.theme.fontWeight.regular};
|
|
||||||
border-color: ${props => (props.isVisible ? props.theme.colors.primary : props.theme.colors.buttonBorderColor)};
|
|
||||||
padding: 8px 10px;
|
|
||||||
min-width: 260px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
height: 12px;
|
|
||||||
width: 20px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
/* eslint-enable max-len */
|
|
||||||
|
|
||||||
const QRCodeWrapper = styled.div`
|
const QRCodeWrapper = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: #000;
|
background-color: #000;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
margin-top: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const RevealsMain = styled.div`
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
height: ${props => (props.isVisible ? '178px' : 0)}
|
|
||||||
transition: all 0.25s ease-in-out;
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
address: string,
|
address: string,
|
||||||
isVisible?: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
isVisible: boolean,
|
isVisible: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class WalletAddress extends PureComponent<Props, State> {
|
export class WalletAddress extends Component<Props, State> {
|
||||||
static defaultProps = {
|
state = {
|
||||||
isVisible: false,
|
isVisible: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: Props) {
|
show = () => {
|
||||||
super(props);
|
this.setState(
|
||||||
|
() => ({ isVisible: true }),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
this.state = { isVisible: Boolean(props.isVisible) };
|
hide = () => {
|
||||||
}
|
this.setState(
|
||||||
|
() => ({ isVisible: false }),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
show = () => this.setState(() => ({ isVisible: true }));
|
|
||||||
|
|
||||||
hide = () => this.setState(() => ({ isVisible: false }));
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { address } = this.props;
|
const { address } = this.props;
|
||||||
const { isVisible } = this.state;
|
const { isVisible } = this.state;
|
||||||
const toggleVisibility = isVisible ? this.hide : this.show;
|
const toggleVisibility = isVisible ? this.hide : this.show;
|
||||||
const buttonLabel = `${isVisible ? 'Hide' : 'Show'} details and QR Code`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ColumnComponent width='100%'>
|
<ColumnComponent width='100%'>
|
||||||
<AddressWrapper data-testid='Address'>
|
<AddressWrapper>
|
||||||
<Input
|
<Input
|
||||||
value={isVisible ? address : truncateAddress(address)}
|
value={isVisible ? address : truncateAddress(address)}
|
||||||
onChange={() => { }}
|
onChange={() => { }}
|
||||||
onFocus={event => event.currentTarget.select()}
|
onFocus={event => event.currentTarget.select()}
|
||||||
/>
|
/>
|
||||||
<Btn
|
<Button
|
||||||
icon={eyeIcon}
|
icon={eyeIcon}
|
||||||
label={buttonLabel}
|
label={`${isVisible ? 'Hide' : 'Show'} full address and QR Code`}
|
||||||
onClick={toggleVisibility}
|
onClick={toggleVisibility}
|
||||||
variant='secondary'
|
variant='secondary'
|
||||||
isVisible={isVisible}
|
|
||||||
/>
|
/>
|
||||||
</AddressWrapper>
|
</AddressWrapper>
|
||||||
<RevealsMain isVisible={isVisible}>
|
{isVisible
|
||||||
<Transition
|
? (
|
||||||
native
|
|
||||||
items={isVisible}
|
|
||||||
enter={[
|
|
||||||
{
|
|
||||||
height: 'auto',
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
leave={{ height: 0, opacity: 0 }}
|
|
||||||
from={{
|
|
||||||
position: 'absolute',
|
|
||||||
overflow: 'hidden',
|
|
||||||
opacity: 0,
|
|
||||||
height: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{show => show
|
|
||||||
&& (props => (
|
|
||||||
<animated.div style={props}>
|
|
||||||
<QRCodeWrapper>
|
<QRCodeWrapper>
|
||||||
<QRCode value={address} />
|
<QRCode value={address} />
|
||||||
</QRCodeWrapper>
|
</QRCodeWrapper>
|
||||||
</animated.div>
|
)
|
||||||
))
|
: null}
|
||||||
}
|
</ColumnComponent>
|
||||||
</Transition>
|
|
||||||
</RevealsMain>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,7 @@ export const MENU_OPTIONS = [
|
||||||
{
|
{
|
||||||
label: 'Dashboard',
|
label: 'Dashboard',
|
||||||
route: DASHBOARD_ROUTE,
|
route: DASHBOARD_ROUTE,
|
||||||
// eslint-disable-next-line
|
icon: (isActive: boolean) => (isActive ? DashboardIconActive : DashboardIcon),
|
||||||
icon: (isActive: boolean) => isActive ? DashboardIconActive : DashboardIcon,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Send',
|
label: 'Send',
|
||||||
|
@ -41,8 +40,7 @@ export const MENU_OPTIONS = [
|
||||||
{
|
{
|
||||||
label: 'Transactions',
|
label: 'Transactions',
|
||||||
route: TRANSACTIONS_ROUTE,
|
route: TRANSACTIONS_ROUTE,
|
||||||
// eslint-disable-next-line
|
icon: (isActive: boolean) => (isActive ? TransactionsIconActive : TransactionsIcon),
|
||||||
icon: (isActive: boolean) => isActive ? TransactionsIconActive : TransactionsIcon,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
// @flow
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { closeErrorModal } from '../redux/modules/app';
|
||||||
|
|
||||||
|
import { LayoutComponent } from '../components/layout';
|
||||||
|
|
||||||
|
import type { Dispatch } from '../types/redux';
|
||||||
|
import type { AppState } from '../types/app-state';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ app }: AppState) => ({
|
||||||
|
isErrorModalVisible: app.isErrorModalVisible,
|
||||||
|
error: app.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||||
|
closeErrorModal: () => dispatch(closeErrorModal()),
|
||||||
|
});
|
||||||
|
|
||||||
|
// $FlowFixMe
|
||||||
|
export const AppContainer = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(LayoutComponent);
|
|
@ -7,6 +7,7 @@ import thunk from 'redux-thunk';
|
||||||
import type { RouterHistory } from 'react-router-dom';
|
import type { RouterHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import { createRootReducer } from './modules/reducer';
|
import { createRootReducer } from './modules/reducer';
|
||||||
|
import { errorHandler } from './errorHandler';
|
||||||
|
|
||||||
export const history: RouterHistory = createHashHistory();
|
export const history: RouterHistory = createHashHistory();
|
||||||
|
|
||||||
|
@ -14,7 +15,8 @@ const shouldEnableDevTools = (process.env.NODE_ENV !== 'production' || process.e
|
||||||
&& window.devToolsExtension;
|
&& window.devToolsExtension;
|
||||||
|
|
||||||
export const configureStore = (initialState: Object) => {
|
export const configureStore = (initialState: Object) => {
|
||||||
const middleware = applyMiddleware(thunk, routerMiddleware(history));
|
// $FlowFixMe
|
||||||
|
const middleware = applyMiddleware(thunk, routerMiddleware(history), errorHandler);
|
||||||
|
|
||||||
const enhancer = compose(
|
const enhancer = compose(
|
||||||
middleware,
|
middleware,
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// @flow
|
||||||
|
import { LOAD_ADDRESSES_ERROR } from './modules/receive';
|
||||||
|
import { LOAD_TRANSACTIONS_ERROR } from './modules/transactions';
|
||||||
|
import { LOAD_WALLET_SUMMARY_ERROR } from './modules/wallet';
|
||||||
|
import { showErrorModal } from './modules/app';
|
||||||
|
|
||||||
|
import type { Middleware } from '../types/redux';
|
||||||
|
|
||||||
|
const ERRORS = [LOAD_ADDRESSES_ERROR, LOAD_TRANSACTIONS_ERROR, LOAD_WALLET_SUMMARY_ERROR];
|
||||||
|
|
||||||
|
export const errorHandler: Middleware = ({ dispatch }) => next => (action) => {
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
if (ERRORS.includes(action.type)) return dispatch(showErrorModal({ error: action.payload.error || 'Something went wrong!' }));
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
// @flow
|
||||||
|
import type { Action } from '../../types/redux';
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
export const SHOW_ERROR_MODAL = 'SHOW_ERROR_MODAL';
|
||||||
|
export const HIDE_ERROR_MODAL = 'HIDE_ERROR_MODAL';
|
||||||
|
|
||||||
|
export const showErrorModal = ({ error }: { error: string }) => ({
|
||||||
|
type: SHOW_ERROR_MODAL,
|
||||||
|
payload: {
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const closeErrorModal = () => ({
|
||||||
|
type: HIDE_ERROR_MODAL,
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export type State = {
|
||||||
|
isErrorModalVisible: boolean,
|
||||||
|
error: string | null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState: State = {
|
||||||
|
isErrorModalVisible: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export default (state: State = initialState, action: Action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case SHOW_ERROR_MODAL:
|
||||||
|
return { isErrorModalVisible: true, error: action.payload.error };
|
||||||
|
case HIDE_ERROR_MODAL:
|
||||||
|
return { isErrorModalVisible: false, error: null };
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
|
@ -4,6 +4,7 @@ import { combineReducers } from 'redux';
|
||||||
import { connectRouter } from 'connected-react-router';
|
import { connectRouter } from 'connected-react-router';
|
||||||
import type { RouterHistory } from 'react-router-dom';
|
import type { RouterHistory } from 'react-router-dom';
|
||||||
|
|
||||||
|
import app from './app';
|
||||||
import wallet from './wallet';
|
import wallet from './wallet';
|
||||||
import transactions from './transactions';
|
import transactions from './transactions';
|
||||||
import send from './send';
|
import send from './send';
|
||||||
|
@ -11,6 +12,7 @@ import receive from './receive';
|
||||||
|
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
export const createRootReducer = (history: RouterHistory) => combineReducers({
|
export const createRootReducer = (history: RouterHistory) => combineReducers({
|
||||||
|
app,
|
||||||
walletSummary: wallet,
|
walletSummary: wallet,
|
||||||
transactions,
|
transactions,
|
||||||
sendStatus: send,
|
sendStatus: send,
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { ReceiveContainer } from '../containers/receive';
|
||||||
import { SettingsContainer } from '../containers/settings';
|
import { SettingsContainer } from '../containers/settings';
|
||||||
import { NotFoundView } from '../views/not-found';
|
import { NotFoundView } from '../views/not-found';
|
||||||
import { ConsoleView } from '../views/console';
|
import { ConsoleView } from '../views/console';
|
||||||
import { LayoutComponent } from '../components/layout';
|
import { AppContainer as LayoutComponent } from '../containers/app';
|
||||||
import { HeaderComponent } from '../components/header';
|
import { HeaderComponent } from '../components/header';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -4,10 +4,12 @@ import type { State as WalletSummaryState } from '../redux/modules/wallet';
|
||||||
import type { State as TransactionsState } from '../redux/modules/transactions';
|
import type { State as TransactionsState } from '../redux/modules/transactions';
|
||||||
import type { State as SendState } from '../redux/modules/send';
|
import type { State as SendState } from '../redux/modules/send';
|
||||||
import type { State as ReceiveState } from '../redux/modules/receive';
|
import type { State as ReceiveState } from '../redux/modules/receive';
|
||||||
|
import type { State as App } from '../redux/modules/app';
|
||||||
|
|
||||||
export type AppState = {
|
export type AppState = {
|
||||||
walletSummary: WalletSummaryState,
|
walletSummary: WalletSummaryState,
|
||||||
transactions: TransactionsState,
|
transactions: TransactionsState,
|
||||||
sendStatus: SendState,
|
sendStatus: SendState,
|
||||||
receive: ReceiveState,
|
receive: ReceiveState,
|
||||||
|
app: App,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
type State = {||};
|
import type { AppState } from './app-state';
|
||||||
|
|
||||||
export type Action = { type: $Subtype<string>, payload: Object };
|
export type Action = { type: $Subtype<string>, payload: Object };
|
||||||
export type GetState = () => State;
|
export type GetState = () => AppState;
|
||||||
export type Dispatch = (action: Action) => void;
|
export type Dispatch = (action: Action) => void;
|
||||||
|
export type Middleware = ({ dispatch: Dispatch, getState: GetState }) => (
|
||||||
|
(Action) => void,
|
||||||
|
) => Action => void;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="125" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h91v20H0z"/><path fill="#4C1" d="M91 0h34v20H91z"/><path fill="url(#b)" d="M0 0h125v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,Geneva,sans-serif" font-size="11"><text x="45.5" y="15" fill="#010101" fill-opacity=".3">flow-coverage</text><text x="45.5" y="14">flow-coverage</text><text x="107" y="15" fill="#010101" fill-opacity=".3">82%</text><text x="107" y="14">82%</text></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="125" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="125" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h91v20H0z"/><path fill="#4C1" d="M91 0h34v20H91z"/><path fill="url(#b)" d="M0 0h125v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,DejaVu Sans,Geneva,sans-serif" font-size="11"><text x="45.5" y="15" fill="#010101" fill-opacity=".3">flow-coverage</text><text x="45.5" y="14">flow-coverage</text><text x="107" y="15" fill="#010101" fill-opacity=".3">83%</text><text x="107" y="14">83%</text></g></svg>
|
Before Width: | Height: | Size: 745 B After Width: | Height: | Size: 745 B |
Loading…
Reference in New Issue