Merge remote-tracking branch 'upstream/master'
This commit is contained in:
		
						commit
						4b0e3eb1fb
					
				| @ -53,7 +53,7 @@ export function fetchAccount(id) { | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchAccountTimeline(id) { | ||||
| export function fetchAccountTimeline(id, replace = false) { | ||||
|   return (dispatch, getState) => { | ||||
|     dispatch(fetchAccountTimelineRequest(id)); | ||||
| 
 | ||||
| @ -62,12 +62,12 @@ export function fetchAccountTimeline(id) { | ||||
| 
 | ||||
|     let params = ''; | ||||
| 
 | ||||
|     if (newestId !== null) { | ||||
|     if (newestId !== null && !replace) { | ||||
|       params = `?since_id=${newestId}`; | ||||
|     } | ||||
| 
 | ||||
|     api(getState).get(`/api/v1/accounts/${id}/statuses${params}`).then(response => { | ||||
|       dispatch(fetchAccountTimelineSuccess(id, response.data)); | ||||
|       dispatch(fetchAccountTimelineSuccess(id, response.data, replace)); | ||||
|     }).catch(error => { | ||||
|       dispatch(fetchAccountTimelineFail(id, error)); | ||||
|     }); | ||||
| @ -184,11 +184,12 @@ export function fetchAccountTimelineRequest(id) { | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchAccountTimelineSuccess(id, statuses) { | ||||
| export function fetchAccountTimelineSuccess(id, statuses, replace) { | ||||
|   return { | ||||
|     type: ACCOUNT_TIMELINE_FETCH_SUCCESS, | ||||
|     id: id, | ||||
|     statuses: statuses | ||||
|     statuses: statuses, | ||||
|     replace: replace | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -11,11 +11,12 @@ export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST'; | ||||
| export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS'; | ||||
| export const TIMELINE_EXPAND_FAIL    = 'TIMELINE_EXPAND_FAIL'; | ||||
| 
 | ||||
| export function refreshTimelineSuccess(timeline, statuses) { | ||||
| export function refreshTimelineSuccess(timeline, statuses, replace) { | ||||
|   return { | ||||
|     type: TIMELINE_REFRESH_SUCCESS, | ||||
|     timeline: timeline, | ||||
|     statuses: statuses | ||||
|     statuses: statuses, | ||||
|     replace: replace | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| @ -41,7 +42,7 @@ export function refreshTimelineRequest(timeline) { | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function refreshTimeline(timeline) { | ||||
| export function refreshTimeline(timeline, replace = false) { | ||||
|   return function (dispatch, getState) { | ||||
|     dispatch(refreshTimelineRequest(timeline)); | ||||
| 
 | ||||
| @ -50,12 +51,12 @@ export function refreshTimeline(timeline) { | ||||
| 
 | ||||
|     let params = ''; | ||||
| 
 | ||||
|     if (newestId !== null) { | ||||
|     if (newestId !== null && !replace) { | ||||
|       params = `?since_id=${newestId}`; | ||||
|     } | ||||
| 
 | ||||
|     api(getState).get(`/api/v1/statuses/${timeline}${params}`).then(function (response) { | ||||
|       dispatch(refreshTimelineSuccess(timeline, response.data)); | ||||
|       dispatch(refreshTimelineSuccess(timeline, response.data, replace)); | ||||
|     }).catch(function (error) { | ||||
|       dispatch(refreshTimelineFail(timeline, error)); | ||||
|     }); | ||||
|  | ||||
| @ -0,0 +1,40 @@ | ||||
| import PureRenderMixin from 'react-addons-pure-render-mixin'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|   padding: '15px', | ||||
|   fontSize: '16px', | ||||
|   background: '#2f3441', | ||||
|   flex: '0 0 auto', | ||||
|   cursor: 'pointer', | ||||
|   color: '#2b90d9' | ||||
| }; | ||||
| 
 | ||||
| const iconStyle = { | ||||
|   display: 'inline-block', | ||||
|   marginRight: '5px' | ||||
| }; | ||||
| 
 | ||||
| const ColumnBackButton = React.createClass({ | ||||
| 
 | ||||
|   contextTypes: { | ||||
|     router: React.PropTypes.object | ||||
|   }, | ||||
| 
 | ||||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   handleClick () { | ||||
|     this.context.router.goBack(); | ||||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     return ( | ||||
|       <div onClick={this.handleClick} style={outerStyle}> | ||||
|         <i className='fa fa-fw fa-chevron-left' style={iconStyle} /> | ||||
|         Back | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default ColumnBackButton; | ||||
| @ -54,9 +54,9 @@ const Mastodon = React.createClass({ | ||||
|               return store.dispatch(deleteFromTimelines(data.id)); | ||||
|             case 'merge': | ||||
|             case 'unmerge': | ||||
|               return store.dispatch(refreshTimeline('home')); | ||||
|               return store.dispatch(refreshTimeline('home', true)); | ||||
|             case 'block': | ||||
|               return store.dispatch(refreshTimeline('mentions')); | ||||
|               return store.dispatch(refreshTimeline('mentions', true)); | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -26,16 +26,16 @@ const Header = React.createClass({ | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={{ flex: '0 0 auto', background: '#2f3441', textAlign: 'center', backgroundImage: `url(${account.get('header')})`, backgroundSize: 'cover', position: 'relative' }}> | ||||
|         <div style={{ background: 'rgba(47, 52, 65, 0.8)', padding: '30px 10px' }}> | ||||
|         <div style={{ background: 'rgba(47, 52, 65, 0.8)', padding: '20px 10px' }}> | ||||
|           <a href={account.get('url')} target='_blank' rel='noopener' style={{ display: 'block', color: 'inherit', textDecoration: 'none' }}> | ||||
|             <div style={{ width: '90px', margin: '0 auto', marginBottom: '15px' }}> | ||||
|             <div style={{ width: '90px', margin: '0 auto', marginBottom: '10px' }}> | ||||
|               <img src={account.get('avatar')} alt='' style={{ display: 'block', width: '90px', height: '90px', borderRadius: '90px' }} /> | ||||
|             </div> | ||||
| 
 | ||||
|             <span style={{ display: 'inline-block', color: '#fff', fontSize: '20px', lineHeight: '27px', fontWeight: '500' }}>{displayName}</span> | ||||
|           </a> | ||||
| 
 | ||||
|           <span style={{ fontSize: '14px', fontWeight: '400', display: 'block', color: '#2b90d9', marginBottom: '15px' }}>@{account.get('acct')}</span> | ||||
|           <span style={{ fontSize: '14px', fontWeight: '400', display: 'block', color: '#2b90d9', marginBottom: '10px' }}>@{account.get('acct')}</span> | ||||
|           <p style={{ color: '#616b86', fontSize: '14px' }}>{account.get('note')}</p> | ||||
| 
 | ||||
|           {info} | ||||
|  | ||||
| @ -18,6 +18,7 @@ import { | ||||
| import LoadingIndicator      from '../../components/loading_indicator'; | ||||
| import ActionBar             from './components/action_bar'; | ||||
| import Column                from '../ui/components/column'; | ||||
| import ColumnBackButton      from '../../components/column_back_button'; | ||||
| 
 | ||||
| const mapStateToProps = (state, props) => ({ | ||||
|   account: getAccount(state, Number(props.params.accountId)), | ||||
| @ -74,6 +75,7 @@ const Account = React.createClass({ | ||||
| 
 | ||||
|     return ( | ||||
|       <Column> | ||||
|         <ColumnBackButton /> | ||||
|         <Header account={account} me={me} /> | ||||
| 
 | ||||
|         <ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} /> | ||||
|  | ||||
| @ -16,6 +16,8 @@ import { | ||||
|   getStatusAncestors, | ||||
|   getStatusDescendants | ||||
| }                            from '../../selectors'; | ||||
| import { ScrollContainer }   from 'react-router-scroll'; | ||||
| import ColumnBackButton      from '../../components/column_back_button'; | ||||
| 
 | ||||
| const mapStateToProps = (state, props) => ({ | ||||
|   status: getStatus(state, Number(props.params.statusId)), | ||||
| @ -81,14 +83,18 @@ const Status = React.createClass({ | ||||
| 
 | ||||
|     return ( | ||||
|       <Column> | ||||
|         <div style={{ overflowY: 'scroll', flex: '1 1 auto' }} className='scrollable'> | ||||
|           <div>{this.renderChildren(ancestors)}</div> | ||||
|         <ColumnBackButton /> | ||||
| 
 | ||||
|           <DetailedStatus status={status} me={me} /> | ||||
|           <ActionBar status={status} me={me} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} /> | ||||
|         <ScrollContainer scrollKey='thread'> | ||||
|           <div style={{ overflowY: 'scroll', flex: '1 1 auto' }} className='scrollable'> | ||||
|             <div>{this.renderChildren(ancestors)}</div> | ||||
| 
 | ||||
|           <div>{this.renderChildren(descendants)}</div> | ||||
|         </div> | ||||
|             <DetailedStatus status={status} me={me} /> | ||||
|             <ActionBar status={status} me={me} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} /> | ||||
| 
 | ||||
|             <div>{this.renderChildren(descendants)}</div> | ||||
|           </div> | ||||
|         </ScrollContainer> | ||||
|       </Column> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @ -77,7 +77,7 @@ function normalizeStatus(state, status) { | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| function normalizeTimeline(state, timeline, statuses) { | ||||
| function normalizeTimeline(state, timeline, statuses, replace = false) { | ||||
|   let ids = Immutable.List([]); | ||||
| 
 | ||||
|   statuses.forEach((status, i) => { | ||||
| @ -85,7 +85,7 @@ function normalizeTimeline(state, timeline, statuses) { | ||||
|     ids   = ids.set(i, status.get('id')); | ||||
|   }); | ||||
| 
 | ||||
|   return state.update(timeline, list => list.unshift(...ids)); | ||||
|   return state.update(timeline, list => (replace ? ids : list.unshift(...ids))); | ||||
| }; | ||||
| 
 | ||||
| function appendNormalizedTimeline(state, timeline, statuses) { | ||||
| @ -99,7 +99,7 @@ function appendNormalizedTimeline(state, timeline, statuses) { | ||||
|   return state.update(timeline, list => list.push(...moreIds)); | ||||
| }; | ||||
| 
 | ||||
| function normalizeAccountTimeline(state, accountId, statuses) { | ||||
| function normalizeAccountTimeline(state, accountId, statuses, replace = false) { | ||||
|   let ids = Immutable.List([]); | ||||
| 
 | ||||
|   statuses.forEach((status, i) => { | ||||
| @ -107,7 +107,7 @@ function normalizeAccountTimeline(state, accountId, statuses) { | ||||
|     ids   = ids.set(i, status.get('id')); | ||||
|   }); | ||||
| 
 | ||||
|   return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => list.unshift(...ids)); | ||||
|   return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => (replace ? ids : list.unshift(...ids))); | ||||
| }; | ||||
| 
 | ||||
| function appendNormalizedAccountTimeline(state, accountId, statuses) { | ||||
| @ -217,7 +217,7 @@ function normalizeSuggestions(state, accounts) { | ||||
| export default function timelines(state = initialState, action) { | ||||
|   switch(action.type) { | ||||
|     case TIMELINE_REFRESH_SUCCESS: | ||||
|       return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses)); | ||||
|       return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.replace); | ||||
|     case TIMELINE_EXPAND_SUCCESS: | ||||
|       return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses)); | ||||
|     case TIMELINE_UPDATE: | ||||
| @ -243,7 +243,7 @@ export default function timelines(state = initialState, action) { | ||||
|     case STATUS_FETCH_SUCCESS: | ||||
|       return normalizeContext(state, Immutable.fromJS(action.status), Immutable.fromJS(action.context.ancestors), Immutable.fromJS(action.context.descendants)); | ||||
|     case ACCOUNT_TIMELINE_FETCH_SUCCESS: | ||||
|       return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses)); | ||||
|       return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace); | ||||
|     case ACCOUNT_TIMELINE_EXPAND_SUCCESS: | ||||
|       return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses)); | ||||
|     case SUGGESTIONS_FETCH_SUCCESS: | ||||
|  | ||||
| @ -37,7 +37,7 @@ class ApiController < ApplicationController | ||||
|   end | ||||
| 
 | ||||
|   def set_maps(statuses) | ||||
|     status_ids      = statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact | ||||
|     status_ids      = statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact.uniq | ||||
|     @reblogs_map    = Status.reblogs_map(status_ids, current_user.account) | ||||
|     @favourites_map = Status.favourites_map(status_ids, current_user.account) | ||||
|   end | ||||
|  | ||||
| @ -8,13 +8,12 @@ class Feed | ||||
|     max_id     = '+inf' if max_id.blank? | ||||
|     since_id   = '-inf' if since_id.blank? | ||||
|     unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).collect(&:last).map(&:to_i) | ||||
|     status_map = {} | ||||
| 
 | ||||
|     # If we're after most recent items and none are there, we need to precompute the feed | ||||
|     if unhydrated.empty? && max_id == '+inf' | ||||
|     if unhydrated.empty? && max_id == '+inf' && since_id == '-inf' | ||||
|       PrecomputeFeedService.new.call(@type, @account, limit) | ||||
|     else | ||||
|       Status.where(id: unhydrated).with_includes.with_counters.each { |status| status_map[status.id] = status } | ||||
|       status_map = Status.where(id: unhydrated).with_includes.with_counters.map { |status| [status.id, status] }.to_h | ||||
|       unhydrated.map { |id| status_map[id] }.compact | ||||
|     end | ||||
|   end | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user