Count unread notifications when window loses focus

signup-info-prompt
Thibaut Girka 2018-09-06 17:47:33 +02:00 committed by ThibG
parent c8875b4d8a
commit d315f1dc02
3 changed files with 65 additions and 6 deletions

View File

@ -28,6 +28,8 @@ export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
export const NOTIFICATIONS_MOUNT = 'NOTIFICATIONS_MOUNT'; export const NOTIFICATIONS_MOUNT = 'NOTIFICATIONS_MOUNT';
export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT'; export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT';
export const NOTIFICATIONS_SET_VISIBILITY = 'NOTIFICATIONS_SET_VISIBILITY';
defineMessages({ defineMessages({
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
}); });
@ -231,3 +233,10 @@ export function unmountNotifications() {
type: NOTIFICATIONS_UNMOUNT, type: NOTIFICATIONS_UNMOUNT,
}; };
}; };
export function notificationsSetVisibility(visibility) {
return {
type: NOTIFICATIONS_SET_VISIBILITY,
visibility: visibility,
};
};

View File

@ -10,7 +10,7 @@ import { isMobile } from 'flavours/glitch/util/is_mobile';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { uploadCompose, resetCompose } from 'flavours/glitch/actions/compose'; import { uploadCompose, resetCompose } from 'flavours/glitch/actions/compose';
import { expandHomeTimeline } from 'flavours/glitch/actions/timelines'; import { expandHomeTimeline } from 'flavours/glitch/actions/timelines';
import { expandNotifications } from 'flavours/glitch/actions/notifications'; import { expandNotifications, notificationsSetVisibility } from 'flavours/glitch/actions/notifications';
import { fetchFilters } from 'flavours/glitch/actions/filters'; import { fetchFilters } from 'flavours/glitch/actions/filters';
import { clearHeight } from 'flavours/glitch/actions/height_cache'; import { clearHeight } from 'flavours/glitch/actions/height_cache';
import { WrappedSwitch, WrappedRoute } from 'flavours/glitch/util/react_router_helpers'; import { WrappedSwitch, WrappedRoute } from 'flavours/glitch/util/react_router_helpers';
@ -206,7 +206,27 @@ export default class UI extends React.Component {
} }
} }
handleVisibilityChange = () => {
const visibility = !document[this.visibilityHiddenProp];
this.props.dispatch(notificationsSetVisibility(visibility));
}
componentWillMount () { componentWillMount () {
if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support
this.visibilityHiddenProp = 'hidden';
this.visibilityChange = 'visibilitychange';
} else if (typeof document.msHidden !== 'undefined') {
this.visibilityHiddenProp = 'msHidden';
this.visibilityChange = 'msvisibilitychange';
} else if (typeof document.webkitHidden !== 'undefined') {
this.visibilityHiddenProp = 'webkitHidden';
this.visibilityChange = 'webkitvisibilitychange';
}
if (this.visibilityChange !== undefined) {
document.addEventListener(this.visibilityChange, this.handleVisibilityChange, false);
this.handleVisibilityChange();
}
window.addEventListener('beforeunload', this.handleBeforeUnload, false); window.addEventListener('beforeunload', this.handleBeforeUnload, false);
window.addEventListener('resize', this.handleResize, { passive: true }); window.addEventListener('resize', this.handleResize, { passive: true });
document.addEventListener('dragenter', this.handleDragEnter, false); document.addEventListener('dragenter', this.handleDragEnter, false);
@ -250,6 +270,10 @@ export default class UI extends React.Component {
} }
componentWillUnmount () { componentWillUnmount () {
if (this.visibilityChange !== undefined) {
document.removeEventListener(this.visibilityChange, this.handleVisibilityChange);
}
window.removeEventListener('beforeunload', this.handleBeforeUnload); window.removeEventListener('beforeunload', this.handleBeforeUnload);
window.removeEventListener('resize', this.handleResize); window.removeEventListener('resize', this.handleResize);
document.removeEventListener('dragenter', this.handleDragEnter); document.removeEventListener('dragenter', this.handleDragEnter);

View File

@ -1,6 +1,7 @@
import { import {
NOTIFICATIONS_MOUNT, NOTIFICATIONS_MOUNT,
NOTIFICATIONS_UNMOUNT, NOTIFICATIONS_UNMOUNT,
NOTIFICATIONS_SET_VISIBILITY,
NOTIFICATIONS_UPDATE, NOTIFICATIONS_UPDATE,
NOTIFICATIONS_EXPAND_SUCCESS, NOTIFICATIONS_EXPAND_SUCCESS,
NOTIFICATIONS_EXPAND_REQUEST, NOTIFICATIONS_EXPAND_REQUEST,
@ -31,6 +32,7 @@ const initialState = ImmutableMap({
lastReadId: '0', lastReadId: '0',
isLoading: false, isLoading: false,
cleaningMode: false, cleaningMode: false,
isTabVisible: true,
// notification removal mark of new notifs loaded whilst cleaningMode is true. // notification removal mark of new notifs loaded whilst cleaningMode is true.
markNewForDelete: false, markNewForDelete: false,
}); });
@ -44,7 +46,7 @@ const notificationToMap = (state, notification) => ImmutableMap({
}); });
const normalizeNotification = (state, notification) => { const normalizeNotification = (state, notification) => {
const top = state.get('top') && state.get('mounted') > 0; const top = !shouldCountUnreadNotifications(state);
if (top) { if (top) {
state = state.set('lastReadId', notification.id); state = state.set('lastReadId', notification.id);
@ -62,7 +64,7 @@ const normalizeNotification = (state, notification) => {
}; };
const expandNormalizedNotifications = (state, notifications, next) => { const expandNormalizedNotifications = (state, notifications, next) => {
const top = state.get('top') && state.get('mounted') > 0; const top = !(shouldCountUnreadNotifications(state));
const lastReadId = state.get('lastReadId'); const lastReadId = state.get('lastReadId');
let items = ImmutableList(); let items = ImmutableList();
@ -112,7 +114,9 @@ const clearUnread = (state) => {
} }
const updateTop = (state, top) => { const updateTop = (state, top) => {
if (top && state.get('mounted') > 0) { state = state.set('top', top);
if (!shouldCountUnreadNotifications(state)) {
state = clearUnread(state); state = clearUnread(state);
} }
@ -120,7 +124,7 @@ const updateTop = (state, top) => {
}; };
const deleteByStatus = (state, statusId) => { const deleteByStatus = (state, statusId) => {
const top = state.get('top') && state.get('mounted') > 0; const top = !(shouldCountUnreadNotifications(state));
if (!top) { if (!top) {
const lastReadId = state.get('lastReadId'); const lastReadId = state.get('lastReadId');
const deletedUnread = state.get('items').filter(item => item !== null && item.get('status') === statusId && compareId(item.get('id'), lastReadId) > 0); const deletedUnread = state.get('items').filter(item => item !== null && item.get('status') === statusId && compareId(item.get('id'), lastReadId) > 0);
@ -157,14 +161,36 @@ const deleteMarkedNotifs = (state) => {
return state.update('items', list => list.filterNot(item => item.get('markedForDelete'))); return state.update('items', list => list.filterNot(item => item.get('markedForDelete')));
}; };
const updateMounted = (state) => {
state = state.update('mounted', count => count + 1);
if (!shouldCountUnreadNotifications(state)) {
state = clearUnread(state);
}
return state;
};
const updateVisibility = (state, visibility) => {
state = state.set('isTabVisible', visibility);
if (!shouldCountUnreadNotifications(state)) {
state = clearUnread(state);
}
return state;
};
const shouldCountUnreadNotifications = (state) => {
return !(state.get('isTabVisible') && state.get('top') && state.get('mounted') > 0);
};
export default function notifications(state = initialState, action) { export default function notifications(state = initialState, action) {
let st; let st;
switch(action.type) { switch(action.type) {
case NOTIFICATIONS_MOUNT: case NOTIFICATIONS_MOUNT:
return (state.get('top') ? clearUnread(state) : state).update('mounted', count => count + 1); return updateMounted(state);
case NOTIFICATIONS_UNMOUNT: case NOTIFICATIONS_UNMOUNT:
return state.update('mounted', count => count - 1); return state.update('mounted', count => count - 1);
case NOTIFICATIONS_SET_VISIBILITY:
return updateVisibility(state, action.visibility);
case NOTIFICATIONS_EXPAND_REQUEST: case NOTIFICATIONS_EXPAND_REQUEST:
case NOTIFICATIONS_DELETE_MARKED_REQUEST: case NOTIFICATIONS_DELETE_MARKED_REQUEST:
return state.set('isLoading', true); return state.set('isLoading', true);