mirror of
https://github.com/wassname/talk.git
synced 2026-07-13 17:45:56 +08:00
Introduce dangling comments and queue cleanup
This commit is contained in:
@@ -27,6 +27,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.dangling {
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0 14px;
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<li
|
||||
tabIndex={0}
|
||||
className={cn(className, 'mdl-card', selectionStateCSS, styles.root, {[styles.selected]: selected}, 'talk-admin-moderate-comment')}
|
||||
className={cn(className, 'mdl-card', selectionStateCSS, styles.root, {[styles.selected]: selected, [styles.dangling]: dangling}, 'talk-admin-moderate-comment')}
|
||||
id={`comment_${comment.id}`}
|
||||
onClick={this.handleFocusOrClick}
|
||||
ref={this.handleRef}
|
||||
@@ -213,6 +214,7 @@ Comment.propTypes = {
|
||||
rejectComment: PropTypes.func.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
dangling: PropTypes.bool,
|
||||
currentAsset: PropTypes.object,
|
||||
showBanUserDialog: PropTypes.func.isRequired,
|
||||
showSuspendUserDialog: PropTypes.func.isRequired,
|
||||
|
||||
@@ -164,11 +164,13 @@ class Moderation extends Component {
|
||||
acceptComment={props.acceptComment}
|
||||
rejectComment={props.rejectComment}
|
||||
loadMore={this.loadMore}
|
||||
commentBelongToQueue={this.props.commentBelongToQueue}
|
||||
isLoadingMore={this.state.isLoadingMore}
|
||||
commentCount={activeTabCount}
|
||||
currentUserId={this.props.auth.user.id}
|
||||
viewUserDetail={viewUserDetail}
|
||||
selectCommentId={props.selectCommentId}
|
||||
cleanUpQueue={props.cleanUpQueue}
|
||||
/>
|
||||
<ModerationKeysModal
|
||||
hideShortcutsNote={props.hideShortcutsNote}
|
||||
@@ -202,10 +204,12 @@ Moderation.propTypes = {
|
||||
selectedCommentId: PropTypes.string,
|
||||
toggleStorySearch: PropTypes.func.isRequired,
|
||||
getModPath: PropTypes.func.isRequired,
|
||||
cleanUpQueue: PropTypes.func.isRequired,
|
||||
storySearchChange: PropTypes.func.isRequired,
|
||||
moderation: PropTypes.object.isRequired,
|
||||
auth: PropTypes.object.isRequired,
|
||||
queueConfig: PropTypes.object.isRequired,
|
||||
commentBelongToQueue: PropTypes.func.isRequired,
|
||||
handleCommentChange: PropTypes.func.isRequired,
|
||||
setSortOrder: PropTypes.func.isRequired,
|
||||
showBanUserDialog: PropTypes.func.isRequired,
|
||||
|
||||
@@ -14,21 +14,3 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.commentLeave {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.commentLeaveActive {
|
||||
opacity: 0.1;
|
||||
transition: opacity 800ms;
|
||||
}
|
||||
|
||||
.commentEnter {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.commentEnterActive {
|
||||
opacity: 1.0;
|
||||
transition: opacity 800ms;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,12 +110,18 @@ class ModerationQueue extends React.Component {
|
||||
componentWillUnmount() {
|
||||
key.unbind('j');
|
||||
key.unbind('k');
|
||||
|
||||
this.props.cleanUpQueue(this.props.activeTab);
|
||||
}
|
||||
|
||||
getCommentCountWithoutDagling(props = this.props) {
|
||||
return props.comments.filter((comment) => 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 (
|
||||
<CellMeasurer
|
||||
cache={this.cache}
|
||||
@@ -282,6 +288,7 @@ class ModerationQueue extends React.Component {
|
||||
data={this.props.data}
|
||||
root={this.props.root}
|
||||
comment={comment}
|
||||
dangling={!this.props.commentBelongToQueue(this.props.activeTab, comment)}
|
||||
selected={comment.id === this.props.selectedCommentId}
|
||||
viewUserDetail={this.props.viewUserDetail}
|
||||
showBanUserDialog={this.props.showBanUserDialog}
|
||||
@@ -380,7 +387,8 @@ ModerationQueue.propTypes = {
|
||||
showSuspendUserDialog: PropTypes.func.isRequired,
|
||||
rejectComment: PropTypes.func.isRequired,
|
||||
acceptComment: PropTypes.func.isRequired,
|
||||
comments: PropTypes.array.isRequired,
|
||||
commentBelongToQueue: PropTypes.func.isRequired,
|
||||
cleanUpQueue: PropTypes.func.isRequired,
|
||||
commentCount: PropTypes.number.isRequired,
|
||||
loadMore: PropTypes.func.isRequired,
|
||||
selectedCommentId: PropTypes.string,
|
||||
|
||||
@@ -11,7 +11,7 @@ import NotFoundAsset from '../components/NotFoundAsset';
|
||||
import {isPremod, getModPath} from '../../../utils';
|
||||
|
||||
import {withSetCommentStatus} from 'coral-framework/graphql/mutations';
|
||||
import {handleCommentChange} from '../graphql';
|
||||
import {handleCommentChange, commentBelongToQueue, cleanUpQueue} from '../graphql';
|
||||
|
||||
import {showBanUserDialog} from 'actions/banUserDialog';
|
||||
import {showSuspendUserDialog} from 'actions/suspendUserDialog';
|
||||
@@ -169,6 +169,14 @@ class ModerationContainer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
cleanUpQueue = (queue) => {
|
||||
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}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user