persist last-intersected status update and restore when ScrollableList is restored

e.g. when navigating from home-timeline to a status conversational  thread and <Back again
This commit is contained in:
Matt Panaro 2019-12-24 01:23:49 -05:00
parent 53c623a999
commit 07e26142ef
6 changed files with 27 additions and 1 deletions

View file

@ -17,6 +17,14 @@ export const TIMELINE_LOAD_PENDING = 'TIMELINE_LOAD_PENDING';
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT'; export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT'; export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
export const CURRENTLY_VIEWING = 'CURRENTLY_VIEWING';
export const updateCurrentlyViewing = (timeline, id) => ({
type: CURRENTLY_VIEWING,
timeline,
id,
});
export const loadPending = timeline => ({ export const loadPending = timeline => ({
type: TIMELINE_LOAD_PENDING, type: TIMELINE_LOAD_PENDING,
timeline, timeline,

View file

@ -20,6 +20,8 @@ export default class IntersectionObserverArticle extends React.Component {
cachedHeight: PropTypes.number, cachedHeight: PropTypes.number,
onHeightChange: PropTypes.func, onHeightChange: PropTypes.func,
children: PropTypes.node, children: PropTypes.node,
currentlyViewing: PropTypes.number,
updateCurrentlyViewing: PropTypes.func,
}; };
state = { state = {
@ -48,6 +50,8 @@ export default class IntersectionObserverArticle extends React.Component {
); );
this.componentMounted = true; this.componentMounted = true;
if(id === this.props.currentlyViewing) this.node.scrollIntoView();
} }
componentWillUnmount () { componentWillUnmount () {
@ -60,6 +64,8 @@ export default class IntersectionObserverArticle extends React.Component {
handleIntersection = (entry) => { handleIntersection = (entry) => {
this.entry = entry; this.entry = entry;
if(entry.intersectionRatio > 0.75 && this.props.updateCurrentlyViewing) this.props.updateCurrentlyViewing(this.id);
scheduleIdleTask(this.calculateHeight); scheduleIdleTask(this.calculateHeight);
this.setState(this.updateStateAfterIntersection); this.setState(this.updateStateAfterIntersection);
} }

View file

@ -36,6 +36,8 @@ export default class ScrollableList extends PureComponent {
emptyMessage: PropTypes.node, emptyMessage: PropTypes.node,
children: PropTypes.node, children: PropTypes.node,
bindToDocument: PropTypes.bool, bindToDocument: PropTypes.bool,
currentlyViewing: PropTypes.number,
updateCurrentlyViewing: PropTypes.func,
}; };
static defaultProps = { static defaultProps = {
@ -309,6 +311,8 @@ export default class ScrollableList extends PureComponent {
listLength={childrenCount} listLength={childrenCount}
intersectionObserverWrapper={this.intersectionObserverWrapper} intersectionObserverWrapper={this.intersectionObserverWrapper}
saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null} saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
currentlyViewing={this.props.currentlyViewing}
updateCurrentlyViewing={this.props.updateCurrentlyViewing}
> >
{React.cloneElement(child, { {React.cloneElement(child, {
getScrollPosition: this.getScrollPosition, getScrollPosition: this.getScrollPosition,

View file

@ -26,6 +26,8 @@ export default class StatusList extends ImmutablePureComponent {
emptyMessage: PropTypes.node, emptyMessage: PropTypes.node,
alwaysPrepend: PropTypes.bool, alwaysPrepend: PropTypes.bool,
timelineId: PropTypes.string, timelineId: PropTypes.string,
currentlyViewing: PropTypes.number,
updateCurrentlyViewing: PropTypes.func,
}; };
static defaultProps = { static defaultProps = {

View file

@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import StatusList from '../../../components/status_list'; import StatusList from '../../../components/status_list';
import { scrollTopTimeline, loadPending } from '../../../actions/timelines'; import { scrollTopTimeline, loadPending, updateCurrentlyViewing } from '../../../actions/timelines';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
@ -39,6 +39,7 @@ const makeMapStateToProps = () => {
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false), isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
hasMore: state.getIn(['timelines', timelineId, 'hasMore']), hasMore: state.getIn(['timelines', timelineId, 'hasMore']),
numPending: getPendingStatusIds(state, { type: timelineId }).size, numPending: getPendingStatusIds(state, { type: timelineId }).size,
currentlyViewing: state.getIn(['timelines', timelineId, 'currentlyViewing'], -1),
}); });
return mapStateToProps; return mapStateToProps;
@ -56,6 +57,7 @@ const mapDispatchToProps = (dispatch, { timelineId }) => ({
onLoadPending: () => dispatch(loadPending(timelineId)), onLoadPending: () => dispatch(loadPending(timelineId)),
updateCurrentlyViewing: id => dispatch(updateCurrentlyViewing(timelineId, id)),
}); });
export default connect(makeMapStateToProps, mapDispatchToProps)(StatusList); export default connect(makeMapStateToProps, mapDispatchToProps)(StatusList);

View file

@ -9,6 +9,7 @@ import {
TIMELINE_CONNECT, TIMELINE_CONNECT,
TIMELINE_DISCONNECT, TIMELINE_DISCONNECT,
TIMELINE_LOAD_PENDING, TIMELINE_LOAD_PENDING,
CURRENTLY_VIEWING,
} from '../actions/timelines'; } from '../actions/timelines';
import { import {
ACCOUNT_BLOCK_SUCCESS, ACCOUNT_BLOCK_SUCCESS,
@ -28,6 +29,7 @@ const initialTimeline = ImmutableMap({
hasMore: true, hasMore: true,
pendingItems: ImmutableList(), pendingItems: ImmutableList(),
items: ImmutableList(), items: ImmutableList(),
currentlyViewing: -1,
}); });
const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent, usePendingItems) => { const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent, usePendingItems) => {
@ -168,6 +170,8 @@ export default function timelines(state = initialState, action) {
initialTimeline, initialTimeline,
map => map.set('online', false).update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) map => map.set('online', false).update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items)
); );
case CURRENTLY_VIEWING:
return state.update(action.timeline, initialTimeline, map => map.set('currentlyViewing', action.id));
default: default:
return state; return state;
} }