From ba0de8fb68f5e67312446d1ac351f06d093fc2b8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 21 Sep 2019 09:12:13 +0200 Subject: [PATCH] Fix updates being hidden behind pending items on unmounted components (#11898) --- app/javascript/mastodon/actions/notifications.js | 11 +++++++++++ .../mastodon/components/scrollable_list.js | 5 +++++ .../mastodon/features/notifications/index.js | 7 ++++++- app/javascript/mastodon/reducers/notifications.js | 14 +++++++++++--- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index ea76255e38..58803d1ae5 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -28,6 +28,9 @@ export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING'; +export const NOTIFICATIONS_MOUNT = 'NOTIFICATIONS_MOUNT'; +export const NOTIFICATIONS_UNMOUNT = 'NOTIFICATIONS_UNMOUNT'; + defineMessages({ mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, group: { id: 'notifications.group', defaultMessage: '{count} notifications' }, @@ -215,3 +218,11 @@ export function setFilter (filterType) { dispatch(saveSettings()); }; }; + +export const mountNotifications = () => ({ + type: NOTIFICATIONS_MOUNT, +}); + +export const unmountNotifications = () => ({ + type: NOTIFICATIONS_UNMOUNT, +}); diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js index 253646ed01..b8fa0c2d9a 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.js @@ -199,7 +199,12 @@ export default class ScrollableList extends PureComponent { this.clearMouseIdleTimer(); this.detachScrollListener(); this.detachIntersectionObserver(); + detachFullscreenListener(this.onFullScreenChange); + + if (this.props.onScrollToTop) { + this.props.onScrollToTop(); + } } onFullScreenChange = () => { diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 7e5de0613a..d16a0f33a4 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Column from '../../components/column'; 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 NotificationContainer from './containers/notification_container'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; @@ -66,11 +66,16 @@ class Notifications extends React.PureComponent { trackScroll: true, }; + componentWillMount() { + this.props.dispatch(mountNotifications()); + } + componentWillUnmount () { this.handleLoadOlder.cancel(); this.handleScrollToTop.cancel(); this.handleScroll.cancel(); this.props.dispatch(scrollTopNotifications(false)); + this.props.dispatch(unmountNotifications()); } handleLoadGap = (maxId) => { diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index 45d3a5c516..aac644950f 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -7,6 +7,8 @@ import { NOTIFICATIONS_CLEAR, NOTIFICATIONS_SCROLL_TOP, NOTIFICATIONS_LOAD_PENDING, + NOTIFICATIONS_MOUNT, + NOTIFICATIONS_UNMOUNT, } from '../actions/notifications'; import { ACCOUNT_BLOCK_SUCCESS, @@ -22,6 +24,7 @@ const initialState = ImmutableMap({ items: ImmutableList(), hasMore: true, top: false, + mounted: false, unread: 0, isLoading: false, }); @@ -35,9 +38,10 @@ const notificationToMap = notification => ImmutableMap({ }); 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); } @@ -63,7 +67,7 @@ const expandNormalizedNotifications = (state, notifications, next, isLoadingRece return state.withMutations(mutable => { 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 => { const lastIndex = 1 + list.findLastIndex( @@ -134,6 +138,10 @@ export default function notifications(state = initialState, action) { return action.timeline === 'home' ? state.update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) : state; + case NOTIFICATIONS_MOUNT: + return state.set('mounted', true); + case NOTIFICATIONS_UNMOUNT: + return state.set('mounted', false); default: return state; }