Improve infinite scroll on notifications
This commit is contained in:
		
					parent
					
						
							
								57f7cf8349
							
						
					
				
			
			
				commit
				
					
						905c829179
					
				
			
		
					 7 changed files with 40 additions and 17 deletions
				
			
		|  | @ -96,13 +96,17 @@ export function expandNotifications() { | ||||||
|   return (dispatch, getState) => { |   return (dispatch, getState) => { | ||||||
|     const url = getState().getIn(['notifications', 'next'], null); |     const url = getState().getIn(['notifications', 'next'], null); | ||||||
| 
 | 
 | ||||||
|     if (url === null) { |     if (url === null || getState().getIn(['notifications', 'isLoading'])) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     dispatch(expandNotificationsRequest()); |     dispatch(expandNotificationsRequest()); | ||||||
| 
 | 
 | ||||||
|     api(getState).get(url).then(response => { |     api(getState).get(url, { | ||||||
|  |       params: { | ||||||
|  |         limit: 5 | ||||||
|  |       } | ||||||
|  |     }).then(response => { | ||||||
|       const next = getLinks(response).refs.find(link => link.rel === 'next'); |       const next = getLinks(response).refs.find(link => link.rel === 'next'); | ||||||
| 
 | 
 | ||||||
|       dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null)); |       dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null)); | ||||||
|  |  | ||||||
|  | @ -20,7 +20,8 @@ const getNotifications = createSelector([ | ||||||
| ], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type')))); | ], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type')))); | ||||||
| 
 | 
 | ||||||
| const mapStateToProps = state => ({ | const mapStateToProps = state => ({ | ||||||
|   notifications: getNotifications(state) |   notifications: getNotifications(state), | ||||||
|  |   isLoading: state.getIn(['notifications', 'isLoading'], true) | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const Notifications = React.createClass({ | const Notifications = React.createClass({ | ||||||
|  | @ -29,7 +30,8 @@ const Notifications = React.createClass({ | ||||||
|     notifications: ImmutablePropTypes.list.isRequired, |     notifications: ImmutablePropTypes.list.isRequired, | ||||||
|     dispatch: React.PropTypes.func.isRequired, |     dispatch: React.PropTypes.func.isRequired, | ||||||
|     trackScroll: React.PropTypes.bool, |     trackScroll: React.PropTypes.bool, | ||||||
|     intl: React.PropTypes.object.isRequired |     intl: React.PropTypes.object.isRequired, | ||||||
|  |     isLoading: React.PropTypes.bool | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   getDefaultProps () { |   getDefaultProps () { | ||||||
|  | @ -42,8 +44,9 @@ const Notifications = React.createClass({ | ||||||
| 
 | 
 | ||||||
|   handleScroll (e) { |   handleScroll (e) { | ||||||
|     const { scrollTop, scrollHeight, clientHeight } = e.target; |     const { scrollTop, scrollHeight, clientHeight } = e.target; | ||||||
|  |     const offset = scrollHeight - scrollTop - clientHeight; | ||||||
| 
 | 
 | ||||||
|     if (scrollTop === scrollHeight - clientHeight) { |     if (250 > offset && !this.props.isLoading) { | ||||||
|       this.props.dispatch(expandNotifications()); |       this.props.dispatch(expandNotifications()); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  | @ -2,6 +2,10 @@ import { | ||||||
|   NOTIFICATIONS_UPDATE, |   NOTIFICATIONS_UPDATE, | ||||||
|   NOTIFICATIONS_REFRESH_SUCCESS, |   NOTIFICATIONS_REFRESH_SUCCESS, | ||||||
|   NOTIFICATIONS_EXPAND_SUCCESS, |   NOTIFICATIONS_EXPAND_SUCCESS, | ||||||
|  |   NOTIFICATIONS_REFRESH_REQUEST, | ||||||
|  |   NOTIFICATIONS_EXPAND_REQUEST, | ||||||
|  |   NOTIFICATIONS_REFRESH_FAIL, | ||||||
|  |   NOTIFICATIONS_EXPAND_FAIL | ||||||
| } from '../actions/notifications'; | } from '../actions/notifications'; | ||||||
| import { ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts'; | import { ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts'; | ||||||
| import Immutable from 'immutable'; | import Immutable from 'immutable'; | ||||||
|  | @ -9,7 +13,8 @@ import Immutable from 'immutable'; | ||||||
| const initialState = Immutable.Map({ | const initialState = Immutable.Map({ | ||||||
|   items: Immutable.List(), |   items: Immutable.List(), | ||||||
|   next: null, |   next: null, | ||||||
|   loaded: false |   loaded: false, | ||||||
|  |   isLoading: true | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const notificationToMap = notification => Immutable.Map({ | const notificationToMap = notification => Immutable.Map({ | ||||||
|  | @ -31,7 +36,11 @@ const normalizeNotifications = (state, notifications, next) => { | ||||||
|     items = items.set(i, notificationToMap(n)); |     items = items.set(i, notificationToMap(n)); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   return state.update('items', list => loaded ? list.unshift(...items) : list.push(...items)).set('next', next).set('loaded', true); |   return state | ||||||
|  |     .update('items', list => loaded ? list.unshift(...items) : list.push(...items)) | ||||||
|  |     .set('next', next) | ||||||
|  |     .set('loaded', true) | ||||||
|  |     .set('isLoading', false); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const appendNormalizedNotifications = (state, notifications, next) => { | const appendNormalizedNotifications = (state, notifications, next) => { | ||||||
|  | @ -41,7 +50,10 @@ const appendNormalizedNotifications = (state, notifications, next) => { | ||||||
|     items = items.set(i, notificationToMap(n)); |     items = items.set(i, notificationToMap(n)); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   return state.update('items', list => list.push(...items)).set('next', next); |   return state | ||||||
|  |     .update('items', list => list.push(...items)) | ||||||
|  |     .set('next', next) | ||||||
|  |     .set('isLoading', false); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const filterNotifications = (state, relationship) => { | const filterNotifications = (state, relationship) => { | ||||||
|  | @ -50,6 +62,11 @@ const filterNotifications = (state, relationship) => { | ||||||
| 
 | 
 | ||||||
| export default function notifications(state = initialState, action) { | export default function notifications(state = initialState, action) { | ||||||
|   switch(action.type) { |   switch(action.type) { | ||||||
|  |   case NOTIFICATIONS_REFRESH_REQUEST: | ||||||
|  |   case NOTIFICATIONS_EXPAND_REQUEST: | ||||||
|  |   case NOTIFICATIONS_REFRESH_FAIL: | ||||||
|  |   case NOTIFICATIONS_EXPAND_FAIL: | ||||||
|  |     return state.set('isLoading', true); | ||||||
|   case NOTIFICATIONS_UPDATE: |   case NOTIFICATIONS_UPDATE: | ||||||
|     return normalizeNotification(state, action.notification); |     return normalizeNotification(state, action.notification); | ||||||
|   case NOTIFICATIONS_REFRESH_SUCCESS: |   case NOTIFICATIONS_REFRESH_SUCCESS: | ||||||
|  |  | ||||||
|  | @ -1,5 +1,3 @@ | ||||||
| @import url(https://fonts.googleapis.com/css?family=Montserrat); |  | ||||||
| 
 |  | ||||||
| .about-body { | .about-body { | ||||||
|   .wrapper { |   .wrapper { | ||||||
|     max-width: 600px; |     max-width: 600px; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| @import 'variables'; | @import 'variables'; | ||||||
| @import url(https://fonts.googleapis.com/css?family=Roboto:400,500,400italic); | @import url(https://fonts.googleapis.com/css?family=Roboto:400,500,400italic); | ||||||
| @import url(https://fonts.googleapis.com/css?family=Roboto+Mono:400,500); | @import url(https://fonts.googleapis.com/css?family=Roboto+Mono:400,500); | ||||||
|  | @import url(https://fonts.googleapis.com/css?family=Montserrat); | ||||||
| @import 'font-awesome'; | @import 'font-awesome'; | ||||||
| 
 | 
 | ||||||
| /* http://meyerweb.com/eric/tools/css/reset/ | /* http://meyerweb.com/eric/tools/css/reset/ | ||||||
|  |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -6,8 +6,10 @@ class Api::V1::NotificationsController < ApiController | ||||||
| 
 | 
 | ||||||
|   respond_to :json |   respond_to :json | ||||||
| 
 | 
 | ||||||
|  |   DEFAULT_NOTIFICATIONS_LIMIT = 15 | ||||||
|  | 
 | ||||||
|   def index |   def index | ||||||
|     @notifications = Notification.where(account: current_account).browserable.paginate_by_max_id(limit_param(15), params[:max_id], params[:since_id]) |     @notifications = Notification.where(account: current_account).browserable.paginate_by_max_id(limit_param(DEFAULT_NOTIFICATIONS_LIMIT), params[:max_id], params[:since_id]) | ||||||
|     @notifications = cache_collection(@notifications, Notification) |     @notifications = cache_collection(@notifications, Notification) | ||||||
|     statuses       = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status) |     statuses       = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status) | ||||||
| 
 | 
 | ||||||
|  | @ -15,7 +17,7 @@ class Api::V1::NotificationsController < ApiController | ||||||
|     set_counters_maps(statuses) |     set_counters_maps(statuses) | ||||||
|     set_account_counters_maps(@notifications.map(&:from_account)) |     set_account_counters_maps(@notifications.map(&:from_account)) | ||||||
| 
 | 
 | ||||||
|     next_path = api_v1_notifications_url(max_id: @notifications.last.id)    if @notifications.size == limit_param(15) |     next_path = api_v1_notifications_url(max_id: @notifications.last.id)    if @notifications.size == limit_param(DEFAULT_NOTIFICATIONS_LIMIT) | ||||||
|     prev_path = api_v1_notifications_url(since_id: @notifications.first.id) unless @notifications.empty? |     prev_path = api_v1_notifications_url(since_id: @notifications.first.id) unless @notifications.empty? | ||||||
| 
 | 
 | ||||||
|     set_pagination_headers(next_path, prev_path) |     set_pagination_headers(next_path, prev_path) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue