Add a notification module
This commit is contained in:
parent
a53deebbcf
commit
481715d8a2
9
frontend/js/actions/notifications.js
Normal file
9
frontend/js/actions/notifications.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export const sendNotification = (data) => ({
|
||||||
|
type: "ADD_NOTIFICATION",
|
||||||
|
payload: data,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const removeNotification = (id) => ({
|
||||||
|
type: "REMOVE_NOTIFICATION",
|
||||||
|
payload: { id },
|
||||||
|
})
|
@ -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} />
|
||||||
|
70
frontend/js/components/notifications/notification.js
Normal file
70
frontend/js/components/notifications/notification.js
Normal 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);
|
34
frontend/js/components/notifications/notifications.js
Normal file
34
frontend/js/components/notifications/notifications.js
Normal 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);
|
@ -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,
|
||||||
});
|
});
|
||||||
|
15
frontend/js/reducers/notifications.js
Normal file
15
frontend/js/reducers/notifications.js
Normal 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;
|
@ -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;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user