From 5ecdfe94cc99ceee424b03bbc6fed7d32c148ae3 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 15 Jun 2017 18:28:08 +0700 Subject: [PATCH] Refactor state updater --- client/coral-admin/src/graphql/utils.js | 149 ++++++++++++------ .../Moderation/containers/Moderation.js | 53 ++++--- 2 files changed, 128 insertions(+), 74 deletions(-) diff --git a/client/coral-admin/src/graphql/utils.js b/client/coral-admin/src/graphql/utils.js index 7c8bd8875..d42029df4 100644 --- a/client/coral-admin/src/graphql/utils.js +++ b/client/coral-admin/src/graphql/utils.js @@ -1,62 +1,111 @@ import update from 'immutability-helper'; +import * as notification from 'coral-admin/src/services/notification'; -export function findCommentInModQueues(root, id, queues = ['all', 'premod', 'flagged', 'accepted', 'rejected']) { - return queues.reduce((comment, queue) => { - return comment ? comment : root[queue].nodes.find((c) => c.id === id); - }, null); +const queues = ['all', 'premod', 'flagged', 'accepted', 'rejected']; + +const ascending = (a, b) => { + const dateA = new Date(a.created_at); + const dateB = new Date(b.created_at); + if (dateA < dateB) { return -1; } + if (dateA > dateB) { return 1; } + return 0; +}; + +const descending = (a, b) => -ascending(a, b); + +function truncate(s, length = 10) { + return (s.length > length) ? `${s.substring(0, length)}...` : s; } -export function handleCommentStatusChange(root, {id, status}, previousStatus) { - const comment = findCommentInModQueues(root, id); - if (!previousStatus && comment) { - previousStatus = comment.status; - } +function queueHasComment(root, queue, id) { + return root[queue].nodes.find((c) => c.id === id); +} - if (status === previousStatus) { +function removeCommentFromQueue(root, queue, id) { + if (!queueHasComment(root, queue, id)) { return root; } - let acceptedNodes = root.accepted.nodes; - let acceptedCount = root.acceptedCount; - let rejectedNodes = root.rejected.nodes; - let rejectedCount = root.rejectedCount; - - if (status === 'ACCEPTED') { - acceptedCount++; - if (comment) { - acceptedNodes = [{...comment, status}, ...acceptedNodes]; - } - } - else if (status === 'REJECTED') { - rejectedCount++; - if (comment) { - rejectedNodes = [{...comment, status}, ...rejectedNodes]; - } - } - - const premodNodes = root.premod.nodes.filter((c) => c.id !== id); - const flaggedNodes = root.flagged.nodes.filter((c) => c.id !== id); - const premodCount = premodNodes.length < root.premod.nodes.length ? root.premodCount - 1 : root.premodCount; - const flaggedCount = flaggedNodes.length < root.flagged.nodes.length ? root.flaggedCount - 1 : root.flaggedCount; - - if (status === 'REJECTED') { - acceptedNodes = root.accepted.nodes.filter((c) => c.id !== id); - acceptedCount = acceptedNodes.length < root.accepted.nodes.length ? root.acceptedCount - 1 : root.acceptedCount; - } - else if (status === 'ACCEPTED') { - rejectedNodes = root.rejected.nodes.filter((c) => c.id !== id); - rejectedCount = rejectedNodes.length < root.rejected.nodes.length ? root.rejectedCount - 1 : root.rejectedCount; - } - return update(root, { - premodCount: {$set: Math.max(0, premodCount)}, - flaggedCount: {$set: Math.max(0, flaggedCount)}, - acceptedCount: {$set: Math.max(0, acceptedCount)}, - rejectedCount: {$set: Math.max(0, rejectedCount)}, - premod: {nodes: {$set: premodNodes}}, - flagged: {nodes: {$set: flaggedNodes}}, - accepted: {nodes: {$set: acceptedNodes}}, - rejected: {nodes: {$set: rejectedNodes}}, + [`${queue}Count`]: {$set: root[`${queue}Count`] - 1}, + [queue]: { + nodes: {$apply: (nodes) => nodes.filter((c) => c.id !== id)}, + }, }); } +function isCommentInCursor(root, queue, comment, sort) { + const cursor = new Date(root[queue].endCursor); + return sort === 'CHRONOLOGICAL' + ? new Date(comment.created_at) <= cursor + : new Date(comment.created_at) >= cursor; +} + +function addCommentToQueue(root, queue, comment, sort) { + if (queueHasComment(root, queue, comment.id)) { + return root; + } + + const sortAlgo = sort === 'CHRONOLOGICAL' ? ascending : descending; + const changes = { + [`${queue}Count`]: {$set: root[`${queue}Count`] + 1}, + }; + + if (isCommentInCursor(root, queue, comment, sort)) { + changes[queue] = { + nodes: {$apply: (nodes) => nodes.concat(comment).sort(sortAlgo)}, + }; + } + + return update(root, changes); +} + +function getCommentQueues(comment) { + const queues = ['all']; + if (comment.status === 'ACCEPTED') { + queues.push('accepted'); + } + else if (comment.status === 'REJECTED') { + queues.push('rejected'); + } + else if (comment.status === 'PREMOD') { + queues.push('premod'); + } + if ( + ['NONE', 'PREMOD'].indexOf(comment.status) >= 0 + && comment.actions && comment.actions.some((a) => a.__typename === 'FlagAction') + ) { + queues.push('flagged'); + } + return queues; +} + +function showNotification(queue, comment, user) { + const text = `${user.username} ${comment.status.toLowerCase()} comment "${truncate(comment.body, 50)}"`; + notification.info(text); +} + +export function handleCommentStatusChange(root, comment, {sort, notify, user, activeQueue}) { + let next = root; + const nextQueues = getCommentQueues(comment); + + queues.forEach((queue) => { + if (nextQueues.indexOf(queue) >= 0 && !queueHasComment(next, queue, comment.id)) { + next = addCommentToQueue(next, queue, comment, sort); + if (notify && activeQueue === queue && isCommentInCursor(next, queue, comment, sort)) { + showNotification(queue, comment, user); + } + } else if(queueHasComment(next, queue, comment.id)){ + next = removeCommentFromQueue(next, queue, comment.id); + if (notify && activeQueue === queue) { + showNotification(queue, comment, user); + } + } + + // TODO: All notification + // TODO: Flagged notification + // TODO: Edited notification + }); + return next; +} + diff --git a/client/coral-admin/src/routes/Moderation/containers/Moderation.js b/client/coral-admin/src/routes/Moderation/containers/Moderation.js index 6b7a9bf1b..25db5f2fc 100644 --- a/client/coral-admin/src/routes/Moderation/containers/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/containers/Moderation.js @@ -10,7 +10,7 @@ import t, {timeago} from 'coral-framework/services/i18n'; import update from 'immutability-helper'; import {withSetUserStatus, withSuspendUser, withSetCommentStatus} from 'coral-framework/graphql/mutations'; -import {handleCommentStatusChange, findCommentInModQueues} from '../../../graphql/utils'; +import {handleCommentStatusChange} from '../../../graphql/utils'; import {fetchSettings} from 'actions/settings'; import {updateAssets} from 'actions/assets'; @@ -31,10 +31,6 @@ import {Spinner} from 'coral-ui'; import Moderation from '../components/Moderation'; import Comment from './Comment'; -function truncate(s, length = 10) { - return (s.length > length) ? `${s.substring(0, length)}...` : s; -} - class ModerationContainer extends Component { unsubscribe = null; @@ -47,25 +43,18 @@ class ModerationContainer extends Component { asset_id: this.props.data.variables.asset_id, }, updateQuery: (prev, {subscriptionData: {data: {commentStatusChanged: {user, comment, previous}}}}) => { - const activeTab = this.activeTab; - - // Status changed was caused by a different user. - if (user && user.id !== this.props.auth.user.id) { - if (findCommentInModQueues(prev, comment.id) && ( - activeTab === 'all' && findCommentInModQueues(prev, comment.id, ['all']) - || activeTab === 'premod' && previous.status === 'PREMOD' - || activeTab === 'flagged' && findCommentInModQueues(prev, comment.id, ['flagged']) - || comment.status === 'ACCEPTED' && activeTab === 'accepted' - || comment.status !== 'ACCEPTED' && previous.status === 'ACCEPTED' && activeTab === 'accepted' - || comment.status === 'REJECTED' && activeTab === 'rejected' - || comment.status !== 'REJECTED' && previous.status === 'REJECTED' && activeTab === 'rejected' - ) - ) { - const text = `${user.username} ${comment.status.toLowerCase()} comment "${truncate(comment.body, 50)}"`; - notification.info(text); - } - } - return handleCommentStatusChange(prev, comment, previous.status, user); + const extraParams = this.props.auth.user.id === user.id + ? {} + : { + notify: true, + user, + activeQueue: this.activeTab, + previous, + }; + return handleCommentStatusChange(prev, comment, { + sort: this.props.moderation.sortOrder, + ...extraParams, + }); }, }); } @@ -218,6 +207,22 @@ const STATUS_CHANGED_SUBSCRIPTION = gql` id status body + created_at + action_summaries { + count + ... on FlagActionSummary { + reason + } + } + actions { + ... on FlagAction { + reason + message + user { + username + } + } + } } previous { status