Skip to content

Commit fd57215

Browse files
authored
Merge pull request #3153 from PrakashDurlabhji/issue_3051
issue #3051 fix
2 parents f8b3240 + c05a780 commit fd57215

File tree

6 files changed

+110
-56
lines changed

6 files changed

+110
-56
lines changed

src/components/NotificationsDropdown/NotificationsDropdown.jsx

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,48 @@ import EnhancedDropdown from './EnhancedDropdown'
99
import NotificationsBell from './NotificationsBell'
1010

1111

12-
const NotificationsDropdown = (props) => {
13-
return (
14-
<div className="notifications-dropdown">
15-
<EnhancedDropdown theme="UserDropdownMenu" pointerShadow noAutoclose onToggle={props.onToggle}>
16-
<div className="dropdown-menu-header">
17-
<NotificationsBell
18-
hasUnread={props.hasUnread}
19-
hasNew={props.hasNew}
20-
onClick={props.onToggle}
21-
/>
22-
</div>
23-
<div className="dropdown-menu-list">
24-
<div className="notifications-dropdown-content">
25-
{props.children}
12+
class NotificationsDropdown extends React.Component {
13+
14+
constructor(props) {
15+
super(props)
16+
this.state = {
17+
isOpen: false
18+
}
19+
20+
this.toggle = this.toggle.bind(this)
21+
}
22+
23+
toggle(isOpen) {
24+
if (typeof isOpen === 'object') {
25+
this.props.onToggle(!this.state.isOpen)
26+
this.setState({ isOpen: !this.state.isOpen})
27+
} else {
28+
this.props.onToggle(isOpen)
29+
this.setState({ isOpen })
30+
}
31+
}
32+
33+
render() {
34+
const { hasUnread, hasNew, children } = this.props
35+
return (
36+
<div className="notifications-dropdown">
37+
<EnhancedDropdown theme="UserDropdownMenu" pointerShadow noAutoclose onToggle={this.toggle}>
38+
<div className="dropdown-menu-header">
39+
<NotificationsBell
40+
hasUnread={hasUnread}
41+
hasNew={hasNew}
42+
onClick={this.toggle}
43+
/>
44+
</div>
45+
<div className="dropdown-menu-list">
46+
<div className="notifications-dropdown-content">
47+
{children}
48+
</div>
2649
</div>
27-
</div>
28-
</EnhancedDropdown>
29-
</div>
30-
)
50+
</EnhancedDropdown>
51+
</div>
52+
)
53+
}
3154
}
3255

3356
NotificationsDropdown.propTypes = {

src/components/NotificationsDropdown/NotificationsDropdownContainer.jsx

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { Link } from 'react-router-dom'
88
import { connect } from 'react-redux'
99
import _ from 'lodash'
1010
import { TransitionGroup, Transition } from 'react-transition-group'
11-
import { getNotifications, toggleNotificationSeen, markAllNotificationsRead, toggleNotificationRead, visitNotifications,
11+
import { getNotifications, toggleNotificationSeen, markAllNotificationsRead, markAllNotificationsSeen, toggleNotificationRead,
1212
toggleBundledNotificationRead, viewOlderNotifications, hideOlderNotifications } from '../../routes/notifications/actions'
1313
import {
1414
splitNotificationsBySources,
1515
filterReadNotifications,
16+
filterSeenNotifications,
1617
limitQuantityInSources,
1718
preRenderNotifications,
1819
} from '../../routes/notifications/helpers/notifications'
@@ -29,9 +30,9 @@ import { NOTIFICATIONS_DROPDOWN_PER_SOURCE, NOTIFICATIONS_NEW_PER_SOURCE, REFRES
2930
import './NotificationsDropdown.scss'
3031

3132
const NotificationsDropdownContainerView = (props) => {
32-
const {initialized, isLoading, lastVisited, sources, notifications, markAllNotificationsRead, toggleNotificationRead, toggleNotificationSeen,
33-
pending, toggleBundledNotificationRead, visitNotifications, oldSourceIds, viewOlderNotifications, isDropdownMobileOpen, isDropdownWebOpen,
34-
toggleNotificationsDropdownMobile, toggleNotificationsDropdownWeb } = props
33+
const {initialized, isLoading, sources, notifications, markAllNotificationsRead, toggleNotificationRead, toggleNotificationSeen,
34+
pending, toggleBundledNotificationRead, oldSourceIds, viewOlderNotifications, isDropdownMobileOpen, isDropdownWebOpen,
35+
toggleNotificationsDropdownMobile, toggleNotificationsDropdownWeb, markAllNotificationsSeen } = props
3536
if (!initialized && isLoading) {
3637
return (
3738
<NotificationsDropdown hasUnread={false}>
@@ -51,9 +52,11 @@ const NotificationsDropdownContainerView = (props) => {
5152
}
5253

5354
const notReadNotifications = filterReadNotifications(notifications)
55+
const notSeenNotifications = filterSeenNotifications(notifications)
5456
const allNotificationsBySources = splitNotificationsBySources(sources, notReadNotifications)
5557

5658
const hasUnread = notReadNotifications.length > 0
59+
const hasUnseen = notSeenNotifications.length > 0
5760
// we have to give Dropdown component some time
5861
// before removing notification item node from the list
5962
// otherwise dropdown thinks we clicked outside and closes dropdown
@@ -70,7 +73,7 @@ const NotificationsDropdownContainerView = (props) => {
7073
}, 0)
7174
}
7275
}
73-
const hasNew = hasUnread && lastVisited < _.maxBy(_.map(notifications, n => new Date(n.date)))
76+
7477
let notificationsEmpty = (
7578
<NotificationsEmpty>
7679
<p className="notifications-empty-note">
@@ -92,6 +95,12 @@ const NotificationsDropdownContainerView = (props) => {
9295
)
9396
}
9497

98+
const markNotificationsSeen = (isOpen) => {
99+
if (isOpen) {
100+
markAllNotificationsSeen(null, notifications)
101+
}
102+
}
103+
95104
// this function checks that notification is not seen yet,
96105
// before marking it as seen
97106
const markNotificationSeen = (notificationId) => {
@@ -118,10 +127,10 @@ const NotificationsDropdownContainerView = (props) => {
118127
return (
119128
<NotificationsDropdown
120129
hasUnread={hasUnread}
121-
hasNew={hasNew}
130+
hasNew={hasUnseen}
122131
onToggle={(isOpen) => {
123132
toggleNotificationsDropdownWeb(isOpen)
124-
visitNotifications()
133+
markNotificationsSeen(isOpen)
125134
}}
126135
>
127136
{isDropdownWebOpen && <div>
@@ -192,10 +201,9 @@ const NotificationsDropdownContainerView = (props) => {
192201
return (
193202
<NotificationsMobilePage
194203
hasUnread={hasUnread}
195-
hasNew={hasNew}
204+
hasNew={hasUnseen}
196205
onToggle={() => {
197206
toggleNotificationsDropdownMobile()
198-
visitNotifications()
199207
}}
200208
isOpen={isDropdownMobileOpen}
201209
>
@@ -253,21 +261,18 @@ class NotificationsDropdownContainer extends React.Component {
253261
constructor(props) {
254262
super(props)
255263
this.state = {
256-
lastVisited: new Date(0),
257264
isDropdownWebOpen: false,
258265
isDropdownMobileOpen: false,
259266
notificationsVisited: false,
260267
}
261268

262269
this.onToggleNotificationsDropdownWeb = this.onToggleNotificationsDropdownWeb.bind(this)
263270
this.onToggleNotificationsDropdownMobile = this.onToggleNotificationsDropdownMobile.bind(this)
264-
this.onVisitNotifications = this.onVisitNotifications.bind(this)
265271
}
266272

267273
componentDidMount() {
268274
this.props.getNotifications()
269275
this.autoRefreshNotifications = setInterval(() => this.props.getNotifications(), REFRESH_NOTIFICATIONS_INTERVAL)
270-
this.setState({ lastVisited: this.props.lastVisited })
271276
}
272277

273278
componentWillUnmount() {
@@ -276,7 +281,6 @@ class NotificationsDropdownContainer extends React.Component {
276281
this.onToggleNotificationsDropdownMobile(false)
277282
this.onToggleNotificationsDropdownWeb(false)
278283
this.props.hideOlderNotifications()
279-
this.state.notificationsVisited && this.props.visitNotifications()
280284
}
281285

282286
componentWillReceiveProps(nextProps) {
@@ -300,25 +304,15 @@ class NotificationsDropdownContainer extends React.Component {
300304
this.setState({ isDropdownMobileOpen: !_.isUndefined(isOpen) ? isOpen : !this.state.isDropdownMobileOpen})
301305
}
302306

303-
onVisitNotifications() {
304-
this.setState({
305-
lastVisited: _.maxBy(_.map(this.props.notifications, n => new Date(n.date))),
306-
notificationsVisited: true
307-
})
308-
}
309-
310307
render() {
311308
const { notifications, ...restProps } = this.props
312309
const preRenderedNotifications = preRenderNotifications(notifications)
313-
314310
return (
315311
<NotificationsDropdownContainerView
316312
{...restProps}
317313
notifications={preRenderedNotifications}
318314
toggleNotificationsDropdownWeb={this.onToggleNotificationsDropdownWeb}
319315
toggleNotificationsDropdownMobile={this.onToggleNotificationsDropdownMobile}
320-
visitNotifications={this.onVisitNotifications}
321-
lastVisited={this.state.lastVisited}
322316
isDropdownMobileOpen={this.state.isDropdownMobileOpen}
323317
isDropdownWebOpen={this.state.isDropdownWebOpen}
324318
/>
@@ -331,9 +325,9 @@ const mapStateToProps = ({ notifications }) => notifications
331325

332326
const mapDispatchToProps = {
333327
getNotifications,
334-
visitNotifications,
335328
toggleNotificationSeen,
336329
markAllNotificationsRead,
330+
markAllNotificationsSeen,
337331
toggleNotificationRead,
338332
toggleBundledNotificationRead,
339333
viewOlderNotifications,

src/config/constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ export const LOAD_ORG_CONFIG_FAILURE = 'LOAD_ORG_CONFIG_FAILURE'
1515
export const GET_NOTIFICATIONS_PENDING = 'GET_NOTIFICATIONS_PENDING'
1616
export const GET_NOTIFICATIONS_SUCCESS = 'GET_NOTIFICATIONS_SUCCESS'
1717
export const GET_NOTIFICATIONS_FAILURE = 'GET_NOTIFICATIONS_FAILURE'
18-
export const VISIT_NOTIFICATIONS = 'VISIT_NOTIFICATIONS'
1918
export const SET_NOTIFICATIONS_FILTER_BY = 'SET_NOTIFICATIONS_FILTER_BY'
2019
export const MARK_ALL_NOTIFICATIONS_READ = 'MARK_ALL_NOTIFICATIONS_READ'
2120
export const TOGGLE_NOTIFICATION_READ = 'TOGGLE_NOTIFICATION_READ'
2221
export const TOGGLE_NOTIFICATION_SEEN = 'TOGGLE_NOTIFICATION_SEEN'
22+
export const MARK_ALL_NOTIFICATIONS_SEEN = 'MARK_ALL_NOTIFICATIONS_SEEN'
2323
export const VIEW_OLDER_NOTIFICATIONS_SUCCESS = 'VIEW_OLDER_NOTIFICATIONS_SUCCESS'
2424
export const HIDE_OLDER_NOTIFICATIONS_SUCCESS = 'HIDE_OLDER_NOTIFICATIONS_SUCCESS'
2525
export const NOTIFICATIONS_PENDING = 'NOTIFICATIONS_PENDING'

src/routes/notifications/actions/index.js

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {
55
GET_NOTIFICATIONS_PENDING,
66
GET_NOTIFICATIONS_SUCCESS,
77
GET_NOTIFICATIONS_FAILURE,
8-
VISIT_NOTIFICATIONS,
98
TOGGLE_NOTIFICATION_SEEN,
109
SET_NOTIFICATIONS_FILTER_BY,
1110
MARK_ALL_NOTIFICATIONS_READ,
11+
MARK_ALL_NOTIFICATIONS_SEEN,
1212
MARK_NOTIFICATIONS_READ,
1313
TOGGLE_NOTIFICATION_READ,
1414
VIEW_OLDER_NOTIFICATIONS_SUCCESS,
@@ -31,10 +31,18 @@ const handleDispatchNotificationReadByType = (type, dispatch, payload, isRead) =
3131
})
3232
}
3333

34+
const handleDispatchNotificationSeenByType = (type, dispatch, payload, isSeen) => {
35+
dispatch({
36+
type,
37+
payload,
38+
isSeen
39+
})
40+
}
41+
3442
const handleDispatchNotificationRead = handleDispatchNotificationReadByType.bind(this, TOGGLE_NOTIFICATION_READ)
3543
const handleDispatchMarkAllNotificationsRead = handleDispatchNotificationReadByType.bind(this, MARK_ALL_NOTIFICATIONS_READ)
3644
const handleDispatchMarkNotificationsRead = handleDispatchNotificationReadByType.bind(this, MARK_NOTIFICATIONS_READ)
37-
45+
const handleDispatchMarkAllNotificationsSeen = handleDispatchNotificationSeenByType.bind(this, MARK_ALL_NOTIFICATIONS_SEEN)
3846
export const getNotifications = () => (dispatch) => {
3947
dispatch({ type: GET_NOTIFICATIONS_PENDING })
4048
notificationsService.getNotifications().then(notifications => {
@@ -51,17 +59,31 @@ export const getNotifications = () => (dispatch) => {
5159
})
5260
}
5361

54-
export const visitNotifications = () => (dispatch) => {
55-
dispatch({
56-
type: VISIT_NOTIFICATIONS
57-
})
58-
}
59-
6062
export const setNotificationsFilterBy = (filterBy) => (dispatch) => dispatch({
6163
type: SET_NOTIFICATIONS_FILTER_BY,
6264
payload: filterBy
6365
})
6466

67+
export const markAllNotificationsSeen = (sourceId, notifications = []) => (dispatch) => {
68+
let ids = null
69+
const sourceNfs = _.filter(notifications, n => !n.seen)
70+
if (sourceNfs.length === 0) {
71+
return
72+
}
73+
ids = _.map(sourceNfs, n => n.id).join('-')
74+
75+
dispatch({
76+
type: NOTIFICATIONS_PENDING
77+
})
78+
79+
handleDispatchMarkAllNotificationsSeen(dispatch, sourceId, true)
80+
notificationsService.markNotificationsSeen(ids).catch(err => {
81+
Alert.error(`Failed to mark notification seen. ${err.message}`)
82+
handleDispatchMarkAllNotificationsSeen(dispatch, sourceId, false)
83+
})
84+
85+
}
86+
6587
export const markAllNotificationsRead = (sourceId, notifications = []) => (dispatch) => {
6688
let ids = null
6789
if (sourceId) {

src/routes/notifications/helpers/notifications.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ export const filterNotificationsByPosts = (notifications, posts) => {
213213
*/
214214
export const filterReadNotifications = (notifications) => _.filter(notifications, { isRead: false })
215215

216+
/**
217+
*
218+
* @param {Array} notifications list of notifications
219+
*
220+
* @return {Array} list of filtered notifications
221+
*/
222+
export const filterSeenNotifications = (notifications) => _.filter(notifications, { seen: false })
223+
216224
/**
217225
* Filter notifications that belongs to project:projectId
218226
*

src/routes/notifications/reducers/index.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {
55
GET_NOTIFICATIONS_PENDING,
66
GET_NOTIFICATIONS_SUCCESS,
77
GET_NOTIFICATIONS_FAILURE,
8-
VISIT_NOTIFICATIONS,
98
TOGGLE_NOTIFICATION_SEEN,
109
SET_NOTIFICATIONS_FILTER_BY,
1110
MARK_ALL_NOTIFICATIONS_READ,
11+
MARK_ALL_NOTIFICATIONS_SEEN,
1212
TOGGLE_NOTIFICATION_READ,
1313
VIEW_OLDER_NOTIFICATIONS_SUCCESS,
1414
HIDE_OLDER_NOTIFICATIONS_SUCCESS,
@@ -26,7 +26,6 @@ const initialState = {
2626
notifications: [],
2727
// ids of sources that will also show old notifications
2828
oldSourceIds: [],
29-
lastVisited: new Date(0),
3029
pending: false,
3130
readers: {},
3231
}
@@ -73,9 +72,6 @@ export default (state = initialState, action) => {
7372
case GET_NOTIFICATIONS_FAILURE:
7473
return { ...state, isLoading: false }
7574

76-
case VISIT_NOTIFICATIONS:
77-
return {...state, lastVisited: _.maxBy(_.map(state.notifications, n => new Date(n.date)))}
78-
7975
case TOGGLE_NOTIFICATION_SEEN: {
8076
const ids = action.payload.split('-')
8177
const newState = {
@@ -109,6 +105,17 @@ export default (state = initialState, action) => {
109105
return newState
110106
}
111107

108+
case MARK_ALL_NOTIFICATIONS_SEEN: {
109+
const newState = {
110+
...state,
111+
pending: false,
112+
...getNotificationsAndFilterBy(state.notifications.map(n => (
113+
!action.payload || n.sourcId === action.payload ? { ...n, seen: action.isSeen } : n
114+
)), state.filterBy)
115+
}
116+
return newState
117+
}
118+
112119
case TOGGLE_NOTIFICATION_READ: {
113120
const newState = {
114121
...state,

0 commit comments

Comments
 (0)