Merge pull request #376 from coralproject/keyboard-shortcuts

Keyboard shortcuts
This commit is contained in:
Kim Gardner
2017-03-03 10:03:52 -05:00
committed by GitHub
6 changed files with 51 additions and 78 deletions
@@ -1,71 +0,0 @@
import React from 'react';
import timeago from 'timeago.js';
import Linkify from 'react-linkify';
import styles from './ModerationList.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations.json';
import Highlighter from 'react-highlight-words';
import {Icon} from 'coral-ui';
import ActionButton from './ActionButton';
const linkify = new Linkify();
// Render a single comment for the list
const Comment = props => {
const {comment, author} = props;
let authorStatus = author.status;
const links = linkify.getMatches(comment.body);
return (
<li tabIndex={props.index} className={`mdl-card mdl-shadow--2dp ${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
<div className={styles.itemHeader}>
<div className={styles.author}>
<span>{author.username || lang.t('comment.anon')}</span>
<span className={styles.created}>{timeago().format(comment.createdAt || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}</span>
{comment.flagged ? <p className={styles.flagged}>{lang.t('comment.flagged')}</p> : null}
</div>
<div className={styles.sideActions}>
{links ?
<span className={styles.hasLinks}><Icon name='error_outline'/> Contains Link</span> : null}
<div className={`actions ${styles.actions}`}>
{props.modActions.map(
(action, i) =>
<ActionButton
option={action}
key={i}
type='COMMENTS'
comment={comment}
user={author}
menuOptionsMap={props.menuOptionsMap}
onClickAction={props.onClickAction}
onClickShowBanDialog={props.onClickShowBanDialog}/>
)}
</div>
{authorStatus === 'banned' ?
<span className={styles.banned}><Icon name='error_outline'/> {lang.t('comment.banned_user')}</span> : null}
</div>
</div>
<div className={styles.itemBody}>
<span className={styles.body}>
<Linkify component='span' properties={{style: linkStyles}}>
<Highlighter
searchWords={props.suspectWords}
textToHighlight={comment.body} />
</Linkify>
</span>
</div>
</li>
);
};
export default Comment;
const linkStyles = {
backgroundColor: 'rgb(255, 219, 135)',
padding: '1px 2px'
};
const lang = new I18n(translations);
@@ -178,6 +178,10 @@
}
}
.selected {
border-radius: 10px;
}
.actionButton {
transform: scale(.8);
@@ -17,22 +17,50 @@ import ModerationQueue from './ModerationQueue';
import ModerationMenu from './components/ModerationMenu';
import ModerationHeader from './components/ModerationHeader';
import NotFoundAsset from './components/NotFoundAsset';
import ModerationKeysModal from '../../components/ModerationKeysModal';
class ModerationContainer extends Component {
state = {
selectedIndex: 0
}
componentWillMount() {
const {toggleModal, singleView} = this.props;
const {selectedIndex} = this.state;
this.props.fetchSettings();
key('s', () => singleView());
key('shift+/', () => toggleModal(true));
key('esc', () => toggleModal(false));
key('j', () => this.setState({selectedIndex: selectedIndex + 1}));
key('k', () => this.setState({selectedIndex: selectedIndex > 0 ? selectedIndex + 1 : selectedIndex}));
key('r', () => this.moderate(false));
key('t', () => this.moderate(true));
}
moderate = (accept) => {
const {data, route, acceptComment, rejectComment} = this.props;
const {selectedIndex} = this.state;
const activeTab = route.path === ':id' ? 'premod' : route.path;
const comments = data[activeTab];
const commentId = {commentId: comments[selectedIndex].id};
if (accept) {
acceptComment(commentId);
} else {
rejectComment(commentId);
}
}
componentWillUnmount() {
key.unbind('s');
key.unbind('shift+/');
key.unbind('esc');
key.unbind('j');
key.unbind('k');
key.unbind('r');
key.unbind('t');
}
componentWillReceiveProps(nextProps) {
@@ -43,7 +71,7 @@ class ModerationContainer extends Component {
}
render () {
const {data, moderation, settings, assets, modQueueResort, ...props} = this.props;
const {data, moderation, settings, assets, modQueueResort, onClose, ...props} = this.props;
const providedAssetId = this.props.params.id;
const activeTab = this.props.route.path === ':id' ? 'premod' : this.props.route.path;
@@ -81,6 +109,8 @@ class ModerationContainer extends Component {
currentAsset={asset}
comments={comments}
activeTab={activeTab}
singleView={moderation.singleView}
selectedIndex={this.state.selectedIndex}
suspectWords={settings.wordlist.suspect}
showBanUserDialog={props.showBanUserDialog}
acceptComment={props.acceptComment}
@@ -92,6 +122,9 @@ class ModerationContainer extends Component {
handleClose={props.hideBanUserDialog}
handleBanUser={props.banUser}
/>
<ModerationKeysModal
open={moderation.modalOpen}
onClose={onClose}/>
</div>
);
}
@@ -1,15 +1,16 @@
import React, {PropTypes} from 'react';
import Comment from './components/Comment';
import styles from './components/styles.css';
import EmptyCard from '../../components/EmptyCard';
import {actionsMap} from './helpers/moderationQueueActionsMap';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations';
const lang = new I18n(translations);
const ModerationQueue = ({comments, ...props}) => {
const ModerationQueue = ({comments, selectedIndex, singleView, ...props}) => {
return (
<div id="moderationList">
<div id="moderationList" className={`${styles.list} ${singleView ? styles.singleView : ''}`}>
<ul style={{paddingLeft: 0}}>
{
comments.length
@@ -20,6 +21,7 @@ const ModerationQueue = ({comments, ...props}) => {
index={i}
comment={comment}
commentType={props.activeTab}
selected={i === selectedIndex}
suspectWords={props.suspectWords}
actions={actionsMap[status]}
showBanUserDialog={props.showBanUserDialog}
@@ -22,7 +22,7 @@ const Comment = ({actions = [], ...props}) => {
const actionSummaries = props.comment.action_summaries;
return (
<li tabIndex={props.index}
className={`mdl-card mdl-shadow--2dp ${styles.Comment} ${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
className={`mdl-card ${props.selected ? 'mdl-shadow--8dp' : 'mdl-shadow--2dp'} ${styles.Comment} ${styles.listItem} ${props.selected ? styles.selected : ''}`}>
<div className={styles.container}>
<div className={styles.itemHeader}>
<div className={styles.author}>
@@ -136,7 +136,7 @@ span {
display: none;
}
&.singleView .listItem.activeItem {
&.singleView .listItem.selected {
display: block;
height: 100%;
font-size: 1.5em;
@@ -168,7 +168,7 @@ span {
min-width: 400px;
margin: 0 auto;
position: relative;
transition: box-shadow 200ms;
transition: all 200ms;
margin-top: 0;
padding: 4px 0 0;
min-height: 220px;
@@ -186,6 +186,11 @@ span {
border-bottom: none;
}
&.selected {
max-width: 670px;
max-height: 410px;
}
.context {
a {
color: #f36451;