From 8698cd3281ac1d699c723a151b14f1e2f2e8b07e Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 18 Oct 2016 23:06:28 +0200 Subject: Remember scroll position when navigating back, do not needlessly reload entire timelines (only fetch since last known ID). Side effect: account timelines no longer update in real-time --- .../javascripts/components/actions/accounts.jsx | 11 ++++++++- .../javascripts/components/actions/timelines.jsx | 11 ++++++++- .../components/components/status_list.jsx | 28 ++++++++++++++++++---- .../javascripts/components/containers/mastodon.jsx | 4 +++- .../components/features/home_timeline/index.jsx | 2 +- .../features/mentions_timeline/index.jsx | 2 +- .../javascripts/components/features/ui/index.jsx | 4 ++-- .../javascripts/components/reducers/timelines.jsx | 12 ++++------ 8 files changed, 55 insertions(+), 19 deletions(-) (limited to 'app/assets/javascripts/components') diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx index 4847c37e2..c1c99d6bd 100644 --- a/app/assets/javascripts/components/actions/accounts.jsx +++ b/app/assets/javascripts/components/actions/accounts.jsx @@ -57,7 +57,16 @@ export function fetchAccountTimeline(id) { return (dispatch, getState) => { dispatch(fetchAccountTimelineRequest(id)); - api(getState).get(`/api/v1/accounts/${id}/statuses`).then(response => { + const ids = getState().getIn(['timelines', 'accounts_timelines', id], Immutable.List()); + const newestId = ids.size > 0 ? ids.first() : null; + + let params = ''; + + if (newestId !== null) { + params = `?since_id=${newestId}`; + } + + api(getState).get(`/api/v1/accounts/${id}/statuses${params}`).then(response => { dispatch(fetchAccountTimelineSuccess(id, response.data)); }).catch(error => { dispatch(fetchAccountTimelineFail(id, error)); diff --git a/app/assets/javascripts/components/actions/timelines.jsx b/app/assets/javascripts/components/actions/timelines.jsx index f92f758f5..5258d7103 100644 --- a/app/assets/javascripts/components/actions/timelines.jsx +++ b/app/assets/javascripts/components/actions/timelines.jsx @@ -45,7 +45,16 @@ export function refreshTimeline(timeline) { return function (dispatch, getState) { dispatch(refreshTimelineRequest(timeline)); - api(getState).get(`/api/v1/statuses/${timeline}`).then(function (response) { + const ids = getState().getIn(['timelines', timeline]); + const newestId = ids.size > 0 ? ids.first() : null; + + let params = ''; + + if (newestId !== null) { + params = `?since_id=${newestId}`; + } + + api(getState).get(`/api/v1/statuses/${timeline}${params}`).then(function (response) { dispatch(refreshTimelineSuccess(timeline, response.data)); }).catch(function (error) { dispatch(refreshTimelineFail(timeline, error)); diff --git a/app/assets/javascripts/components/components/status_list.jsx b/app/assets/javascripts/components/components/status_list.jsx index f70d53263..4977d84ce 100644 --- a/app/assets/javascripts/components/components/status_list.jsx +++ b/app/assets/javascripts/components/components/status_list.jsx @@ -1,6 +1,7 @@ -import Status from './status'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import Status from './status'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { ScrollContainer } from 'react-router-scroll'; const StatusList = React.createClass({ @@ -11,9 +12,16 @@ const StatusList = React.createClass({ onFavourite: React.PropTypes.func, onDelete: React.PropTypes.func, onScrollToBottom: React.PropTypes.func, + trackScroll: React.PropTypes.bool, me: React.PropTypes.number }, + getDefaultProps () { + return { + trackScroll: true + }; + }, + mixins: [PureRenderMixin], handleScroll (e) { @@ -25,9 +33,9 @@ const StatusList = React.createClass({ }, render () { - const { statuses, onScrollToBottom, ...other } = this.props; + const { statuses, onScrollToBottom, trackScroll, ...other } = this.props; - return ( + const scrollableArea = (
{statuses.map((status) => { @@ -36,6 +44,16 @@ const StatusList = React.createClass({
); + + if (trackScroll) { + return ( + + {scrollableArea} + + ); + } else { + return scrollableArea; + } } }); diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index e5c0887a9..4eb9f83c8 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -10,11 +10,13 @@ import { setAccessToken } from '../actions/meta'; import { setAccountSelf } from '../actions/accounts'; import PureRenderMixin from 'react-addons-pure-render-mixin'; import { + applyRouterMiddleware, Router, Route, hashHistory, IndexRoute } from 'react-router'; +import { useScroll } from 'react-router-scroll'; import UI from '../features/ui'; import Account from '../features/account'; import Status from '../features/status'; @@ -71,7 +73,7 @@ const Mastodon = React.createClass({ render () { return ( - + diff --git a/app/assets/javascripts/components/features/home_timeline/index.jsx b/app/assets/javascripts/components/features/home_timeline/index.jsx index 9be3f3964..e4cd8bdca 100644 --- a/app/assets/javascripts/components/features/home_timeline/index.jsx +++ b/app/assets/javascripts/components/features/home_timeline/index.jsx @@ -19,7 +19,7 @@ const HomeTimeline = React.createClass({ render () { return ( - + ); }, diff --git a/app/assets/javascripts/components/features/mentions_timeline/index.jsx b/app/assets/javascripts/components/features/mentions_timeline/index.jsx index a1b511d3e..919a75d18 100644 --- a/app/assets/javascripts/components/features/mentions_timeline/index.jsx +++ b/app/assets/javascripts/components/features/mentions_timeline/index.jsx @@ -19,7 +19,7 @@ const MentionsTimeline = React.createClass({ render () { return ( - + ); }, diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx index fab32a31e..06a9d2f50 100644 --- a/app/assets/javascripts/components/features/ui/index.jsx +++ b/app/assets/javascripts/components/features/ui/index.jsx @@ -28,8 +28,8 @@ const UI = React.createClass({ - - + + {this.props.children} diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx index 65bccb44d..06534971d 100644 --- a/app/assets/javascripts/components/reducers/timelines.jsx +++ b/app/assets/javascripts/components/reducers/timelines.jsx @@ -85,7 +85,7 @@ function normalizeTimeline(state, timeline, statuses) { ids = ids.set(i, status.get('id')); }); - return state.set(timeline, ids); + return state.update(timeline, list => list.unshift(...ids)); }; function appendNormalizedTimeline(state, timeline, statuses) { @@ -100,16 +100,14 @@ function appendNormalizedTimeline(state, timeline, statuses) { }; function normalizeAccountTimeline(state, accountId, statuses) { - state = state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => { - return (list.size > 0) ? list.clear() : list; - }); + let ids = Immutable.List([]); statuses.forEach((status, i) => { state = normalizeStatus(state, status); - state = state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => list.set(i, status.get('id'))); + ids = ids.set(i, status.get('id')); }); - return state; + return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => list.unshift(...ids)); }; function appendNormalizedAccountTimeline(state, accountId, statuses) { @@ -137,7 +135,7 @@ function updateTimeline(state, timeline, status) { return list.unshift(status.get('id')); }); - state = state.updateIn(['accounts_timelines', status.getIn(['account', 'id'])], Immutable.List([]), list => (list.includes(status.get('id')) ? list : list.unshift(status.get('id')))); + //state = state.updateIn(['accounts_timelines', status.getIn(['account', 'id'])], Immutable.List([]), list => (list.includes(status.get('id')) ? list : list.unshift(status.get('id')))); return state; }; -- cgit