Adding following/followers lists to the UI
This commit is contained in:
		
							parent
							
								
									909d0d5e88
								
							
						
					
					
						commit
						1c84d505c8
					
				| @ -32,6 +32,14 @@ export const ACCOUNT_TIMELINE_EXPAND_REQUEST = 'ACCOUNT_TIMELINE_EXPAND_REQUEST' | ||||
| export const ACCOUNT_TIMELINE_EXPAND_SUCCESS = 'ACCOUNT_TIMELINE_EXPAND_SUCCESS'; | ||||
| export const ACCOUNT_TIMELINE_EXPAND_FAIL    = 'ACCOUNT_TIMELINE_EXPAND_FAIL'; | ||||
| 
 | ||||
| export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST'; | ||||
| export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS'; | ||||
| export const FOLLOWERS_FETCH_FAIL    = 'FOLLOWERS_FETCH_FAIL'; | ||||
| 
 | ||||
| export const FOLLOWING_FETCH_REQUEST = 'FOLLOWING_FETCH_REQUEST'; | ||||
| export const FOLLOWING_FETCH_SUCCESS = 'FOLLOWING_FETCH_SUCCESS'; | ||||
| export const FOLLOWING_FETCH_FAIL    = 'FOLLOWING_FETCH_FAIL'; | ||||
| 
 | ||||
| export function setAccountSelf(account) { | ||||
|   return { | ||||
|     type: ACCOUNT_SET_SELF, | ||||
| @ -289,3 +297,73 @@ export function unblockAccountFail(error) { | ||||
|     error: error | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchFollowers(id) { | ||||
|   return (dispatch, getState) => { | ||||
|     dispatch(fetchFollowersRequest(id)); | ||||
| 
 | ||||
|     api(getState).get(`/api/v1/accounts/${id}/followers`).then(response => { | ||||
|       dispatch(fetchFollowersSuccess(id, response.data)); | ||||
|     }).catch(error => { | ||||
|       dispatch(fetchFollowersFail(id, error)); | ||||
|     }); | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchFollowersRequest(id) { | ||||
|   return { | ||||
|     type: FOLLOWERS_FETCH_REQUEST, | ||||
|     id: id | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchFollowersSuccess(id, accounts) { | ||||
|   return { | ||||
|     type: FOLLOWERS_FETCH_SUCCESS, | ||||
|     id: id, | ||||
|     accounts: accounts | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchFollowersFail(id, error) { | ||||
|   return { | ||||
|     type: FOLLOWERS_FETCH_FAIL, | ||||
|     id: id, | ||||
|     error: error | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchFollowing(id) { | ||||
|   return (dispatch, getState) => { | ||||
|     dispatch(fetchFollowingRequest(id)); | ||||
| 
 | ||||
|     api(getState).get(`/api/v1/accounts/${id}/following`).then(response => { | ||||
|       dispatch(fetchFollowingSuccess(id, response.data)); | ||||
|     }).catch(error => { | ||||
|       dispatch(fetchFollowingFail(id, error)); | ||||
|     }); | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchFollowingRequest(id) { | ||||
|   return { | ||||
|     type: FOLLOWING_FETCH_REQUEST, | ||||
|     id: id | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchFollowingSuccess(id, accounts) { | ||||
|   return { | ||||
|     type: FOLLOWING_FETCH_SUCCESS, | ||||
|     id: id, | ||||
|     accounts: accounts | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchFollowingFail(id, error) { | ||||
|   return { | ||||
|     type: FOLLOWING_FETCH_FAIL, | ||||
|     id: id, | ||||
|     error: error | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| @ -22,10 +22,10 @@ export function fetchSuggestionsRequest() { | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function fetchSuggestionsSuccess(suggestions) { | ||||
| export function fetchSuggestionsSuccess(accounts) { | ||||
|   return { | ||||
|     type: SUGGESTIONS_FETCH_SUCCESS, | ||||
|     suggestions: suggestions | ||||
|     accounts: accounts | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -26,6 +26,8 @@ import AccountTimeline    from '../features/account_timeline'; | ||||
| import HomeTimeline       from '../features/home_timeline'; | ||||
| import MentionsTimeline   from '../features/mentions_timeline'; | ||||
| import Compose            from '../features/compose'; | ||||
| import Followers          from '../features/followers'; | ||||
| import Following          from '../features/following'; | ||||
| 
 | ||||
| const store = configureStore(); | ||||
| 
 | ||||
| @ -83,6 +85,8 @@ const Mastodon = React.createClass({ | ||||
|             <Route path='/statuses/:statusId' component={Status} /> | ||||
|             <Route path='/accounts/:accountId' component={Account}> | ||||
|               <IndexRoute component={AccountTimeline} /> | ||||
|               <Route path='/accounts/:accountId/followers' component={Followers} /> | ||||
|               <Route path='/accounts/:accountId/following' component={Following} /> | ||||
|             </Route> | ||||
|           </Route> | ||||
|         </Router> | ||||
|  | ||||
| @ -1,6 +1,27 @@ | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import DropdownMenu       from '../../../components/dropdown_menu'; | ||||
| import { Link }           from 'react-router'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|   borderTop: '1px solid #363c4b', | ||||
|   borderBottom: '1px solid #363c4b', | ||||
|   lineHeight: '36px', | ||||
|   overflow: 'hidden', | ||||
|   flex: '0 0 auto', | ||||
|   display: 'flex' | ||||
| }; | ||||
| 
 | ||||
| const outerDropdownStyle = { | ||||
|   padding: '10px', | ||||
|   flex: '1 1 auto' | ||||
| }; | ||||
| 
 | ||||
| const outerLinksStyle = { | ||||
|   flex: '1 1 auto', | ||||
|   display: 'flex', | ||||
|   lineHeight: '18px' | ||||
| }; | ||||
| 
 | ||||
| const ActionBar = React.createClass({ | ||||
| 
 | ||||
| @ -34,26 +55,26 @@ const ActionBar = React.createClass({ | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={{ borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', lineHeight: '36px', overflow: 'hidden', flex: '0 0 auto', display: 'flex' }}> | ||||
|         <div style={{ padding: '10px', flex: '1 1 auto' }}> | ||||
|       <div style={outerStyle}> | ||||
|         <div style={outerDropdownStyle}> | ||||
|           <DropdownMenu items={menu} icon='bars' size={24} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div style={{ flex: '1 1 auto', display: 'flex', lineHeight: '18px' }}> | ||||
|           <div style={{ overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px', paddingRight: '5px' }}> | ||||
|         <div style={outerLinksStyle}> | ||||
|           <Link to={`/accounts/${account.get('id')}`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px', paddingRight: '5px' }}> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Posts</span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('statuses_count')}</span> | ||||
|           </div> | ||||
|           </Link> | ||||
| 
 | ||||
|           <div style={{ overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px 5px' }}> | ||||
|           <Link to={`/accounts/${account.get('id')}/following`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', borderLeft: '1px solid #363c4b', padding: '10px 5px' }}> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Follows</span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('following_count')}</span> | ||||
|           </div> | ||||
|           </Link> | ||||
| 
 | ||||
|           <div style={{ overflow: 'hidden', width: '80px', padding: '10px 5px', borderLeft: '1px solid #363c4b' }}> | ||||
|           <Link to={`/accounts/${account.get('id')}/followers`} style={{ textDecoration: 'none', overflow: 'hidden', width: '80px', padding: '10px 5px', borderLeft: '1px solid #363c4b' }}> | ||||
|             <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Followers</span> | ||||
|             <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('followers_count')}</span> | ||||
|           </div> | ||||
|           </Link> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|  | ||||
| @ -14,17 +14,23 @@ import { mentionCompose }    from '../../actions/compose'; | ||||
| import Header                from './components/header'; | ||||
| import { | ||||
|   getAccountTimeline, | ||||
|   getAccount | ||||
|   makeGetAccount | ||||
| }                            from '../../selectors'; | ||||
| 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)), | ||||
|   me: state.getIn(['timelines', 'me']) | ||||
| }); | ||||
| const makeMapStateToProps = () => { | ||||
|   const getAccount = makeGetAccount(); | ||||
| 
 | ||||
|   const mapStateToProps = (state, props) => ({ | ||||
|     account: getAccount(state, Number(props.params.accountId)), | ||||
|     me: state.getIn(['timelines', 'me']) | ||||
|   }); | ||||
| 
 | ||||
|   return mapStateToProps; | ||||
| }; | ||||
| 
 | ||||
| const Account = React.createClass({ | ||||
| 
 | ||||
| @ -92,4 +98,4 @@ const Account = React.createClass({ | ||||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps)(Account); | ||||
| export default connect(makeMapStateToProps)(Account); | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import Avatar             from '../../../components/avatar'; | ||||
| import DisplayName        from '../../../components/display_name'; | ||||
| import { Link }           from 'react-router'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|  | ||||
| @ -0,0 +1,66 @@ | ||||
| import PureRenderMixin    from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import Avatar             from '../../../components/avatar'; | ||||
| import { Link }           from 'react-router'; | ||||
| 
 | ||||
| const outerStyle = { | ||||
|   padding: '10px' | ||||
| }; | ||||
| 
 | ||||
| const displayNameStyle = { | ||||
|   display: 'block', | ||||
|   fontWeight: '500', | ||||
|   overflow: 'hidden', | ||||
|   textOverflow: 'ellipsis', | ||||
|   color: '#fff' | ||||
| }; | ||||
| 
 | ||||
| const acctStyle = { | ||||
|   display: 'block', | ||||
|   overflow: 'hidden', | ||||
|   textOverflow: 'ellipsis' | ||||
| }; | ||||
| 
 | ||||
| const itemStyle = { | ||||
|   display: 'block', | ||||
|   color: '#9baec8', | ||||
|   overflow: 'hidden', | ||||
|   textDecoration: 'none' | ||||
| }; | ||||
| 
 | ||||
| const Account = React.createClass({ | ||||
| 
 | ||||
|   propTypes: { | ||||
|     account: ImmutablePropTypes.map.isRequired, | ||||
|     me: React.PropTypes.number.isRequired | ||||
|   }, | ||||
| 
 | ||||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   render () { | ||||
|     const { account } = this.props; | ||||
| 
 | ||||
|     if (!account) { | ||||
|       return <div />; | ||||
|     } | ||||
| 
 | ||||
|     let displayName = account.get('display_name'); | ||||
| 
 | ||||
|     if (displayName.length === 0) { | ||||
|       displayName = account.get('username'); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <div style={outerStyle}> | ||||
|         <Link key={account.get('id')} style={itemStyle} to={`/accounts/${account.get('id')}`}> | ||||
|           <div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} size={36} /></div> | ||||
|           <strong style={displayNameStyle}>{displayName}</strong> | ||||
|           <span style={acctStyle}>{account.get('acct')}</span> | ||||
|         </Link> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default Account; | ||||
| @ -0,0 +1,20 @@ | ||||
| import { connect }            from 'react-redux'; | ||||
| import { makeGetAccount }     from '../../../selectors'; | ||||
| import Account                from '../components/account'; | ||||
| 
 | ||||
| const makeMapStateToProps = () => { | ||||
|   const getAccount = makeGetAccount(); | ||||
| 
 | ||||
|   const mapStateToProps = (state, props) => ({ | ||||
|     account: getAccount(state, props.id), | ||||
|     me: state.getIn(['timelines', 'me']) | ||||
|   }); | ||||
| 
 | ||||
|   return mapStateToProps; | ||||
| }; | ||||
| 
 | ||||
| const mapDispatchToProps = (dispatch) => ({ | ||||
|   // | ||||
| }); | ||||
| 
 | ||||
| export default connect(makeMapStateToProps, mapDispatchToProps)(Account); | ||||
| @ -0,0 +1,51 @@ | ||||
| import { connect }            from 'react-redux'; | ||||
| import PureRenderMixin        from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes     from 'react-immutable-proptypes'; | ||||
| import LoadingIndicator       from '../../components/loading_indicator'; | ||||
| import { fetchFollowers }     from '../../actions/accounts'; | ||||
| import { ScrollContainer }    from 'react-router-scroll'; | ||||
| import AccountContainer       from './containers/account_container'; | ||||
| 
 | ||||
| const mapStateToProps = (state, props) => ({ | ||||
|   accountIds: state.getIn(['user_lists', 'followers', Number(props.params.accountId)]) | ||||
| }); | ||||
| 
 | ||||
| const Followers = React.createClass({ | ||||
| 
 | ||||
|   propTypes: { | ||||
|     params: React.PropTypes.object.isRequired, | ||||
|     dispatch: React.PropTypes.func.isRequired, | ||||
|     accountIds: ImmutablePropTypes.list | ||||
|   }, | ||||
| 
 | ||||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   componentWillMount () { | ||||
|     this.props.dispatch(fetchFollowers(Number(this.props.params.accountId))); | ||||
|   }, | ||||
| 
 | ||||
|   componentWillReceiveProps(nextProps) { | ||||
|     if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) { | ||||
|       this.props.dispatch(fetchFollowers(Number(nextProps.params.accountId))); | ||||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { accountIds } = this.props; | ||||
| 
 | ||||
|     if (!accountIds) { | ||||
|       return <LoadingIndicator />; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <ScrollContainer scrollKey='followers'> | ||||
|         <div style={{ overflowY: 'scroll', flex: '1 1 auto', overflowX: 'hidden' }} className='scrollable'> | ||||
|           {accountIds.map(id => <AccountContainer key={id} id={id} />)} | ||||
|         </div> | ||||
|       </ScrollContainer> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps)(Followers); | ||||
| @ -0,0 +1,51 @@ | ||||
| import { connect }            from 'react-redux'; | ||||
| import PureRenderMixin        from 'react-addons-pure-render-mixin'; | ||||
| import ImmutablePropTypes     from 'react-immutable-proptypes'; | ||||
| import LoadingIndicator       from '../../components/loading_indicator'; | ||||
| import { fetchFollowing }     from '../../actions/accounts'; | ||||
| import { ScrollContainer }    from 'react-router-scroll'; | ||||
| import AccountContainer       from '../followers/containers/account_container'; | ||||
| 
 | ||||
| const mapStateToProps = (state, props) => ({ | ||||
|   accountIds: state.getIn(['user_lists', 'following', Number(props.params.accountId)]) | ||||
| }); | ||||
| 
 | ||||
| const Following = React.createClass({ | ||||
| 
 | ||||
|   propTypes: { | ||||
|     params: React.PropTypes.object.isRequired, | ||||
|     dispatch: React.PropTypes.func.isRequired, | ||||
|     accountIds: ImmutablePropTypes.list | ||||
|   }, | ||||
| 
 | ||||
|   mixins: [PureRenderMixin], | ||||
| 
 | ||||
|   componentWillMount () { | ||||
|     this.props.dispatch(fetchFollowing(Number(this.props.params.accountId))); | ||||
|   }, | ||||
| 
 | ||||
|   componentWillReceiveProps(nextProps) { | ||||
|     if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) { | ||||
|       this.props.dispatch(fetchFollowing(Number(nextProps.params.accountId))); | ||||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   render () { | ||||
|     const { accountIds } = this.props; | ||||
| 
 | ||||
|     if (!accountIds) { | ||||
|       return <LoadingIndicator />; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <ScrollContainer scrollKey='following'> | ||||
|         <div style={{ overflowY: 'scroll', flex: '1 1 auto', overflowX: 'hidden' }} className='scrollable'> | ||||
|           {accountIds.map(id => <AccountContainer key={id} id={id} />)} | ||||
|         </div> | ||||
|       </ScrollContainer> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| }); | ||||
| 
 | ||||
| export default connect(mapStateToProps)(Following); | ||||
| @ -6,7 +6,6 @@ const GettingStarted = () => { | ||||
|     <Column> | ||||
|       <div className='static-content'> | ||||
|         <h1>Getting started</h1> | ||||
|         <p>Mastodon is still in development and one of the lacking areas at the moment is user discovery.</p> | ||||
|         <p>You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the form in the bottom of the sidebar.</p> | ||||
|         <p>If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.</p> | ||||
|         <p>The developer of this project can be followed as Gargron@mastodon.social</p> | ||||
|  | ||||
| @ -6,6 +6,8 @@ import follow                from './follow'; | ||||
| import notifications         from './notifications'; | ||||
| import { loadingBarReducer } from 'react-redux-loading-bar'; | ||||
| import modal                 from './modal'; | ||||
| import user_lists            from './user_lists'; | ||||
| import suggestions           from './suggestions'; | ||||
| 
 | ||||
| export default combineReducers({ | ||||
|   timelines, | ||||
| @ -15,4 +17,6 @@ export default combineReducers({ | ||||
|   notifications, | ||||
|   loadingBar: loadingBarReducer, | ||||
|   modal, | ||||
|   user_lists, | ||||
|   suggestions | ||||
| }); | ||||
|  | ||||
							
								
								
									
										13
									
								
								app/assets/javascripts/components/reducers/suggestions.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								app/assets/javascripts/components/reducers/suggestions.jsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions'; | ||||
| import Immutable                     from 'immutable'; | ||||
| 
 | ||||
| const initialState = Immutable.List(); | ||||
| 
 | ||||
| export default function suggestions(state = initialState, action) { | ||||
|   switch(action.type) { | ||||
|     case SUGGESTIONS_FETCH_SUCCESS: | ||||
|       return Immutable.List(action.accounts.map(item => item.id)); | ||||
|     default: | ||||
|       return state; | ||||
|   } | ||||
| } | ||||
| @ -18,7 +18,9 @@ import { | ||||
|   ACCOUNT_BLOCK_SUCCESS, | ||||
|   ACCOUNT_UNBLOCK_SUCCESS, | ||||
|   ACCOUNT_TIMELINE_FETCH_SUCCESS, | ||||
|   ACCOUNT_TIMELINE_EXPAND_SUCCESS | ||||
|   ACCOUNT_TIMELINE_EXPAND_SUCCESS, | ||||
|   FOLLOWERS_FETCH_SUCCESS, | ||||
|   FOLLOWING_FETCH_SUCCESS | ||||
| }                                from '../actions/accounts'; | ||||
| import { | ||||
|   STATUS_FETCH_SUCCESS, | ||||
| @ -206,12 +208,12 @@ function normalizeContext(state, status, ancestors, descendants) { | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| function normalizeSuggestions(state, accounts) { | ||||
| function normalizeAccounts(state, accounts) { | ||||
|   accounts.forEach(account => { | ||||
|     state = state.setIn(['accounts', account.get('id')], account); | ||||
|   }); | ||||
| 
 | ||||
|   return state.set('suggestions', accounts.map(account => account.get('id'))); | ||||
|   return state; | ||||
| }; | ||||
| 
 | ||||
| export default function timelines(state = initialState, action) { | ||||
| @ -247,7 +249,9 @@ export default function timelines(state = initialState, action) { | ||||
|     case ACCOUNT_TIMELINE_EXPAND_SUCCESS: | ||||
|       return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses)); | ||||
|     case SUGGESTIONS_FETCH_SUCCESS: | ||||
|       return normalizeSuggestions(state, Immutable.fromJS(action.suggestions)); | ||||
|     case FOLLOWERS_FETCH_SUCCESS: | ||||
|     case FOLLOWING_FETCH_SUCCESS: | ||||
|       return normalizeAccounts(state, Immutable.fromJS(action.accounts)); | ||||
|     default: | ||||
|       return state; | ||||
|   } | ||||
|  | ||||
							
								
								
									
										21
									
								
								app/assets/javascripts/components/reducers/user_lists.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/assets/javascripts/components/reducers/user_lists.jsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| import { | ||||
|   FOLLOWERS_FETCH_SUCCESS, | ||||
|   FOLLOWING_FETCH_SUCCESS | ||||
| }                          from '../actions/accounts'; | ||||
| import Immutable           from 'immutable'; | ||||
| 
 | ||||
| const initialState = Immutable.Map({ | ||||
|   followers: Immutable.Map(), | ||||
|   following: Immutable.Map() | ||||
| }); | ||||
| 
 | ||||
| export default function userLists(state = initialState, action) { | ||||
|   switch(action.type) { | ||||
|     case FOLLOWERS_FETCH_SUCCESS: | ||||
|       return state.setIn(['followers', action.id], Immutable.List(action.accounts.map(item => item.id))); | ||||
|     case FOLLOWING_FETCH_SUCCESS: | ||||
|       return state.setIn(['following', action.id], Immutable.List(action.accounts.map(item => item.id))); | ||||
|     default: | ||||
|       return state; | ||||
|   } | ||||
| }; | ||||
| @ -7,13 +7,15 @@ const getAccounts = state => state.getIn(['timelines', 'accounts']); | ||||
| const getAccountBase         = (state, id) => state.getIn(['timelines', 'accounts', id], null); | ||||
| const getAccountRelationship = (state, id) => state.getIn(['timelines', 'relationships', id]); | ||||
| 
 | ||||
| export const getAccount = createSelector([getAccountBase, getAccountRelationship], (base, relationship) => { | ||||
|   if (base === null) { | ||||
|     return null; | ||||
|   } | ||||
| export const makeGetAccount = () => { | ||||
|   return createSelector([getAccountBase, getAccountRelationship], (base, relationship) => { | ||||
|     if (base === null) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|   return base.set('relationship', relationship); | ||||
| }); | ||||
|     return base.set('relationship', relationship); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| const getStatusBase = (state, id) => state.getIn(['timelines', 'statuses', id], null); | ||||
| 
 | ||||
| @ -65,7 +67,7 @@ export const getNotifications = createSelector([getNotificationsBase], (base) => | ||||
|   return arr; | ||||
| }); | ||||
| 
 | ||||
| const getSuggestionsBase = (state) => state.getIn(['timelines', 'suggestions']); | ||||
| const getSuggestionsBase = (state) => state.get('suggestions'); | ||||
| 
 | ||||
| export const getSuggestions = createSelector([getSuggestionsBase, getAccounts], (base, accounts) => { | ||||
|   return base.map(accountId => accounts.get(accountId)); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user