Merge branch 'master' into provide-valid-optimistic-status

This commit is contained in:
Kim Gardner
2017-08-01 11:59:34 -04:00
committed by GitHub
15 changed files with 165 additions and 76 deletions
+12 -6
View File
@@ -1,5 +1,5 @@
import React from 'react';
import {Router, Route, IndexRedirect, Redirect} from 'react-router';
import {Router, Route, IndexRedirect, IndexRoute} from 'react-router';
import {history} from 'coral-framework/helpers/router';
import Configure from 'routes/Configure';
@@ -15,7 +15,7 @@ const routes = (
<div>
<Route exact path="/admin/install" component={Install}/>
<Route path='/admin' component={Layout}>
<IndexRedirect to='/admin/moderate/all' />
<IndexRedirect to='/admin/moderate' />
<Route path='configure' component={Configure} />
<Route path='stories' component={Stories} />
<Route path='dashboard' component={Dashboard} />
@@ -35,10 +35,15 @@ const routes = (
{/* Moderation Routes */}
<Route path='moderate' component={ModerationLayout}>
<IndexRoute components={Moderation} />
<Route path='all' components={Moderation}>
<Route path=':id' components={Moderation} />
</Route>
<Route path='accepted' components={Moderation}>
<Route path='new' components={Moderation}>
<Route path=':id' components={Moderation} />
</Route>
<Route path='approved' components={Moderation}>
<Route path=':id' components={Moderation} />
</Route>
<Route path='premod' components={Moderation}>
@@ -47,11 +52,12 @@ const routes = (
<Route path='rejected' components={Moderation}>
<Route path=':id' components={Moderation} />
</Route>
<Route path='flagged' components={Moderation}>
<Route path='reported' components={Moderation}>
<Route path=':id' components={Moderation} />
</Route>
<Redirect from=':id' to='all/:id' />
<IndexRedirect to='all' />
<Route path=':id' components={Moderation} />
</Route>
</Route>
</div>
@@ -60,7 +60,7 @@ class UserDetailComment extends React.Component {
</div>
<div className={styles.story}>
Story: {comment.asset.title}
{<Link to={`/admin/moderate/all/${comment.asset.id}`}>{t('modqueue.moderate')}</Link>}
{<Link to={`/admin/moderate/${comment.asset.id}`}>{t('modqueue.moderate')}</Link>}
</div>
<CommentAnimatedEdit body={comment.body}>
<div className={styles.bodyContainer}>
+26 -13
View File
@@ -1,7 +1,7 @@
import update from 'immutability-helper';
import * as notification from 'coral-admin/src/services/notification';
const queues = ['all', 'premod', 'flagged', 'accepted', 'rejected'];
const queues = ['all', 'premod', 'reported', 'approved', 'rejected', 'new'];
const limit = 10;
const ascending = (a, b) => {
@@ -64,23 +64,35 @@ function addCommentToQueue(root, queue, comment, sort) {
return update(root, changes);
}
/**
* getCommentQueues determines in which queues a comment should be placed.
*/
function getCommentQueues(comment) {
const queues = ['all'];
if (comment.status === 'ACCEPTED') {
queues.push('accepted');
}
else if (comment.status === 'REJECTED') {
const isFlagged = comment.actions && comment.actions.some((a) => a.__typename === 'FlagAction');
switch(comment.status) {
case 'ACCEPTED':
queues.push('approved');
break;
case 'REJECTED':
queues.push('rejected');
}
else if (comment.status === 'PREMOD') {
break;
case 'PREMOD':
queues.push('premod');
queues.push('new');
if (isFlagged) {
queues.push('reported');
}
break;
case 'NONE':
queues.push('new');
if (isFlagged) {
queues.push('reported');
}
break;
}
if (
['NONE', 'PREMOD'].indexOf(comment.status) >= 0
&& comment.actions && comment.actions.some((a) => a.__typename === 'FlagAction')
) {
queues.push('flagged');
}
return queues;
}
@@ -98,6 +110,7 @@ function getCommentQueues(comment) {
*/
export function handleCommentChange(root, comment, sort, notify) {
let next = root;
const nextQueues = getCommentQueues(comment);
let notificationShown = false;
@@ -100,7 +100,7 @@ class Comment extends React.Component {
<div className={styles.moderateArticle}>
Story: {comment.asset.title}
{!props.currentAsset &&
<Link to={`/admin/moderate/all/${comment.asset.id}`}>{t('modqueue.moderate')}</Link>}
<Link to={`/admin/moderate/${comment.asset.id}`}>{t('modqueue.moderate')}</Link>}
</div>
<CommentAnimatedEdit body={comment.body}>
<div className={styles.itemBody}>
@@ -58,8 +58,7 @@ export default class Moderation extends Component {
}
getComments = () => {
const {root, route} = this.props;
const activeTab = route.path === ':id' ? 'premod' : route.path;
const {root, activeTab} = this.props;
return root[activeTab].nodes;
}
@@ -101,25 +100,28 @@ export default class Moderation extends Component {
}
render () {
const {root, moderation, settings, viewUserDetail, hideUserDetail, activeTab, ...props} = this.props;
const {root, moderation, settings, viewUserDetail, hideUserDetail, activeTab, getModPath, premodEnabled, ...props} = this.props;
const assetId = this.props.params.id;
const {asset} = root;
const comments = root[activeTab];
let activeTabCount;
switch(activeTab) {
case 'all':
activeTabCount = root.allCount;
break;
case 'accepted':
activeTabCount = root.acceptedCount;
case 'new':
activeTabCount = root.newCount;
break;
case 'approved':
activeTabCount = root.approvedCount;
break;
case 'premod':
activeTabCount = root.premodCount;
break;
case 'flagged':
activeTabCount = root.flaggedCount;
case 'reported':
activeTabCount = root.reportedCount;
break;
case 'rejected':
activeTabCount = root.rejectedCount;
@@ -137,12 +139,16 @@ export default class Moderation extends Component {
<ModerationMenu
asset={asset}
allCount={root.allCount}
acceptedCount={root.acceptedCount}
newCount={root.newCount}
getModPath={getModPath}
approvedCount={root.approvedCount}
premodCount={root.premodCount}
rejectedCount={root.rejectedCount}
flaggedCount={root.flaggedCount}
reportedCount={root.reportedCount}
selectSort={this.props.setSortOrder}
sort={this.props.moderation.sortOrder}
premodEnabled={premodEnabled}
activeTab={activeTab}
/>
<ModerationQueue
data={this.props.data}
@@ -4,52 +4,72 @@ import styles from './styles.css';
import {SelectField, Option} from 'react-mdl-selectfield';
import {Icon} from 'coral-ui';
import {Link} from 'react-router';
import cn from 'classnames';
import t from 'coral-framework/services/i18n';
const ModerationMenu = (
{asset, allCount, acceptedCount, premodCount, rejectedCount, flaggedCount, selectSort, sort}
) => {
function getPath (type) {
return asset ? `/admin/moderate/${type}/${asset.id}` : `/admin/moderate/${type}`;
}
const ModerationMenu = ({
asset = {},
allCount,
approvedCount,
premodCount,
newCount,
rejectedCount,
reportedCount,
selectSort,
sort,
premodEnabled,
getModPath,
activeTab
}) => {
return (
<div className="mdl-tabs">
<div className={`mdl-tabs__tab-bar ${styles.tabBar}`}>
<div className={styles.tabBarPadding} />
<div>
{
premodEnabled ? (
<Link
to={getModPath('premod', asset.id)}
className={cn('mdl-tabs__tab', styles.tab, {[styles.active]: activeTab === 'premod'})}
activeClassName={styles.active}>
<Icon name='access_time' className={styles.tabIcon} /> {t('modqueue.premod')} <CommentCount count={premodCount} />
</Link>
) : (
<Link
to={getModPath('new', asset.id)}
className={cn('mdl-tabs__tab', styles.tab, {[styles.active]: activeTab === 'new'})}
activeClassName={styles.active}>
<Icon name='question_answer' className={styles.tabIcon} /> {t('modqueue.new')} <CommentCount count={newCount} />
</Link>
)
}
<Link
to={getPath('all')}
className={`mdl-tabs__tab ${styles.tab}`}
to={getModPath('reported', asset.id)}
className={cn('mdl-tabs__tab', styles.tab, {[styles.active]: activeTab === 'reported'})}
activeClassName={styles.active}>
<Icon name='question_answer' className={styles.tabIcon} /> {t('modqueue.all')} <CommentCount count={allCount} />
<Icon name='flag' className={styles.tabIcon} /> {t('modqueue.reported')} <CommentCount count={reportedCount} />
</Link>
<Link
to={getPath('premod')}
className={`mdl-tabs__tab ${styles.tab}`}
to={getModPath('approved', asset.id)}
className={cn('mdl-tabs__tab', styles.tab, {[styles.active]: activeTab === 'approved'})}
activeClassName={styles.active}>
<Icon name='access_time' className={styles.tabIcon} /> {t('modqueue.premod')} <CommentCount count={premodCount} />
<Icon name='check' className={styles.tabIcon} /> {t('modqueue.approved')} <CommentCount count={approvedCount} />
</Link>
<Link
to={getPath('flagged')}
className={`mdl-tabs__tab ${styles.tab}`}
activeClassName={styles.active}>
<Icon name='flag' className={styles.tabIcon} /> {t('modqueue.flagged')} <CommentCount count={flaggedCount} />
</Link>
<Link
to={getPath('accepted')}
className={`mdl-tabs__tab ${styles.tab}`}
activeClassName={styles.active}>
<Icon name='check' className={styles.tabIcon} /> {t('modqueue.approved')} <CommentCount count={acceptedCount} />
</Link>
<Link
to={getPath('rejected')}
className={`mdl-tabs__tab ${styles.tab}`}
to={getModPath('rejected', asset.id)}
className={cn('mdl-tabs__tab', styles.tab, {[styles.active]: activeTab === 'rejected'})}
activeClassName={styles.active}>
<Icon name='close' className={styles.tabIcon} /> {t('modqueue.rejected')} <CommentCount count={rejectedCount} />
</Link>
<Link
to={getModPath('all', asset.id)}
className={cn('mdl-tabs__tab', styles.tab, {[styles.active]: activeTab === 'all'})}
activeClassName={styles.active}>
<Icon name='question_answer' className={styles.tabIcon} /> {t('modqueue.all')} <CommentCount count={allCount} />
</Link>
</div>
<SelectField
className={styles.selectField}
@@ -68,7 +88,7 @@ ModerationMenu.propTypes = {
allCount: PropTypes.number.isRequired,
premodCount: PropTypes.number.isRequired,
rejectedCount: PropTypes.number.isRequired,
flaggedCount: PropTypes.number.isRequired,
reportedCount: PropTypes.number.isRequired,
asset: PropTypes.shape({
id: PropTypes.string
})
@@ -64,6 +64,7 @@ const StorySearch = (props) => {
? <Spinner />
: assets.map((story, i) => {
const storyOpen = story.closedAt === null || new Date(story.closedAt) > new Date();
return <Story
key={i}
id={story.id}
@@ -8,6 +8,7 @@ import t from 'coral-framework/services/i18n';
import update from 'immutability-helper';
import truncate from 'lodash/truncate';
import NotFoundAsset from '../components/NotFoundAsset';
import {isPremod, getModPath} from '../../../utils';
import {withSetCommentStatus} from 'coral-framework/graphql/mutations';
import {handleCommentChange} from '../../../graphql/utils';
@@ -37,7 +38,18 @@ function prepareNotificationText(text) {
class ModerationContainer extends Component {
subscriptions = [];
get activeTab() { return this.props.route.path; }
get activeTab() {
const {root: {asset, settings}, router, route} = this.props;
// Grab premod from asset or from settings
const premod = !router.params.id ? settings.moderation : asset.settings.moderation;
const queue = isPremod(premod) ? 'premod' : 'new';
const activeTab = route.path && route.path !== ':id' ? route.path : queue;
return activeTab;
}
subscribeToUpdates(variables = this.props.data.variables) {
const sub1 = this.props.data.subscribeToMore({
@@ -153,13 +165,16 @@ class ModerationContainer extends Component {
case 'all':
variables.statuses = null;
break;
case 'accepted':
case 'new':
variables.statuses = ['NONE', 'PREMOD'];
break;
case 'approved':
variables.statuses = ['ACCEPTED'];
break;
case 'premod':
variables.statuses = ['PREMOD'];
break;
case 'flagged':
case 'reported':
variables.statuses = ['NONE', 'PREMOD'];
variables.action_type = 'FLAG';
break;
@@ -184,7 +199,7 @@ class ModerationContainer extends Component {
};
render () {
const {root, root: {asset}, data, params: {id: assetId}} = this.props;
const {root, root: {asset, settings}, data, params: {id: assetId}} = this.props;
if (data.error) {
return <div>Error</div>;
@@ -209,10 +224,12 @@ class ModerationContainer extends Component {
return <Moderation
{...this.props}
getModPath={getModPath}
loadMore={this.loadMore}
acceptComment={this.acceptComment}
rejectComment={this.rejectComment}
activeTab={this.activeTab}
premodEnabled={assetId ? isPremod(asset.settings.moderation) : isPremod(settings.moderation)}
/>;
}
}
@@ -304,7 +321,14 @@ const withModQueueQuery = withQuery(gql`
}) {
...CoralAdmin_Moderation_CommentConnection
}
accepted: comments(query: {
new: comments(query: {
statuses: [NONE, PREMOD],
asset_id: $asset_id,
sort: $sort
}) {
...CoralAdmin_Moderation_CommentConnection
}
approved: comments(query: {
statuses: [ACCEPTED],
asset_id: $asset_id,
sort: $sort
@@ -318,7 +342,7 @@ const withModQueueQuery = withQuery(gql`
}) {
...CoralAdmin_Moderation_CommentConnection
}
flagged: comments(query: {
reported: comments(query: {
action_type: FLAG,
asset_id: $asset_id,
statuses: [NONE, PREMOD],
@@ -337,11 +361,18 @@ const withModQueueQuery = withQuery(gql`
id
title
url
settings {
moderation
}
}
allCount: commentCount(query: {
asset_id: $asset_id
})
acceptedCount: commentCount(query: {
newCount: commentCount(query: {
statuses: [NONE, PREMOD],
asset_id: $asset_id
})
approvedCount: commentCount(query: {
statuses: [ACCEPTED],
asset_id: $asset_id
})
@@ -353,13 +384,14 @@ const withModQueueQuery = withQuery(gql`
statuses: [REJECTED],
asset_id: $asset_id
})
flaggedCount: commentCount(query: {
reportedCount: commentCount(query: {
action_type: FLAG,
asset_id: $asset_id,
statuses: [NONE, PREMOD]
})
settings {
organizationName
moderation
}
}
${commentConnectionFragment}
@@ -380,7 +412,11 @@ const withQueueCountPolling = withQuery(gql`
allCount: commentCount(query: {
asset_id: $asset_id
})
acceptedCount: commentCount(query: {
newCount: commentCount(query: {
statuses: [NONE, PREMOD],
asset_id: $asset_id
})
approvedCount: commentCount(query: {
statuses: [ACCEPTED],
asset_id: $asset_id
})
@@ -392,7 +428,7 @@ const withQueueCountPolling = withQuery(gql`
statuses: [REJECTED],
asset_id: $asset_id
})
flaggedCount: commentCount(query: {
reportedCount: commentCount(query: {
action_type: FLAG,
asset_id: $asset_id,
statuses: [NONE, PREMOD]
@@ -59,13 +59,13 @@ class StorySearchContainer extends React.Component {
goToStory = (id) => {
const {router} = this.props;
router.push(`/admin/moderate/all/${id}`);
router.push(`/admin/moderate/${id}`);
this.clearAndCloseSearch();
}
goToModerateAll = () => {
const {router} = this.props;
router.push('/admin/moderate/all');
router.push('/admin/moderate');
this.clearAndCloseSearch();
}
+4
View File
@@ -0,0 +1,4 @@
export const isPremod = (mod) => mod === 'PRE';
export const getModPath = (type = 'all', assetId) =>
assetId ? `/admin/moderate/${type}/${assetId}` : `/admin/moderate/${type}`;
+2 -2
View File
@@ -42,14 +42,14 @@ export default class Popup extends Component {
this.onUnload();
const interval = setInterval(() => {
if (this.ref.onload === null) {
if (this.ref && this.ref.onload === null) {
this.setCallbacks();
clearInterval(interval);
}
}, 50);
this.detectCloseInterval = setInterval(() => {
if (this.ref.closed) {
if (!this.ref || this.ref.closed) {
clearInterval(this.detectCloseInterval);
this.onClose();
}
@@ -5,7 +5,7 @@ import t from 'coral-framework/services/i18n';
const ModerationLink = (props) => props.isAdmin ? (
<div className={styles.moderationLink}>
<a href={`/admin/moderate/all/${props.assetId}`} target="_blank">
<a href={`/admin/moderate/${props.assetId}`} target="_blank">
{t('moderate_this_stream')}
</a>
</div>
+2
View File
@@ -262,6 +262,7 @@ en:
dont_like_username: "Don't like username"
empty_queue: "No more comments to moderate! You're all caught up. Go have some ☕️"
flagged: flagged
reported: reported
impersonating: Impersonating
less_detail: "Less detail"
likes: likes
@@ -269,6 +270,7 @@ en:
mod_faster: "Moderate faster with keyboard shortcuts"
moderate: "Moderate →"
more_detail: "More detail"
new: New
newest_first: "Newest First"
navigation: Navigation
next_comment: "Go to the next comment"
+1
View File
@@ -261,6 +261,7 @@ es:
mod_faster: "Moderar más rápido con atajos de teclado"
moderate: "Moderar →"
more_detail: "Más detalles"
new: Nuevo
newest_first: "Primero el más nuevo"
navigation: Navegación
next_comment: "Ir al siguiente comentario"
@@ -15,7 +15,7 @@ export default class Tag extends React.Component {
}
showTooltip = e => {
showTooltip = (e) => {
e.preventDefault();
this.setState({
tooltip: true