Add a notification module

This commit is contained in:
Grégoire Delattre 2019-07-12 13:57:10 +02:00
parent a53deebbcf
commit 481715d8a2
7 changed files with 155 additions and 1 deletions

View File

@ -0,0 +1,9 @@
export const sendNotification = (data) => ({
type: "ADD_NOTIFICATION",
payload: data,
})
export const removeNotification = (id) => ({
type: "REMOVE_NOTIFICATION",
payload: { id },
})

View File

@ -26,6 +26,7 @@ import store, { history } from "./store"
// Components // Components
import { AdminPanel } from "./components/admins/panel" import { AdminPanel } from "./components/admins/panel"
import { Notifications } from "./components/notifications/notifications"
import Alert from "./components/alerts/alert" import Alert from "./components/alerts/alert"
import MovieList from "./components/movies/list" import MovieList from "./components/movies/list"
import MoviesRoute from "./components/movies/route" import MoviesRoute from "./components/movies/route"
@ -48,6 +49,7 @@ const App = () => (
<WsHandler /> <WsHandler />
<NavBar /> <NavBar />
<Alert /> <Alert />
<Notifications />
<Container fluid> <Container fluid>
<Switch> <Switch>
<AdminRoute path="/admin" exact component={AdminPanel} /> <AdminRoute path="/admin" exact component={AdminPanel} />

View File

@ -0,0 +1,70 @@
import React, { useState } from "react"
import PropTypes from "prop-types"
import { connect } from "react-redux"
import { Toast } from "react-bootstrap"
import { removeNotification } from "../../actions/notifications"
const NotificationConnected = ({
id,
icon,
title,
message,
imageUrl,
autohide,
delay,
removeNotification,
}) => {
const [show, setShow] = useState(true)
const hide = () => {
setShow(false);
setTimeout(() => removeNotification(id), 200);
}
return (
<Toast
show={show}
onClose={hide}
autohide={autohide}
delay={delay}
>
<Toast.Header>
{icon !== "" &&
<i className={`fa fa-${icon} mr-2`} />
}
<strong className="mr-auto">{title}</strong>
</Toast.Header>
<Toast.Body>
{message !== "" &&
<span>{message}</span>
}
{imageUrl !== "" &&
<img src={imageUrl} className="img-fluid mt-2 mr-auto" />
}
</Toast.Body>
</Toast>
)
}
NotificationConnected.propTypes = {
id: PropTypes.string,
icon: PropTypes.string,
title: PropTypes.string,
message: PropTypes.string,
imageUrl: PropTypes.string,
autohide: PropTypes.bool,
delay: PropTypes.number,
removeNotification: PropTypes.func,
};
NotificationConnected.defaultProps = {
autohide: false,
delay: 5000,
icon: "",
imageUrl: "",
title: "Info",
message: "",
};
export const Notification = connect(null, {removeNotification})(NotificationConnected);

View File

@ -0,0 +1,34 @@
import React from "react"
import PropTypes from "prop-types"
import { List } from "immutable"
import { connect } from "react-redux"
import { Notification } from "./notification"
const NotificationsConnected = ({ notifications }) => {
return (
<div className="notifications">
{notifications.map((el) => (
<Notification
key={el.get("id")}
id={el.get("id")}
icon={el.get("icon", "video-camera")}
title={el.get("title", "Notification")}
message={el.get("message")}
autohide={el.get("autohide", true)}
delay={el.get("delay", 10000)}
imageUrl={el.get("imageUrl")}
/>
))}
</div>
)
}
NotificationsConnected.propTypes = {
notifications: PropTypes.instanceOf(List),
}
const mapStateToProps = (state) => ({
notifications: state.notifications,
});
export const Notifications = connect(mapStateToProps)(NotificationsConnected);

View File

@ -8,6 +8,7 @@ import alerts from "./alerts"
import torrentStore from "./torrents" import torrentStore from "./torrents"
import adminStore from "./admins" import adminStore from "./admins"
import polochon from "./polochon" import polochon from "./polochon"
import notifications from "./notifications"
export default combineReducers({ export default combineReducers({
movieStore, movieStore,
@ -18,4 +19,5 @@ export default combineReducers({
torrentStore, torrentStore,
adminStore, adminStore,
polochon, polochon,
notifications,
}); });

View File

@ -0,0 +1,15 @@
import { List, fromJS } from "immutable"
const defaultState = List();
const handlers = {
"ADD_NOTIFICATION": (state, action) => state.push(fromJS({
id: Math.random().toString(36).substring(7),
...action.payload
})),
"REMOVE_NOTIFICATION": (state, action) =>
state.filter((e) => (e.get("id") !== action.payload.id)),
}
export default (state = defaultState, action) =>
handlers[action.type] ? handlers[action.type](state, action) : state;

View File

@ -77,7 +77,7 @@ div.show.dropdown.nav-item > div {
.list-details { .list-details {
position: sticky; position: sticky;
top: $body-padding-top; top: $body-padding-top;
z-index: 1000; z-index: $zindex-sticky;
height: calc(100vh - #{$body-padding-top}); height: calc(100vh - #{$body-padding-top});
} }
@ -130,3 +130,25 @@ div.sweet-alert > h2 {
.wishlist-button:hover > i { .wishlist-button:hover > i {
color: $primary; color: $primary;
} }
.notifications {
position: fixed;
top: $body-padding-top;
z-index: $zindex-fixed;
max-height: calc(100vh - #{$body-padding-top});
right: 1rem;
width: 18rem;
}
.toast-header {
background-color: $card-cap-bg;
color: $gray-100;
> button > span {
color: $white;
}
}
.toast {
background-color: $card-bg;
}