forked from treehouse/mastodon
Replacing follow requests in the settings area with in-UI column
parent
004382e4d0
commit
3689c119f0
|
@ -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…
Reference in New Issue