This commit is contained in:
okbel
2018-05-28 14:52:03 -03:00
parent 6d35c648d6
commit b0d76f2f6a
10 changed files with 417 additions and 1 deletions
@@ -0,0 +1,14 @@
import {
SHOW_REJECT_USERNAME_DIALOG,
HIDE_REJECT_USERNAME_DIALOG,
} from '../constants/rejectUsernameDialog';
export const showRejectUsernameDialog = ({ userId, username }) => ({
type: SHOW_REJECT_USERNAME_DIALOG,
userId,
username,
});
export const hideRejectUsernameDialog = () => ({
type: HIDE_REJECT_USERNAME_DIALOG,
});
@@ -0,0 +1,90 @@
.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: 400px;
top: 50%;
transform: translateY(-50%);
padding: 20px;
border-radius: 4px;
}
.header {
color: black;
font-size: 1.5em;
font-weight: 500;
margin: 0 0 8px 0;
}
.close {
display: block;
position: absolute;
top: 24px;
right: 20px;
}
.closeButton {
userSelect: none;
outline: none;
border: none;
touchAction: manipulation;
&::-moz-focus-inner: {
border: 0;
}
background: 0;
padding: 0;
font-size: 24px;
line-height: 14px;
cursor: pointer;
color: #363636;
&:hover {
color: #6b6b6b;
}
}
.legend {
padding: 0;
font-weight: bold;
}
div.radioGroup {
margin-top: 6px;
}
label.radioGroup {
&:global(.is-checked) > :global(.mdl-radio__outer-circle),
> :global(.mdl-radio__outer-circle) {
border-color: #212121;
}
> :global(.mdl-radio__inner-circle) {
background: #212121;
}
> :global(.mdl-radio__label) {
font-size: 14px;
line-height: 20px;
}
}
.messageInput {
border-radius: 3px;
width: 100%;
padding: 10px;
font-size: 14px;
box-sizing: border-box;
}
.cancel {
margin-right: 5px;
}
.perform {
min-width: 90px;
}
.buttons {
margin-top: 8px;
margin-bottom: 6px;
text-align: right;
}
@@ -0,0 +1,188 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Dialog } from 'coral-ui';
import { RadioGroup, Radio } from 'react-mdl';
import styles from './SuspendUserDialog.css';
import cn from 'classnames';
import Button from 'coral-ui/components/Button';
import t, { timeago } from 'coral-framework/services/i18n';
import { dateAdd } from 'coral-framework/utils';
const initialState = { step: 0, duration: '3' };
function durationsToDate(hours) {
// Add 1 minute more to help `timeago.js` to display the correct duration.
return dateAdd(new Date(), 'minute', hours * 60 + 1);
}
class SuspendUserDialog extends React.Component {
state = initialState;
componentWillReceiveProps(next) {
if (this.props.open && !next.open) {
this.setState(initialState);
}
}
handleDurationChange = event => {
this.setState({ duration: event.target.value });
};
handleMessageChange = event => {
this.setState({ message: event.target.value });
};
goToStep1 = () => {
this.setState({
step: 1,
message: t(
'suspenduser.email_message_suspend',
this.props.username,
this.props.organizationName,
timeago(durationsToDate(this.state.duration))
),
});
};
handlePerform = () => {
this.props.onPerform({
message: this.state.message,
// Add 1 minute more to help `timeago.js` to display the correct duration.
until: durationsToDate(this.state.duration),
});
};
renderStep0() {
const { onCancel, username } = this.props;
const { duration } = this.state;
return (
<section className="talk-admin-suspend-user-dialog-step-0">
<h1 className={styles.header}>{t('suspenduser.title_suspend')}</h1>
<p className={styles.description}>
{t('suspenduser.description_suspend', username)}
</p>
<fieldset>
<legend className={styles.legend}>
{t('suspenduser.select_duration')}
</legend>
<RadioGroup
name="status filter"
value={duration}
childContainer="div"
onChange={this.handleDurationChange}
className={styles.radioGroup}
>
<Radio value="1">{t('suspenduser.one_hour')}</Radio>
<Radio value="3">{t('suspenduser.hours', 3)}</Radio>
<Radio value="24">{t('suspenduser.hours', 24)}</Radio>
<Radio value="168">{t('suspenduser.days', 7)}</Radio>
</RadioGroup>
</fieldset>
<div className={styles.buttons}>
<Button
cStyle="white"
className={styles.cancel}
onClick={onCancel}
raised
>
{t('suspenduser.cancel')}
</Button>
<Button
cStyle="black"
className={cn(
styles.perform,
'talk-admin-suspend-user-dialog-confirm'
)}
onClick={this.goToStep1}
raised
>
{t('suspenduser.suspend_user')}
</Button>
</div>
</section>
);
}
renderStep1() {
const { message } = this.state;
const { onCancel, username } = this.props;
return (
<section className="talk-admin-suspend-user-dialog-step-1">
<h1 className={styles.header}>{t('suspenduser.title_notify')}</h1>
<p className={styles.description}>
{t('suspenduser.description_notify', username)}
</p>
<fieldset>
<legend className={styles.legend}>
{t('suspenduser.write_message')}
</legend>
<textarea
rows={5}
className={styles.messageInput}
value={message}
onChange={this.handleMessageChange}
/>
</fieldset>
<div className={styles.buttons}>
<Button
cStyle="white"
className={styles.cancel}
onClick={onCancel}
raised
>
{t('suspenduser.cancel')}
</Button>
<Button
cStyle="black"
className={cn(
styles.perform,
'talk-admin-suspend-user-dialog-send'
)}
onClick={this.handlePerform}
disabled={this.state.message.length === 0}
raised
>
{t('suspenduser.send')}
</Button>
</div>
</section>
);
}
render() {
const { open, onCancel } = this.props;
const { step } = this.state;
return (
<Dialog
className={cn(styles.dialog, 'talk-admin-suspend-user-dialog')}
onCancel={onCancel}
open={open}
>
<div className={styles.close}>
<button
aria-label="Close"
onClick={onCancel}
className={styles.closeButton}
>
×
</button>
</div>
{step === 0 && this.renderStep0()}
{step === 1 && this.renderStep1()}
</Dialog>
);
}
}
SuspendUserDialog.propTypes = {
open: PropTypes.bool.isRequired,
onPerform: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
organizationName: PropTypes.string,
username: PropTypes.string,
};
export default SuspendUserDialog;
@@ -51,6 +51,12 @@ class UserDetail extends React.Component {
username: this.props.root.user.username,
});
showRejectUsername = () =>
this.props.showBanUserDialog({
userId: this.props.root.user.id,
username: this.props.root.user.username,
});
renderLoading() {
return (
<ClickOutside onClickOutside={this.props.hideUserDetail}>
@@ -230,7 +236,7 @@ class UserDetail extends React.Component {
</ActionsMenuItem>
) : (
<ActionsMenuItem
onClick={() => this.rejectUsername({ id: user.id })}
onClick={this.showRejectUsername}
disabled={me.id === user.id}
>
{t('user_detail.reject_username')}
@@ -0,0 +1,2 @@
export const SHOW_REJECT_USERNAME_DIALOG = 'SHOW_REJECT_USERNAME_DIALOG';
export const HIDE_REJECT_USERNAME_DIALOG = 'HIDE_REJECT_USERNAME_DIALOG';
@@ -6,6 +6,7 @@ import Login from '../containers/Login';
import { FullLoading } from '../components/FullLoading';
import BanUserDialog from './BanUserDialog';
import SuspendUserDialog from './SuspendUserDialog';
import RejectUsernameDialog from './RejectUsernameDialog';
import { toggleModal as toggleShortcutModal } from '../actions/moderation';
import { logout } from 'coral-framework/actions/auth';
import { can } from 'coral-framework/services/perms';
@@ -41,6 +42,7 @@ class LayoutContainer extends React.Component {
>
<BanUserDialog />
<SuspendUserDialog />
<RejectUsernameDialog />
<UserDetail />
{children}
</Layout>
@@ -0,0 +1,79 @@
// import React, { Component } from 'react';
// import PropTypes from 'prop-types';
// import { connect } from 'react-redux';
// import { bindActionCreators } from 'redux';
// import RejectUsernameDialog from '../components/RejectUsernameDialog';
// import { hideRejectUsernameDialog } from '../actions/rejectUsernameDialog';
// import { withRejectUsername } from 'coral-framework/graphql/mutations';
// import { compose } from 'react-apollo';
// import t from 'coral-framework/services/i18n';
// class RejectUsernameDialogContainer extends Component {
// banUser = async () => {
// const {
// userId,
// commentId,
// commentStatus,
// banUser,
// setCommentStatus,
// hideBanUserDialog,
// } = this.props;
// await banUser({ id: userId, message: '' });
// 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={RejectUsernameDialog.props.open}
// onPerform={this.banUser}
// onCancel={this.props.hideBanUserDialog}
// username={this.props.username}
// />
// );
// }
// }
// BanUserDialogContainer.propTypes = {
// banUser: PropTypes.func.isRequired,
// hideBanUserDialog: PropTypes.func,
// open: PropTypes.bool,
// username: PropTypes.string,
// commentStatus: PropTypes.string,
// };
// const mapStateToProps = ({
// banUserDialog: { open, userId, username, commentId, commentStatus },
// }) => ({
// open,
// userId,
// username,
// commentId,
// commentStatus,
// });
// const mapDispatchToProps = dispatch => ({
// ...bindActionCreators(
// {
// hideRejectUsernameDialog,
// },
// dispatch
// ),
// });
// export default compose(
// connect(mapStateToProps, mapDispatchToProps),
// withRejectUsername
// )(RejectUsernameDialogContainer);
@@ -23,11 +23,13 @@ import {
withUnbanUser,
withUnsuspendUser,
withRejectUsername,
withPostFlag,
} from 'coral-framework/graphql/mutations';
import UserDetailComment from './UserDetailComment';
import update from 'immutability-helper';
import { showBanUserDialog } from 'actions/banUserDialog';
import { showSuspendUserDialog } from 'actions/suspendUserDialog';
import { showRejectUsernameDialog } from 'actions/rejectUsernameDialog';
const commentConnectionFragment = gql`
fragment CoralAdmin_UserDetail_CommentConnection on CommentConnection {
@@ -279,6 +281,7 @@ const mapDispatchToProps = dispatch => ({
{
showBanUserDialog,
showSuspendUserDialog,
showRejectUsernameDialog,
changeTab,
clearUserDetailSelections,
toggleSelectCommentInUserDetail,
@@ -297,5 +300,6 @@ export default compose(
withUnbanUser,
withUnsuspendUser,
withRejectUsername,
withPostFlag,
withRouter
)(UserDetailContainer);
+2
View File
@@ -5,10 +5,12 @@ import moderation from './moderation';
import install from './install';
import banUserDialog from './banUserDialog';
import suspendUserDialog from './suspendUserDialog';
import rejectUsernameDialog from './rejectUsernameDialog';
import userDetail from './userDetail';
import ui from './ui';
export default {
rejectUsernameDialog,
banUserDialog,
configure,
suspendUserDialog,
@@ -0,0 +1,29 @@
import {
SHOW_REJECT_USERNAME_DIALOG,
HIDE_REJECT_USERNAME_DIALOG,
} from '../constants/rejectUsernameDialog';
const initialState = {
open: false,
userId: null,
username: '',
};
export default function rejectUsernameDialog(state = initialState, action) {
switch (action.type) {
case SHOW_REJECT_USERNAME_DIALOG:
return {
...state,
open: true,
userId: action.userId,
username: action.username,
};
case HIDE_REJECT_USERNAME_DIALOG:
return {
...state,
open: false,
};
default:
return state;
}
}