mirror of
https://github.com/wassname/talk.git
synced 2026-08-20 12:50:41 +08:00
Merge branch 'master' into issue-676-set-domain-whitelist
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import {SHOW_BAN_USER_DIALOG, HIDE_BAN_USER_DIALOG} from '../constants/banUserDialog';
|
||||
|
||||
export const showBanUserDialog = ({userId, username, commentId, commentStatus}) =>
|
||||
({type: SHOW_BAN_USER_DIALOG, userId, username, commentId, commentStatus});
|
||||
|
||||
export const hideBanUserDialog = () => ({type: HIDE_BAN_USER_DIALOG});
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
SET_COMMENTER_STATUS,
|
||||
SHOW_BANUSER_DIALOG,
|
||||
HIDE_BANUSER_DIALOG,
|
||||
SHOW_SUSPENDUSER_DIALOG,
|
||||
HIDE_SUSPENDUSER_DIALOG
|
||||
SHOW_REJECT_USERNAME_DIALOG,
|
||||
HIDE_REJECT_USERNAME_DIALOG
|
||||
} from '../constants/community';
|
||||
|
||||
import coralApi from '../../../coral-framework/helpers/request';
|
||||
@@ -69,6 +69,6 @@ export const setCommenterStatus = (id, status) => (dispatch) => {
|
||||
export const showBanUserDialog = (user) => ({type: SHOW_BANUSER_DIALOG, user});
|
||||
export const hideBanUserDialog = () => ({type: HIDE_BANUSER_DIALOG});
|
||||
|
||||
// Suspend User Dialog
|
||||
export const showSuspendUserDialog = (user) => ({type: SHOW_SUSPENDUSER_DIALOG, user});
|
||||
export const hideSuspendUserDialog = () => ({type: HIDE_SUSPENDUSER_DIALOG});
|
||||
// Reject Username Dialog
|
||||
export const showRejectUsernameDialog = (user) => ({type: SHOW_REJECT_USERNAME_DIALOG, user});
|
||||
export const hideRejectUsernameDialog = () => ({type: HIDE_REJECT_USERNAME_DIALOG});
|
||||
|
||||
@@ -3,16 +3,6 @@ import * as actions from 'constants/moderation';
|
||||
export const toggleModal = (open) => ({type: actions.TOGGLE_MODAL, open});
|
||||
export const singleView = () => ({type: actions.SINGLE_VIEW});
|
||||
|
||||
// Ban User Dialog
|
||||
export const showBanUserDialog = (user, commentId, commentStatus, showRejectedNote) => ({type: actions.SHOW_BANUSER_DIALOG, user, commentId, commentStatus, showRejectedNote});
|
||||
export const hideBanUserDialog = () => ({type: actions.HIDE_BANUSER_DIALOG});
|
||||
|
||||
// Suspend User Dialog
|
||||
export const showSuspendUserDialog = (userId, username, commentId, commentStatus) =>
|
||||
({type: actions.SHOW_SUSPEND_USER_DIALOG, userId, username, commentId, commentStatus});
|
||||
|
||||
export const hideSuspendUserDialog = () => ({type: actions.HIDE_SUSPEND_USER_DIALOG});
|
||||
|
||||
// hide shortcuts note
|
||||
export const hideShortcutsNote = () => {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import {SHOW_SUSPEND_USER_DIALOG, HIDE_SUSPEND_USER_DIALOG} from '../constants/suspendUserDialog.js';
|
||||
|
||||
export const showSuspendUserDialog = ({userId, username, commentId, commentStatus}) =>
|
||||
({type: SHOW_SUSPEND_USER_DIALOG, userId, username, commentId, commentStatus});
|
||||
|
||||
export const hideSuspendUserDialog = () => ({type: HIDE_SUSPEND_USER_DIALOG});
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Dialog} from 'coral-ui';
|
||||
import styles from './BanUserDialog.css';
|
||||
|
||||
import Button from 'coral-ui/components/Button';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const BanUserDialog = ({open, onCancel, onPerform, username, info}) => (
|
||||
<Dialog
|
||||
className={styles.dialog}
|
||||
id="banUserDialog"
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
title={t('bandialog.ban_user')}>
|
||||
<span className={styles.close} onClick={onCancel}>×</span>
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h2>{t('bandialog.ban_user')}</h2>
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h3>{t('bandialog.are_you_sure', username)}</h3>
|
||||
<i>{info}</i>
|
||||
</div>
|
||||
<div className={styles.buttons}>
|
||||
<Button cStyle="cancel" className={styles.cancel} onClick={onCancel} raised>
|
||||
{t('bandialog.cancel')}
|
||||
</Button>
|
||||
<Button cStyle="black" className={styles.ban} onClick={onPerform} raised>
|
||||
{t('bandialog.yes_ban_user')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
BanUserDialog.propTypes = {
|
||||
open: PropTypes.bool,
|
||||
onPerform: PropTypes.func.isRequired,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
username: PropTypes.string,
|
||||
info: PropTypes.string,
|
||||
};
|
||||
|
||||
export default BanUserDialog;
|
||||
@@ -8,8 +8,8 @@ import SuspendUserModal from './SuspendUserModal';
|
||||
|
||||
// Each action has different meaning and configuration
|
||||
const menuOptionsMap = {
|
||||
'reject': {status: 'REJECTED', icon: 'close', key: 'r'},
|
||||
'approve': {status: 'ACCEPTED', icon: 'done', key: 't'},
|
||||
'reject': {status: 'REJECTED', icon: 'close', key: 'f'},
|
||||
'approve': {status: 'ACCEPTED', icon: 'done', key: 'd'},
|
||||
'flag': {status: 'FLAGGED', icon: 'flag', filter: 'Untouched'},
|
||||
'ban': {status: 'BANNED', icon: 'not interested'}
|
||||
};
|
||||
|
||||
+2
-4
@@ -49,7 +49,6 @@ class SuspendUserDialog extends React.Component {
|
||||
handlePerform = () => {
|
||||
|
||||
this.props.onPerform({
|
||||
id: this.props.userId,
|
||||
message: this.state.message,
|
||||
|
||||
// Add 1 minute more to help `timeago.js` to display the correct duration.
|
||||
@@ -153,11 +152,10 @@ class SuspendUserDialog extends React.Component {
|
||||
|
||||
SuspendUserDialog.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
onPerform: PropTypes.func.isRequired,
|
||||
username: PropTypes.string,
|
||||
userId: PropTypes.string,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
organizationName: PropTypes.string,
|
||||
username: PropTypes.string,
|
||||
};
|
||||
|
||||
export default SuspendUserDialog;
|
||||
@@ -0,0 +1,2 @@
|
||||
export const SHOW_BAN_USER_DIALOG = 'SHOW_BAN_USER_DIALOG';
|
||||
export const HIDE_BAN_USER_DIALOG = 'HIDE_BAN_USER_DIALOG';
|
||||
@@ -13,5 +13,5 @@ export const FETCH_FLAGGED_COMMENTERS_FAILURE = 'FETCH_FLAGGED_COMMENTERS_FAILUR
|
||||
export const SHOW_BANUSER_DIALOG = 'SHOW_BANUSER_DIALOG';
|
||||
export const HIDE_BANUSER_DIALOG = 'HIDE_BANUSER_DIALOG';
|
||||
|
||||
export const SHOW_SUSPENDUSER_DIALOG = 'SHOW_SUSPENDUSER_DIALOG';
|
||||
export const HIDE_SUSPENDUSER_DIALOG = 'HIDE_SUSPENDUSER_DIALOG';
|
||||
export const SHOW_REJECT_USERNAME_DIALOG = 'SHOW_REJECT_USERNAME_DIALOG';
|
||||
export const HIDE_REJECT_USERNAME_DIALOG = 'HIDE_REJECT_USERNAME_DIALOG';
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
export const TOGGLE_MODAL = 'TOGGLE_MODAL';
|
||||
export const SINGLE_VIEW = 'SINGLE_VIEW';
|
||||
export const SHOW_BANUSER_DIALOG = 'SHOW_BANUSER_DIALOG';
|
||||
export const HIDE_BANUSER_DIALOG = 'HIDE_BANUSER_DIALOG';
|
||||
export const HIDE_SHORTCUTS_NOTE = 'HIDE_SHORTCUTS_NOTE';
|
||||
export const SHOW_SUSPEND_USER_DIALOG = 'SHOW_SUSPEND_USER_DIALOG';
|
||||
export const HIDE_SUSPEND_USER_DIALOG = 'HIDE_SUSPEND_USER_DIALOG';
|
||||
export const VIEW_USER_DETAIL = 'VIEW_USER_DETAIL';
|
||||
export const HIDE_USER_DETAIL = 'HIDE_USER_DETAIL';
|
||||
export const SET_SORT_ORDER = 'MODERATION_SET_SORT_ORDER';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const SHOW_SUSPEND_USER_DIALOG = 'SHOW_SUSPEND_USER_DIALOG';
|
||||
export const HIDE_SUSPEND_USER_DIALOG = 'HIDE_SUSPEND_USER_DIALOG';
|
||||
@@ -0,0 +1,63 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import BanUserDialog from '../components/BanUserDialog';
|
||||
import {hideBanUserDialog} from '../actions/banUserDialog';
|
||||
import {withSetUserStatus, withSetCommentStatus} from 'coral-framework/graphql/mutations';
|
||||
import {compose} from 'react-apollo';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
class BanUserDialogContainer extends Component {
|
||||
|
||||
banUser = async () => {
|
||||
const {userId, commentId, commentStatus, setUserStatus, setCommentStatus, hideBanUserDialog} = this.props;
|
||||
await setUserStatus({userId, status: 'BANNED'});
|
||||
hideBanUserDialog();
|
||||
if (commentId && commentStatus && commentStatus !== 'REJECTED') {
|
||||
await setCommentStatus({commentId, status: 'REJECTED'});
|
||||
}
|
||||
}
|
||||
|
||||
getInfo() {
|
||||
let note = t('bandialog.note_ban_user');
|
||||
if (this.props.commentStatus && this.props.commentStatus !== 'REJECTED') {
|
||||
note = t('bandialog.note_reject_comment');
|
||||
}
|
||||
return t('bandialog.note', note);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<BanUserDialog
|
||||
open={this.props.open}
|
||||
onPerform={this.banUser}
|
||||
onCancel={this.props.hideBanUserDialog}
|
||||
username={this.props.username}
|
||||
info={this.getInfo()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({banUserDialog: {open, userId, username, commentId, commentStatus}}) => ({
|
||||
open,
|
||||
userId,
|
||||
username,
|
||||
commentId,
|
||||
commentStatus,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
...bindActionCreators({
|
||||
hideBanUserDialog,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
export default compose(
|
||||
withSetUserStatus,
|
||||
withSetCommentStatus,
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
),
|
||||
)(BanUserDialogContainer);
|
||||
@@ -5,6 +5,8 @@ import {fetchConfig} from '../actions/config';
|
||||
import AdminLogin from '../components/AdminLogin';
|
||||
import {logout} from 'coral-framework/actions/auth';
|
||||
import {FullLoading} from '../components/FullLoading';
|
||||
import BanUserDialog from './BanUserDialog';
|
||||
import SuspendUserDialog from './SuspendUserDialog';
|
||||
import {toggleModal as toggleShortcutModal} from '../actions/moderation';
|
||||
import {checkLogin, handleLogin, requestPasswordReset} from '../actions/auth';
|
||||
import {can} from 'coral-framework/services/perms';
|
||||
@@ -52,7 +54,11 @@ class LayoutContainer extends Component {
|
||||
handleLogout={handleLogout}
|
||||
toggleShortcutModal={toggleShortcutModal}
|
||||
{...this.props}
|
||||
/>
|
||||
>
|
||||
<BanUserDialog />
|
||||
<SuspendUserDialog />
|
||||
{this.props.children}
|
||||
</Layout>
|
||||
);
|
||||
} else if (loggedIn) {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import SuspendUserDialog from '../components/SuspendUserDialog';
|
||||
import {hideSuspendUserDialog} from '../actions/suspendUserDialog';
|
||||
import {withSetCommentStatus, withSuspendUser} from 'coral-framework/graphql/mutations';
|
||||
import {compose, gql} from 'react-apollo';
|
||||
import * as notification from 'coral-admin/src/services/notification';
|
||||
import t, {timeago} from 'coral-framework/services/i18n';
|
||||
import withQuery from 'coral-framework/hocs/withQuery';
|
||||
import get from 'lodash/get';
|
||||
|
||||
class SuspendUserDialogContainer extends Component {
|
||||
|
||||
suspendUser = async ({message, until}) => {
|
||||
const {userId, username, commentStatus, commentId, hideSuspendUserDialog, setCommentStatus, suspendUser} = this.props;
|
||||
hideSuspendUserDialog();
|
||||
try {
|
||||
const result = await suspendUser({id: userId, message, until});
|
||||
if (result.data.suspendUser.errors) {
|
||||
throw result.data.suspendUser.errors;
|
||||
}
|
||||
notification.success(
|
||||
t('suspenduser.notify_suspend_until', username, timeago(until)),
|
||||
);
|
||||
if (commentId && commentStatus && commentStatus !== 'REJECTED') {
|
||||
return setCommentStatus({commentId, status: 'REJECTED'})
|
||||
.then((result) => {
|
||||
if (result.data.setCommentStatus.errors) {
|
||||
throw result.data.setCommentStatus.errors;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
notification.showMutationErrors(err);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SuspendUserDialog
|
||||
open={this.props.open}
|
||||
onPerform={this.suspendUser}
|
||||
onCancel={this.props.hideSuspendUserDialog}
|
||||
organizationName={get(this.props, 'root.settings.organizationName')}
|
||||
username={this.props.username}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const withOrganizationName = withQuery(gql`
|
||||
query CoralAdmin_SuspendUserDialog {
|
||||
settings {
|
||||
organizationName
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const mapStateToProps = ({suspendUserDialog: {open, userId, username, commentId, commentStatus}}) => ({
|
||||
open,
|
||||
userId,
|
||||
username,
|
||||
commentId,
|
||||
commentStatus,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
...bindActionCreators({
|
||||
hideSuspendUserDialog,
|
||||
}, dispatch),
|
||||
});
|
||||
|
||||
export default compose(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
),
|
||||
withSuspendUser,
|
||||
withSetCommentStatus,
|
||||
withOrganizationName,
|
||||
)(SuspendUserDialogContainer);
|
||||
@@ -0,0 +1,30 @@
|
||||
import {SHOW_BAN_USER_DIALOG, HIDE_BAN_USER_DIALOG} from '../constants/banUserDialog';
|
||||
|
||||
const initialState = {
|
||||
open: false,
|
||||
userId: null,
|
||||
username: '',
|
||||
commentId: null,
|
||||
commentStatus: '',
|
||||
};
|
||||
|
||||
export default function banUserDialog(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case SHOW_BAN_USER_DIALOG:
|
||||
return {
|
||||
...state,
|
||||
open: true,
|
||||
userId: action.userId,
|
||||
username: action.username,
|
||||
commentId: action.commentId,
|
||||
commentStatus: action.commentStatus,
|
||||
};
|
||||
case HIDE_BAN_USER_DIALOG:
|
||||
return {
|
||||
...state,
|
||||
open: false,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
SET_COMMENTER_STATUS,
|
||||
SHOW_BANUSER_DIALOG,
|
||||
HIDE_BANUSER_DIALOG,
|
||||
SHOW_SUSPENDUSER_DIALOG,
|
||||
HIDE_SUSPENDUSER_DIALOG
|
||||
SHOW_REJECT_USERNAME_DIALOG,
|
||||
HIDE_REJECT_USERNAME_DIALOG
|
||||
} from '../constants/community';
|
||||
|
||||
const initialState = Map({
|
||||
@@ -24,7 +24,7 @@ const initialState = Map({
|
||||
pagePeople: 0,
|
||||
user: Map({}),
|
||||
banDialog: false,
|
||||
suspendDialog: false
|
||||
rejectUsernameDialog: false
|
||||
});
|
||||
|
||||
export default function community (state = initialState, action) {
|
||||
@@ -79,14 +79,14 @@ export default function community (state = initialState, action) {
|
||||
user: Map(action.user),
|
||||
banDialog: true
|
||||
});
|
||||
case HIDE_SUSPENDUSER_DIALOG:
|
||||
case HIDE_REJECT_USERNAME_DIALOG:
|
||||
return state
|
||||
.set('suspendDialog', false);
|
||||
case SHOW_SUSPENDUSER_DIALOG:
|
||||
.set('rejectUsernameDialog', false);
|
||||
case SHOW_REJECT_USERNAME_DIALOG:
|
||||
return state
|
||||
.merge({
|
||||
user: Map(action.user),
|
||||
suspendDialog: true
|
||||
rejectUsernameDialog: true
|
||||
});
|
||||
default :
|
||||
return state;
|
||||
|
||||
@@ -5,9 +5,13 @@ import community from './community';
|
||||
import moderation from './moderation';
|
||||
import install from './install';
|
||||
import config from './config';
|
||||
import banUserDialog from './banUserDialog';
|
||||
import suspendUserDialog from './suspendUserDialog';
|
||||
|
||||
export default {
|
||||
auth,
|
||||
banUserDialog,
|
||||
suspendUserDialog,
|
||||
assets,
|
||||
settings,
|
||||
community,
|
||||
|
||||
@@ -1,61 +1,23 @@
|
||||
import {fromJS, Map, Set} from 'immutable';
|
||||
import {fromJS, Set} from 'immutable';
|
||||
import * as actions from '../constants/moderation';
|
||||
|
||||
const initialState = fromJS({
|
||||
singleView: false,
|
||||
modalOpen: false,
|
||||
user: {},
|
||||
commentId: null,
|
||||
commentStatus: null,
|
||||
userDetailId: null,
|
||||
userDetailActiveTab: 'all',
|
||||
userDetailStatuses: ['NONE', 'ACCEPTED', 'REJECTED', 'PREMOD'],
|
||||
userDetailSelectedIds: new Set(),
|
||||
banDialog: false,
|
||||
storySearchVisible: false,
|
||||
storySearchString: '',
|
||||
shortcutsNoteVisible: window.localStorage.getItem('coral:shortcutsNote') || 'show',
|
||||
sortOrder: 'REVERSE_CHRONOLOGICAL',
|
||||
suspendUserDialog: {
|
||||
show: false,
|
||||
userId: null,
|
||||
username: '',
|
||||
commentId: null,
|
||||
commentStatus: '',
|
||||
},
|
||||
});
|
||||
|
||||
export default function moderation (state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case actions.MODERATION_CLEAR_STATE:
|
||||
return initialState;
|
||||
case actions.HIDE_BANUSER_DIALOG:
|
||||
return state
|
||||
.set('banDialog', false)
|
||||
.set('commentStatus', null);
|
||||
case actions.SHOW_BANUSER_DIALOG:
|
||||
return state
|
||||
.merge({
|
||||
user: Map(action.user),
|
||||
commentId: action.commentId,
|
||||
commentStatus: action.commentStatus,
|
||||
showRejectedNote: action.showRejectedNote,
|
||||
banDialog: true
|
||||
});
|
||||
case actions.SHOW_SUSPEND_USER_DIALOG:
|
||||
return state
|
||||
.mergeDeep({
|
||||
suspendUserDialog: {
|
||||
show: true,
|
||||
userId: action.userId,
|
||||
username: action.username,
|
||||
commentId: action.commentId,
|
||||
commentStatus: action.commentStatus,
|
||||
}
|
||||
});
|
||||
case actions.HIDE_SUSPEND_USER_DIALOG:
|
||||
return state
|
||||
.setIn(['suspendUserDialog', 'show'], false);
|
||||
case actions.SET_ACTIVE_TAB:
|
||||
return state
|
||||
.set('activeTab', action.activeTab);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import {SHOW_SUSPEND_USER_DIALOG, HIDE_SUSPEND_USER_DIALOG} from '../constants/suspendUserDialog';
|
||||
|
||||
const initialState = {
|
||||
open: false,
|
||||
userId: null,
|
||||
username: '',
|
||||
commentId: null,
|
||||
commentStatus: '',
|
||||
};
|
||||
|
||||
export default function suspendUserDialog(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case SHOW_SUSPEND_USER_DIALOG:
|
||||
return {
|
||||
...state,
|
||||
open: true,
|
||||
userId: action.userId,
|
||||
username: action.username,
|
||||
commentId: action.commentId,
|
||||
commentStatus: action.commentStatus,
|
||||
};
|
||||
case HIDE_SUSPEND_USER_DIALOG:
|
||||
return {
|
||||
...state,
|
||||
open: false,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,18 @@
|
||||
import React from 'react';
|
||||
import styles from './Community.css';
|
||||
import BanUserButton from './BanUserButton';
|
||||
import {Button} from 'coral-ui';
|
||||
import {menuActionsMap} from '../../Moderation/helpers/moderationQueueActionsMap';
|
||||
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const ActionButton = ({type = '', user, ...props}) => {
|
||||
if (type === 'BAN') {
|
||||
return <BanUserButton icon='not interested' user={user} onClick={() => props.showBanUserDialog(user)} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={`${type.toLowerCase()} ${styles.actionButton}`}
|
||||
cStyle={type.toLowerCase()}
|
||||
icon={menuActionsMap[type].icon}
|
||||
onClick={() => {
|
||||
type === 'APPROVE' ? props.approveUser({userId: user.id}) : props.showSuspendUserDialog({user: user});
|
||||
type === 'APPROVE' ? props.approveUser({userId: user.id}) : props.showRejectUsernameDialog({user: user});
|
||||
}}
|
||||
>{t(`modqueue.${menuActionsMap[type].text}`)}</Button>
|
||||
);
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
.banButton {
|
||||
-webkit-transform: scale(.8);
|
||||
transform: scale(.8);
|
||||
margin: 0;
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Button, Icon} from 'coral-ui';
|
||||
import styles from './BanUserButton.css';
|
||||
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const BanUserButton = ({user, ...props}) => (
|
||||
<div className={styles.ban}>
|
||||
<Button cStyle='ban'
|
||||
className={`ban ${styles.banButton}`}
|
||||
disabled={user.status === 'BANNED' ? 'disabled' : ''}
|
||||
onClick={props.onClick}
|
||||
raised>
|
||||
<Icon name='not_interested' />
|
||||
{t('comment.ban_user')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
BanUserButton.propTypes = {
|
||||
onClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default BanUserButton;
|
||||
@@ -1,164 +0,0 @@
|
||||
.dialog {
|
||||
border: none;
|
||||
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
|
||||
width: 500px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
height: 184px;
|
||||
padding: 20px;
|
||||
|
||||
h2 {
|
||||
color: black;
|
||||
font-size: 1.76em;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: black;
|
||||
font-size: 1.4em;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.textField {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.textField label {
|
||||
font-size: 1.08em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.textField input {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border: none;
|
||||
outline: none;
|
||||
border: 1px solid rgba(0,0,0,.12);
|
||||
padding: 10px 6px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin: 20px auto 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer span {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #2c69b6;
|
||||
cursor: pointer;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.socialConnections {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.signInButton {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.close {
|
||||
font-size: 20px;
|
||||
line-height: 14px;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
position: absolute;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
color: #6b6b6b;
|
||||
}
|
||||
|
||||
input.error{
|
||||
border: solid 2px #f44336;
|
||||
}
|
||||
|
||||
.errorMsg, .hint {
|
||||
color: grey;
|
||||
font-weight: 600;
|
||||
padding: 3px 0 16px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.alert--success {
|
||||
border: solid 1px #1ec00e;
|
||||
background: #cbf1b8;
|
||||
color: #006900;
|
||||
}
|
||||
|
||||
.alert--error {
|
||||
background: #FFEBEE;
|
||||
color: #B71C1C;
|
||||
}
|
||||
|
||||
.userBox a {
|
||||
color: #2c69b6;
|
||||
cursor: pointer;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.attention {
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: #B71C1C;
|
||||
color: #FFEBEE;
|
||||
font-weight: bolder;
|
||||
padding: 4px;
|
||||
vertical-align: middle;
|
||||
border-radius: 20px;
|
||||
box-sizing: border-box;
|
||||
font-size: 9px;
|
||||
line-height: 7px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.passwordRequestSuccess {
|
||||
border: 1px solid green;
|
||||
background-color: lightgreen;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.passwordRequestFailure {
|
||||
border: 1px solid orange;
|
||||
background-color: 1px solid coral;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
margin-right: 10px;
|
||||
width: 47%;
|
||||
}
|
||||
|
||||
.ban {
|
||||
width: 47%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin: 20px 0;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Dialog} from 'coral-ui';
|
||||
import styles from './BanUserDialog.css';
|
||||
|
||||
import Button from 'coral-ui/components/Button';
|
||||
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const BanUserDialog = ({open, handleClose, handleBanUser, user}) => (
|
||||
<Dialog
|
||||
className={styles.dialog}
|
||||
id="banuserDialog"
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onCancel={handleClose}
|
||||
title={t('community.ban_user')}>
|
||||
<span className={styles.close} onClick={handleClose}>×</span>
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h2>{t('community.ban_user')}</h2>
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h3>{t('community.are_you_sure', user.username)}</h3>
|
||||
<i>{t('community.note')}</i>
|
||||
</div>
|
||||
<div className={styles.buttons}>
|
||||
<Button cStyle="cancel" className={styles.cancel} onClick={handleClose} raised>
|
||||
{t('community.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
cStyle="black" className={styles.ban}
|
||||
onClick={() => {
|
||||
handleBanUser({userId: user.id}).then(() => {
|
||||
handleClose();
|
||||
});
|
||||
}}
|
||||
raised>
|
||||
{t('community.yes_ban_user')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
BanUserDialog.propTypes = {
|
||||
handleBanUser: PropTypes.func.isRequired,
|
||||
handleClose: PropTypes.func.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default BanUserDialog;
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, {Component} from 'react';
|
||||
|
||||
import CommunityMenu from './CommunityMenu';
|
||||
import BanUserDialog from './BanUserDialog';
|
||||
import SuspendUserDialog from './SuspendUserDialog';
|
||||
import People from './People';
|
||||
import FlaggedAccounts from './FlaggedAccounts';
|
||||
import RejectUsernameDialog from './RejectUsernameDialog';
|
||||
|
||||
export default class Community extends Component {
|
||||
|
||||
@@ -80,19 +79,15 @@ export default class Community extends Component {
|
||||
<FlaggedAccounts
|
||||
commenters={users}
|
||||
showBanUserDialog={props.showBanUserDialog}
|
||||
showSuspendUserDialog={props.showSuspendUserDialog}
|
||||
showRejectUsernameDialog={props.showRejectUsernameDialog}
|
||||
approveUser={props.approveUser}
|
||||
rejectUsername={props.rejectUsername}
|
||||
showSuspendUserDialog={props.showSuspendUserDialog}
|
||||
currentUser={this.props.currentUser}
|
||||
/>
|
||||
<BanUserDialog
|
||||
open={community.banDialog}
|
||||
user={community.user}
|
||||
handleClose={props.hideBanUserDialog}
|
||||
handleBanUser={props.banUser}
|
||||
/>
|
||||
<SuspendUserDialog
|
||||
open={community.suspendDialog}
|
||||
handleClose={props.hideSuspendUserDialog}
|
||||
<RejectUsernameDialog
|
||||
open={community.rejectUsernameDialog}
|
||||
handleClose={props.hideRejectUsernameDialog}
|
||||
user={community.user}
|
||||
rejectUsername={props.rejectUsername}
|
||||
/>
|
||||
|
||||
@@ -9,8 +9,6 @@ const FlaggedAccounts = ({...props}) => {
|
||||
const {commenters} = props;
|
||||
const hasResults = commenters && !!commenters.length;
|
||||
|
||||
// if (commenter.status === 'PENDING' && commenter.actions.length > 0) {
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.mainFlaggedContent}>
|
||||
@@ -24,8 +22,9 @@ const FlaggedAccounts = ({...props}) => {
|
||||
modActionButtons={['APPROVE', 'REJECT']}
|
||||
showBanUserDialog={props.showBanUserDialog}
|
||||
showSuspendUserDialog={props.showSuspendUserDialog}
|
||||
showRejectUsernameDialog={props.showRejectUsernameDialog}
|
||||
approveUser={props.approveUser}
|
||||
suspendUser={props.suspendUser}
|
||||
currentUser={props.currentUser}
|
||||
/>;
|
||||
})
|
||||
: <EmptyCard>{t('community.no_flagged_accounts')}</EmptyCard>
|
||||
|
||||
+17
-17
@@ -1,30 +1,30 @@
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
|
||||
import {Dialog, Button} from 'coral-ui';
|
||||
import styles from './SuspendUserDialog.css';
|
||||
import styles from './RejectUsernameDialog.css';
|
||||
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const stages = [
|
||||
{
|
||||
title: 'suspenduser.title_reject',
|
||||
description: 'suspenduser.description_reject',
|
||||
title: 'reject_username.title_reject',
|
||||
description: 'reject_username.description_reject',
|
||||
options: {
|
||||
'j': 'suspenduser.no_cancel',
|
||||
'k': 'suspenduser.yes_suspend'
|
||||
'j': 'reject_username.no_cancel',
|
||||
'k': 'reject_username.yes_suspend'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'suspenduser.title_notify',
|
||||
description: 'suspenduser.description_notify',
|
||||
title: 'reject_username.title_notify',
|
||||
description: 'reject_username.description_notify',
|
||||
options: {
|
||||
'j': 'bandialog.cancel',
|
||||
'k': 'suspenduser.send'
|
||||
'k': 'reject_username.send'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
class SuspendUserDialog extends Component {
|
||||
class RejectUsernameDialog extends Component {
|
||||
|
||||
state = {email: '', stage: 0}
|
||||
|
||||
@@ -35,7 +35,7 @@ class SuspendUserDialog extends Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({email: t('suspenduser.email_message_reject'), about: t('suspenduser.username')});
|
||||
this.setState({email: t('reject_username.email_message_reject'), about: t('reject_username.username')});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -72,22 +72,22 @@ class SuspendUserDialog extends Component {
|
||||
|
||||
return <Dialog
|
||||
className={styles.suspendDialog}
|
||||
id="suspendUserDialog"
|
||||
id="rejectUsernameDialog"
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onCancel={handleClose}
|
||||
title={t('suspenduser.suspend_user')}>
|
||||
title={t('reject_username.suspend_user')}>
|
||||
<div className={styles.title}>
|
||||
{t(stages[stage].title, t('suspenduser.username'))}
|
||||
{t(stages[stage].title, t('reject_username.username'))}
|
||||
</div>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.description}>
|
||||
{t(stages[stage].description, t('suspenduser.username'))}
|
||||
{t(stages[stage].description, t('reject_username.username'))}
|
||||
</div>
|
||||
{
|
||||
stage === 1 &&
|
||||
<div className={styles.writeContainer}>
|
||||
<div className={styles.emailMessage}>{t('suspenduser.write_message')}</div>
|
||||
<div className={styles.emailMessage}>{t('reject_username.write_message')}</div>
|
||||
<div className={styles.emailContainer}>
|
||||
<textarea
|
||||
rows={5}
|
||||
@@ -100,7 +100,7 @@ class SuspendUserDialog extends Component {
|
||||
<div className={styles.modalButtons}>
|
||||
{Object.keys(stages[stage].options).map((key, i) => (
|
||||
<Button key={i} onClick={this.onActionClick(stage, i)}>
|
||||
{t(stages[stage].options[key], t('suspenduser.username'))}
|
||||
{t(stages[stage].options[key], t('reject_username.username'))}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
@@ -109,4 +109,4 @@ class SuspendUserDialog extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default SuspendUserDialog;
|
||||
export default RejectUsernameDialog;
|
||||
@@ -2,6 +2,8 @@ import React from 'react';
|
||||
import styles from './Community.css';
|
||||
|
||||
import ActionButton from './ActionButton';
|
||||
import ActionsMenu from 'coral-admin/src/components/ActionsMenu';
|
||||
import ActionsMenuItem from 'coral-admin/src/components/ActionsMenuItem';
|
||||
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
@@ -18,6 +20,16 @@ const User = (props) => {
|
||||
const {user, modActionButtons} = props;
|
||||
let userStatus = user.status;
|
||||
|
||||
const showSuspenUserDialog = () => props.showSuspendUserDialog({
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
});
|
||||
|
||||
const showBanUserDialog = () => props.showBanUserDialog({
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
});
|
||||
|
||||
// Do not display unless the user status is 'pending' or 'banned'.
|
||||
// This means that they have already been reviewed and approved.
|
||||
return (userStatus === 'PENDING' || userStatus === 'BANNED') &&
|
||||
@@ -28,12 +40,19 @@ const User = (props) => {
|
||||
<span>
|
||||
{user.username}
|
||||
</span>
|
||||
<ActionButton
|
||||
className={styles.banButton}
|
||||
type='BAN'
|
||||
user={user}
|
||||
showBanUserDialog={props.showBanUserDialog}
|
||||
/>
|
||||
{props.currentUser.id !== user.id &&
|
||||
<ActionsMenu icon="not_interested">
|
||||
<ActionsMenuItem
|
||||
disabled={user.status === 'BANNED'}
|
||||
onClick={showSuspenUserDialog}>
|
||||
Suspend User</ActionsMenuItem>
|
||||
<ActionsMenuItem
|
||||
disabled={user.status === 'BANNED'}
|
||||
onClick={showBanUserDialog}>
|
||||
Ban User
|
||||
</ActionsMenuItem>
|
||||
</ActionsMenu>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -78,11 +97,10 @@ const User = (props) => {
|
||||
<div className={`actions ${styles.actions}`}>
|
||||
{modActionButtons.map((action, i) =>
|
||||
<ActionButton key={i}
|
||||
type={action.toUpperCase()}
|
||||
user={user}
|
||||
approveUser={props.approveUser}
|
||||
suspendUser={props.suspendUser}
|
||||
showSuspendUserDialog={props.showSuspendUserDialog}
|
||||
type={action.toUpperCase()}
|
||||
user={user}
|
||||
approveUser={props.approveUser}
|
||||
showRejectUsernameDialog={props.showRejectUsernameDialog}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -7,14 +7,15 @@ import {Spinner} from 'coral-ui';
|
||||
|
||||
import {withSetUserStatus, withRejectUsername} from 'coral-framework/graphql/mutations';
|
||||
|
||||
import {showBanUserDialog} from 'actions/banUserDialog';
|
||||
import {showSuspendUserDialog} from 'actions/suspendUserDialog';
|
||||
|
||||
import {
|
||||
fetchAccounts,
|
||||
updateSorting,
|
||||
newPage,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog
|
||||
showRejectUsernameDialog,
|
||||
hideRejectUsernameDialog
|
||||
} from '../../../actions/community';
|
||||
|
||||
import Community from '../components/Community';
|
||||
@@ -85,16 +86,17 @@ export const withCommunityQuery = withQuery(gql`
|
||||
});
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
community: state.community.toJS()
|
||||
community: state.community.toJS(),
|
||||
currentUser: state.auth.toJS().user,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) =>
|
||||
bindActionCreators({
|
||||
fetchAccounts,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog,
|
||||
showRejectUsernameDialog,
|
||||
hideRejectUsernameDialog,
|
||||
updateSorting,
|
||||
newPage,
|
||||
}, dispatch);
|
||||
|
||||
@@ -165,7 +165,7 @@ const StreamSettings = ({updateSettings, settingsError, settings, errors}) => {
|
||||
checked={settings.autoCloseStream} />
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
{t('configure.close_after')}
|
||||
<div className={styles.settingsHeader}>{t('configure.comment_count_header')}</div>
|
||||
<br />
|
||||
<Textfield
|
||||
type='number'
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Dialog} from 'coral-ui';
|
||||
import styles from './BanUserDialog.css';
|
||||
|
||||
import Button from 'coral-ui/components/Button';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const onBanClick = (userId, commentId, commentStatus, handleBanUser, rejectComment, handleClose) => (e) => {
|
||||
e.preventDefault();
|
||||
handleBanUser({userId})
|
||||
.then(handleClose)
|
||||
.then(() => commentStatus === 'REJECTED' ? null : rejectComment({commentId}));
|
||||
};
|
||||
|
||||
const BanUserDialog = ({open, handleClose, handleBanUser, rejectComment, user, commentId, commentStatus, showRejectedNote}) => (
|
||||
<Dialog
|
||||
className={styles.dialog}
|
||||
id="banuserDialog"
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onCancel={handleClose}
|
||||
title={t('bandialog.ban_user')}>
|
||||
<span className={styles.close} onClick={handleClose}>×</span>
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h2>{t('bandialog.ban_user')}</h2>
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h3>{t('bandialog.are_you_sure', user.username)}</h3>
|
||||
<i>{showRejectedNote && t('bandialog.note')}</i>
|
||||
</div>
|
||||
<div className={styles.buttons}>
|
||||
<Button cStyle="cancel" className={styles.cancel} onClick={handleClose} raised>
|
||||
{t('bandialog.cancel')}
|
||||
</Button>
|
||||
<Button cStyle="black" className={styles.ban} onClick={onBanClick(user.id, commentId, commentStatus, handleBanUser, rejectComment, handleClose)} raised>
|
||||
{t('bandialog.yes_ban_user')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
BanUserDialog.propTypes = {
|
||||
handleBanUser: PropTypes.func.isRequired,
|
||||
handleClose: PropTypes.func.isRequired,
|
||||
rejectComment: PropTypes.func.isRequired,
|
||||
commentId: PropTypes.string,
|
||||
user: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default BanUserDialog;
|
||||
@@ -65,6 +65,20 @@ class Comment extends React.Component {
|
||||
selectionStateCSS = selected ? 'mdl-shadow--16dp' : 'mdl-shadow--2dp';
|
||||
}
|
||||
|
||||
const showSuspenUserDialog = () => props.showSuspendUserDialog({
|
||||
userId: comment.user.id,
|
||||
username: comment.user.username,
|
||||
commentId: comment.id,
|
||||
commentStatus: comment.status,
|
||||
});
|
||||
|
||||
const showBanUserDialog = () => props.showBanUserDialog({
|
||||
userId: comment.user.id,
|
||||
username: comment.user.username,
|
||||
commentId: comment.id,
|
||||
commentStatus: comment.status,
|
||||
});
|
||||
|
||||
return (
|
||||
<li
|
||||
tabIndex={props.index}
|
||||
@@ -102,11 +116,11 @@ class Comment extends React.Component {
|
||||
<ActionsMenu icon="not_interested">
|
||||
<ActionsMenuItem
|
||||
disabled={comment.user.status === 'BANNED'}
|
||||
onClick={() => props.showSuspendUserDialog(comment.user.id, comment.user.username, comment.id, comment.status)}>
|
||||
onClick={showSuspenUserDialog}>
|
||||
Suspend User</ActionsMenuItem>
|
||||
<ActionsMenuItem
|
||||
disabled={comment.user.status === 'BANNED'}
|
||||
onClick={() => props.showBanUserDialog(comment.user, comment.id, comment.status, comment.status !== 'REJECTED')}>
|
||||
onClick={showBanUserDialog}>
|
||||
Ban User
|
||||
</ActionsMenuItem>
|
||||
</ActionsMenu>
|
||||
@@ -217,6 +231,7 @@ class Comment extends React.Component {
|
||||
? <FlagBox
|
||||
actions={flagActions}
|
||||
actionSummaries={flagActionSummaries}
|
||||
viewUserDetail={() => viewUserDetail(comment.user.id)}
|
||||
/>
|
||||
: null}
|
||||
</li>
|
||||
|
||||
@@ -67,3 +67,17 @@
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
.username {
|
||||
color: #393B44;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
padding: 2px 5px;
|
||||
border-radius: 2px;
|
||||
margin-left: -5px;
|
||||
transition: background-color 200ms ease;
|
||||
&:hover {
|
||||
background-color: #E0E0E0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class FlagBox extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {actionSummaries, actions} = this.props;
|
||||
const {actionSummaries, actions, viewUserDetail} = this.props;
|
||||
const {showDetail} = this.state;
|
||||
|
||||
return (
|
||||
@@ -59,9 +59,14 @@ class FlagBox extends Component {
|
||||
<li key={i}>
|
||||
{this.reasonMap(summary.reason)} (<strong>{summary.count}</strong>)
|
||||
<ul>
|
||||
{
|
||||
actionList.map((action, j) => <li key={`${i}_${j}`} className={styles.subDetail}><span>{action.user.username}</span> {action.message}</li>)
|
||||
}
|
||||
{actionList.map((action, j) =>
|
||||
<li key={`${i}_${j}`} className={styles.subDetail}>
|
||||
<a className={styles.username} onClick={viewUserDetail}>
|
||||
{action.user.username}
|
||||
</a>
|
||||
{action.message}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -2,8 +2,6 @@ import React, {Component} from 'react';
|
||||
import key from 'keymaster';
|
||||
import styles from './styles.css';
|
||||
|
||||
import BanUserDialog from './BanUserDialog';
|
||||
import SuspendUserDialog from './SuspendUserDialog';
|
||||
import ModerationQueue from './ModerationQueue';
|
||||
import ModerationMenu from './ModerationMenu';
|
||||
import ModerationHeader from './ModerationHeader';
|
||||
@@ -29,8 +27,8 @@ export default class Moderation extends Component {
|
||||
key('esc', () => toggleModal(false));
|
||||
key('j', this.select(true));
|
||||
key('k', this.select(false));
|
||||
key('r', this.moderate(false));
|
||||
key('t', this.moderate(true));
|
||||
key('f', this.moderate(false));
|
||||
key('d', this.moderate(true));
|
||||
}
|
||||
|
||||
onClose = () => {
|
||||
@@ -88,8 +86,8 @@ export default class Moderation extends Component {
|
||||
key.unbind('esc');
|
||||
key.unbind('j');
|
||||
key.unbind('k');
|
||||
key.unbind('r');
|
||||
key.unbind('t');
|
||||
key.unbind('f');
|
||||
key.unbind('d');
|
||||
}
|
||||
|
||||
componentDidUpdate(_, prevState) {
|
||||
@@ -169,24 +167,6 @@ export default class Moderation extends Component {
|
||||
viewUserDetail={viewUserDetail}
|
||||
hideUserDetail={hideUserDetail}
|
||||
/>
|
||||
<BanUserDialog
|
||||
open={moderation.banDialog}
|
||||
user={moderation.user}
|
||||
commentId={moderation.commentId}
|
||||
commentStatus={moderation.commentStatus}
|
||||
handleClose={props.hideBanUserDialog}
|
||||
handleBanUser={props.banUser}
|
||||
showRejectedNote={moderation.showRejectedNote}
|
||||
rejectComment={props.rejectComment}
|
||||
/>
|
||||
<SuspendUserDialog
|
||||
open={moderation.suspendUserDialog.show}
|
||||
username={moderation.suspendUserDialog.username}
|
||||
userId={moderation.suspendUserDialog.userId}
|
||||
organizationName={root.settings.organizationName}
|
||||
onCancel={props.hideSuspendUserDialog}
|
||||
onPerform={this.props.suspendUser}
|
||||
/>
|
||||
<ModerationKeysModal
|
||||
hideShortcutsNote={props.hideShortcutsNote}
|
||||
shortcutsNoteVisible={moderation.shortcutsNoteVisible}
|
||||
|
||||
@@ -4,23 +4,20 @@ import {bindActionCreators} from 'redux';
|
||||
import {compose, gql} from 'react-apollo';
|
||||
import withQuery from 'coral-framework/hocs/withQuery';
|
||||
import {getDefinitionName} from 'coral-framework/utils';
|
||||
import * as notification from 'coral-admin/src/services/notification';
|
||||
import t, {timeago} from 'coral-framework/services/i18n';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
import update from 'immutability-helper';
|
||||
import truncate from 'lodash/truncate';
|
||||
import NotFoundAsset from '../components/NotFoundAsset';
|
||||
|
||||
import {withSetUserStatus, withSuspendUser, withSetCommentStatus} from 'coral-framework/graphql/mutations';
|
||||
import {withSetCommentStatus} from 'coral-framework/graphql/mutations';
|
||||
import {handleCommentChange} from '../../../graphql/utils';
|
||||
|
||||
import {fetchSettings} from 'actions/settings';
|
||||
import {showBanUserDialog} from 'actions/banUserDialog';
|
||||
import {showSuspendUserDialog} from 'actions/suspendUserDialog';
|
||||
import {
|
||||
toggleModal,
|
||||
singleView,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog,
|
||||
hideShortcutsNote,
|
||||
toggleStorySearch,
|
||||
viewUserDetail,
|
||||
@@ -138,37 +135,6 @@ class ModerationContainer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
suspendUser = async (args) => {
|
||||
this.props.hideSuspendUserDialog();
|
||||
try {
|
||||
const result = await this.props.suspendUser(args);
|
||||
if (result.data.suspendUser.errors) {
|
||||
throw result.data.suspendUser.errors;
|
||||
}
|
||||
notification.success(
|
||||
t('suspenduser.notify_suspend_until',
|
||||
this.props.moderation.suspendUserDialog.username,
|
||||
timeago(args.until)),
|
||||
);
|
||||
const {commentStatus, commentId} = this.props.moderation.suspendUserDialog;
|
||||
if (commentStatus !== 'REJECTED') {
|
||||
return this.props.rejectComment({commentId})
|
||||
.then((result) => {
|
||||
if (result.data.setCommentStatus.errors) {
|
||||
throw result.data.setCommentStatus.errors;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
notification.showMutationErrors(err);
|
||||
}
|
||||
};
|
||||
|
||||
banUser = ({userId}) => {
|
||||
return this.props.setUserStatus({userId, status: 'BANNED'});
|
||||
}
|
||||
|
||||
acceptComment = ({commentId}) => {
|
||||
return this.props.setCommentStatus({commentId, status: 'ACCEPTED'});
|
||||
}
|
||||
@@ -245,10 +211,8 @@ class ModerationContainer extends Component {
|
||||
return <Moderation
|
||||
{...this.props}
|
||||
loadMore={this.loadMore}
|
||||
banUser={this.banUser}
|
||||
acceptComment={this.acceptComment}
|
||||
rejectComment={this.rejectComment}
|
||||
suspendUser={this.suspendUser}
|
||||
activeTab={this.activeTab}
|
||||
/>;
|
||||
}
|
||||
@@ -458,11 +422,9 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
singleView,
|
||||
fetchSettings,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
hideShortcutsNote,
|
||||
toggleStorySearch,
|
||||
showSuspendUserDialog,
|
||||
hideSuspendUserDialog,
|
||||
viewUserDetail,
|
||||
hideUserDetail,
|
||||
setSortOrder,
|
||||
@@ -474,8 +436,6 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
withSetCommentStatus,
|
||||
withSetUserStatus,
|
||||
withSuspendUser,
|
||||
withQueueCountPolling,
|
||||
withModQueueQuery,
|
||||
)(ModerationContainer);
|
||||
|
||||
@@ -5,9 +5,9 @@ export const actionsMap = {
|
||||
};
|
||||
|
||||
export const menuActionsMap = {
|
||||
'REJECT': {status: 'REJECTED', text: 'reject', icon: 'close', key: 'r'},
|
||||
'REJECT': {status: 'REJECTED', text: 'reject', icon: 'close', key: 'f'},
|
||||
'REJECTED': {status: 'REJECTED', text: 'rejected', icon: 'close'},
|
||||
'APPROVE': {status: 'ACCEPTED', text: 'approve', icon: 'done', key: 't'},
|
||||
'APPROVE': {status: 'ACCEPTED', text: 'approve', icon: 'done', key: 'd'},
|
||||
'FLAGGED': {status: 'FLAGGED', text: 'flag', icon: 'flag', filter: 'Untouched'},
|
||||
'BAN': {status: 'BANNED', text: 'ban_user', icon: 'not interested'},
|
||||
'': {icon: 'done'}
|
||||
|
||||
@@ -30,7 +30,7 @@ class ResponseError {
|
||||
* Exports a HOC with the same signature as `graphql`, that will
|
||||
* apply mutation options registered in the graphRegistry.
|
||||
*/
|
||||
export default (document, config) => (WrappedComponent) => {
|
||||
export default (document, config = {}) => (WrappedComponent) => {
|
||||
config = {
|
||||
...config,
|
||||
options: config.options || {},
|
||||
|
||||
@@ -14,7 +14,7 @@ const withSkipOnErrors = (reducer) => (prev, action, ...rest) => {
|
||||
* Exports a HOC with the same signature as `graphql`, that will
|
||||
* apply query options registered in the graphRegistry.
|
||||
*/
|
||||
export default (document, config) => (WrappedComponent) => {
|
||||
export default (document, config = {}) => (WrappedComponent) => {
|
||||
config = {
|
||||
...config,
|
||||
options: config.options || {},
|
||||
|
||||
Reference in New Issue
Block a user