mirror of
https://github.com/trezor/trezor-wallet
synced 2025-01-12 09:00:58 +00:00
Merge pull request #91 from satoshilabs/notification-groups
Implement notification groups + little refactor
This commit is contained in:
commit
b9820daf87
@ -0,0 +1,113 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
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 { getIcon, getColor } from 'utils/notification';
|
||||||
|
|
||||||
|
const Wrapper = styled.div``;
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: ${colors.WHITE};
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-bottom: 1px solid ${colors.BACKGROUND};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Left = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Right = styled.div``;
|
||||||
|
const Body = styled.div``;
|
||||||
|
|
||||||
|
const Title = styled.div`
|
||||||
|
color: ${props => props.color};
|
||||||
|
`;
|
||||||
|
|
||||||
|
class Group extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = {
|
||||||
|
visibleCount: 1,
|
||||||
|
visible: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle = () => {
|
||||||
|
if (this.state.visible) {
|
||||||
|
this.setState({
|
||||||
|
visible: false,
|
||||||
|
visibleCount: 0,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
visible: true,
|
||||||
|
visibleCount: this.props.groupNotifications.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { type, groupNotifications } = this.props;
|
||||||
|
const color = getColor(type);
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
{groupNotifications.length > 1 && (
|
||||||
|
<Header onClick={this.toggle}>
|
||||||
|
<Left>
|
||||||
|
<Icon
|
||||||
|
color={color}
|
||||||
|
size={30}
|
||||||
|
icon={getIcon(type)}
|
||||||
|
/>
|
||||||
|
<Title color={color}>
|
||||||
|
{groupNotifications.length} {groupNotifications.length > 1 ? `${type}s` : type}
|
||||||
|
</Title>
|
||||||
|
</Left>
|
||||||
|
<Right>
|
||||||
|
<Icon
|
||||||
|
icon={ICONS.ARROW_DOWN}
|
||||||
|
color={colors.TEXT_SECONDARY}
|
||||||
|
size={24}
|
||||||
|
isActive={this.state.visible}
|
||||||
|
canAnimate
|
||||||
|
/>
|
||||||
|
</Right>
|
||||||
|
</Header>
|
||||||
|
)}
|
||||||
|
<Body>
|
||||||
|
{groupNotifications
|
||||||
|
.slice(0, this.state.visibleCount)
|
||||||
|
.map(notification => (
|
||||||
|
<Notification
|
||||||
|
key={notification.title}
|
||||||
|
type={notification.type}
|
||||||
|
title={notification.title}
|
||||||
|
message={notification.message}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Body>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Group.propTypes = {
|
||||||
|
type: PropTypes.string,
|
||||||
|
groupNotifications: PropTypes.arrayOf({
|
||||||
|
key: PropTypes.string,
|
||||||
|
type: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
message: PropTypes.string,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Group;
|
56
src/components/Notification/NotificationGroups/index.js
Normal file
56
src/components/Notification/NotificationGroups/index.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { PRIORITY } from 'constants/notifications';
|
||||||
|
import Group from './components/Group';
|
||||||
|
|
||||||
|
const Wrapper = styled.div``;
|
||||||
|
|
||||||
|
class NotificationsGroup extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.notifications = [
|
||||||
|
{ type: 'warning', title: 'adddaa', message: 'aaaa' },
|
||||||
|
{ type: 'error', title: 'aaddda', message: 'aaaa' },
|
||||||
|
{ type: 'info', title: 'aafffa', message: 'aaaa' },
|
||||||
|
{ type: 'error', title: 'aggaa', message: 'aaaa' },
|
||||||
|
{ type: 'warning', title: 'aasssa', message: 'aaaa' },
|
||||||
|
{ type: 'success', title: 'afaa', message: 'aaaa' },
|
||||||
|
{ type: 'error', title: 'aada', message: 'aaaa' },
|
||||||
|
{ type: 'error', title: 'aafffa', message: 'aaaa' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
groupNotifications = notifications => notifications
|
||||||
|
.reduce((acc, obj) => {
|
||||||
|
const key = obj.type;
|
||||||
|
if (!acc[key]) {
|
||||||
|
acc[key] = [];
|
||||||
|
}
|
||||||
|
acc[key].push(obj);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
sortByPriority(notifications) {
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { notifications } = this;
|
||||||
|
const notificationGroups = this.groupNotifications(notifications);
|
||||||
|
const sortedNotifications = this.sortByPriority(notificationGroups);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
{Object.keys(sortedNotifications).map(group => (
|
||||||
|
<Group
|
||||||
|
groupNotifications={notificationGroups[group]}
|
||||||
|
type={group}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotificationsGroup;
|
@ -1,16 +1,36 @@
|
|||||||
|
/* @flow */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import colors from 'config/colors';
|
import colors from 'config/colors';
|
||||||
|
import { getColor, getIcon } from 'utils/notification';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import icons from 'config/icons';
|
import icons from 'config/icons';
|
||||||
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
||||||
|
|
||||||
import * as NotificationActions from 'actions/NotificationActions';
|
import * as NotificationActions from 'actions/NotificationActions';
|
||||||
import Loader from 'components/Loader';
|
import Loader from 'components/Loader';
|
||||||
|
import type { Action, State } from 'flowtype';
|
||||||
import NotificationButton from './components/NotificationButton';
|
import NotificationButton from './components/NotificationButton';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
key?: number,
|
||||||
|
notifications: $ElementType<State, 'notifications'>,
|
||||||
|
close: (notif?: any) => Action,
|
||||||
|
};
|
||||||
|
|
||||||
|
type NProps = {
|
||||||
|
type: string,
|
||||||
|
cancelable?: boolean;
|
||||||
|
title: string;
|
||||||
|
message?: string;
|
||||||
|
actions?: Array<any>;
|
||||||
|
close?: typeof NotificationActions.close,
|
||||||
|
loading?: boolean
|
||||||
|
};
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
color: ${colors.TEXT_PRIMARY};
|
color: ${colors.TEXT_PRIMARY};
|
||||||
@ -92,27 +112,6 @@ const ActionContent = styled.div`
|
|||||||
export const Notification = (props: NProps): React$Element<string> => {
|
export const Notification = (props: NProps): React$Element<string> => {
|
||||||
const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action
|
const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action
|
||||||
|
|
||||||
const getIconColor = (type) => {
|
|
||||||
let color;
|
|
||||||
switch (type) {
|
|
||||||
case 'info':
|
|
||||||
color = colors.INFO_PRIMARY;
|
|
||||||
break;
|
|
||||||
case 'error':
|
|
||||||
color = colors.ERROR_PRIMARY;
|
|
||||||
break;
|
|
||||||
case 'warning':
|
|
||||||
color = colors.WARNING_PRIMARY;
|
|
||||||
break;
|
|
||||||
case 'success':
|
|
||||||
color = colors.SUCCESS_PRIMARY;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper type={props.type}>
|
<Wrapper type={props.type}>
|
||||||
@ -120,7 +119,7 @@ export const Notification = (props: NProps): React$Element<string> => {
|
|||||||
{props.cancelable && (
|
{props.cancelable && (
|
||||||
<CloseClick onClick={() => close()}>
|
<CloseClick onClick={() => close()}>
|
||||||
<Icon
|
<Icon
|
||||||
color={getIconColor(props.type)}
|
color={getColor(props.type)}
|
||||||
icon={icons.CLOSE}
|
icon={icons.CLOSE}
|
||||||
size={20}
|
size={20}
|
||||||
/>
|
/>
|
||||||
@ -129,8 +128,8 @@ export const Notification = (props: NProps): React$Element<string> => {
|
|||||||
<Body>
|
<Body>
|
||||||
<MessageContent>
|
<MessageContent>
|
||||||
<StyledIcon
|
<StyledIcon
|
||||||
color={getIconColor(props.type)}
|
color={getColor(props.type)}
|
||||||
icon={icons[props.type.toUpperCase()]}
|
icon={getIcon(props.type)}
|
||||||
/>
|
/>
|
||||||
<Texts>
|
<Texts>
|
||||||
<Title>{ props.title }</Title>
|
<Title>{ props.title }</Title>
|
||||||
@ -161,7 +160,7 @@ export const Notification = (props: NProps): React$Element<string> => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NotificationGroup = (props) => {
|
export const NotificationGroup = (props: Props) => {
|
||||||
const { notifications, close } = props;
|
const { notifications, close } = props;
|
||||||
return notifications.map(n => (
|
return notifications.map(n => (
|
||||||
<Notification
|
<Notification
|
||||||
|
8
src/constants/notifications.js
Normal file
8
src/constants/notifications.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export default {
|
||||||
|
PRIORITY: {
|
||||||
|
error: 0,
|
||||||
|
warning: 1,
|
||||||
|
info: 2,
|
||||||
|
success: 3,
|
||||||
|
},
|
||||||
|
};
|
31
src/utils/notification.js
Normal file
31
src/utils/notification.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import colors from 'config/colors';
|
||||||
|
import icons from 'config/icons';
|
||||||
|
|
||||||
|
const getColor = (type) => {
|
||||||
|
let color;
|
||||||
|
switch (type) {
|
||||||
|
case 'info':
|
||||||
|
color = colors.INFO_PRIMARY;
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
color = colors.ERROR_PRIMARY;
|
||||||
|
break;
|
||||||
|
case 'warning':
|
||||||
|
color = colors.WARNING_PRIMARY;
|
||||||
|
break;
|
||||||
|
case 'success':
|
||||||
|
color = colors.SUCCESS_PRIMARY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
color = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getIcon = type => icons[type.toUpperCase()];
|
||||||
|
|
||||||
|
export {
|
||||||
|
getColor,
|
||||||
|
getIcon,
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user