diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2016-10-18 23:06:28 +0200 |
---|---|---|
committer | Eugen Rochko <eugen@zeonfederated.com> | 2016-10-18 23:06:28 +0200 |
commit | 8698cd3281ac1d699c723a151b14f1e2f2e8b07e (patch) | |
tree | 00365bb754c9322615538fdef2d9c8adbde3bc11 /app/assets/javascripts/components | |
parent | 1d2175f73c8e6e92768ae51f66cb94d64d2265a7 (diff) |
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
Diffstat (limited to 'app/assets/javascripts/components')
8 files changed, 55 insertions, 19 deletions
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 = ( <div style={{ overflowY: 'scroll', flex: '1 1 auto', overflowX: 'hidden' }} className='scrollable' onScroll={this.handleScroll}> <div> {statuses.map((status) => { @@ -36,6 +44,16 @@ const StatusList = React.createClass({ </div> </div> ); + + if (trackScroll) { + return ( + <ScrollContainer scrollKey='status-list'> + {scrollableArea} + </ScrollContainer> + ); + } 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 ( <Provider store={store}> - <Router history={hashHistory}> + <Router history={hashHistory} render={applyRouterMiddleware(useScroll())}> <Route path='/' component={UI}> <IndexRoute component={GettingStarted} /> <Route path='/statuses/new' component={Compose} /> 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 ( <Column icon='home' heading='Home'> - <StatusListContainer type='home' /> + <StatusListContainer {...this.props} type='home' /> </Column> ); }, 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 ( <Column icon='at' heading='Mentions'> - <StatusListContainer type='mentions' /> + <StatusListContainer {...this.props} type='mentions' /> </Column> ); }, 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({ <MediaQuery minWidth={layoutBreakpoint}> <ColumnsArea> <Compose /> - <HomeTimeline /> - <MentionsTimeline /> + <HomeTimeline trackScroll={false} /> + <MentionsTimeline trackScroll={false} /> {this.props.children} </ColumnsArea> </MediaQuery> 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; }; |