mirror of
https://github.com/strapi/strapi.git
synced 2026-01-07 20:58:16 +00:00
Undo old notif code and add new notif
Signed-off-by: HichamELBSI <elabbassih@gmail.com>
This commit is contained in:
parent
ab6fb45c17
commit
4add565de4
@ -32,7 +32,8 @@ import { StrapiProvider } from 'strapi-helper-plugin';
|
||||
import { merge } from 'lodash';
|
||||
import Fonts from './components/Fonts';
|
||||
import { freezeApp, pluginLoaded, unfreezeApp, updatePlugin } from './containers/App/actions';
|
||||
import { showNotification, showNewNotification } from './containers/NotificationProvider/actions';
|
||||
import { showNotification } from './containers/NotificationProvider/actions';
|
||||
import { showNotification as showNewNotification } from './containers/NewNotification/actions';
|
||||
|
||||
import basename from './utils/basename';
|
||||
import getInjectors from './utils/reducerInjectors';
|
||||
|
||||
@ -0,0 +1,168 @@
|
||||
import styled, { createGlobalStyle } from 'styled-components';
|
||||
import { themePropTypes } from 'strapi-helper-plugin';
|
||||
|
||||
const GlobalNotification = createGlobalStyle`
|
||||
.notificationIcon {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
> div {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 10px;
|
||||
left: 5px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid ${props => props.theme.main.colors.green};
|
||||
display: flex;
|
||||
svg {
|
||||
margin: auto;
|
||||
color: ${props => props.theme.main.colors.green};
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notificationContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 220px;
|
||||
margin: 0;
|
||||
padding-right: 10px;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.notificationTitle {
|
||||
margin-bottom: 0;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.8rem;
|
||||
}
|
||||
|
||||
.notificationClose {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 20px;
|
||||
margin-right: 15px;
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
font-size: 1.4rem;
|
||||
color: #BBC2BF;
|
||||
transition: opacity 0.1s ease;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin: auto;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 100!important;
|
||||
}
|
||||
}
|
||||
|
||||
.notificationSuccess{
|
||||
.notificationIcon {
|
||||
div {
|
||||
border-color: ${props => props.theme.main.colors.green};
|
||||
}
|
||||
svg {
|
||||
color: ${props => props.theme.main.colors.green};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notificationWarning {
|
||||
.notificationIcon {
|
||||
div {
|
||||
border-color: ${props => props.theme.main.colors.orange};
|
||||
}
|
||||
svg {
|
||||
color: ${props => props.theme.main.colors.orange};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notificationError {
|
||||
.notificationIcon {
|
||||
div {
|
||||
border-color: ${props => props.theme.main.colors.red};
|
||||
}
|
||||
svg {
|
||||
color: ${props => props.theme.main.colors.red};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notificationInfo {
|
||||
.notificationIcon {
|
||||
div {
|
||||
border-color: ${props => props.theme.main.colors.blue};
|
||||
}
|
||||
svg {
|
||||
color: ${props => props.theme.main.colors.blue};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Li = styled.li`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 300px;
|
||||
min-height: 60px;
|
||||
margin-bottom: 14px;
|
||||
background: ${props => props.theme.main.colors.white};
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.15);
|
||||
color: #333740;
|
||||
transition: all 0.15s ease;
|
||||
overflow: hidden;
|
||||
z-index: 10;
|
||||
padding: 1rem;
|
||||
border-left: 2px solid ${props => props.theme.main.colors.green};
|
||||
&.notificationError {
|
||||
border-color: ${props => props.theme.main.colors.red};
|
||||
}
|
||||
&.notificationWarning {
|
||||
border-color: ${props => props.theme.main.colors.orange};
|
||||
}
|
||||
&.notificationInfo {
|
||||
border-color: ${props => props.theme.main.colors.blue};
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
`;
|
||||
|
||||
Li.defaultProps = {
|
||||
theme: {
|
||||
main: {
|
||||
colors: {
|
||||
leftMenu: {},
|
||||
},
|
||||
sizes: {
|
||||
header: {},
|
||||
leftMenu: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Li.propTypes = {
|
||||
...themePropTypes,
|
||||
};
|
||||
|
||||
export default Li;
|
||||
|
||||
export { GlobalNotification };
|
||||
@ -6,106 +6,84 @@
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Padded, Text, Flex } from '@buffetjs/core';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { isObject } from 'lodash';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Remove } from '@buffetjs/icons';
|
||||
import Li, { GlobalNotification } from './Li';
|
||||
|
||||
import { NotificationWrapper, IconWrapper, LinkArrow, RemoveWrapper } from './styledComponents';
|
||||
|
||||
const types = {
|
||||
success: {
|
||||
icon: 'check',
|
||||
color: 'green',
|
||||
},
|
||||
warning: {
|
||||
icon: 'exclamation',
|
||||
color: 'orange',
|
||||
},
|
||||
info: {
|
||||
icon: 'info',
|
||||
color: 'blue',
|
||||
},
|
||||
};
|
||||
|
||||
const Notification = ({ notification, onHideNotification }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const { title, message, link, type, id, onClose } = notification;
|
||||
|
||||
const formattedMessage = formatMessage(typeof message === 'string' ? { id: message } : message);
|
||||
|
||||
const handleClose = () => {
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
|
||||
onHideNotification(id);
|
||||
class Notification extends React.Component {
|
||||
// eslint-disable-line react/prefer-stateless-function
|
||||
handleCloseClicked = () => {
|
||||
this.props.onHideNotification(this.props.notification.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<NotificationWrapper color={types[type].color}>
|
||||
<Padded top left right bottom size="smd">
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<IconWrapper>
|
||||
<FontAwesomeIcon icon={types[type].icon} />
|
||||
</IconWrapper>
|
||||
<Padded left size="sm" style={{ width: '80%', flex: 1 }}>
|
||||
{title && (
|
||||
<Text
|
||||
fontSize="xs"
|
||||
textTransform="uppercase"
|
||||
color="grey"
|
||||
title={formatMessage(title)}
|
||||
>
|
||||
{formatMessage(title)}
|
||||
</Text>
|
||||
)}
|
||||
<Flex>
|
||||
{message && (
|
||||
<Text title={formattedMessage} ellipsis>
|
||||
{formattedMessage}
|
||||
</Text>
|
||||
)}
|
||||
{link && (
|
||||
<a href={link.url} target="_blank" rel="noopener noreferrer">
|
||||
<Padded left size="xs">
|
||||
<Flex alignItems="center">
|
||||
<Text
|
||||
style={{ maxWidth: '120px' }}
|
||||
ellipsis
|
||||
fontWeight="bold"
|
||||
color="blue"
|
||||
title={formatMessage(link.label)}
|
||||
>
|
||||
{formatMessage(link.label)}
|
||||
</Text>
|
||||
<Padded left size="xs" />
|
||||
<LinkArrow />
|
||||
</Flex>
|
||||
</Padded>
|
||||
</a>
|
||||
)}
|
||||
</Flex>
|
||||
</Padded>
|
||||
<RemoveWrapper>
|
||||
<Remove onClick={handleClose} />
|
||||
</RemoveWrapper>
|
||||
</Flex>
|
||||
</Padded>
|
||||
</NotificationWrapper>
|
||||
);
|
||||
};
|
||||
options = {
|
||||
success: {
|
||||
icon: 'check',
|
||||
title: 'Success',
|
||||
class: 'notificationSuccess',
|
||||
},
|
||||
warning: {
|
||||
icon: 'exclamation',
|
||||
title: 'Warning',
|
||||
class: 'notificationWarning',
|
||||
},
|
||||
error: {
|
||||
icon: 'exclamation',
|
||||
title: 'Error',
|
||||
class: 'notificationError',
|
||||
},
|
||||
info: {
|
||||
icon: 'info',
|
||||
title: 'Info',
|
||||
class: 'notificationInfo',
|
||||
},
|
||||
};
|
||||
|
||||
render() {
|
||||
const options = this.options[this.props.notification.status] || this.options.info;
|
||||
const {
|
||||
notification: { message },
|
||||
} = this.props;
|
||||
const content =
|
||||
isObject(message) && message.id ? (
|
||||
<FormattedMessage id={message.id} defaultMessage={message.id} values={message.values} />
|
||||
) : (
|
||||
<FormattedMessage id={message} defaultMessage={message} />
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<GlobalNotification />
|
||||
<Li
|
||||
key={this.props.notification.id}
|
||||
className={`${options.class}`}
|
||||
onClick={this.handleCloseClicked}
|
||||
>
|
||||
<div className={`notificationIcon`}>
|
||||
<div>
|
||||
<FontAwesomeIcon icon={options.icon} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="notificationContent">
|
||||
<p className="notificationTitle">{content}</p>
|
||||
</div>
|
||||
<div className={`notificationClose`}>
|
||||
<Remove onClick={this.handleCloseClicked} />
|
||||
</div>
|
||||
</Li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Notification.defaultProps = {
|
||||
notification: {
|
||||
id: 1,
|
||||
type: 'success',
|
||||
message: {
|
||||
id: 'notification.success.saved',
|
||||
defaultMessage: 'Saved',
|
||||
},
|
||||
message: 'app.utils.defaultMessage',
|
||||
status: 'success',
|
||||
},
|
||||
onClose: () => null,
|
||||
};
|
||||
|
||||
Notification.propTypes = {
|
||||
@ -114,27 +92,12 @@ Notification.propTypes = {
|
||||
message: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
values: PropTypes.object,
|
||||
}),
|
||||
]),
|
||||
title: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string,
|
||||
values: PropTypes.object,
|
||||
}),
|
||||
link: PropTypes.shape({
|
||||
url: PropTypes.string.isRequired,
|
||||
label: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string,
|
||||
values: PropTypes.object,
|
||||
}).isRequired,
|
||||
}),
|
||||
type: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
}),
|
||||
onClose: PropTypes.func,
|
||||
onHideNotification: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -9,29 +9,33 @@ import PropTypes from 'prop-types';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
|
||||
import Notification from '../Notification';
|
||||
import NotificationsWrapper from './NotificationsWrapper';
|
||||
import Wrapper from './Wrapper';
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
const NotificationsContainer = ({ notifications, onHideNotification }) => {
|
||||
if (notifications.length === 0) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<NotificationsWrapper>
|
||||
{notifications.map(notification => (
|
||||
<CSSTransition
|
||||
key={notification.id}
|
||||
classNames="notification"
|
||||
timeout={{
|
||||
enter: 500,
|
||||
exit: 300,
|
||||
}}
|
||||
>
|
||||
<Notification notification={notification} onHideNotification={onHideNotification} />
|
||||
</CSSTransition>
|
||||
))}
|
||||
</NotificationsWrapper>
|
||||
);
|
||||
const notifs = notifications.map((notification, i) => (
|
||||
<CSSTransition
|
||||
key={i}
|
||||
classNames="notification"
|
||||
timeout={{
|
||||
enter: 500,
|
||||
exit: 300,
|
||||
}}
|
||||
>
|
||||
<Notification
|
||||
key={notification.id}
|
||||
onHideNotification={onHideNotification}
|
||||
notification={notification}
|
||||
/>
|
||||
</CSSTransition>
|
||||
));
|
||||
|
||||
return <Wrapper>{notifs}</Wrapper>;
|
||||
};
|
||||
|
||||
NotificationsContainer.defaultProps = {
|
||||
@ -39,9 +43,7 @@ NotificationsContainer.defaultProps = {
|
||||
{
|
||||
id: 1,
|
||||
message: 'app.utils.defaultMessage',
|
||||
title: null,
|
||||
link: null,
|
||||
type: 'success',
|
||||
status: 'success',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -27,6 +27,7 @@ import PrivateRoute from '../PrivateRoute';
|
||||
import Theme from '../Theme';
|
||||
import { Content, Wrapper } from './components';
|
||||
import { getDataSucceeded } from './actions';
|
||||
import NewNotification from '../NewNotification';
|
||||
|
||||
function App(props) {
|
||||
const getDataRef = useRef();
|
||||
@ -97,6 +98,7 @@ function App(props) {
|
||||
<Wrapper>
|
||||
<GlobalStyle />
|
||||
<NotificationProvider />
|
||||
<NewNotification />
|
||||
<Content>
|
||||
<Switch>
|
||||
<Route
|
||||
|
||||
@ -82,7 +82,7 @@ const HomePage = ({ global: { strapiVersion }, history: { push } }) => {
|
||||
id: 'notification.version.update.link',
|
||||
},
|
||||
},
|
||||
timeout: 100000,
|
||||
blockTransition: true,
|
||||
onClose: () => localStorage.setItem('STRAPI_UPDATE_NOTIF', true),
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,158 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Padded, Text, Flex } from '@buffetjs/core';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Remove } from '@buffetjs/icons';
|
||||
|
||||
import { HIDE_NEW_NOTIFICATION } from '../constants';
|
||||
import { NotificationWrapper, IconWrapper, LinkArrow, RemoveWrapper } from './styledComponents';
|
||||
|
||||
const types = {
|
||||
success: {
|
||||
icon: 'check',
|
||||
color: 'green',
|
||||
},
|
||||
warning: {
|
||||
icon: 'exclamation',
|
||||
color: 'orange',
|
||||
},
|
||||
info: {
|
||||
icon: 'info',
|
||||
color: 'blue',
|
||||
},
|
||||
};
|
||||
|
||||
const Notification = ({ notification }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const { title, message, link, type, id, onClose, timeout, blockTransition } = notification;
|
||||
|
||||
const formattedMessage = formatMessage(typeof message === 'string' ? { id: message } : message);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: HIDE_NEW_NOTIFICATION,
|
||||
id,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
let timeoutToClear;
|
||||
|
||||
if (!blockTransition) {
|
||||
timeoutToClear = setTimeout(() => {
|
||||
handleClose();
|
||||
}, timeout || 2500);
|
||||
}
|
||||
|
||||
return () => clearTimeout(timeoutToClear);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [blockTransition]);
|
||||
|
||||
return (
|
||||
<NotificationWrapper color={types[type].color}>
|
||||
<Padded top left right bottom size="smd">
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<IconWrapper>
|
||||
<FontAwesomeIcon icon={types[type].icon} />
|
||||
</IconWrapper>
|
||||
<Padded left size="sm" style={{ width: '80%', flex: 1 }}>
|
||||
{title && (
|
||||
<Text
|
||||
fontSize="xs"
|
||||
textTransform="uppercase"
|
||||
color="grey"
|
||||
title={formatMessage(title)}
|
||||
>
|
||||
{formatMessage(title)}
|
||||
</Text>
|
||||
)}
|
||||
<Flex>
|
||||
{message && (
|
||||
<Text title={formattedMessage} ellipsis>
|
||||
{formattedMessage}
|
||||
</Text>
|
||||
)}
|
||||
{link && (
|
||||
<a href={link.url} target="_blank" rel="noopener noreferrer">
|
||||
<Padded left size="xs">
|
||||
<Flex alignItems="center">
|
||||
<Text
|
||||
style={{ maxWidth: '120px' }}
|
||||
ellipsis
|
||||
fontWeight="bold"
|
||||
color="blue"
|
||||
title={formatMessage(link.label)}
|
||||
>
|
||||
{formatMessage(link.label)}
|
||||
</Text>
|
||||
<Padded left size="xs" />
|
||||
<LinkArrow />
|
||||
</Flex>
|
||||
</Padded>
|
||||
</a>
|
||||
)}
|
||||
</Flex>
|
||||
</Padded>
|
||||
<RemoveWrapper>
|
||||
<Remove onClick={handleClose} />
|
||||
</RemoveWrapper>
|
||||
</Flex>
|
||||
</Padded>
|
||||
</NotificationWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
Notification.defaultProps = {
|
||||
notification: {
|
||||
id: 1,
|
||||
type: 'success',
|
||||
message: {
|
||||
id: 'notification.success.saved',
|
||||
defaultMessage: 'Saved',
|
||||
},
|
||||
onClose: () => null,
|
||||
timeout: 2500,
|
||||
blockTransition: false,
|
||||
},
|
||||
};
|
||||
|
||||
Notification.propTypes = {
|
||||
notification: PropTypes.shape({
|
||||
id: PropTypes.number,
|
||||
message: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string,
|
||||
values: PropTypes.object,
|
||||
}),
|
||||
]),
|
||||
title: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string,
|
||||
values: PropTypes.object,
|
||||
}),
|
||||
link: PropTypes.shape({
|
||||
url: PropTypes.string.isRequired,
|
||||
label: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string,
|
||||
values: PropTypes.object,
|
||||
}).isRequired,
|
||||
}),
|
||||
type: PropTypes.string,
|
||||
onClose: PropTypes.func,
|
||||
timeout: PropTypes.number,
|
||||
blockTransition: PropTypes.bool,
|
||||
}),
|
||||
};
|
||||
|
||||
export default Notification;
|
||||
@ -43,7 +43,6 @@ const RemoveWrapper = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 20px;
|
||||
margin-right: 15px;
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
font-size: 1.4rem;
|
||||
@ -0,0 +1,14 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { SHOW_NEW_NOTIFICATION } from './constants';
|
||||
|
||||
let notifIf = 0;
|
||||
|
||||
export function showNotification(config) {
|
||||
notifIf++;
|
||||
|
||||
return {
|
||||
id: notifIf,
|
||||
type: SHOW_NEW_NOTIFICATION,
|
||||
config,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
export const SHOW_NEW_NOTIFICATION = 'SHOW_NEW_NOTIFICATION';
|
||||
export const HIDE_NEW_NOTIFICATION = 'HIDE_NEW_NOTIFICATION';
|
||||
@ -0,0 +1,39 @@
|
||||
/**
|
||||
*
|
||||
* NotificationsContainer
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
|
||||
import Notification from './Notification';
|
||||
import NotificationsWrapper from './Wrapper';
|
||||
|
||||
const NotificationsContainer = () => {
|
||||
const notifications = useSelector(state => state.get('newNotification').notifications);
|
||||
|
||||
if (notifications.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NotificationsWrapper>
|
||||
{notifications.map(notification => (
|
||||
<CSSTransition
|
||||
key={notification.id}
|
||||
classNames="notification"
|
||||
timeout={{
|
||||
enter: 500,
|
||||
exit: 300,
|
||||
}}
|
||||
>
|
||||
<Notification notification={notification} />
|
||||
</CSSTransition>
|
||||
))}
|
||||
</NotificationsWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationsContainer;
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
*
|
||||
* NotificationProvider reducer
|
||||
*
|
||||
*/
|
||||
|
||||
import produce from 'immer';
|
||||
import { get } from 'lodash';
|
||||
import { SHOW_NEW_NOTIFICATION, HIDE_NEW_NOTIFICATION } from './constants';
|
||||
|
||||
const initialState = {
|
||||
notifications: [],
|
||||
};
|
||||
|
||||
const notificationReducer = (state = initialState, action) =>
|
||||
// eslint-disable-next-line consistent-return
|
||||
produce(state, draftState => {
|
||||
switch (action.type) {
|
||||
case SHOW_NEW_NOTIFICATION: {
|
||||
draftState.notifications.push({
|
||||
...action.config,
|
||||
id: action.id,
|
||||
type: get(action, ['config', 'type'], 'success'),
|
||||
message: get(action, ['config', 'message'], {
|
||||
id: 'notification.success.saved',
|
||||
defaultMessage: 'Saved',
|
||||
}),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case HIDE_NEW_NOTIFICATION: {
|
||||
const indexToRemove = state.notifications.findIndex(notif => notif.id === action.id);
|
||||
|
||||
if (indexToRemove !== -1) {
|
||||
draftState.notifications.splice(indexToRemove, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
return draftState;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default notificationReducer;
|
||||
@ -0,0 +1,69 @@
|
||||
import reducer from '../reducer';
|
||||
import { SHOW_NEW_NOTIFICATION, HIDE_NEW_NOTIFICATION } from '../constants';
|
||||
|
||||
describe('AMDIN | CONTAINERS | NEWNOTIFICATION | reducer', () => {
|
||||
describe('DEFAULT_ACTION', () => {
|
||||
it('should return the initialState', () => {
|
||||
const state = {
|
||||
test: true,
|
||||
};
|
||||
|
||||
expect(reducer(state, {})).toEqual(state);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SHOW_NEW_NOTIFICATION', () => {
|
||||
it('should add a notification', () => {
|
||||
const action = {
|
||||
type: SHOW_NEW_NOTIFICATION,
|
||||
id: 1,
|
||||
config: {
|
||||
type: 'success',
|
||||
message: {
|
||||
id: 'notification.message',
|
||||
},
|
||||
},
|
||||
};
|
||||
const initialState = {
|
||||
notifications: [],
|
||||
};
|
||||
const expected = {
|
||||
notifications: [{ id: 1, message: { id: 'notification.message' }, type: 'success' }],
|
||||
};
|
||||
|
||||
expect(reducer(initialState, action)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('HIDE_NEW_NOTIFICATION', () => {
|
||||
it('should remove a notification if the notification exist', () => {
|
||||
const action = {
|
||||
type: HIDE_NEW_NOTIFICATION,
|
||||
id: 1,
|
||||
};
|
||||
const initialState = {
|
||||
notifications: [{ id: 1, message: { id: 'notification.message' }, type: 'success' }],
|
||||
};
|
||||
const expected = {
|
||||
notifications: [],
|
||||
};
|
||||
|
||||
expect(reducer(initialState, action)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should not remove the notification if the notification does not exist', () => {
|
||||
const action = {
|
||||
type: HIDE_NEW_NOTIFICATION,
|
||||
id: 3,
|
||||
};
|
||||
const initialState = {
|
||||
notifications: [{ id: 1, message: { id: 'notification.message' }, type: 'success' }],
|
||||
};
|
||||
const expected = {
|
||||
notifications: [{ id: 1, message: { id: 'notification.message' }, type: 'success' }],
|
||||
};
|
||||
|
||||
expect(reducer(initialState, action)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -7,40 +7,25 @@
|
||||
/* eslint-disable import/no-cycle */
|
||||
import { dispatch } from '../../app';
|
||||
|
||||
import { SHOW_NOTIFICATION, HIDE_NOTIFICATION, SHOW_NEW_NOTIFICATION } from './constants';
|
||||
import { SHOW_NOTIFICATION, HIDE_NOTIFICATION } from './constants';
|
||||
|
||||
let nextNotificationId = 0;
|
||||
|
||||
const show = (config = {}) => {
|
||||
export function showNotification(message, status) {
|
||||
nextNotificationId++; // eslint-disable-line no-plusplus
|
||||
|
||||
// Start timeout to hide the notification
|
||||
(id => {
|
||||
setTimeout(() => {
|
||||
dispatch(hideNotification(id));
|
||||
}, config.timeout || 2500);
|
||||
}, 250000);
|
||||
})(nextNotificationId);
|
||||
};
|
||||
|
||||
// TODO : To remove when the old notification api will be deleted from the codebase
|
||||
export function showNotification(message, status) {
|
||||
show();
|
||||
|
||||
return {
|
||||
id: nextNotificationId,
|
||||
type: SHOW_NOTIFICATION,
|
||||
message,
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
export function showNewNotification(config) {
|
||||
show(config);
|
||||
|
||||
return {
|
||||
id: nextNotificationId,
|
||||
type: SHOW_NEW_NOTIFICATION,
|
||||
config,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -6,4 +6,3 @@
|
||||
|
||||
export const SHOW_NOTIFICATION = 'app/NotificationProvider/SHOW_NOTIFICATION';
|
||||
export const HIDE_NOTIFICATION = 'app/NotificationProvider/HIDE_NOTIFICATION';
|
||||
export const SHOW_NEW_NOTIFICATION = 'app/NotificatoinProvider/SHOW_NEW_NOTIFICATION';
|
||||
|
||||
@ -25,7 +25,7 @@ export class NotificationProvider extends React.Component {
|
||||
}
|
||||
|
||||
NotificationProvider.propTypes = {
|
||||
notifications: PropTypes.array.isRequired,
|
||||
notifications: PropTypes.object.isRequired,
|
||||
onHideNotification: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
const NotificationComponent = () => {
|
||||
const notificationInstances = document.querySelectorAll('*[id^="strapi-notif"]');
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: (notificationInstances.length + 1) * 50,
|
||||
left: 10,
|
||||
backgroundColor: 'red',
|
||||
color: 'black',
|
||||
padding: '10px',
|
||||
border: '1px solid',
|
||||
}}
|
||||
>
|
||||
My custom notification
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const displayNotif = () => {
|
||||
const div = document.createElement('div');
|
||||
div.setAttribute('id', 'strapi-notif');
|
||||
|
||||
document.body.appendChild(div);
|
||||
|
||||
ReactDOM.render(<NotificationComponent />, div);
|
||||
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(div);
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
export default displayNotif;
|
||||
@ -4,52 +4,45 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import produce from 'immer';
|
||||
import { get } from 'lodash';
|
||||
import { SHOW_NOTIFICATION, SHOW_NEW_NOTIFICATION, HIDE_NOTIFICATION } from './constants';
|
||||
import { fromJS } from 'immutable';
|
||||
import { SHOW_NOTIFICATION, HIDE_NOTIFICATION } from './constants';
|
||||
|
||||
const initialState = {
|
||||
const initialState = fromJS({
|
||||
notifications: [],
|
||||
};
|
||||
});
|
||||
|
||||
const notificationReducer = (state = initialState, action) =>
|
||||
// eslint-disable-next-line consistent-return
|
||||
produce(state, draftState => {
|
||||
console.log(state);
|
||||
switch (action.type) {
|
||||
case SHOW_NEW_NOTIFICATION: {
|
||||
draftState.notifications.push({
|
||||
...action.config,
|
||||
id: action.id,
|
||||
type: get(action, ['config', 'type'], 'success'),
|
||||
message: get(action, ['config', 'message'], {
|
||||
id: 'notification.success.saved',
|
||||
defaultMessage: 'Saved',
|
||||
}),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHOW_NOTIFICATION: {
|
||||
draftState.notifications.push({
|
||||
function notificationProviderReducer(state = initialState, action) {
|
||||
// Init variable
|
||||
let index;
|
||||
|
||||
switch (action.type) {
|
||||
case SHOW_NOTIFICATION:
|
||||
return state.set(
|
||||
'notifications',
|
||||
state.get('notifications').push({
|
||||
message: action.message || 'app.utils.defaultMessage',
|
||||
type: action.status || 'success',
|
||||
status: action.status || 'success',
|
||||
id: action.id,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case HIDE_NOTIFICATION: {
|
||||
const indexToRemove = state.notifications.findIndex(notif => notif.id === action.id);
|
||||
|
||||
if (indexToRemove !== -1) {
|
||||
draftState.notifications.splice(indexToRemove, 1);
|
||||
})
|
||||
);
|
||||
case HIDE_NOTIFICATION:
|
||||
// Check that the index exists
|
||||
state.get('notifications').forEach((notification, i) => {
|
||||
if (notification.id === action.id) {
|
||||
index = i;
|
||||
}
|
||||
break;
|
||||
});
|
||||
|
||||
if (typeof index !== 'undefined') {
|
||||
// Remove the notification
|
||||
return state.set('notifications', state.get('notifications').splice(index, 1));
|
||||
}
|
||||
|
||||
default: {
|
||||
return draftState;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Notification not found, return the current state
|
||||
return state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default notificationReducer;
|
||||
export default notificationProviderReducer;
|
||||
|
||||
@ -14,15 +14,13 @@ const selectNotificationProviderDomain = () => state => state.get('notification'
|
||||
*/
|
||||
|
||||
const selectNotificationProvider = () =>
|
||||
createSelector(
|
||||
selectNotificationProviderDomain(),
|
||||
notificationProviderState => notificationProviderState
|
||||
createSelector(selectNotificationProviderDomain(), notificationProviderState =>
|
||||
notificationProviderState.toJS()
|
||||
);
|
||||
|
||||
const selectNotifications = () =>
|
||||
createSelector(
|
||||
selectNotificationProviderDomain(),
|
||||
notificationProviderState => notificationProviderState.notifications
|
||||
createSelector(selectNotificationProviderDomain(), notificationProviderState =>
|
||||
notificationProviderState.get('notifications')
|
||||
);
|
||||
|
||||
export default selectNotificationProvider;
|
||||
|
||||
@ -8,6 +8,7 @@ import globalReducer from './containers/App/reducer';
|
||||
import adminReducer from './containers/Admin/reducer';
|
||||
import languageProviderReducer from './containers/LanguageProvider/reducer';
|
||||
import notificationProviderReducer from './containers/NotificationProvider/reducer';
|
||||
import newNotificationReducer from './containers/NewNotification/reducer';
|
||||
|
||||
/**
|
||||
* Creates the main reducer with the dynamically injected ones
|
||||
@ -18,6 +19,7 @@ export default function createReducer(injectedReducers) {
|
||||
admin: adminReducer,
|
||||
language: languageProviderReducer,
|
||||
notification: notificationProviderReducer,
|
||||
newNotification: newNotificationReducer,
|
||||
...injectedReducers,
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user