From 08b3de4d5ee73f06966724108a3975a9110140e3 Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 9 Nov 2018 17:43:25 +0100 Subject: [PATCH 1/3] Remove unused computation of reblog references from updateTimeline (#9244) --- app/javascript/mastodon/actions/timelines.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 8cb06c157..74ad55652 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -16,8 +16,6 @@ export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT'; export function updateTimeline(timeline, status, accept) { return (dispatch, getState) => { - const references = status.reblog ? getState().get('statuses').filter((item, itemId) => (itemId === status.reblog.id || item.get('reblog') === status.reblog.id)).map((_, itemId) => itemId) : []; - if (typeof accept === 'function' && !accept(status)) { return; } @@ -28,7 +26,6 @@ export function updateTimeline(timeline, status, accept) { type: TIMELINE_UPDATE, timeline, status, - references, }); }; }; From 2f86fc5e0a45eb2002d4b481ae093197ad523479 Mon Sep 17 00:00:00 2001 From: Les Orchard Date: Fri, 9 Nov 2018 12:06:43 -0500 Subject: [PATCH 2/3] Identify manual scrolling to cancel scroll to top reset on mouse idle (#9245) --- .../mastodon/components/scrollable_list.js | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js index 91a895bce..7551d0b1f 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.js @@ -59,6 +59,13 @@ export default class ScrollableList extends PureComponent { } else if (this.props.onScroll) { this.props.onScroll(); } + + if (!this.lastScrollWasSynthetic) { + // If the last scroll wasn't caused by setScrollTop(), assume it was + // intentional and cancel any pending scroll reset on mouse idle + this.scrollToTopOnMouseIdle = false; + } + this.lastScrollWasSynthetic = false; } }, 150, { trailing: true, @@ -66,8 +73,16 @@ export default class ScrollableList extends PureComponent { mouseIdleTimer = null; mouseMovedRecently = false; + lastScrollWasSynthetic = false; scrollToTopOnMouseIdle = false; + setScrollTop = newScrollTop => { + if (this.node.scrollTop !== newScrollTop) { + this.lastScrollWasSynthetic = true; + this.node.scrollTop = newScrollTop; + } + }; + clearMouseIdleTimer = () => { if (this.mouseIdleTimer === null) { return; @@ -99,7 +114,7 @@ export default class ScrollableList extends PureComponent { handleMouseIdle = () => { if (this.scrollToTopOnMouseIdle) { - this.node.scrollTop = 0; + this.setScrollTop(0); } this.mouseMovedRecently = false; @@ -132,11 +147,7 @@ export default class ScrollableList extends PureComponent { // Reset the scroll position when a new child comes in in order not to // jerk the scrollbar around if you're already scrolled down the page. if (snapshot !== null) { - const newScrollTop = this.node.scrollHeight - snapshot; - - if (this.node.scrollTop !== newScrollTop) { - this.node.scrollTop = newScrollTop; - } + this.setScrollTop(this.node.scrollHeight - snapshot); } } From 3cecf3e5b995f5035b17fd9572c17ba1ede9346b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 10 Nov 2018 15:04:13 +0100 Subject: [PATCH 3/3] Fix loading indicator inconsistency (#9252) * Use global loading indicator when loading more items * Use local loading indicator for notifications And remove global loading indicator for initial custom emojis load --- .../mastodon/actions/custom_emojis.js | 3 +++ app/javascript/mastodon/actions/favourites.js | 3 +++ .../mastodon/actions/notifications.js | 16 ++++++++++------ app/javascript/mastodon/actions/timelines.js | 19 ++++++++++--------- .../mastodon/features/notifications/index.js | 1 + 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/app/javascript/mastodon/actions/custom_emojis.js b/app/javascript/mastodon/actions/custom_emojis.js index aa37bc423..7b7d0091b 100644 --- a/app/javascript/mastodon/actions/custom_emojis.js +++ b/app/javascript/mastodon/actions/custom_emojis.js @@ -19,6 +19,7 @@ export function fetchCustomEmojis() { export function fetchCustomEmojisRequest() { return { type: CUSTOM_EMOJIS_FETCH_REQUEST, + skipLoading: true, }; }; @@ -26,6 +27,7 @@ export function fetchCustomEmojisSuccess(custom_emojis) { return { type: CUSTOM_EMOJIS_FETCH_SUCCESS, custom_emojis, + skipLoading: true, }; }; @@ -33,5 +35,6 @@ export function fetchCustomEmojisFail(error) { return { type: CUSTOM_EMOJIS_FETCH_FAIL, error, + skipLoading: true, }; }; diff --git a/app/javascript/mastodon/actions/favourites.js b/app/javascript/mastodon/actions/favourites.js index 124cf8c44..9448b1efe 100644 --- a/app/javascript/mastodon/actions/favourites.js +++ b/app/javascript/mastodon/actions/favourites.js @@ -30,6 +30,7 @@ export function fetchFavouritedStatuses() { export function fetchFavouritedStatusesRequest() { return { type: FAVOURITED_STATUSES_FETCH_REQUEST, + skipLoading: true, }; }; @@ -38,6 +39,7 @@ export function fetchFavouritedStatusesSuccess(statuses, next) { type: FAVOURITED_STATUSES_FETCH_SUCCESS, statuses, next, + skipLoading: true, }; }; @@ -45,6 +47,7 @@ export function fetchFavouritedStatusesFail(error) { return { type: FAVOURITED_STATUSES_FETCH_FAIL, error, + skipLoading: true, }; }; diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index ad6430b82..92c70e155 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -93,6 +93,7 @@ const noOp = () => {}; export function expandNotifications({ maxId } = {}, done = noOp) { return (dispatch, getState) => { const notifications = getState().get('notifications'); + const isLoadingMore = !!maxId; if (notifications.get('isLoading')) { done(); @@ -108,7 +109,7 @@ export function expandNotifications({ maxId } = {}, done = noOp) { params.since_id = notifications.getIn(['items', 0]); } - dispatch(expandNotificationsRequest()); + dispatch(expandNotificationsRequest(isLoadingMore)); api(getState).get('/api/v1/notifications', { params }).then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); @@ -116,34 +117,37 @@ export function expandNotifications({ maxId } = {}, done = noOp) { dispatch(importFetchedAccounts(response.data.map(item => item.account))); dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status))); - dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null)); + dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore)); fetchRelatedRelationships(dispatch, response.data); done(); }).catch(error => { - dispatch(expandNotificationsFail(error)); + dispatch(expandNotificationsFail(error, isLoadingMore)); done(); }); }; }; -export function expandNotificationsRequest() { +export function expandNotificationsRequest(isLoadingMore) { return { type: NOTIFICATIONS_EXPAND_REQUEST, + skipLoading: !isLoadingMore, }; }; -export function expandNotificationsSuccess(notifications, next) { +export function expandNotificationsSuccess(notifications, next, isLoadingMore) { return { type: NOTIFICATIONS_EXPAND_SUCCESS, notifications, next, + skipLoading: !isLoadingMore, }; }; -export function expandNotificationsFail(error) { +export function expandNotificationsFail(error, isLoadingMore) { return { type: NOTIFICATIONS_EXPAND_FAIL, error, + skipLoading: !isLoadingMore, }; }; diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 74ad55652..81c4c8425 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -63,6 +63,7 @@ const parseTags = (tags = {}, mode) => { export function expandTimeline(timelineId, path, params = {}, done = noOp) { return (dispatch, getState) => { const timeline = getState().getIn(['timelines', timelineId], ImmutableMap()); + const isLoadingMore = !!params.max_id; if (timeline.get('isLoading')) { done(); @@ -73,15 +74,15 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) { params.since_id = timeline.getIn(['items', 0]); } - dispatch(expandTimelineRequest(timelineId)); + dispatch(expandTimelineRequest(timelineId, isLoadingMore)); api(getState).get(path, { params }).then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(importFetchedStatuses(response.data)); - dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206)); + dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingMore)); done(); }).catch(error => { - dispatch(expandTimelineFail(timelineId, error)); + dispatch(expandTimelineFail(timelineId, error, isLoadingMore)); done(); }); }; @@ -103,31 +104,31 @@ export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, don }, done); }; -export function expandTimelineRequest(timeline) { +export function expandTimelineRequest(timeline, isLoadingMore) { return { type: TIMELINE_EXPAND_REQUEST, timeline, - skipLoading: true, + skipLoading: !isLoadingMore, }; }; -export function expandTimelineSuccess(timeline, statuses, next, partial) { +export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingMore) { return { type: TIMELINE_EXPAND_SUCCESS, timeline, statuses, next, partial, - skipLoading: true, + skipLoading: !isLoadingMore, }; }; -export function expandTimelineFail(timeline, error) { +export function expandTimelineFail(timeline, error, isLoadingMore) { return { type: TIMELINE_EXPAND_FAIL, timeline, error, - skipLoading: true, + skipLoading: !isLoadingMore, }; }; diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 66af0baaf..aa82dbbb9 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -153,6 +153,7 @@ class Notifications extends React.PureComponent { scrollKey={`notifications-${columnId}`} trackScroll={!pinned} isLoading={isLoading} + showLoading={isLoading && notifications.size === 0} hasMore={hasMore} emptyMessage={emptyMessage} onLoadMore={this.handleLoadOlder}