Add list of muted user to UI and Getting Started (#1799)
Add the same UI that already exists for blocked users for muted ones and add it to the "Getting Started" menu.gh/stable
parent
92cd207c50
commit
fe8dd58bc1
|
@ -0,0 +1,82 @@
|
|||
import api, { getLinks } from '../api'
|
||||
import { fetchRelationships } from './accounts';
|
||||
|
||||
export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
|
||||
export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS';
|
||||
export const MUTES_FETCH_FAIL = 'MUTES_FETCH_FAIL';
|
||||
|
||||
export const MUTES_EXPAND_REQUEST = 'MUTES_EXPAND_REQUEST';
|
||||
export const MUTES_EXPAND_SUCCESS = 'MUTES_EXPAND_SUCCESS';
|
||||
export const MUTES_EXPAND_FAIL = 'MUTES_EXPAND_FAIL';
|
||||
|
||||
export function fetchMutes() {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchMutesRequest());
|
||||
|
||||
api(getState).get('/api/v1/mutes').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(fetchMutesSuccess(response.data, next ? next.uri : null));
|
||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
||||
}).catch(error => dispatch(fetchMutesFail(error)));
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchMutesRequest() {
|
||||
return {
|
||||
type: MUTES_FETCH_REQUEST
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchMutesSuccess(accounts, next) {
|
||||
return {
|
||||
type: MUTES_FETCH_SUCCESS,
|
||||
accounts,
|
||||
next
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchMutesFail(error) {
|
||||
return {
|
||||
type: MUTES_FETCH_FAIL,
|
||||
error
|
||||
};
|
||||
};
|
||||
|
||||
export function expandMutes() {
|
||||
return (dispatch, getState) => {
|
||||
const url = getState().getIn(['user_lists', 'mutes', 'next']);
|
||||
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandMutesRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(expandMutesSuccess(response.data, next ? next.uri : null));
|
||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
||||
}).catch(error => dispatch(expandMutesFail(error)));
|
||||
};
|
||||
};
|
||||
|
||||
export function expandMutesRequest() {
|
||||
return {
|
||||
type: MUTES_EXPAND_REQUEST
|
||||
};
|
||||
};
|
||||
|
||||
export function expandMutesSuccess(accounts, next) {
|
||||
return {
|
||||
type: MUTES_EXPAND_SUCCESS,
|
||||
accounts,
|
||||
next
|
||||
};
|
||||
};
|
||||
|
||||
export function expandMutesFail(error) {
|
||||
return {
|
||||
type: MUTES_EXPAND_FAIL,
|
||||
error
|
||||
};
|
||||
};
|
|
@ -10,7 +10,8 @@ const messages = defineMessages({
|
|||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
|
||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock' }
|
||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock' },
|
||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute' }
|
||||
});
|
||||
|
||||
const buttonsStyle = {
|
||||
|
@ -25,6 +26,7 @@ const Account = React.createClass({
|
|||
me: React.PropTypes.number.isRequired,
|
||||
onFollow: React.PropTypes.func.isRequired,
|
||||
onBlock: React.PropTypes.func.isRequired,
|
||||
onMute: React.PropTypes.func.isRequired,
|
||||
intl: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
|
@ -38,6 +40,10 @@ const Account = React.createClass({
|
|||
this.props.onBlock(this.props.account);
|
||||
},
|
||||
|
||||
handleMute () {
|
||||
this.props.onMute(this.props.account);
|
||||
},
|
||||
|
||||
render () {
|
||||
const { account, me, intl } = this.props;
|
||||
|
||||
|
@ -51,11 +57,14 @@ const Account = React.createClass({
|
|||
const following = account.getIn(['relationship', 'following']);
|
||||
const requested = account.getIn(['relationship', 'requested']);
|
||||
const blocking = account.getIn(['relationship', 'blocking']);
|
||||
const muting = account.getIn(['relationship', 'muting']);
|
||||
|
||||
if (requested) {
|
||||
buttons = <IconButton disabled={true} icon='hourglass' title={intl.formatMessage(messages.requested)} />
|
||||
} else if (blocking) {
|
||||
buttons = <IconButton active={true} icon='unlock-alt' title={intl.formatMessage(messages.unblock)} onClick={this.handleBlock} />;
|
||||
buttons = <IconButton active={true} icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
|
||||
} else if (muting) {
|
||||
buttons = <IconButton active={true} icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />;
|
||||
} else {
|
||||
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import FollowRequests from '../features/follow_requests';
|
|||
import GenericNotFound from '../features/generic_not_found';
|
||||
import FavouritedStatuses from '../features/favourited_statuses';
|
||||
import Blocks from '../features/blocks';
|
||||
import Mutes from '../features/mutes';
|
||||
import Report from '../features/report';
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import en from 'react-intl/locale-data/en';
|
||||
|
@ -171,6 +172,7 @@ const Mastodon = React.createClass({
|
|||
|
||||
<Route path='follow_requests' component={FollowRequests} />
|
||||
<Route path='blocks' component={Blocks} />
|
||||
<Route path='mutes' component={Mutes} />
|
||||
<Route path='report' component={Report} />
|
||||
|
||||
<Route path='*' component={GenericNotFound} />
|
||||
|
|
|
@ -14,6 +14,7 @@ const messages = defineMessages({
|
|||
sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||
info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }
|
||||
});
|
||||
|
||||
|
@ -37,6 +38,7 @@ const GettingStarted = ({ intl, me }) => {
|
|||
<ColumnLink icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
|
||||
{followRequests}
|
||||
<ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
|
||||
<ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
|
||||
<ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
|
||||
<ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
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 ColumnBackButtonSlim from '../../components/column_back_button_slim';
|
||||
import AccountContainer from '../../containers/account_container';
|
||||
import { fetchMutes, expandMutes } from '../../actions/mutes';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.mutes', defaultMessage: 'Muted users' }
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
accountIds: state.getIn(['user_lists', 'mutes', 'items'])
|
||||
});
|
||||
|
||||
const Mutes = 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(fetchMutes());
|
||||
},
|
||||
|
||||
handleScroll (e) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||
|
||||
if (scrollTop === scrollHeight - clientHeight) {
|
||||
this.props.dispatch(expandMutes());
|
||||
}
|
||||
},
|
||||
|
||||
render () {
|
||||
const { intl, accountIds } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
<Column>
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Column icon='users' heading={intl.formatMessage(messages.heading)}>
|
||||
<ColumnBackButtonSlim />
|
||||
<ScrollContainer scrollKey='mutes'>
|
||||
<div className='scrollable' onScroll={this.handleScroll}>
|
||||
{accountIds.map(id =>
|
||||
<AccountContainer key={id} id={id} />
|
||||
)}
|
||||
</div>
|
||||
</ScrollContainer>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(Mutes));
|
|
@ -31,6 +31,7 @@ const en = {
|
|||
"column.favourites": "Favourites",
|
||||
"column.follow_requests": "Follow requests",
|
||||
"column.home": "Home",
|
||||
"column.mutes": "Muted users",
|
||||
"column.notifications": "Notifications",
|
||||
"column.public": "Federated timeline",
|
||||
"compose_form.placeholder": "What is on your mind?",
|
||||
|
@ -68,6 +69,7 @@ const en = {
|
|||
"navigation_bar.follow_requests": "Follow requests",
|
||||
"navigation_bar.info": "Extended information",
|
||||
"navigation_bar.logout": "Logout",
|
||||
"navigation_bar.mutes": "Muted users",
|
||||
"navigation_bar.preferences": "Preferences",
|
||||
"navigation_bar.public_timeline": "Federated timeline",
|
||||
"notification.favourite": "{name} favourited your status",
|
||||
|
|
|
@ -15,6 +15,10 @@ import {
|
|||
BLOCKS_FETCH_SUCCESS,
|
||||
BLOCKS_EXPAND_SUCCESS
|
||||
} from '../actions/blocks';
|
||||
import {
|
||||
MUTES_FETCH_SUCCESS,
|
||||
MUTES_EXPAND_SUCCESS
|
||||
} from '../actions/mutes';
|
||||
import { COMPOSE_SUGGESTIONS_READY } from '../actions/compose';
|
||||
import {
|
||||
REBLOG_SUCCESS,
|
||||
|
@ -94,6 +98,8 @@ export default function accounts(state = initialState, action) {
|
|||
case FOLLOW_REQUESTS_EXPAND_SUCCESS:
|
||||
case BLOCKS_FETCH_SUCCESS:
|
||||
case BLOCKS_EXPAND_SUCCESS:
|
||||
case MUTES_FETCH_SUCCESS:
|
||||
case MUTES_EXPAND_SUCCESS:
|
||||
return normalizeAccounts(state, action.accounts);
|
||||
case NOTIFICATIONS_REFRESH_SUCCESS:
|
||||
case NOTIFICATIONS_EXPAND_SUCCESS:
|
||||
|
|
|
@ -16,6 +16,10 @@ import {
|
|||
BLOCKS_FETCH_SUCCESS,
|
||||
BLOCKS_EXPAND_SUCCESS
|
||||
} from '../actions/blocks';
|
||||
import {
|
||||
MUTES_FETCH_SUCCESS,
|
||||
MUTES_EXPAND_SUCCESS
|
||||
} from '../actions/mutes';
|
||||
import Immutable from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
|
@ -24,7 +28,8 @@ const initialState = Immutable.Map({
|
|||
reblogged_by: Immutable.Map(),
|
||||
favourited_by: Immutable.Map(),
|
||||
follow_requests: Immutable.Map(),
|
||||
blocks: Immutable.Map()
|
||||
blocks: Immutable.Map(),
|
||||
mutes: Immutable.Map()
|
||||
});
|
||||
|
||||
const normalizeList = (state, type, id, accounts, next) => {
|
||||
|
@ -65,6 +70,10 @@ export default function userLists(state = initialState, action) {
|
|||
return state.setIn(['blocks', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
|
||||
case BLOCKS_EXPAND_SUCCESS:
|
||||
return state.updateIn(['blocks', 'items'], list => list.push(...action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
|
||||
case MUTES_FETCH_SUCCESS:
|
||||
return state.setIn(['mutes', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
|
||||
case MUTES_EXPAND_SUCCESS:
|
||||
return state.updateIn(['mutes', 'items'], list => list.push(...action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
Reference in New Issue