Replacing follow requests in the settings area with in-UI column
This commit is contained in:
		
					parent
					
						
							
								004382e4d0
							
						
					
				
			
			
				commit
				
					
						3689c119f0
					
				
			
		
					 17 changed files with 334 additions and 87 deletions
				
			
		|  | @ -51,6 +51,22 @@ export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST'; | ||||||
| export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS'; | export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS'; | ||||||
| export const RELATIONSHIPS_FETCH_FAIL    = 'RELATIONSHIPS_FETCH_FAIL'; | export const RELATIONSHIPS_FETCH_FAIL    = 'RELATIONSHIPS_FETCH_FAIL'; | ||||||
| 
 | 
 | ||||||
|  | export const FOLLOW_REQUESTS_FETCH_REQUEST = 'FOLLOW_REQUESTS_FETCH_REQUEST'; | ||||||
|  | export const FOLLOW_REQUESTS_FETCH_SUCCESS = 'FOLLOW_REQUESTS_FETCH_SUCCESS'; | ||||||
|  | export const FOLLOW_REQUESTS_FETCH_FAIL    = 'FOLLOW_REQUESTS_FETCH_FAIL'; | ||||||
|  | 
 | ||||||
|  | export const FOLLOW_REQUESTS_EXPAND_REQUEST = 'FOLLOW_REQUESTS_EXPAND_REQUEST'; | ||||||
|  | export const FOLLOW_REQUESTS_EXPAND_SUCCESS = 'FOLLOW_REQUESTS_EXPAND_SUCCESS'; | ||||||
|  | export const FOLLOW_REQUESTS_EXPAND_FAIL    = 'FOLLOW_REQUESTS_EXPAND_FAIL'; | ||||||
|  | 
 | ||||||
|  | export const FOLLOW_REQUEST_AUTHORIZE_REQUEST = 'FOLLOW_REQUEST_AUTHORIZE_REQUEST'; | ||||||
|  | export const FOLLOW_REQUEST_AUTHORIZE_SUCCESS = 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS'; | ||||||
|  | export const FOLLOW_REQUEST_AUTHORIZE_FAIL    = 'FOLLOW_REQUEST_AUTHORIZE_FAIL'; | ||||||
|  | 
 | ||||||
|  | export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST'; | ||||||
|  | export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS'; | ||||||
|  | export const FOLLOW_REQUEST_REJECT_FAIL    = 'FOLLOW_REQUEST_REJECT_FAIL'; | ||||||
|  | 
 | ||||||
| export function setAccountSelf(account) { | export function setAccountSelf(account) { | ||||||
|   return { |   return { | ||||||
|     type: ACCOUNT_SET_SELF, |     type: ACCOUNT_SET_SELF, | ||||||
|  | @ -509,3 +525,140 @@ export function fetchRelationshipsFail(error) { | ||||||
|     error |     error | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | export function fetchFollowRequests() { | ||||||
|  |   return (dispatch, getState) => { | ||||||
|  |     dispatch(fetchFollowRequestsRequest()); | ||||||
|  | 
 | ||||||
|  |     api(getState).get('/api/v1/follow_requests').then(response => { | ||||||
|  |       const next = getLinks(response).refs.find(link => link.rel === 'next'); | ||||||
|  |       dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null)) | ||||||
|  |     }).catch(error => dispatch(fetchFollowRequestsFail(error))); | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function fetchFollowRequestsRequest() { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUESTS_FETCH_REQUEST | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function fetchFollowRequestsSuccess(accounts, next) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUESTS_FETCH_SUCCESS, | ||||||
|  |     accounts, | ||||||
|  |     next | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function fetchFollowRequestsFail(error) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUESTS_FETCH_FAIL, | ||||||
|  |     error | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function expandFollowRequests() { | ||||||
|  |   return (dispatch, getState) => { | ||||||
|  |     const url = getState().getIn(['user_lists', 'follow_requests', 'next']); | ||||||
|  | 
 | ||||||
|  |     if (url === null) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     dispatch(expandFollowRequestsRequest()); | ||||||
|  | 
 | ||||||
|  |     api(getState).get(url).then(response => { | ||||||
|  |       const next = getLinks(response).refs.find(link => link.rel === 'next'); | ||||||
|  |       dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null)) | ||||||
|  |     }).catch(error => dispatch(expandFollowRequestsFail(error))); | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function expandFollowRequestsRequest() { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUESTS_EXPAND_REQUEST | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function expandFollowRequestsSuccess(accounts, next) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUESTS_EXPAND_SUCCESS, | ||||||
|  |     accounts, | ||||||
|  |     next | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function expandFollowRequestsFail(error) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUESTS_EXPAND_FAIL, | ||||||
|  |     error | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function authorizeFollowRequest(id) { | ||||||
|  |   return (dispatch, getState) => { | ||||||
|  |     dispatch(authorizeFollowRequestRequest(id)); | ||||||
|  | 
 | ||||||
|  |     api(getState) | ||||||
|  |       .post(`/api/v1/follow_requests/${id}/authorize`) | ||||||
|  |       .then(response => dispatch(authorizeFollowRequestSuccess(id))) | ||||||
|  |       .catch(error => dispatch(authorizeFollowRequestFail(id, error))); | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function authorizeFollowRequestRequest(id) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUEST_AUTHORIZE_REQUEST, | ||||||
|  |     id | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function authorizeFollowRequestSuccess(id) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS, | ||||||
|  |     id | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function authorizeFollowRequestFail(id, error) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUEST_AUTHORIZE_FAIL, | ||||||
|  |     id, | ||||||
|  |     error | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export function rejectFollowRequest(id) { | ||||||
|  |   return (dispatch, getState) => { | ||||||
|  |     dispatch(rejectFollowRequestRequest(id)); | ||||||
|  | 
 | ||||||
|  |     api(getState) | ||||||
|  |       .post(`/api/v1/follow_requests/${id}/reject`) | ||||||
|  |       .then(response => dispatch(rejectFollowRequestSuccess(id))) | ||||||
|  |       .catch(error => dispatch(rejectFollowRequestFail(id, error))); | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function rejectFollowRequestRequest(id) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUEST_REJECT_REQUEST, | ||||||
|  |     id | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function rejectFollowRequestSuccess(id) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUEST_REJECT_SUCCESS, | ||||||
|  |     id | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export function rejectFollowRequestFail(id, error) { | ||||||
|  |   return { | ||||||
|  |     type: FOLLOW_REQUEST_REJECT_FAIL, | ||||||
|  |     id, | ||||||
|  |     error | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @ -27,11 +27,11 @@ const StatusList = React.createClass({ | ||||||
| 
 | 
 | ||||||
|     this._oldScrollPosition = scrollHeight - scrollTop; |     this._oldScrollPosition = scrollHeight - scrollTop; | ||||||
| 
 | 
 | ||||||
|     if (scrollTop === scrollHeight - clientHeight) { |     if (scrollTop === scrollHeight - clientHeight && this.props.onScrollToBottom) { | ||||||
|       this.props.onScrollToBottom(); |       this.props.onScrollToBottom(); | ||||||
|     } else if (scrollTop < 100) { |     } else if (scrollTop < 100 && this.props.onScrollToTop) { | ||||||
|       this.props.onScrollToTop(); |       this.props.onScrollToTop(); | ||||||
|     } else { |     } else if (this.props.onScroll) { | ||||||
|       this.props.onScroll(); |       this.props.onScroll(); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ import Reblogs from '../features/reblogs'; | ||||||
| import Favourites from '../features/favourites'; | import Favourites from '../features/favourites'; | ||||||
| import HashtagTimeline from '../features/hashtag_timeline'; | import HashtagTimeline from '../features/hashtag_timeline'; | ||||||
| import Notifications from '../features/notifications'; | import Notifications from '../features/notifications'; | ||||||
|  | import FollowRequests from '../features/follow_requests'; | ||||||
| import { IntlProvider, addLocaleData } from 'react-intl'; | import { IntlProvider, addLocaleData } from 'react-intl'; | ||||||
| import en from 'react-intl/locale-data/en'; | import en from 'react-intl/locale-data/en'; | ||||||
| import de from 'react-intl/locale-data/de'; | import de from 'react-intl/locale-data/de'; | ||||||
|  | @ -125,6 +126,8 @@ const Mastodon = React.createClass({ | ||||||
|                 <Route path='followers' component={Followers} /> |                 <Route path='followers' component={Followers} /> | ||||||
|                 <Route path='following' component={Following} /> |                 <Route path='following' component={Following} /> | ||||||
|               </Route> |               </Route> | ||||||
|  | 
 | ||||||
|  |               <Route path='follow_requests' component={FollowRequests} /> | ||||||
|             </Route> |             </Route> | ||||||
|           </Router> |           </Router> | ||||||
|         </Provider> |         </Provider> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,61 @@ | ||||||
|  | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
|  | import Permalink from '../../../components/permalink'; | ||||||
|  | import Avatar from '../../../components/avatar'; | ||||||
|  | import DisplayName from '../../../components/display_name'; | ||||||
|  | import emojify from '../../../emoji'; | ||||||
|  | import IconButton from '../../../components/icon_button'; | ||||||
|  | import { defineMessages, injectIntl } from 'react-intl'; | ||||||
|  | 
 | ||||||
|  | const messages = defineMessages({ | ||||||
|  |   authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' }, | ||||||
|  |   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const outerStyle = { | ||||||
|  |   padding: '14px 10px' | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const panelStyle = { | ||||||
|  |   background: '#2f3441', | ||||||
|  |   display: 'flex', | ||||||
|  |   flexDirection: 'row', | ||||||
|  |   borderTop: '1px solid #363c4b', | ||||||
|  |   borderBottom: '1px solid #363c4b', | ||||||
|  |   padding: '10px 0' | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const btnStyle = { | ||||||
|  |   flex: '1 1 auto', | ||||||
|  |   textAlign: 'center' | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => { | ||||||
|  |   const content = { __html: emojify(account.get('note')) }; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div> | ||||||
|  |       <div style={outerStyle}> | ||||||
|  |         <Permalink href={account.get('url')} className='detailed-status__display-name' style={{ display: 'block', overflow: 'hidden', marginBottom: '15px' }}> | ||||||
|  |           <div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} size={48} /></div> | ||||||
|  |           <DisplayName account={account} /> | ||||||
|  |         </Permalink> | ||||||
|  | 
 | ||||||
|  |         <div style={{ color: '#616b86', fontSize: '14px' }} className='account__header__content' dangerouslySetInnerHTML={content} /> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <div style={panelStyle}> | ||||||
|  |         <div style={btnStyle}><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div> | ||||||
|  |         <div style={btnStyle}><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ) | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AccountAuthorize.propTypes = { | ||||||
|  |   account: ImmutablePropTypes.map.isRequired, | ||||||
|  |   onAuthorize: React.PropTypes.func.isRequired, | ||||||
|  |   onReject: React.PropTypes.func.isRequired, | ||||||
|  |   intl: React.PropTypes.object.isRequired | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export default injectIntl(AccountAuthorize); | ||||||
|  | @ -0,0 +1,26 @@ | ||||||
|  | import { connect } from 'react-redux'; | ||||||
|  | import { makeGetAccount } from '../../../selectors'; | ||||||
|  | import AccountAuthorize from '../components/account_authorize'; | ||||||
|  | import { authorizeFollowRequest, rejectFollowRequest } from '../../../actions/accounts'; | ||||||
|  | 
 | ||||||
|  | const makeMapStateToProps = () => { | ||||||
|  |   const getAccount = makeGetAccount(); | ||||||
|  | 
 | ||||||
|  |   const mapStateToProps = (state, props) => ({ | ||||||
|  |     account: getAccount(state, props.id) | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   return mapStateToProps; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const mapDispatchToProps = (dispatch, { id }) => ({ | ||||||
|  |   onAuthorize (account) { | ||||||
|  |     dispatch(authorizeFollowRequest(id)); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   onReject (account) { | ||||||
|  |     dispatch(rejectFollowRequest(id)); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export default connect(makeMapStateToProps, mapDispatchToProps)(AccountAuthorize); | ||||||
|  | @ -0,0 +1,66 @@ | ||||||
|  | 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 { ScrollContainer } from 'react-router-scroll'; | ||||||
|  | import Column from '../ui/components/column'; | ||||||
|  | import AccountAuthorizeContainer from './containers/account_authorize_container'; | ||||||
|  | import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts'; | ||||||
|  | import { defineMessages, injectIntl } from 'react-intl'; | ||||||
|  | 
 | ||||||
|  | const messages = defineMessages({ | ||||||
|  |   heading: { id: 'column.follow_requests', defaultMessage: 'Follow requests' } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const mapStateToProps = state => ({ | ||||||
|  |   accountIds: state.getIn(['user_lists', 'follow_requests', 'items']) | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const FollowRequests = React.createClass({ | ||||||
|  |   propTypes: { | ||||||
|  |     params: React.PropTypes.object.isRequired, | ||||||
|  |     dispatch: React.PropTypes.func.isRequired, | ||||||
|  |     accountIds: ImmutablePropTypes.list, | ||||||
|  |     intl: React.PropTypes.object.isRequired | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   mixins: [PureRenderMixin], | ||||||
|  | 
 | ||||||
|  |   componentWillMount () { | ||||||
|  |     this.props.dispatch(fetchFollowRequests()); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   handleScroll (e) { | ||||||
|  |     const { scrollTop, scrollHeight, clientHeight } = e.target; | ||||||
|  | 
 | ||||||
|  |     if (scrollTop === scrollHeight - clientHeight) { | ||||||
|  |       this.props.dispatch(expandFollowRequests()); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   render () { | ||||||
|  |     const { intl, accountIds } = this.props; | ||||||
|  | 
 | ||||||
|  |     if (!accountIds) { | ||||||
|  |       return ( | ||||||
|  |         <Column> | ||||||
|  |           <LoadingIndicator /> | ||||||
|  |         </Column> | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |       <Column icon='users' heading={intl.formatMessage(messages.heading)}> | ||||||
|  |         <ScrollContainer scrollKey='follow_requests'> | ||||||
|  |           <div className='scrollable' onScroll={this.handleScroll}> | ||||||
|  |             {accountIds.map(id => | ||||||
|  |               <AccountAuthorizeContainer key={id} id={id} /> | ||||||
|  |             )} | ||||||
|  |           </div> | ||||||
|  |         </ScrollContainer> | ||||||
|  |       </Column> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export default connect(mapStateToProps)(injectIntl(FollowRequests)); | ||||||
|  | @ -7,7 +7,8 @@ import { connect } from 'react-redux'; | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
|   heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, |   heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, | ||||||
|   public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Public timeline' }, |   public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Public timeline' }, | ||||||
|   settings: { id: 'navigation_bar.settings', defaultMessage: 'Settings' } |   settings: { id: 'navigation_bar.settings', defaultMessage: 'Settings' }, | ||||||
|  |   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' } | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const mapStateToProps = state => ({ | const mapStateToProps = state => ({ | ||||||
|  | @ -32,6 +33,7 @@ const GettingStarted = ({ intl, me }) => { | ||||||
|         <div style={hamburgerStyle}><i className='fa fa-bars' /></div> |         <div style={hamburgerStyle}><i className='fa fa-bars' /></div> | ||||||
|         <ColumnLink icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' /> |         <ColumnLink icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' /> | ||||||
|         <ColumnLink icon='cog' text={intl.formatMessage(messages.settings)} href='/settings/profile' /> |         <ColumnLink icon='cog' text={intl.formatMessage(messages.settings)} href='/settings/profile' /> | ||||||
|  |         <ColumnLink icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' /> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <div className='static-content'> |       <div className='static-content'> | ||||||
|  | @ -43,4 +45,9 @@ const GettingStarted = ({ intl, me }) => { | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | GettingStarted.propTypes = { | ||||||
|  |   intl: React.PropTypes.object.isRequired, | ||||||
|  |   me: React.PropTypes.number | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| export default connect(mapStateToProps)(injectIntl(GettingStarted)); | export default connect(mapStateToProps)(injectIntl(GettingStarted)); | ||||||
|  |  | ||||||
|  | @ -6,7 +6,8 @@ import { | ||||||
|   FOLLOWING_FETCH_SUCCESS, |   FOLLOWING_FETCH_SUCCESS, | ||||||
|   FOLLOWING_EXPAND_SUCCESS, |   FOLLOWING_EXPAND_SUCCESS, | ||||||
|   ACCOUNT_TIMELINE_FETCH_SUCCESS, |   ACCOUNT_TIMELINE_FETCH_SUCCESS, | ||||||
|   ACCOUNT_TIMELINE_EXPAND_SUCCESS |   ACCOUNT_TIMELINE_EXPAND_SUCCESS, | ||||||
|  |   FOLLOW_REQUESTS_FETCH_SUCCESS | ||||||
| } from '../actions/accounts'; | } from '../actions/accounts'; | ||||||
| import { COMPOSE_SUGGESTIONS_READY } from '../actions/compose'; | import { COMPOSE_SUGGESTIONS_READY } from '../actions/compose'; | ||||||
| import { | import { | ||||||
|  | @ -78,6 +79,7 @@ export default function accounts(state = initialState, action) { | ||||||
|     case FAVOURITES_FETCH_SUCCESS: |     case FAVOURITES_FETCH_SUCCESS: | ||||||
|     case COMPOSE_SUGGESTIONS_READY: |     case COMPOSE_SUGGESTIONS_READY: | ||||||
|     case SEARCH_SUGGESTIONS_READY: |     case SEARCH_SUGGESTIONS_READY: | ||||||
|  |     case FOLLOW_REQUESTS_FETCH_SUCCESS: | ||||||
|       return normalizeAccounts(state, action.accounts); |       return normalizeAccounts(state, action.accounts); | ||||||
|     case NOTIFICATIONS_REFRESH_SUCCESS: |     case NOTIFICATIONS_REFRESH_SUCCESS: | ||||||
|     case NOTIFICATIONS_EXPAND_SUCCESS: |     case NOTIFICATIONS_EXPAND_SUCCESS: | ||||||
|  |  | ||||||
|  | @ -2,7 +2,10 @@ import { | ||||||
|   FOLLOWERS_FETCH_SUCCESS, |   FOLLOWERS_FETCH_SUCCESS, | ||||||
|   FOLLOWERS_EXPAND_SUCCESS, |   FOLLOWERS_EXPAND_SUCCESS, | ||||||
|   FOLLOWING_FETCH_SUCCESS, |   FOLLOWING_FETCH_SUCCESS, | ||||||
|   FOLLOWING_EXPAND_SUCCESS |   FOLLOWING_EXPAND_SUCCESS, | ||||||
|  |   FOLLOW_REQUESTS_FETCH_SUCCESS, | ||||||
|  |   FOLLOW_REQUEST_AUTHORIZE_SUCCESS, | ||||||
|  |   FOLLOW_REQUEST_REJECT_SUCCESS | ||||||
| } from '../actions/accounts'; | } from '../actions/accounts'; | ||||||
| import { | import { | ||||||
|   REBLOGS_FETCH_SUCCESS, |   REBLOGS_FETCH_SUCCESS, | ||||||
|  | @ -14,7 +17,8 @@ const initialState = Immutable.Map({ | ||||||
|   followers: Immutable.Map(), |   followers: Immutable.Map(), | ||||||
|   following: Immutable.Map(), |   following: Immutable.Map(), | ||||||
|   reblogged_by: Immutable.Map(), |   reblogged_by: Immutable.Map(), | ||||||
|   favourited_by: Immutable.Map() |   favourited_by: Immutable.Map(), | ||||||
|  |   follow_requests: Immutable.Map() | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const normalizeList = (state, type, id, accounts, next) => { | const normalizeList = (state, type, id, accounts, next) => { | ||||||
|  | @ -44,6 +48,11 @@ export default function userLists(state = initialState, action) { | ||||||
|       return state.setIn(['reblogged_by', action.id], Immutable.List(action.accounts.map(item => item.id))); |       return state.setIn(['reblogged_by', action.id], Immutable.List(action.accounts.map(item => item.id))); | ||||||
|     case FAVOURITES_FETCH_SUCCESS: |     case FAVOURITES_FETCH_SUCCESS: | ||||||
|       return state.setIn(['favourited_by', action.id], Immutable.List(action.accounts.map(item => item.id))); |       return state.setIn(['favourited_by', action.id], Immutable.List(action.accounts.map(item => item.id))); | ||||||
|  |     case FOLLOW_REQUESTS_FETCH_SUCCESS: | ||||||
|  |       return state.setIn(['follow_requests', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next); | ||||||
|  |     case FOLLOW_REQUEST_AUTHORIZE_SUCCESS: | ||||||
|  |     case FOLLOW_REQUEST_REJECT_SUCCESS: | ||||||
|  |       return state.updateIn(['follow_requests', 'items'], list => list.filterNot(item => item === action.id)); | ||||||
|     default: |     default: | ||||||
|       return state; |       return state; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| # frozen_string_literal: true |  | ||||||
| 
 |  | ||||||
| class FollowRequestsController < ApplicationController |  | ||||||
|   layout 'auth' |  | ||||||
| 
 |  | ||||||
|   before_action :authenticate_user! |  | ||||||
|   before_action :set_follow_request, except: :index |  | ||||||
| 
 |  | ||||||
|   def index |  | ||||||
|     @follow_requests = FollowRequest.where(target_account: current_account) |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def authorize |  | ||||||
|     @follow_request.authorize! |  | ||||||
|     redirect_to follow_requests_path |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   def reject |  | ||||||
|     @follow_request.reject! |  | ||||||
|     redirect_to follow_requests_path |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   private |  | ||||||
| 
 |  | ||||||
|   def set_follow_request |  | ||||||
|     @follow_request = FollowRequest.find(params[:id]) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
|  | @ -1,2 +0,0 @@ | ||||||
| module FollowRequestsHelper |  | ||||||
| end |  | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| - content_for :page_title do |  | ||||||
|   = t('follow_requests.title') |  | ||||||
| 
 |  | ||||||
| - if @follow_requests.empty? |  | ||||||
|   %p.nothing-here= t('accounts.nothing_here') |  | ||||||
| - else |  | ||||||
|   %table.table |  | ||||||
|     %tbody |  | ||||||
|       - @follow_requests.each do |follow_request| |  | ||||||
|         %tr |  | ||||||
|           %td= link_to follow_request.account.acct, web_path("accounts/#{follow_request.account.id}") |  | ||||||
|           %td{ style: 'text-align: right' } |  | ||||||
|             = table_link_to 'check-circle', t('follow_requests.authorize'), authorize_follow_request_path(follow_request), method: :post |  | ||||||
|             = table_link_to 'times-circle', t('follow_requests.reject'), reject_follow_request_path(follow_request), method: :post |  | ||||||
| 
 |  | ||||||
| .form-footer= render "settings/shared/links" |  | ||||||
|  | @ -1,8 +1,6 @@ | ||||||
| %ul.no-list | %ul.no-list | ||||||
|   - if controller_name != 'profiles' |   - if controller_name != 'profiles' | ||||||
|     %li= link_to t('settings.edit_profile'), settings_profile_path |     %li= link_to t('settings.edit_profile'), settings_profile_path | ||||||
|   - if controller_name != 'follow_requests' |  | ||||||
|     %li= link_to t('follow_requests.title'), follow_requests_path |  | ||||||
|   - if controller_name != 'preferences' |   - if controller_name != 'preferences' | ||||||
|     %li= link_to t('settings.preferences'), settings_preferences_path |     %li= link_to t('settings.preferences'), settings_preferences_path | ||||||
|   - if controller_name != 'registrations' |   - if controller_name != 'registrations' | ||||||
|  |  | ||||||
|  | @ -40,10 +40,6 @@ en: | ||||||
|       x_minutes: "%{count}m" |       x_minutes: "%{count}m" | ||||||
|       x_months: "%{count}mo" |       x_months: "%{count}mo" | ||||||
|       x_seconds: "%{count}s" |       x_seconds: "%{count}s" | ||||||
|   follow_requests: |  | ||||||
|     authorize: Authorize |  | ||||||
|     reject: Reject |  | ||||||
|     title: Follow requests |  | ||||||
|   generic: |   generic: | ||||||
|     changes_saved_msg: Changes successfully saved! |     changes_saved_msg: Changes successfully saved! | ||||||
|     powered_by: powered by %{link} |     powered_by: powered by %{link} | ||||||
|  |  | ||||||
|  | @ -48,13 +48,6 @@ Rails.application.routes.draw do | ||||||
|   resources :media, only: [:show] |   resources :media, only: [:show] | ||||||
|   resources :tags,  only: [:show] |   resources :tags,  only: [:show] | ||||||
| 
 | 
 | ||||||
|   resources :follow_requests do |  | ||||||
|     member do |  | ||||||
|       post :authorize |  | ||||||
|       post :reject |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   namespace :admin do |   namespace :admin do | ||||||
|     resources :pubsubhubbub, only: [:index] |     resources :pubsubhubbub, only: [:index] | ||||||
|     resources :domain_blocks, only: [:index, :create] |     resources :domain_blocks, only: [:index, :create] | ||||||
|  |  | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| require 'rails_helper' |  | ||||||
| 
 |  | ||||||
| RSpec.describe FollowRequestsController, type: :controller do |  | ||||||
|   render_views |  | ||||||
| 
 |  | ||||||
|   before do |  | ||||||
|     sign_in Fabricate(:user), scope: :user |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   describe 'GET #index' do |  | ||||||
|     it 'returns http success' do |  | ||||||
|       get :index |  | ||||||
|       expect(response).to have_http_status(:success) |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| require 'rails_helper' |  | ||||||
| 
 |  | ||||||
| RSpec.describe FollowRequestsHelper, type: :helper do |  | ||||||
| 
 |  | ||||||
| end |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue