feat(status-pill): move status-pill to redux
This commit is contained in:
parent
c9f39f4a2d
commit
d137f8724a
|
@ -7,9 +7,7 @@ import { ZcashLogo } from './zcash-logo';
|
|||
import { TextComponent } from './text';
|
||||
import { Divider } from './divider';
|
||||
import { RowComponent } from './row';
|
||||
import { StatusPill } from './status-pill';
|
||||
|
||||
import { withSyncStatus } from '../../services/sync-status';
|
||||
import { StatusPillContainer } from '../containers/status-pill';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
height: ${props => props.theme.headerHeight};
|
||||
|
@ -62,8 +60,6 @@ type Props = {
|
|||
title: string,
|
||||
};
|
||||
|
||||
const Status = withSyncStatus(StatusPill);
|
||||
|
||||
export const HeaderComponent = ({ title }: Props) => (
|
||||
<Wrapper id='header'>
|
||||
<LogoWrapper>
|
||||
|
@ -72,7 +68,7 @@ export const HeaderComponent = ({ title }: Props) => (
|
|||
<TitleWrapper>
|
||||
<TitleRow alignItems='center' justifyContent='space-around'>
|
||||
<Title value={title} />
|
||||
<Status type='syncing' progress={0} />
|
||||
<StatusPillContainer />
|
||||
</TitleRow>
|
||||
<Divider opacity={0.2} />
|
||||
</TitleWrapper>
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
import React, { PureComponent } from 'react';
|
||||
import styled, { keyframes, withTheme } from 'styled-components';
|
||||
import eres from 'eres';
|
||||
|
||||
import { TextComponent } from './text';
|
||||
|
||||
import rpc from '../../services/api';
|
||||
import { DARK } from '../constants/themes';
|
||||
|
||||
import readyIconDark from '../assets/images/green_check_dark.png';
|
||||
|
@ -16,6 +14,8 @@ import syncIconLight from '../assets/images/sync_icon_light.png';
|
|||
import errorIconDark from '../assets/images/error_icon_dark.png';
|
||||
import errorIconLight from '../assets/images/error_icon_light.png';
|
||||
|
||||
import type { MapDispatchToProps, MapStateToProps } from '../containers/status-pill';
|
||||
|
||||
const rotate = keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
|
@ -55,94 +55,85 @@ const StatusPillLabel = styled(TextComponent)`
|
|||
|
||||
type Props = {
|
||||
theme: AppTheme,
|
||||
};
|
||||
} & MapStateToProps &
|
||||
MapDispatchToProps;
|
||||
|
||||
type State = {
|
||||
type: string,
|
||||
icon: string,
|
||||
progress: number,
|
||||
isSyncing: boolean,
|
||||
};
|
||||
const MINUTE_IN_MILI = 60000;
|
||||
|
||||
class Component extends PureComponent<Props, State> {
|
||||
class Component extends PureComponent<Props> {
|
||||
timer: ?IntervalID = null;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
componentDidMount() {
|
||||
const { getBlockchainStatus } = this.props;
|
||||
|
||||
const { theme } = props;
|
||||
|
||||
const syncIcon = theme.mode === DARK
|
||||
? syncIconDark
|
||||
: syncIconLight;
|
||||
|
||||
this.state = {
|
||||
type: 'syncing',
|
||||
icon: syncIcon,
|
||||
progress: 0,
|
||||
isSyncing: true,
|
||||
};
|
||||
this.timer = setInterval(() => getBlockchainStatus(), 2000);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.timer = setInterval(() => {
|
||||
this.getBlockchainStatus();
|
||||
}, 2000);
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { getBlockchainStatus, nodeSyncType } = this.props;
|
||||
if (prevProps.nodeSyncType === 'syncing' && nodeSyncType === 'ready') {
|
||||
// if the status is "ready", we can increase the interval to avoid useless rpc calls
|
||||
this.cleanUpdateInterval();
|
||||
this.timer = setInterval(() => getBlockchainStatus(), MINUTE_IN_MILI);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.cleanUpdateInterval();
|
||||
}
|
||||
|
||||
cleanUpdateInterval = () => {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getBlockchainStatus = async () => {
|
||||
isSyncing = () => {
|
||||
const { nodeSyncType } = this.props;
|
||||
return nodeSyncType === 'syncing';
|
||||
};
|
||||
|
||||
getReadyIcon = () => {
|
||||
const { theme } = this.props;
|
||||
return theme.mode === DARK ? readyIconDark : readyIconLight;
|
||||
};
|
||||
|
||||
const readyIcon = theme.mode === DARK
|
||||
? readyIconDark
|
||||
: readyIconLight;
|
||||
const errorIcon = theme.mode === DARK
|
||||
? errorIconDark
|
||||
: errorIconLight;
|
||||
getErrorIcon = () => {
|
||||
const { theme } = this.props;
|
||||
return theme.mode === DARK ? errorIconDark : errorIconLight;
|
||||
};
|
||||
|
||||
const [blockchainErr, blockchaininfo] = await eres(rpc.getblockchaininfo());
|
||||
getSyncingIcon = () => {
|
||||
const { theme } = this.props;
|
||||
return theme.mode === DARK ? syncIconDark : syncIconLight;
|
||||
};
|
||||
|
||||
if (blockchainErr || !blockchaininfo) return;
|
||||
getIcon = () => {
|
||||
const { nodeSyncType } = this.props;
|
||||
|
||||
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,
|
||||
}));
|
||||
switch (nodeSyncType) {
|
||||
case 'syncing':
|
||||
return this.getSyncingIcon();
|
||||
case 'ready':
|
||||
return this.getReadyIcon();
|
||||
case 'error':
|
||||
return this.getErrorIcon();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
type, icon, progress, isSyncing,
|
||||
} = this.state;
|
||||
const showPercent = isSyncing ? `(${progress.toFixed(2)}%)` : '';
|
||||
const typeText = type === 'ready' ? 'Synced' : type;
|
||||
const icon = this.getIcon();
|
||||
const { nodeSyncType, nodeSyncProgress } = this.props;
|
||||
const percent = nodeSyncType === 'syncing' ? `(${nodeSyncProgress.toFixed(2)}%)` : '';
|
||||
const typeText = nodeSyncType === 'ready' ? 'Synced' : nodeSyncType;
|
||||
|
||||
return (
|
||||
<Wrapper id='status-pill'>
|
||||
<Icon src={icon} animated={isSyncing} />
|
||||
<StatusPillLabel value={`${typeText} ${showPercent}`} />
|
||||
{icon && <Icon src={icon} animated={this.isSyncing()} />}
|
||||
<StatusPillLabel value={`${typeText} ${percent}`} />
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,21 @@ import { LayoutComponent } from '../components/layout';
|
|||
import type { Dispatch } from '../types/redux';
|
||||
import type { AppState } from '../types/app-state';
|
||||
|
||||
const mapStateToProps = ({ app }: AppState) => ({
|
||||
export type MapStateToProps = {|
|
||||
isErrorModalVisible: boolean,
|
||||
error: string | null,
|
||||
|};
|
||||
|
||||
const mapStateToProps = ({ app }: AppState): MapStateToProps => ({
|
||||
isErrorModalVisible: app.isErrorModalVisible,
|
||||
error: app.error,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => ({
|
||||
export type MapDispatchToProps = {|
|
||||
closeErrorModal: () => void,
|
||||
|};
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({
|
||||
closeErrorModal: () => dispatch(closeErrorModal()),
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// @flow
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import eres from 'eres';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import { updateNodeSyncStatus } from '../redux/modules/app';
|
||||
|
||||
import { StatusPill } from '../components/status-pill';
|
||||
|
||||
import rpc from '../../services/api';
|
||||
|
||||
import type { Dispatch } from '../types/redux';
|
||||
import type { AppState } from '../types/app-state';
|
||||
|
||||
export type MapStateToProps = {|
|
||||
nodeSyncProgress: number,
|
||||
nodeSyncType: 'ready' | 'syncing' | 'error',
|
||||
|};
|
||||
|
||||
const mapStateToProps = ({ app }: AppState): MapStateToProps => ({
|
||||
nodeSyncProgress: app.nodeSyncProgress,
|
||||
nodeSyncType: app.nodeSyncType,
|
||||
});
|
||||
|
||||
export type MapDispatchToProps = {|
|
||||
getBlockchainStatus: () => Promise<void>,
|
||||
|};
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch): MapDispatchToProps => ({
|
||||
getBlockchainStatus: async () => {
|
||||
const [blockchainErr, blockchaininfo] = await eres(rpc.getblockchaininfo());
|
||||
|
||||
if (blockchainErr || !blockchaininfo) {
|
||||
return dispatch(
|
||||
updateNodeSyncStatus({
|
||||
nodeSyncProgress: 0,
|
||||
nodeSyncType: 'error',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const newProgress = blockchaininfo.verificationprogress * 100;
|
||||
|
||||
dispatch(
|
||||
updateNodeSyncStatus({
|
||||
nodeSyncProgress: newProgress,
|
||||
nodeSyncType: new BigNumber(newProgress).gt(99.99) ? 'ready' : 'syncing',
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// $FlowFixMe
|
||||
export const StatusPillContainer = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(StatusPill);
|
|
@ -2,9 +2,17 @@
|
|||
|
||||
import type { Action } from '../../types/redux';
|
||||
|
||||
export type State = {|
|
||||
isErrorModalVisible: boolean,
|
||||
error: string | null,
|
||||
nodeSyncProgress: number,
|
||||
nodeSyncType: 'ready' | 'syncing' | 'error',
|
||||
|};
|
||||
|
||||
// Actions
|
||||
export const SHOW_ERROR_MODAL = 'SHOW_ERROR_MODAL';
|
||||
export const HIDE_ERROR_MODAL = 'HIDE_ERROR_MODAL';
|
||||
export const UPDATE_NODE_SYNC_STATUS = 'UPDATE_NODE_SYNC_STATUS';
|
||||
|
||||
export const showErrorModal = ({ error }: { error: string }) => ({
|
||||
type: SHOW_ERROR_MODAL,
|
||||
|
@ -18,14 +26,25 @@ export const closeErrorModal = () => ({
|
|||
payload: {},
|
||||
});
|
||||
|
||||
export type State = {
|
||||
isErrorModalVisible: boolean,
|
||||
error: string | null,
|
||||
};
|
||||
export const updateNodeSyncStatus = ({
|
||||
nodeSyncProgress,
|
||||
nodeSyncType,
|
||||
}: {
|
||||
nodeSyncProgress: number,
|
||||
nodeSyncType: $PropertyType<State, 'nodeSyncType'>,
|
||||
}) => ({
|
||||
type: UPDATE_NODE_SYNC_STATUS,
|
||||
payload: {
|
||||
nodeSyncProgress,
|
||||
nodeSyncType,
|
||||
},
|
||||
});
|
||||
|
||||
const initialState: State = {
|
||||
isErrorModalVisible: false,
|
||||
error: null,
|
||||
nodeSyncProgress: 0,
|
||||
nodeSyncType: 'syncing',
|
||||
};
|
||||
|
||||
// eslint-disable-next-line
|
||||
|
@ -35,6 +54,12 @@ export default (state: State = initialState, action: Action) => {
|
|||
return { isErrorModalVisible: true, error: action.payload.error };
|
||||
case HIDE_ERROR_MODAL:
|
||||
return { isErrorModalVisible: false, error: null };
|
||||
case UPDATE_NODE_SYNC_STATUS:
|
||||
return {
|
||||
...state,
|
||||
nodeSyncProgress: action.payload.nodeSyncProgress,
|
||||
nodeSyncType: action.payload.nodeSyncType,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -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">89%</text><text x="107" y="14">89%</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">88%</text><text x="107" y="14">88%</text></g></svg>
|
Before Width: | Height: | Size: 745 B After Width: | Height: | Size: 745 B |
|
@ -1,63 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { type ComponentType, Component } from 'react';
|
||||
import eres from 'eres';
|
||||
|
||||
import rpc from './api';
|
||||
|
||||
type Props = {};
|
||||
|
||||
type State = {
|
||||
type?: 'syncing' | 'ready' | 'error',
|
||||
progress: number,
|
||||
};
|
||||
|
||||
/* eslint-disable max-len */
|
||||
export const withSyncStatus = <PassedProps: {}>(
|
||||
WrappedComponent: ComponentType<PassedProps>,
|
||||
): ComponentType<$Diff<PassedProps, Props>> => class extends Component<PassedProps, State> {
|
||||
timer: ?IntervalID = null;
|
||||
|
||||
state = {
|
||||
type: 'syncing',
|
||||
progress: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.timer = setInterval(() => {
|
||||
this.getBlockchainStatus();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
getBlockchainStatus = async () => {
|
||||
const [blockchainErr, blockchaininfo] = await eres(
|
||||
rpc.getblockchaininfo(),
|
||||
);
|
||||
|
||||
const newProgress = blockchaininfo.verificationprogress * 100;
|
||||
|
||||
this.setState({
|
||||
progress: newProgress,
|
||||
...(newProgress > 99.99 ? {
|
||||
type: 'ready',
|
||||
} : {}),
|
||||
});
|
||||
|
||||
if (blockchainErr) {
|
||||
this.setState(() => ({ type: 'error' }));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { type, progress } = this.state;
|
||||
|
||||
return <WrappedComponent {...this.props} {...this.state} type={type} progress={progress} />;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue