1
0
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:
Vladimir Volek 2018-10-10 11:31:31 +02:00 committed by GitHub
commit 6dfd941c44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 160 additions and 97 deletions

View File

@ -3,4 +3,5 @@ build
build-devel
coverage
node_modules
src/flowtype/npm
src/flowtype/npm
**/_old/*

View File

@ -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

View File

@ -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}>

View File

@ -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}>&copy; {getYear(new Date())}</Copy>
<StyledLink href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" isGreen>SatoshiLabs</StyledLink>

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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" />);
};

View File

@ -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={

View File

@ -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={

View File

@ -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';

View File

@ -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``;

View File

@ -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>
);
};

View File

@ -1,6 +1,5 @@
/* @flow */
import { DEVICE } from 'trezor-connect';
import type { Device } from 'trezor-connect';
import * as CONNECT from 'actions/constants/TrezorConnect';

View File

@ -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,

View File

@ -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
// }

View File

@ -19,7 +19,7 @@ export type StateProps = {
}
type DispatchProps = {
selectFirstAvailableDevice: typeof RouterActions.selectFirstAvailableDevice,
}
type OwnProps = {

View File

@ -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;

View File

@ -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';

View File

@ -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>

View File

@ -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);

View File

@ -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>

View File

@ -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>
);
};

View File

@ -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';

View File

@ -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``;