Fix updates being hidden behind pending items on unmounted components (#11898)

This commit is contained in:
Eugen Rochko 2019-09-21 09:12:13 +02:00 committed by GitHub
parent a90243a712
commit ba0de8fb68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 4 deletions

View file

@ -28,6 +28,9 @@ export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING'; export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING';
export const NOTIFICATIONS_MOUNT = 'NOTIFICATIONS_MOUNT';
export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT';
defineMessages({ defineMessages({
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
group: { id: 'notifications.group', defaultMessage: '{count} notifications' }, group: { id: 'notifications.group', defaultMessage: '{count} notifications' },
@ -215,3 +218,11 @@ export function setFilter (filterType) {
dispatch(saveSettings()); dispatch(saveSettings());
}; };
}; };
export const mountNotifications = () => ({
type: NOTIFICATIONS_MOUNT,
});
export const unmountNotifications = () => ({
type: NOTIFICATIONS_UNMOUNT,
});

View file

@ -199,7 +199,12 @@ export default class ScrollableList extends PureComponent {
this.clearMouseIdleTimer(); this.clearMouseIdleTimer();
this.detachScrollListener(); this.detachScrollListener();
this.detachIntersectionObserver(); this.detachIntersectionObserver();
detachFullscreenListener(this.onFullScreenChange); detachFullscreenListener(this.onFullScreenChange);
if (this.props.onScrollToTop) {
this.props.onScrollToTop();
}
} }
onFullScreenChange = () => { onFullScreenChange = () => {

View file

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import Column from '../../components/column'; import Column from '../../components/column';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import { expandNotifications, scrollTopNotifications, loadPending } from '../../actions/notifications'; import { expandNotifications, scrollTopNotifications, loadPending, mountNotifications, unmountNotifications } from '../../actions/notifications';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import NotificationContainer from './containers/notification_container'; import NotificationContainer from './containers/notification_container';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@ -66,11 +66,16 @@ class Notifications extends React.PureComponent {
trackScroll: true, trackScroll: true,
}; };
componentWillMount() {
this.props.dispatch(mountNotifications());
}
componentWillUnmount () { componentWillUnmount () {
this.handleLoadOlder.cancel(); this.handleLoadOlder.cancel();
this.handleScrollToTop.cancel(); this.handleScrollToTop.cancel();
this.handleScroll.cancel(); this.handleScroll.cancel();
this.props.dispatch(scrollTopNotifications(false)); this.props.dispatch(scrollTopNotifications(false));
this.props.dispatch(unmountNotifications());
} }
handleLoadGap = (maxId) => { handleLoadGap = (maxId) => {

View file

@ -7,6 +7,8 @@ import {
NOTIFICATIONS_CLEAR, NOTIFICATIONS_CLEAR,
NOTIFICATIONS_SCROLL_TOP, NOTIFICATIONS_SCROLL_TOP,
NOTIFICATIONS_LOAD_PENDING, NOTIFICATIONS_LOAD_PENDING,
NOTIFICATIONS_MOUNT,
NOTIFICATIONS_UNMOUNT,
} from '../actions/notifications'; } from '../actions/notifications';
import { import {
ACCOUNT_BLOCK_SUCCESS, ACCOUNT_BLOCK_SUCCESS,
@ -22,6 +24,7 @@ const initialState = ImmutableMap({
items: ImmutableList(), items: ImmutableList(),
hasMore: true, hasMore: true,
top: false, top: false,
mounted: false,
unread: 0, unread: 0,
isLoading: false, isLoading: false,
}); });
@ -35,9 +38,10 @@ const notificationToMap = notification => ImmutableMap({
}); });
const normalizeNotification = (state, notification, usePendingItems) => { const normalizeNotification = (state, notification, usePendingItems) => {
const top = state.get('top'); const top = state.get('top');
const mounted = state.get('mounted');
if (usePendingItems || !top || !state.get('pendingItems').isEmpty()) { if (usePendingItems || (!top && mounted) || !state.get('pendingItems').isEmpty()) {
return state.update('pendingItems', list => list.unshift(notificationToMap(notification))).update('unread', unread => unread + 1); return state.update('pendingItems', list => list.unshift(notificationToMap(notification))).update('unread', unread => unread + 1);
} }
@ -63,7 +67,7 @@ const expandNormalizedNotifications = (state, notifications, next, isLoadingRece
return state.withMutations(mutable => { return state.withMutations(mutable => {
if (!items.isEmpty()) { if (!items.isEmpty()) {
usePendingItems = isLoadingRecent && (usePendingItems || !mutable.get('top') || !mutable.get('pendingItems').isEmpty()); usePendingItems = isLoadingRecent && (usePendingItems || (!mutable.get('top') && mutable.get('mounted')) || !mutable.get('pendingItems').isEmpty());
mutable.update(usePendingItems ? 'pendingItems' : 'items', list => { mutable.update(usePendingItems ? 'pendingItems' : 'items', list => {
const lastIndex = 1 + list.findLastIndex( const lastIndex = 1 + list.findLastIndex(
@ -134,6 +138,10 @@ export default function notifications(state = initialState, action) {
return action.timeline === 'home' ? return action.timeline === 'home' ?
state.update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) : state.update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) :
state; state;
case NOTIFICATIONS_MOUNT:
return state.set('mounted', true);
case NOTIFICATIONS_UNMOUNT:
return state.set('mounted', false);
default: default:
return state; return state;
} }