From 145ed3797810cc652976a6198d6b23618e570bfd Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Tue, 15 Sep 2020 19:27:12 +0200 Subject: Refactor and change logic to only update marker if we reached the last read notification. --- .../flavours/glitch/reducers/notifications.js | 34 +++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'app/javascript/flavours/glitch/reducers') diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js index 31d9611a3..33f014dd1 100644 --- a/app/javascript/flavours/glitch/reducers/notifications.js +++ b/app/javascript/flavours/glitch/reducers/notifications.js @@ -55,16 +55,16 @@ const notificationToMap = (state, notification) => ImmutableMap({ }); const normalizeNotification = (state, notification, usePendingItems) => { - const top = !shouldCountUnreadNotifications(state); + const top = state.get('top'); if (usePendingItems || !state.get('pendingItems').isEmpty()) { return state.update('pendingItems', list => list.unshift(notificationToMap(state, notification))).update('unread', unread => unread + 1); } - if (top) { - state = state.set('lastReadId', notification.id); - } else { + if (shouldCountUnreadNotifications(state)) { state = state.update('unread', unread => unread + 1); + } else { + state = state.set('lastReadId', notification.id); } return state.update('items', list => { @@ -77,7 +77,6 @@ const normalizeNotification = (state, notification, usePendingItems) => { }; const expandNormalizedNotifications = (state, notifications, next, isLoadingRecent, usePendingItems) => { - const top = !(shouldCountUnreadNotifications(state)); const lastReadId = state.get('lastReadId'); let items = ImmutableList(); @@ -102,18 +101,19 @@ const expandNormalizedNotifications = (state, notifications, next, isLoadingRece }); } - if (top) { - if (!items.isEmpty()) { - mutable.update('lastReadId', id => compareId(id, items.first().get('id')) > 0 ? id : items.first().get('id')); - } - } else { - mutable.update('unread', unread => unread + items.count(item => compareId(item.get('id'), lastReadId) > 0)); - } - if (!next) { mutable.set('hasMore', false); } + if (shouldCountUnreadNotifications(state)) { + mutable.update('unread', unread => unread + items.count(item => compareId(item.get('id'), lastReadId) > 0)); + } else { + const mostRecentId = items.first().get('id'); + if (compareId(lastReadId, mostRecentId) < 0) { + mutable.set('lastReadId', mostRecentId); + } + } + mutable.set('isLoading', false); }); }; @@ -197,7 +197,13 @@ const updateVisibility = (state, visibility) => { }; const shouldCountUnreadNotifications = (state) => { - return !(state.get('isTabVisible') && state.get('top') && state.get('mounted') > 0); + const isTabVisible = state.get('isTabVisible'); + const isOnTop = state.get('top'); + const isMounted = state.get('mounted') > 0; + const lastReadId = state.get('lastReadId'); + const lastItemReached = !state.get('hasMore') || lastReadId === '0' || (!state.get('items').isEmpty() && compareId(state.get('items').last().get('id'), lastReadId) <= 0); + + return !(isTabVisible && isOnTop && isMounted && lastItemReached); }; const recountUnread = (state, last_read_id) => { -- cgit From 94c290d7d2179adeb0437c00b571f62c41c7fd12 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Tue, 15 Sep 2020 20:54:26 +0200 Subject: Only update read marker when giving focus/mounting column --- app/javascript/flavours/glitch/features/notifications/index.js | 4 ++-- app/javascript/flavours/glitch/reducers/notifications.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'app/javascript/flavours/glitch/reducers') diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.js index cef0a0d2a..1c8c68218 100644 --- a/app/javascript/flavours/glitch/features/notifications/index.js +++ b/app/javascript/flavours/glitch/features/notifications/index.js @@ -57,7 +57,7 @@ const mapStateToProps = state => ({ hasMore: state.getIn(['notifications', 'hasMore']), numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size, notifCleaningActive: state.getIn(['notifications', 'cleaningMode']), - lastReadId: state.getIn(['notifications', 'lastReadId']), + lastReadId: state.getIn(['notifications', 'readMarkerId']), }); /* glitch */ @@ -227,7 +227,7 @@ class Notifications extends React.PureComponent { accountId={item.get('account')} onMoveUp={this.handleMoveUp} onMoveDown={this.handleMoveDown} - unread={lastReadId && compareId(item.get('id'), lastReadId) > 0} + unread={lastReadId !== '0' && compareId(item.get('id'), lastReadId) > 0} /> )); } else { diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js index 33f014dd1..7f6784eae 100644 --- a/app/javascript/flavours/glitch/reducers/notifications.js +++ b/app/javascript/flavours/glitch/reducers/notifications.js @@ -39,6 +39,7 @@ const initialState = ImmutableMap({ mounted: 0, unread: 0, lastReadId: '0', + readMarkerId: '0', isLoading: false, cleaningMode: false, isTabVisible: true, @@ -183,6 +184,7 @@ const deleteMarkedNotifs = (state) => { const updateMounted = (state) => { state = state.update('mounted', count => count + 1); if (!shouldCountUnreadNotifications(state)) { + state = state.set('readMarkerId', state.get('lastReadId')); state = clearUnread(state); } return state; @@ -191,6 +193,7 @@ const updateMounted = (state) => { const updateVisibility = (state, visibility) => { state = state.set('isTabVisible', visibility); if (!shouldCountUnreadNotifications(state)) { + state = state.set('readMarkerId', state.get('lastReadId')); state = clearUnread(state); } return state; @@ -212,6 +215,10 @@ const recountUnread = (state, last_read_id) => { mutable.set('lastReadId', last_read_id); } + if (compareId(last_read_id, mutable.get('readMarkerId')) > 0) { + mutable.set('readMarkerId', last_read_id); + } + if (state.get('unread') > 0 || shouldCountUnreadNotifications(state)) { mutable.set('unread', mutable.get('pendingItems').count(item => item !== null) + mutable.get('items').count(item => item && compareId(item.get('id'), last_read_id) > 0)); } -- cgit From f1c0cf98061eee5eb6130b167d033c851ee58f14 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Tue, 15 Sep 2020 23:42:58 +0200 Subject: Add button to manually mark all notifications as read --- .../flavours/glitch/actions/notifications.js | 8 ++++++ .../glitch/features/notifications/index.js | 32 ++++++++++++++++++++-- .../flavours/glitch/reducers/notifications.js | 4 +++ 3 files changed, 41 insertions(+), 3 deletions(-) (limited to 'app/javascript/flavours/glitch/reducers') diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js index ceb1e6df6..b44469cf4 100644 --- a/app/javascript/flavours/glitch/actions/notifications.js +++ b/app/javascript/flavours/glitch/actions/notifications.js @@ -45,6 +45,8 @@ export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT'; export const NOTIFICATIONS_SET_VISIBILITY = 'NOTIFICATIONS_SET_VISIBILITY'; +export const NOTIFICATIONS_MARK_AS_READ = 'NOTIFICATIONS_MARK_AS_READ'; + defineMessages({ mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, }); @@ -318,3 +320,9 @@ export function setFilter (filterType) { dispatch(saveSettings()); }; }; + +export function markNotificationsAsRead() { + return { + type: NOTIFICATIONS_MARK_AS_READ, + }; +}; diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.js index 1c8c68218..d1a0640cb 100644 --- a/app/javascript/flavours/glitch/features/notifications/index.js +++ b/app/javascript/flavours/glitch/features/notifications/index.js @@ -12,6 +12,7 @@ import { mountNotifications, unmountNotifications, loadPending, + markNotificationsAsRead, } from 'flavours/glitch/actions/notifications'; import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns'; import NotificationContainer from './containers/notification_container'; @@ -31,6 +32,7 @@ import NotificationPurgeButtonsContainer from 'flavours/glitch/containers/notifi const messages = defineMessages({ title: { id: 'column.notifications', defaultMessage: 'Notifications' }, enterNotifCleaning : { id: 'notification_purge.start', defaultMessage: 'Enter notification cleaning mode' }, + markAsRead : { id: 'notifications.mark_as_read', defaultMessage: 'Mark every notification as read' }, }); const getNotifications = createSelector([ @@ -58,6 +60,7 @@ const mapStateToProps = state => ({ numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size, notifCleaningActive: state.getIn(['notifications', 'cleaningMode']), lastReadId: state.getIn(['notifications', 'readMarkerId']), + canMarkAsRead: !state.getIn(['notifications', 'items']).isEmpty() && state.getIn(['notifications', 'readMarkerId']) !== '0' && compareId(state.getIn(['notifications', 'items']).first().get('id'), state.getIn(['notifications', 'readMarkerId'])) > 0, }); /* glitch */ @@ -65,6 +68,9 @@ const mapDispatchToProps = dispatch => ({ onEnterCleaningMode(yes) { dispatch(enterNotificationClearingMode(yes)); }, + onMarkAsRead() { + dispatch(markNotificationsAsRead()); + }, onMount() { dispatch(mountNotifications()); }, @@ -96,6 +102,7 @@ class Notifications extends React.PureComponent { onMount: PropTypes.func, onUnmount: PropTypes.func, lastReadId: PropTypes.string, + canMarkAsRead: PropTypes.bool, }; static defaultProps = { @@ -197,8 +204,12 @@ class Notifications extends React.PureComponent { this.props.onEnterCleaningMode(!this.props.notifCleaningActive); } + handleMarkAsRead = () => { + this.props.onMarkAsRead(); + } + render () { - const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId } = this.props; + const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead } = this.props; const { notifCleaning, notifCleaningActive } = this.props; const { animatingNCD } = this.state; const pinned = !!columnId; @@ -256,6 +267,21 @@ class Notifications extends React.PureComponent { ); + const extraButtons = []; + + if (canMarkAsRead) { + extraButtons.push( + + ); + } + const notifCleaningButtonClassName = classNames('column-header__button', { 'active': notifCleaningActive, }); @@ -267,7 +293,7 @@ class Notifications extends React.PureComponent { const msgEnterNotifCleaning = intl.formatMessage(messages.enterNotifCleaning); - const notifCleaningButton = ( + extraButtons.push(