change(ui) - notifications count and list

This commit is contained in:
Shekar Siri 2022-06-08 12:56:37 +02:00
parent 8273fc08bc
commit d8911e93c1
6 changed files with 137 additions and 173 deletions

View file

@ -1,182 +1,46 @@
import React, { useEffect } from 'react';
import stl from './notifications.module.css';
import ListItem from './ListItem';
import { connect } from 'react-redux';
import { Button, SlideModal, Icon, Popup, NoContent } from 'UI';
import { Icon, Popup } from 'UI';
import { fetchList, setViewed, clearAll } from 'Duck/notifications';
import { setLastRead } from 'Duck/announcements';
import cn from 'classnames';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import { useModal } from 'App/components/Modal';
import AlertTriggersModal from 'Shared/AlertTriggersModal';
import { useStore } from 'App/mstore';
import { useObserver } from 'mobx-react-lite';
const AUTOREFRESH_INTERVAL = 5 * 60 * 1000;
// @withToggle('visible', 'toggleVisisble')
// @withRouter
// class Notifications extends React.Component {
// state = { alertType: '' };
// constructor(props) {
// super(props);
// // setTimeout(() => {
// // props.fetchList();
// // }, 1000);
// setInterval(() => {
// props.fetchList();
// }, AUTOREFRESH_INTERVAL);
// }
// writeOption = (e, { name, value }) => this.setState({ [ name ]: value });
// navigateToUrl = notification => { // TODO should be able to open the alert edit form
// if (notification.options.source === 'ALERT') {
// const { initAlert } = this.props;
// this.props.fetchAlerts().then(function() {
// const { alerts } = this.props;
// const alert = alerts.find(i => i.alertId === notification.options.sourceId)
// initAlert(alert.toJS());
// }.bind(this));
// }
// }
// onClearAll = () => {
// const { notifications } = this.props;
// const firstItem = notifications.first();
// this.props.clearAll({ endTimestamp: firstItem.createdAt.ts });
// }
// onClear = notification => {
// this.props.setViewed(notification.notificationId)
// }
// toggleModal = () => {
// this.props.toggleVisisble(!this.props.visible);
// }
// render() {
// const { notifications, visible, loading, clearing, clearingAll } = this.props;
// const { alertType } = this.state;
// const unReadNotificationsCount = notifications.filter(({viewed}) => !viewed).size
// const filteredList = alertType === '' ?
// notifications :
// notifications.filter(i => i.filterKey === alertType);
// return (
// <div>
// <Popup
// content={ `Alerts` }
// >
// <div className={ stl.button } onClick={ this.toggleModal } data-active={ visible }>
// <div className={ stl.counter } data-hidden={ unReadNotificationsCount === 0 }>
// { unReadNotificationsCount }
// </div>
// <Icon name="bell" size="18" />
// </div>
// </Popup>
// <SlideModal
// title={
// <div className="flex items-center justify-between">
// <div>Alerts</div>
// { unReadNotificationsCount > 0 && (
// <div className="">
// <Button
// loading={clearingAll}
// variant="text"
// onClick={this.props.setLastRead}
// disabled={unReadNotificationsCount === 0}
// >
// <span
// className={ cn("text-sm color-gray-medium", { 'invisible' : clearingAll })}
// onClick={this.onClearAll}>
// IGNORE ALL
// </span>
// </Button>
// </div>
// )}
// </div>
// }
// right
// isDisplayed={ visible }
// onClose={ visible && this.toggleModal }
// bgColor="white"
// size="small"
// content={
// <div className="">
// <NoContent
// title={
// <div className="flex items-center justify-between">
// <AnimatedSVG name={ICONS.EMPTY_STATE} size="100" />
// </div>
// }
// subtext="There are no alerts to show."
// show={ !loading && notifications.size === 0 }
// size="small"
// >
// {
// filteredList.map(item => (
// <div className="border-b" key={item.key}>
// <ListItem
// key={item.key}
// alert={item}
// onClear={() => this.onClear(item)}
// loading={clearing}
// />
// </div>
// ))
// }
// </NoContent>
// </div>
// }
// />
// </div>
// );
// }
// }
// export default connect(state => ({
// notifications: state.getIn(['notifications', 'list']),
// loading: state.getIn(['notifications', 'fetchRequest', 'loading']),
// clearing: state.getIn(['notifications', 'setViewed', 'loading']),
// clearingAll: state.getIn(['notifications', 'clearAll', 'loading']),
// alerts: state.getIn(['alerts', 'list']),
// }), { fetchList, setLastRead, setViewed, clearAll, fetchAlerts, initAlert })(Notifications);
interface Props {
notifications: any;
fetchList: any;
}
function Notifications(props: Props) {
const { notifications } = props;
// const { notifications } = props;
const { showModal } = useModal();
const unReadNotificationsCount = notifications.filter(({viewed}: any) => !viewed).size
// const unReadNotificationsCount = notifications.filter(({viewed}: any) => !viewed).size
const { notificationStore } = useStore();
const count = useObserver(() => notificationStore.notificationsCount);
useEffect(() => {
if (notifications.size === 0) {
props.fetchList();
}
notificationStore.fetchNotificationsCount();
const interval = setInterval(() => {
props.fetchList();
notificationStore.fetchNotificationsCount()
}, AUTOREFRESH_INTERVAL);
return () => clearInterval(interval);
}, []);
return (
<Popup
content={ `Alerts` }
>
<div className={ stl.button } onClick={ () => showModal(<AlertTriggersModal unReadNotificationsCount={unReadNotificationsCount} />, { right: true }) }>
<div className={ stl.counter } data-hidden={ unReadNotificationsCount === 0 }>
{ unReadNotificationsCount }
return useObserver(() => (
<Popup content={ `Alerts` } >
<div className={ stl.button } onClick={ () => showModal(<AlertTriggersModal />, { right: true }) }>
<div className={ stl.counter } data-hidden={ count === 0 }>
{ count }
</div>
<Icon name="bell" size="18" />
</div>
</Popup>
);
</Popup>
));
}
export default connect((state: any) => ({

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { Button, NoContent } from 'UI';
import { connect } from 'react-redux';
import { fetchList, setViewed, clearAll } from 'Duck/notifications';
@ -6,9 +6,10 @@ import { setLastRead } from 'Duck/announcements';
import cn from 'classnames';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import ListItem from './ListItem'
import { useStore } from 'App/mstore';
import { useObserver } from 'mobx-react-lite';
interface Props {
unReadNotificationsCount: number;
setLastRead: Function;
clearingAll: boolean;
@ -20,7 +21,10 @@ interface Props {
setViewed: Function;
}
function AlertTriggersModal(props: Props) {
const { list, unReadNotificationsCount, loading, clearingAll, clearing } = props;
const { loading, clearingAll, clearing } = props;
const { notificationStore } = useStore();
const count = useObserver(() => notificationStore.notificationsCount);
const list = useObserver(() => notificationStore.notifications);
const onClearAll = () => {
const { list } = props;
@ -29,21 +33,24 @@ function AlertTriggersModal(props: Props) {
}
const onClear = (notification: any) => {
console.log('onClear', notification);
props.setViewed(notification.notificationId)
}
useEffect(() => {
notificationStore.fetchNotifications();
}, [])
return (
<div className="bg-white box-shadow h-screen overflow-y-auto" style={{ width: '450px'}}>
<div className="flex items-center justify-between p-5 text-2xl">
<div>Alerts</div>
{ unReadNotificationsCount > 0 && (
{ count > 0 && (
<div className="">
<Button
loading={clearingAll}
variant="text"
onClick={props.setLastRead}
disabled={unReadNotificationsCount === 0}
disabled={count === 0}
>
<span
className={ cn("text-sm color-gray-medium", { 'invisible' : clearingAll })}
@ -63,7 +70,7 @@ function AlertTriggersModal(props: Props) {
</div>
}
subtext="There are no alerts to show."
show={ !loading && unReadNotificationsCount === 0 }
show={ !loading && count === 0 }
size="small"
>
{list.map((item: any, i: any) => (

View file

@ -8,6 +8,7 @@ import { makeAutoObservable, observable, action } from "mobx"
import { dashboardService, metricService, sessionService, userService, auditService } from 'App/services';
import SettingsStore from './settingsStore';
import AuditStore from './auditStore';
import NotificationStore from './notificationStore';
export class RootStore {
dashboardStore: IDashboardSotre;
@ -16,6 +17,7 @@ export class RootStore {
userStore: UserStore;
roleStore: RoleStore;
auditStore: AuditStore;
notificationStore: NotificationStore
limits: any;
@ -26,6 +28,7 @@ export class RootStore {
this.userStore = new UserStore();
this.roleStore = new RoleStore();
this.auditStore = new AuditStore();
this.notificationStore = new NotificationStore();
makeAutoObservable(this, {
limits: observable,
fetchLimits: action,

View file

@ -0,0 +1,42 @@
import { action, makeAutoObservable, observable } from "mobx";
import { userService } from 'App/services';
import Notification from './types/notification';
export default class NotificationStore {
notificationsCount: any = 0;
notifications: any = [];
constructor() {
makeAutoObservable(this, {
notificationsCount: observable,
notifications: observable,
fetchNotificationsCount: action,
fetchNotifications: action,
});
}
fetchNotifications(): Promise<any> {
return new Promise((resolve, reject) => {
userService.getNotifications()
.then((response: any) => {
this.notifications = response.map((notification: any) => new Notification().fromJson(notification));
resolve(response);
}).catch((error: any) => {
reject(error);
});
});
}
fetchNotificationsCount(): Promise<any> {
return new Promise((resolve, reject) => {
userService.getNotificationsCount()
.then((response: any) => {
this.notificationsCount = response.count;
resolve(response);
}).catch((error: any) => {
reject(error);
});
});
}
}

View file

@ -0,0 +1,36 @@
import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx"
import { DateTime } from 'luxon';
export default class Notification {
notificationId: string
level: string
editedAt: string
createdAt: string
text: string
link: string
viewed: boolean
title: string
description: string
type: string
filterKey: string
options: any = { source: '', sourceId: '', projectId: '', sourceMeta: ''}
constructor() {
}
fromJson(json: any) {
this.notificationId = json.notificationId
this.level = json.level
this.editedAt = json.editedAt && DateTime.fromMillis(json.editedAt || 0);
this.createdAt = json.createdAt && DateTime.fromMillis(json.createdAt || 0);
this.text = json.text
this.link = json.link
this.viewed = json.viewed
this.title = json.title
this.description = json.description
this.type = json.type
this.filterKey = json.filterKey
this.options = json.options
return this
}
}

View file

@ -14,45 +14,45 @@ export default class UserService {
all() {
return this.client.get('/client/members')
.then(response => response.json())
.then(response => response.data || []);
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || []);
}
one(userId: string) {
return this.client.get('/users/' + userId)
.then(response => response.json())
.then(response => response.data || {});
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {});
}
save(user: IUser): Promise<any> {
const data = user.toSave();
if (user.userId) {
return this.client.put('/client/members/' + user.userId, data)
.then(response => response.json())
.then(response => response.data || {})
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {})
} else {
return this.client.post('/client/members', data)
.then(response => response.json())
.then(response => response.data || {});
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {});
}
}
generateInviteCode(userId: any): Promise<any> {
return this.client.get(`/client/members/${userId}/reset`)
.then(response => response.json())
.then(response => response.data || {});
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {});
}
delete(userId: string) {
return this.client.delete('/client/members/' + userId)
.then(response => response.json())
.then(response => response.data || {});
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {});
}
getRoles() {
return this.client.get('/client/roles')
.then(response => response.json())
.then(response => response.data || []);
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || []);
}
getLimits() {
@ -60,4 +60,16 @@ export default class UserService {
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {});
}
getNotificationsCount() {
return this.client.get('/notifications/count')
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {});
}
getNotifications() {
return this.client.get('/notifications')
.then((response: { json: () => any; }) => response.json())
.then((response: { data: any; }) => response.data || {});
}
}