diff --git a/client/coral-admin/src/actions/moderation.js b/client/coral-admin/src/actions/moderation.js
index 75ec5fc09..b7117181e 100644
--- a/client/coral-admin/src/actions/moderation.js
+++ b/client/coral-admin/src/actions/moderation.js
@@ -6,3 +6,15 @@ export const singleView = () => ({type: actions.SINGLE_VIEW});
// Ban User Dialog
export const showBanUserDialog = (user, commentId, showRejectedNote) => ({type: actions.SHOW_BANUSER_DIALOG, user, commentId, showRejectedNote});
export const hideBanUserDialog = (showDialog) => ({type: actions.HIDE_BANUSER_DIALOG, showDialog});
+
+// hide shortcuts note
+export const hideShortcutsNote = () => {
+ try {
+ window.localStorage.setItem('coral:shortcutsNote', 'hide');
+ } catch (e) {
+
+ // above will fail in Safari private mode
+ }
+
+ return {type: actions.HIDE_SHORTCUTS_NOTE};
+};
diff --git a/client/coral-admin/src/components/ModerationKeysModal.css b/client/coral-admin/src/components/ModerationKeysModal.css
index a030b6efa..73697d930 100644
--- a/client/coral-admin/src/components/ModerationKeysModal.css
+++ b/client/coral-admin/src/components/ModerationKeysModal.css
@@ -18,3 +18,47 @@
border-radius: 4px;
border: 1px solid #999;
}
+
+.callToAction {
+ position: fixed;
+ left: 10px;
+ bottom: 10px;
+ width: 280px;
+ height: 200px;
+ background: white;
+ padding: 15px;
+ box-sizing: border-box;
+ box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.15);
+
+ .ctaHeader {
+ font-size: 16px;
+ margin-bottom: 5px;
+ }
+
+ ul {
+ padding-left: 0;
+ list-style: none;
+ margin: 0 0 10px 0;
+ }
+
+ p, li {
+ font-size: 12px;
+ margin-bottom: 5px;
+ }
+
+ li span:first-child, p:last-child span:first-child {
+ display: inline-block;
+ width: 120px;
+ }
+}
+
+.closeButton {
+ float: right;
+ font-size: 20px;
+ cursor: pointer;
+}
+
+.smallKey {
+ padding: 5px;
+ border: 1px solid #ccc;
+}
diff --git a/client/coral-admin/src/components/ModerationKeysModal.js b/client/coral-admin/src/components/ModerationKeysModal.js
index 5a951e588..3d43c621d 100644
--- a/client/coral-admin/src/components/ModerationKeysModal.js
+++ b/client/coral-admin/src/components/ModerationKeysModal.js
@@ -1,9 +1,11 @@
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations.json';
-import React from 'react';
+import React, {PropTypes} from 'react';
import Modal from 'components/Modal';
import styles from './ModerationKeysModal.css';
+const lang = new I18n(translations);
+
const shortcuts = [
{
title: 'modqueue.navigation',
@@ -23,29 +25,50 @@ const shortcuts = [
}
];
-export default ({open, onClose}) => (
-
- {lang.t('modqueue.shortcuts')}
-
- {shortcuts.map((shortcut, i) => (
-
-
-
- | {lang.t(shortcut.title)} |
-
-
-
- {Object.keys(shortcut.shortcuts).map(key => (
-
- | {key} |
- {lang.t(shortcut.shortcuts[key])} |
-
- ))}
-
-
- ))}
-
-
-);
+export default class ModerationKeysModal extends React.Component {
-const lang = new I18n(translations);
+ static propTypes = {
+ hideShortcutsNote: PropTypes.func.isRequired,
+ shortcutsNoteVisible: PropTypes.string.isRequired
+ }
+
+ render () {
+ const {open, onClose, hideShortcutsNote, shortcutsNoteVisible} = this.props;
+ return (
+
+
+
×
+
{lang.t('modqueue.mod-faster')}
+
{lang.t('modqueue.try-these')}:
+
+ - {lang.t('modqueue.approve')} t
+ - {lang.t('modqueue.reject')} r
+
+
{lang.t('modqueue.view-more-shortcuts')} {lang.t('modqueue.shift-key')} + /
+
+
+ {lang.t('modqueue.shortcuts')}
+
+ {shortcuts.map((shortcut, i) => (
+
+
+
+ | {lang.t(shortcut.title)} |
+
+
+
+ {Object.keys(shortcut.shortcuts).map(key => (
+
+ | {key} |
+ {lang.t(shortcut.shortcuts[key])} |
+
+ ))}
+
+
+ ))}
+
+
+
+ );
+ }
+}
diff --git a/client/coral-admin/src/constants/moderation.js b/client/coral-admin/src/constants/moderation.js
index f50cbb439..14672146e 100644
--- a/client/coral-admin/src/constants/moderation.js
+++ b/client/coral-admin/src/constants/moderation.js
@@ -2,3 +2,4 @@ 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';
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index 3487f9fde..2c556f36f 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -10,7 +10,7 @@ import {banUser, setCommentStatus} from '../../graphql/mutations';
import {fetchSettings} from 'actions/settings';
import {updateAssets} from 'actions/assets';
-import {toggleModal, singleView, showBanUserDialog, hideBanUserDialog} from 'actions/moderation';
+import {toggleModal, singleView, showBanUserDialog, hideBanUserDialog, hideShortcutsNote} from 'actions/moderation';
import {Spinner} from 'coral-ui';
import BanUserDialog from '../../components/BanUserDialog';
@@ -183,6 +183,8 @@ class ModerationContainer extends Component {
rejectComment={props.rejectComment}
/>
@@ -204,6 +206,7 @@ const mapDispatchToProps = dispatch => ({
fetchSettings: () => dispatch(fetchSettings()),
showBanUserDialog: (user, commentId, showRejectedNote) => dispatch(showBanUserDialog(user, commentId, showRejectedNote)),
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
+ hideShortcutsNote: () => dispatch(hideShortcutsNote()),
});
export default compose(
diff --git a/client/coral-admin/src/reducers/moderation.js b/client/coral-admin/src/reducers/moderation.js
index cf270543c..a40ac865f 100644
--- a/client/coral-admin/src/reducers/moderation.js
+++ b/client/coral-admin/src/reducers/moderation.js
@@ -6,7 +6,8 @@ const initialState = Map({
modalOpen: false,
user: Map({}),
commentId: null,
- banDialog: false
+ banDialog: false,
+ shortcutsNoteVisible: window.localStorage.getItem('coral:shortcutsNote') || 'show'
});
export default function moderation (state = initialState, action) {
@@ -31,6 +32,9 @@ export default function moderation (state = initialState, action) {
case actions.SINGLE_VIEW:
return state
.set('singleView', !state.get('singleView'));
+ case actions.HIDE_SHORTCUTS_NOTE:
+ return state
+ .set('shortcutsNoteVisible', 'hide');
default :
return state;
}
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index f317b71a7..9e167232e 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -44,6 +44,10 @@
"close": "Close",
"actions": "Actions",
"navigation": "Navigation",
+ "mod-faster": "Moderate faster with keyboard shortcuts",
+ "try-these": "Try these",
+ "view-more-shortcuts": "View more shortcuts",
+ "shift-key": "⇧",
"approve": "Approve comment",
"reject": "Reject comment",
"nextcomment": "Go to the next comment",
@@ -226,6 +230,10 @@
"rejected": "rechazado",
"flagged": "marcado",
"shortcuts": "Atajos de teclado",
+ "mod-faster": "Moderar más rápido con atajos del teclado",
+ "try-these": "Intenta estos",
+ "view-more-shortcuts": "Ver más atajos",
+ "shift-key": "⇧",
"close": "Cerrar",
"emptyqueue": "No se encontro ningún usuario. Están escondidos.",
"showshortcuts": "Mostrar atajos",
diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/components/Comment.js
index b2289716a..4f8426159 100644
--- a/client/coral-embed-stream/src/components/Comment.js
+++ b/client/coral-embed-stream/src/components/Comment.js
@@ -64,6 +64,8 @@ class Comment extends React.Component {
currentUser: PropTypes.shape({
id: PropTypes.string.isRequired
}),
+ charCountEnable: PropTypes.bool.isRequired,
+ maxCharCount: PropTypes.number,
comment: PropTypes.shape({
depth: PropTypes.number,
action_summaries: PropTypes.array.isRequired,
@@ -121,6 +123,8 @@ class Comment extends React.Component {
ignoreUser,
disableReply,
commentIsIgnored,
+ maxCharCount,
+ charCountEnable,
} = this.props;
const likeSummary = getActionSummary('LikeActionSummary', comment);
@@ -256,6 +260,8 @@ class Comment extends React.Component {
commentPostedHandler={() => {
setActiveReplyBox('');
}}
+ charCountEnable={charCountEnable}
+ maxCharCount={maxCharCount}
setActiveReplyBox={setActiveReplyBox}
parentId={parentId || comment.id}
addNotification={addNotification}
@@ -288,6 +294,8 @@ class Comment extends React.Component {
addCommentTag={addCommentTag}
removeCommentTag={removeCommentTag}
ignoreUser={ignoreUser}
+ charCountEnable={charCountEnable}
+ maxCharCount={maxCharCount}
showSignInDialog={showSignInDialog}
reactKey={reply.id}
key={reply.id}
diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js
index cedd1cc1c..2875d0397 100644
--- a/client/coral-embed-stream/src/components/Stream.js
+++ b/client/coral-embed-stream/src/components/Stream.js
@@ -91,7 +91,8 @@ class Stream extends React.Component {
premod={asset.settings.moderation}
isReply={false}
authorId={user.id}
- charCount={asset.settings.charCountEnable && asset.settings.charCount} />
+ charCountEnable={asset.settings.charCountEnable}
+ maxCharCount={asset.settings.charCount} />
: null
}
@@ -125,7 +126,10 @@ class Stream extends React.Component {
key={highlightedComment.id}
commentIsIgnored={commentIsIgnored}
reactKey={highlightedComment.id}
- comment={highlightedComment} />
+ comment={highlightedComment}
+ charCountEnable={asset.settings.charCountEnable}
+ maxCharCount={asset.settings.charCount}
+ />
:
)
}
diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css
index d66e43e27..087859e89 100644
--- a/client/coral-embed-stream/style/default.css
+++ b/client/coral-embed-stream/style/default.css
@@ -195,7 +195,8 @@ hr {
padding: 5px;
min-height: 100px;
margin-top: 10px;
- font-size: 14px;
+ font-size: 16px;
+ border: 1px solid #ccc;
}
.coral-plugin-commentbox-button-container {
diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js
index c4e0df432..6dd2d486f 100644
--- a/client/coral-plugin-commentbox/CommentBox.js
+++ b/client/coral-plugin-commentbox/CommentBox.js
@@ -123,11 +123,11 @@ class CommentBox extends Component {
handleChange = e => this.setState({body: e.target.value});
render () {
- const {styles, isReply, authorId, charCount} = this.props;
+ const {styles, isReply, authorId, maxCharCount} = this.props;
let {cancelButtonClicked} = this.props;
const length = this.state.body.length;
- const enablePostComment = !length || (charCount && length > charCount);
+ const enablePostComment = !length || (maxCharCount && length > maxCharCount);
if (isReply && typeof cancelButtonClicked !== 'function') {
console.warn('the CommentBox component should have a cancelButtonClicked callback defined if it lives in a Reply');
@@ -152,8 +152,8 @@ class CommentBox extends Component {
onChange={this.handleChange}
rows={3}/>
- charCount ? `${name}-char-max` : ''}`}>
- {charCount && `${charCount - length} ${lang.t('characters-remaining')}`}
+
maxCharCount ? `${name}-char-max` : ''}`}>
+ {maxCharCount && `${maxCharCount - length} ${lang.t('characters-remaining')}`}
{
* @param {String} status the new status of the comment
*/
-const setCommentStatus = ({loaders: {Comments}}, {id, status}) => {
+const setCommentStatus = ({user, loaders: {Comments}}, {id, status}) => {
return CommentsService
- .setStatus(id, status)
+ .pushStatus(id, status, user ? user.id : null)
.then((comment) => {
// If the loaders are present, clear the caches for these values because we
diff --git a/services/comments.js b/services/comments.js
index 7e9cca20f..d77118705 100644
--- a/services/comments.js
+++ b/services/comments.js
@@ -213,7 +213,15 @@ module.exports = class CommentsService {
* @return {Promise}
*/
static pushStatus(id, status, assigned_by = null) {
- return CommentModel.update({id}, {
+
+ // Check to see if the comment status is in the allowable set of statuses.
+ if (STATUSES.indexOf(status) === -1) {
+
+ // Comment status is not supported! Error out here.
+ return Promise.reject(new Error(`status ${status} is not supported`));
+ }
+
+ return CommentModel.findOneAndUpdate({id}, {
$push: {
status_history: {
type: status,
@@ -292,25 +300,4 @@ module.exports = class CommentsService {
return CommentModel.find(query);
}
-
- /**
- * Sets Comment Status
- * @param {String} id identifier of the comment (uuid)
- * @param {String} status the new status of the comment
- * @return {Promise}
- */
-
- static setStatus(id, status) {
-
- // Check to see if the comment status is in the allowable set of statuses.
- if (STATUSES.indexOf(status) === -1) {
-
- // Comment status is not supported! Error out here.
- return Promise.reject(new Error(`status ${status} is not supported`));
- }
-
- return CommentModel.findOneAndUpdate({id}, {
- $set: {status}
- });
- }
};