mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-16 05:19:12 +00:00
Merge pull request #155 from trezor/flowtype/notifications
Flowtype/notifications
This commit is contained in:
commit
6dfd941c44
@ -3,4 +3,5 @@ build
|
||||
build-devel
|
||||
coverage
|
||||
node_modules
|
||||
src/flowtype/npm
|
||||
src/flowtype/npm
|
||||
**/_old/*
|
@ -1,9 +1,24 @@
|
||||
import React from 'react';
|
||||
/* @flow */
|
||||
|
||||
import * as React from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import colors from 'config/colors';
|
||||
import { TRANSITION, FONT_WEIGHT, FONT_SIZE } from 'config/variables';
|
||||
|
||||
type Props = {
|
||||
children: React.Node,
|
||||
className?: string,
|
||||
onClick?: () => any,
|
||||
onMouseEnter?: () => void,
|
||||
onMouseLeave?: () => void,
|
||||
onFocus?: () => void,
|
||||
isDisabled?: boolean,
|
||||
isWhite?: boolean,
|
||||
isWebUsb?: boolean,
|
||||
isTransparent?: boolean,
|
||||
}
|
||||
|
||||
const Wrapper = styled.button`
|
||||
padding: ${props => (props.icon ? '4px 24px 4px 15px' : '11px 24px')};
|
||||
border-radius: 3px;
|
||||
@ -108,8 +123,8 @@ const Wrapper = styled.button`
|
||||
|
||||
const Button = ({
|
||||
children,
|
||||
className,
|
||||
onClick = () => { },
|
||||
className = '',
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onFocus,
|
||||
@ -117,7 +132,7 @@ const Button = ({
|
||||
isWhite = false,
|
||||
isWebUsb = false,
|
||||
isTransparent = false,
|
||||
}) => {
|
||||
}: Props) => {
|
||||
const newClassName = isWebUsb ? `${className} trezor-webusb-button` : className;
|
||||
return (
|
||||
<Wrapper
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled, { css } from 'styled-components';
|
||||
@ -6,6 +8,12 @@ import Icon from 'components/Icon';
|
||||
import icons from 'config/icons';
|
||||
import { FONT_SIZE } from 'config/variables';
|
||||
|
||||
type Props = {
|
||||
onClick: (event: KeyboardEvent) => void,
|
||||
isChecked: boolean,
|
||||
children: string,
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -54,10 +62,10 @@ const Label = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
class Checkbox extends PureComponent {
|
||||
handleKeyboard(e) {
|
||||
if (e.keyCode === 32) {
|
||||
this.props.onClick(e);
|
||||
class Checkbox extends PureComponent<Props> {
|
||||
handleKeyboard(event: KeyboardEvent) {
|
||||
if (event.keyCode === 32) {
|
||||
this.props.onClick(event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +78,7 @@ class Checkbox extends PureComponent {
|
||||
return (
|
||||
<Wrapper
|
||||
onClick={onClick}
|
||||
onKeyUp={e => this.handleKeyboard(e)}
|
||||
onKeyUp={event => this.handleKeyboard(event)}
|
||||
tabIndex={0}
|
||||
>
|
||||
<IconWrapper isChecked={isChecked}>
|
||||
|
@ -11,6 +11,11 @@ import { connect } from 'react-redux';
|
||||
import colors from 'config/colors';
|
||||
import * as LogActions from 'actions/LogActions';
|
||||
|
||||
type Props = {
|
||||
opened: boolean,
|
||||
toggle: () => any,
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
@ -30,7 +35,7 @@ const Copy = styled.div`
|
||||
margin-right: 20px;
|
||||
`;
|
||||
|
||||
const Footer = ({ opened, toggle }) => (
|
||||
const Footer = ({ opened, toggle }: Props) => (
|
||||
<Wrapper>
|
||||
<Copy title={window.COMMITHASH}>© {getYear(new Date())}</Copy>
|
||||
<StyledLink href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" isGreen>SatoshiLabs</StyledLink>
|
||||
|
@ -1,8 +1,24 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import colors from 'config/colors';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
|
||||
type Props = {
|
||||
icon: Array<string>,
|
||||
className?: string,
|
||||
hoverColor?: string,
|
||||
canAnimate?: boolean,
|
||||
size?: number,
|
||||
isActive?: boolean,
|
||||
color?: string,
|
||||
onMouseEnter?: () => void,
|
||||
onMouseLeave?: () => void,
|
||||
onFocus?: () => void,
|
||||
onClick?: () => void,
|
||||
}
|
||||
|
||||
const chooseIconAnimationType = (canAnimate, isActive) => {
|
||||
if (canAnimate) {
|
||||
if (isActive) {
|
||||
@ -58,7 +74,7 @@ const Icon = ({
|
||||
onMouseLeave,
|
||||
onFocus,
|
||||
onClick,
|
||||
}) => (
|
||||
}: Props) => (
|
||||
<SvgWrapper
|
||||
className={className}
|
||||
canAnimate={canAnimate}
|
||||
@ -101,5 +117,4 @@ Icon.propTypes = {
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
|
||||
export default Icon;
|
@ -1,10 +1,23 @@
|
||||
import React from 'react';
|
||||
/* @flow */
|
||||
|
||||
import * as React from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import Icon from 'components/Icon';
|
||||
import colors from 'config/colors';
|
||||
import { TRANSITION } from 'config/variables';
|
||||
|
||||
type Props = {
|
||||
type: string;
|
||||
icon?: {
|
||||
type: Array<string>;
|
||||
color: string;
|
||||
size: number;
|
||||
};
|
||||
onClick: () => void;
|
||||
children: React.Node;
|
||||
};
|
||||
|
||||
const Wrapper = styled.button`
|
||||
padding: 12px 58px;
|
||||
border-radius: 3px;
|
||||
@ -62,10 +75,9 @@ const IconWrapper = styled.span`
|
||||
`;
|
||||
|
||||
const NotificationButton = ({
|
||||
children, className, icon, onClick = () => { }, type = null,
|
||||
}) => (
|
||||
type, icon, onClick, children,
|
||||
}: Props) => (
|
||||
<Wrapper
|
||||
className={className}
|
||||
icon={icon}
|
||||
onClick={onClick}
|
||||
type={type}
|
||||
@ -84,15 +96,14 @@ const NotificationButton = ({
|
||||
);
|
||||
|
||||
NotificationButton.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
className: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
icon: PropTypes.shape({
|
||||
type: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
color: PropTypes.string,
|
||||
size: PropTypes.number,
|
||||
}),
|
||||
onClick: PropTypes.func,
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default NotificationButton;
|
@ -1,9 +1,8 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import media from 'styled-media-query';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import styled, { css } from 'styled-components';
|
||||
import colors from 'config/colors';
|
||||
import { getColor, getIcon } from 'utils/notification';
|
||||
@ -13,21 +12,17 @@ import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
||||
|
||||
import * as NotificationActions from 'actions/NotificationActions';
|
||||
import Loader from 'components/Loader';
|
||||
import type { Action, State } from 'flowtype';
|
||||
import type { CallbackAction } from 'reducers/NotificationReducer';
|
||||
|
||||
import NotificationButton from './components/NotificationButton';
|
||||
|
||||
type Props = {
|
||||
key?: number,
|
||||
notifications: $ElementType<State, 'notifications'>,
|
||||
close: (notif?: any) => Action,
|
||||
};
|
||||
|
||||
type NProps = {
|
||||
type Props = {
|
||||
type: string,
|
||||
cancelable?: boolean;
|
||||
title: string;
|
||||
message?: string;
|
||||
actions?: Array<any>;
|
||||
actions?: Array<CallbackAction>;
|
||||
close?: typeof NotificationActions.close,
|
||||
loading?: boolean
|
||||
};
|
||||
@ -128,10 +123,9 @@ const ActionContent = styled.div`
|
||||
`}
|
||||
`;
|
||||
|
||||
export const Notification = (props: NProps): React$Element<string> => {
|
||||
const Notification = (props: Props): React$Element<string> => {
|
||||
const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action
|
||||
|
||||
|
||||
return (
|
||||
<Wrapper type={props.type}>
|
||||
{props.loading && <Loader size={50} /> }
|
||||
@ -167,7 +161,6 @@ export const Notification = (props: NProps): React$Element<string> => {
|
||||
<NotificationButton
|
||||
key={action.label}
|
||||
type={props.type}
|
||||
text={action.label}
|
||||
onClick={() => { close(); action.callback(); }}
|
||||
>{action.label}
|
||||
</NotificationButton>
|
||||
@ -179,26 +172,14 @@ export const Notification = (props: NProps): React$Element<string> => {
|
||||
);
|
||||
};
|
||||
|
||||
export const NotificationGroup = (props: Props) => {
|
||||
const { notifications, close } = props;
|
||||
return notifications.map(n => (
|
||||
<Notification
|
||||
key={n.title}
|
||||
type={n.type}
|
||||
title={n.title}
|
||||
message={n.message}
|
||||
cancelable={n.cancelable}
|
||||
actions={n.actions}
|
||||
close={close}
|
||||
/>
|
||||
));
|
||||
Notification.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
cancelable: PropTypes.bool,
|
||||
title: PropTypes.string.isRequired,
|
||||
message: PropTypes.string,
|
||||
actions: PropTypes.arrayOf(PropTypes.object),
|
||||
close: PropTypes.func,
|
||||
loading: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
state => ({
|
||||
notifications: state.notifications,
|
||||
}),
|
||||
dispatch => ({
|
||||
close: bindActionCreators(NotificationActions.close, dispatch),
|
||||
}),
|
||||
)(NotificationGroup);
|
||||
export default Notification;
|
@ -1,11 +1,11 @@
|
||||
/* @flow */
|
||||
import * as React from 'react';
|
||||
import { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
|
||||
import type { Props } from '../../index';
|
||||
|
||||
export default (props: Props) => {
|
||||
const { online } = props.wallet;
|
||||
if (online) return null;
|
||||
return (<Notification type="error" title="Wallet is offline" />);
|
||||
return (<Notification key="wallet-offline" type="error" title="Wallet is offline" />);
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
import * as React from 'react';
|
||||
import { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
|
||||
import type { Props } from '../../index';
|
||||
|
||||
@ -8,6 +8,7 @@ export default (props: Props) => {
|
||||
if (props.connect.transport && props.connect.transport.outdated) {
|
||||
return (
|
||||
<Notification
|
||||
key="update-bridge"
|
||||
type="warning"
|
||||
title="New Trezor Bridge is available"
|
||||
actions={
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
import * as React from 'react';
|
||||
import { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
|
||||
import type { Props } from '../../index';
|
||||
|
||||
@ -10,6 +10,7 @@ export default (props: Props) => {
|
||||
if (!outdated) return null;
|
||||
return (
|
||||
<Notification
|
||||
key="update-firmware"
|
||||
type="warning"
|
||||
title="Firmware update"
|
||||
actions={
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
import * as React from 'react';
|
||||
import { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
|
||||
import type { Props } from '../../index';
|
||||
|
||||
|
@ -4,7 +4,7 @@ import styled from 'styled-components';
|
||||
import Icon from 'components/Icon';
|
||||
import ICONS from 'config/icons';
|
||||
import colors from 'config/colors';
|
||||
import { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
import { getIcon, getColor } from 'utils/notification';
|
||||
|
||||
const Wrapper = styled.div``;
|
||||
|
@ -1,5 +1,7 @@
|
||||
/* @flow */
|
||||
import { Notification } from 'components/Notification';
|
||||
|
||||
import * as React from 'react';
|
||||
import Notification from 'components/Notification';
|
||||
|
||||
import type { Props } from '../../index';
|
||||
|
||||
@ -8,9 +10,14 @@ export default (props: Props) => {
|
||||
if (!location) return null;
|
||||
|
||||
const notifications: Array<Notification> = [];
|
||||
// Example:
|
||||
// if (location.state.device) {
|
||||
// notifications.push(<Notification key="example" type="info" title="Static example" />);
|
||||
// }
|
||||
|
||||
return notifications;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{notifications}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
@ -1,6 +1,5 @@
|
||||
/* @flow */
|
||||
|
||||
|
||||
import { DEVICE } from 'trezor-connect';
|
||||
import type { Device } from 'trezor-connect';
|
||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||
|
@ -13,6 +13,7 @@ export type CallbackAction = {
|
||||
}
|
||||
|
||||
export type NotificationEntry = {
|
||||
+key: string; // React.Key
|
||||
+id: ?string;
|
||||
+devicePath: ?string;
|
||||
+type: string;
|
||||
@ -38,6 +39,7 @@ const initialState: State = [
|
||||
const addNotification = (state: State, payload: any): State => {
|
||||
const newState: State = state.filter(e => !e.cancelable);
|
||||
newState.push({
|
||||
key: new Date().getTime().toString(),
|
||||
id: payload.id,
|
||||
devicePath: payload.devicePath,
|
||||
type: payload.type,
|
||||
|
11
src/store.js
11
src/store.js
@ -46,9 +46,8 @@ if (process.env.NODE_ENV === 'development') {
|
||||
collapsed: true,
|
||||
});
|
||||
|
||||
const { devToolsExtension }: ?Function = window;
|
||||
if (typeof devToolsExtension === 'function') {
|
||||
enhancers.push(devToolsExtension());
|
||||
if (window && typeof window.devToolsExtension === 'function') {
|
||||
enhancers.push(window.devToolsExtension());
|
||||
}
|
||||
|
||||
composedEnhancers = compose(
|
||||
@ -67,9 +66,3 @@ export default createStore(
|
||||
initialState,
|
||||
composedEnhancers,
|
||||
);
|
||||
|
||||
// if (process.env.NODE_ENV === 'production') {
|
||||
// module.exports = require('./store.dev'); // eslint-disable-line global-require
|
||||
// } else {
|
||||
// module.exports = require('./store.dev'); // eslint-disable-line global-require
|
||||
// }
|
@ -19,7 +19,7 @@ export type StateProps = {
|
||||
}
|
||||
|
||||
type DispatchProps = {
|
||||
|
||||
selectFirstAvailableDevice: typeof RouterActions.selectFirstAvailableDevice,
|
||||
}
|
||||
|
||||
type OwnProps = {
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
min-width: 720px;
|
||||
|
@ -12,6 +12,7 @@ import Loader from 'components/Loader';
|
||||
import P from 'components/Paragraph';
|
||||
import Icon from 'components/Icon';
|
||||
import ICONS from 'config/icons';
|
||||
import * as RouterActions from 'actions/RouterActions';
|
||||
|
||||
import type { State as TrezorConnectState } from 'reducers/TrezorConnectReducer';
|
||||
|
||||
|
@ -7,7 +7,8 @@ import Footer from 'components/Footer';
|
||||
import Log from 'components/Log';
|
||||
import Link from 'components/Link';
|
||||
import Loader from 'components/Loader';
|
||||
import Notifications, { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
import ContextNotifications from 'components/notifications/Context';
|
||||
import colors from 'config/colors';
|
||||
import P from 'components/Paragraph';
|
||||
import { H2 } from 'components/Heading';
|
||||
@ -98,7 +99,7 @@ export default (props: Props) => {
|
||||
type="error"
|
||||
/>
|
||||
)}
|
||||
<Notifications />
|
||||
<ContextNotifications />
|
||||
{shouldShowInitializationError && <InitializationError error={connectError} />}
|
||||
<Log />
|
||||
<LandingContent>
|
||||
|
@ -107,7 +107,7 @@ const DiscoveryLoadingText = styled.span`
|
||||
`;
|
||||
|
||||
// TODO: Refactorize deviceStatus & selectedAccounts
|
||||
const AccountMenu = (props: Props): ?React$Element<string> => {
|
||||
const AccountMenu = (props: Props) => {
|
||||
const selected = props.wallet.selectedDevice;
|
||||
const { location } = props.router;
|
||||
const urlParams = location.state;
|
||||
@ -117,7 +117,7 @@ const AccountMenu = (props: Props): ?React$Element<string> => {
|
||||
const { config } = props.localStorage;
|
||||
const selectedCoin = config.coins.find(c => c.network === location.state.network);
|
||||
|
||||
if (!selected || !selectedCoin) return;
|
||||
if (!selected || !selectedCoin) return null;
|
||||
|
||||
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
|
||||
|
||||
|
@ -1,7 +1,20 @@
|
||||
/* @flow */
|
||||
|
||||
import styled from 'styled-components';
|
||||
import colors from 'config/colors';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
type Props = {
|
||||
pathname: string;
|
||||
}
|
||||
|
||||
type State = {
|
||||
style: {
|
||||
width: number;
|
||||
left: number;
|
||||
}
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
@ -13,8 +26,6 @@ const Wrapper = styled.div`
|
||||
`;
|
||||
|
||||
class Indicator extends Component<Props, State> {
|
||||
reposition: () => void;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
@ -28,25 +39,19 @@ class Indicator extends Component<Props, State> {
|
||||
this.reposition = this.reposition.bind(this);
|
||||
}
|
||||
|
||||
state: State;
|
||||
|
||||
handleResize() {
|
||||
this.reposition();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.reposition();
|
||||
window.addEventListener('resize', this.reposition, false);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.reposition, false);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.reposition();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.reposition, false);
|
||||
}
|
||||
|
||||
reposition() {
|
||||
const tabs = document.querySelector('.account-tabs');
|
||||
if (!tabs) return;
|
||||
@ -66,6 +71,12 @@ class Indicator extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
reposition: () => void;
|
||||
|
||||
handleResize() {
|
||||
this.reposition();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Wrapper style={this.state.style}>{ this.props.pathname }</Wrapper>
|
||||
|
@ -1,10 +1,19 @@
|
||||
/* @flow */
|
||||
|
||||
import styled from 'styled-components';
|
||||
import React from 'react';
|
||||
import { FONT_SIZE } from 'config/variables';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import colors from 'config/colors';
|
||||
|
||||
import type { Location } from 'react-router';
|
||||
|
||||
import Indicator from './components/Indicator';
|
||||
|
||||
type Props = {
|
||||
location: Location;
|
||||
};
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
@ -39,9 +48,11 @@ const StyledNavLink = styled(NavLink)`
|
||||
`;
|
||||
|
||||
|
||||
const TopNavigationAccount = (props) => {
|
||||
const urlParams = props.match.params;
|
||||
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;
|
||||
const TopNavigationAccount = (props: Props) => {
|
||||
const { state, pathname } = props.location;
|
||||
if (!state) return null;
|
||||
|
||||
const basePath = `/device/${state.device}/network/${state.network}/account/${state.account}`;
|
||||
|
||||
return (
|
||||
<Wrapper className="account-tabs">
|
||||
@ -49,7 +60,7 @@ const TopNavigationAccount = (props) => {
|
||||
<StyledNavLink to={`${basePath}/receive`}>Receive</StyledNavLink>
|
||||
<StyledNavLink to={`${basePath}/send`}>Send</StyledNavLink>
|
||||
{/* <StyledNavLink to={`${basePath}/signverify`}>Sign & Verify</StyledNavLink> */}
|
||||
<Indicator pathname={props.match.pathname} />
|
||||
<Indicator pathname={pathname} />
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ import styled from 'styled-components';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import colors from 'config/colors';
|
||||
import { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
import * as TrezorConnectActions from 'actions/TrezorConnectActions';
|
||||
|
||||
import type { State, Dispatch } from 'flowtype';
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Notification } from 'components/Notification';
|
||||
import Notification from 'components/Notification';
|
||||
|
||||
const Wrapper = styled.div``;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user