diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.css b/client/coral-admin/src/routes/Moderation/components/Comment.css
index 708761b95..119688b73 100644
--- a/client/coral-admin/src/routes/Moderation/components/Comment.css
+++ b/client/coral-admin/src/routes/Moderation/components/Comment.css
@@ -27,6 +27,10 @@
}
}
+.dangling {
+ background-color: #efefef;
+}
+
.container {
padding: 0 14px;
}
diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js
index 0856e4657..293071cec 100644
--- a/client/coral-admin/src/routes/Moderation/components/Comment.js
+++ b/client/coral-admin/src/routes/Moderation/components/Comment.js
@@ -82,6 +82,7 @@ class Comment extends React.Component {
currentUserId,
currentAsset,
clearHeightCache,
+ dangling,
} = this.props;
const selectionStateCSS = selected ? 'mdl-shadow--16dp' : 'mdl-shadow--2dp';
@@ -90,7 +91,7 @@ class Comment extends React.Component {
return (
props.commentBelongToQueue(props.activeTab, comment)).length;
}
async selectDown() {
const view = this.state.view;
const index = view.findIndex(({id}) => id === this.props.selectedCommentId);
- if (index === view.length - 1 && this.props.comments.length !== this.props.commentCount) {
+ if (index === view.length - 1 && this.getCommentCountWithoutDagling() !== this.props.commentCount) {
await this.props.loadMore();
this.selectDown();
return;
@@ -139,20 +145,20 @@ class ModerationQueue extends React.Component {
}
componentDidUpdate (prev) {
- const {comments, commentCount} = this.props;
+ const {commentCount, selectedCommentId} = this.props;
// if the user just moderated the last (visible) comment
// AND there are more comments available on the server,
// go ahead and load more comments
- if (prev.comments.length > 0 && comments.length === 0 && commentCount > 0) {
+ if (prev.comments.length > 0 && this.getCommentCountWithoutDagling() === 0 && commentCount > 0) {
this.props.loadMore();
}
// Scroll to selected comment.
- if (prev.selectedCommentId !== this.props.selectedCommentId && this.listRef) {
+ if (prev.selectedCommentId !== selectedCommentId && this.listRef) {
const view = this.state.view;
- const index = view.findIndex(({id}) => id === this.props.selectedCommentId);
+ const index = view.findIndex(({id}) => id === selectedCommentId);
this.listRef.scrollToRow(index);
}
@@ -244,7 +250,7 @@ class ModerationQueue extends React.Component {
const view = this.state.view;
const rowCount = view.length + 1;
if (index === rowCount - 1) {
- const hasMore = this.props.comments.length < this.props.commentCount;
+ const hasMore = this.getCommentCountWithoutDagling() < this.props.commentCount;
return (
{
+ if (!this.props.data.loading) {
+ this.props.data.updateQuery((query) => {
+ return cleanUpQueue(query, queue, this.props.queueConfig);
+ });
+ }
+ }
+
acceptComment = ({commentId}) => {
return this.props.setCommentStatus({commentId, status: 'ACCEPTED'});
}
@@ -177,6 +185,10 @@ class ModerationContainer extends Component {
return this.props.setCommentStatus({commentId, status: 'REJECTED'});
}
+ commentBelongToQueue = (queue, comment) => {
+ return commentBelongToQueue(queue, comment, this.props.queueConfig);
+ }
+
loadMore = (tab) => {
const variables = {
limit: 20,
@@ -248,6 +260,8 @@ class ModerationContainer extends Component {
queueConfig={currentQueueConfig}
handleCommentChange={this.handleCommentChange}
selectedCommentId={this.props.selectedCommentId}
+ commentBelongToQueue={this.commentBelongToQueue}
+ cleanUpQueue={this.cleanUpQueue}
/>;
}
}
@@ -389,6 +403,9 @@ const withModQueueQuery = withQuery(({queueConfig}) => gql`
organizationName
moderation
}
+ me {
+ id
+ }
...${getDefinitionName(Comment.fragments.root)}
}
${Comment.fragments.root}
diff --git a/client/coral-admin/src/routes/Moderation/graphql.js b/client/coral-admin/src/routes/Moderation/graphql.js
index e99bb4a7e..ec6bc4f88 100644
--- a/client/coral-admin/src/routes/Moderation/graphql.js
+++ b/client/coral-admin/src/routes/Moderation/graphql.js
@@ -16,16 +16,21 @@ function queueHasComment(root, queue, id) {
return root[queue].nodes.find((c) => c.id === id);
}
-function removeCommentFromQueue(root, queue, id) {
+function removeCommentFromQueue(root, queue, id, dangling = false) {
if (!queueHasComment(root, queue, id)) {
return root;
}
- return update(root, {
+ const changes = {
[`${queue}Count`]: {$set: root[`${queue}Count`] - 1},
- [queue]: {
+ };
+
+ if (!dangling) {
+ changes[queue] = {
nodes: {$apply: (nodes) => nodes.filter((c) => c.id !== id)},
- },
- });
+ };
+ }
+
+ return update(root, changes);
}
function shouldCommentBeAdded(root, queue, comment, sortOrder) {
@@ -70,24 +75,28 @@ export function sortComments(nodes, sortOrder) {
function getCommentQueues(comment, queueConfig) {
const queues = [];
Object.keys(queueConfig).forEach((key) => {
- const {action_type, statuses, tags} = queueConfig[key];
- let addToQueues = true;
- if (statuses && statuses.indexOf(comment.status) === -1) {
- addToQueues = false;
- }
- if (tags && (!comment.tags || !comment.tags.some((tagLink) => tags.indexOf(tagLink.tag.name) >= 0))) {
- addToQueues = false;
- }
- if (action_type && (!comment.actions || !comment.actions.some((a) => a.__typename.toLowerCase() === `${action_type.toLowerCase()}action`))) {
- addToQueues = false;
- }
- if (addToQueues) {
+ if (commentBelongToQueue(key, comment, queueConfig)) {
queues.push(key);
}
});
return queues;
}
+export function commentBelongToQueue(queue, comment, queueConfig) {
+ const {action_type, statuses, tags} = queueConfig[queue];
+ let belong = true;
+ if (statuses && statuses.indexOf(comment.status) === -1) {
+ belong = false;
+ }
+ if (tags && (!comment.tags || !comment.tags.some((tagLink) => tags.indexOf(tagLink.tag.name) >= 0))) {
+ belong = false;
+ }
+ if (action_type && (!comment.actions || !comment.actions.some((a) => a.__typename.toLowerCase() === `${action_type.toLowerCase()}action`))) {
+ belong = false;
+ }
+ return belong;
+}
+
function isVisible(id) {
return !!document.getElementById(`comment_${id}`);
}
@@ -96,6 +105,37 @@ function isEndOfListVisible(root, queue) {
return root[queue].nodes.length === 0 || !!document.getElementById('end-of-comment-list');
}
+function applyCommentChanges(root, comment, queueConfig) {
+ const queues = Object.keys(queueConfig);
+ for (let i = 0; i < queues.length; i++) {
+ const queue = queues[i];
+ const index = root[queue].nodes.findIndex(({id}) => id === comment.id);
+ if (index > -1) {
+ return update(root, {
+ [queue]: {
+ nodes: {
+ [index]: {$merge: comment},
+ },
+ },
+ });
+ }
+ }
+ return root;
+}
+
+export function cleanUpQueue(root, queue, queueConfig) {
+ return update(root, {
+ [queue]: {
+ nodes: {
+ $apply: (nodes) =>
+ sortComments(
+ nodes.filter((comment) => commentBelongToQueue(queue, comment, queueConfig))
+ ).slice(0, 50),
+ },
+ },
+ });
+}
+
/**
* Assimilate comment changes into current store.
* @param {Object} root current state of the store
@@ -129,7 +169,8 @@ export function handleCommentChange(root, comment, sortOrder, notify, queueConfi
}
}
} else if(queueHasComment(next, queue, comment.id)){
- next = removeCommentFromQueue(next, queue, comment.id);
+ const dangling = activeQueue === queue && comment.status_history[comment.status_history.length - 1].assigned_by.id !== root.me.id;
+ next = removeCommentFromQueue(next, queue, comment.id, dangling);
if (notify && isVisible(comment.id)) {
showNotificationOnce();
}
@@ -138,6 +179,8 @@ export function handleCommentChange(root, comment, sortOrder, notify, queueConfi
if (notify && isVisible(comment.id)) {
showNotificationOnce();
}
+
+ next = applyCommentChanges(next, comment, queueConfig);
});
return next;
}