Skip to content
Snippets Groups Projects
Commit 8698cd32 authored by Eugen Rochko's avatar Eugen Rochko
Browse files

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
parent 1d2175f7
No related branches found
No related tags found
No related merge requests found
......@@ -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));
......
......@@ -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));
......
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;
}
}
});
......
......@@ -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} />
......
......@@ -19,7 +19,7 @@ const HomeTimeline = React.createClass({
render () {
return (
<Column icon='home' heading='Home'>
<StatusListContainer type='home' />
<StatusListContainer {...this.props} type='home' />
</Column>
);
},
......
......@@ -19,7 +19,7 @@ const MentionsTimeline = React.createClass({
render () {
return (
<Column icon='at' heading='Mentions'>
<StatusListContainer type='mentions' />
<StatusListContainer {...this.props} type='mentions' />
</Column>
);
},
......
......@@ -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>
......
......@@ -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;
};
......
......@@ -41,6 +41,7 @@
"sinon": "^1.17.6"
},
"dependencies": {
"react-responsive": "^1.1.5"
"react-responsive": "^1.1.5",
"react-router-scroll": "^0.3.2"
}
}
......@@ -1743,6 +1743,10 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0"
randombytes "^2.0.0"
dom-helpers@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-2.4.0.tgz#9bb4b245f637367b1fa670274272aa28fe06c367"
dom-serializer@~0.1.0, dom-serializer@0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
......@@ -3892,6 +3896,14 @@ react-responsive:
matchmedia "^0.1.2"
object-assign "^4.0.1"
react-router-scroll:
version "0.3.2"
resolved "https://registry.yarnpkg.com/react-router-scroll/-/react-router-scroll-0.3.2.tgz#ba8b1d01b3681dc5a68d72865d35c10e84065e52"
dependencies:
history "^2.1.2"
scroll-behavior "^0.8.0"
warning "^3.0.0"
react-router@^2.8.0:
version "2.8.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-2.8.1.tgz#73e9491f6ceb316d0f779829081863e378ee4ed7"
......@@ -4147,6 +4159,13 @@ sax@^1.1.4, sax@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
scroll-behavior@^0.8.0:
version "0.8.2"
resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.8.2.tgz#ace13e40b001d8d4d007aec0e7fb668cf9043546"
dependencies:
dom-helpers "^2.4.0"
invariant "^2.2.1"
semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment