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
This commit is contained in:
		
							parent
							
								
									1d2175f73c
								
							
						
					
					
						commit
						8698cd3281
					
				| @ -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)); | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
|  | ||||
| @ -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" | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										19
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								yarn.lock
									
									
									
									
									
								
							| @ -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" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user