mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-03-23 19:30:34 -04:00
Introduce comment thread limit on studio comments. When a user clicks reply on a thread that has reached the limit, display a comment status message in place of the comment composer. The user should only see one status box in a message thread.
This commit is contained in:
parent
460010e64f
commit
e57cd9a111
5 changed files with 96 additions and 23 deletions
src
|
@ -338,6 +338,7 @@
|
|||
"comments.cancel": "Cancel",
|
||||
"comments.lengthWarning": "{remainingCharacters, plural, one {1 character left} other {{remainingCharacters} characters left}}",
|
||||
"comments.loadMoreReplies": "See more replies",
|
||||
"comments.reachedThreadLimit": "This comment thread has reached its limit. To continue commenting, you can start a new thread.",
|
||||
"comments.status.delbyusr": "Deleted by project owner",
|
||||
"comments.status.censbyfilter": "Censored by filter",
|
||||
"comments.status.delbyparentcomment": "Parent comment deleted",
|
||||
|
|
|
@ -26,6 +26,7 @@ class Comment extends React.Component {
|
|||
'handleConfirmReport',
|
||||
'handleCancelReport',
|
||||
'handlePostReply',
|
||||
'handleReply',
|
||||
'handleToggleReplying',
|
||||
'handleRestore',
|
||||
'setRef'
|
||||
|
@ -49,6 +50,14 @@ class Comment extends React.Component {
|
|||
this.props.onAddComment(comment);
|
||||
}
|
||||
|
||||
handleReply () {
|
||||
if (this.props.hasReachedThreadLimit) {
|
||||
this.props.onReply(this.props.id, (this.props.parentId || this.props.id));
|
||||
} else {
|
||||
this.handleToggleReplying();
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleReplying () {
|
||||
this.setState({replying: !this.state.replying});
|
||||
}
|
||||
|
@ -220,7 +229,7 @@ class Comment extends React.Component {
|
|||
{(canReply && visible) ? (
|
||||
<span
|
||||
className="comment-reply"
|
||||
onClick={this.handleToggleReplying}
|
||||
onClick={this.handleReply}
|
||||
>
|
||||
<FormattedMessage id="comments.reply" />
|
||||
</span>
|
||||
|
@ -278,10 +287,12 @@ Comment.propTypes = {
|
|||
canRestore: PropTypes.bool,
|
||||
content: PropTypes.string,
|
||||
datetimeCreated: PropTypes.string,
|
||||
hasReachedThreadLimit: PropTypes.bool,
|
||||
highlighted: PropTypes.bool,
|
||||
id: PropTypes.number,
|
||||
onAddComment: PropTypes.func,
|
||||
onDelete: PropTypes.func,
|
||||
onReply: PropTypes.func,
|
||||
onReport: PropTypes.func,
|
||||
onRestore: PropTypes.func,
|
||||
parentId: PropTypes.number,
|
||||
|
|
|
@ -279,6 +279,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
.thread-limit-status {
|
||||
width: calc(100% - 10rem);
|
||||
margin-left: auto;
|
||||
|
||||
.comment-status-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.compose-disabled {
|
||||
opacity: .5;
|
||||
}
|
||||
|
|
|
@ -6,9 +6,13 @@ const FormattedMessage = require('react-intl').FormattedMessage;
|
|||
|
||||
const FlexRow = require('../../../components/flex-row/flex-row.jsx');
|
||||
const Comment = require('./comment.jsx');
|
||||
const CommentingStatus = require('../../../components/commenting-status/commenting-status.jsx');
|
||||
|
||||
require('./comment.scss');
|
||||
|
||||
// Thread limit only applies if hasThreadLimit prop is true
|
||||
const THREAD_LIMIT = 25;
|
||||
|
||||
class TopLevelComment extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
@ -16,11 +20,14 @@ class TopLevelComment extends React.Component {
|
|||
'handleExpandThread',
|
||||
'handleAddComment',
|
||||
'handleDeleteReply',
|
||||
'handleReplyStatus',
|
||||
'handleReportReply',
|
||||
'handleRestoreReply'
|
||||
]);
|
||||
this.state = {
|
||||
expanded: this.props.defaultExpanded
|
||||
expanded: this.props.defaultExpanded,
|
||||
threadLimitCommentId: '',
|
||||
threadLimitParentId: ''
|
||||
};
|
||||
|
||||
// A cache of {userId: username, ...} in order to show reply usernames
|
||||
|
@ -55,6 +62,10 @@ class TopLevelComment extends React.Component {
|
|||
this.props.onAddComment(comment, this.props.id);
|
||||
}
|
||||
|
||||
handleReplyStatus (id, parentId) {
|
||||
this.setState({threadLimitCommentId: id, threadLimitParentId: parentId});
|
||||
}
|
||||
|
||||
authorUsername (authorId) {
|
||||
if (this.authorUsernameCache[authorId]) return this.authorUsernameCache[authorId];
|
||||
|
||||
|
@ -80,6 +91,7 @@ class TopLevelComment extends React.Component {
|
|||
canRestore,
|
||||
content,
|
||||
datetimeCreated,
|
||||
hasThreadLimit,
|
||||
highlightedCommentId,
|
||||
id,
|
||||
moreRepliesToLoad,
|
||||
|
@ -93,12 +105,28 @@ class TopLevelComment extends React.Component {
|
|||
|
||||
const parentVisible = visibility === 'visible';
|
||||
|
||||
// Check whether this comment thread has reached the thread limit
|
||||
const hasReachedThreadLimit = hasThreadLimit && replies.length >= THREAD_LIMIT;
|
||||
|
||||
/*
|
||||
Check all the following conditions:
|
||||
- hasReachedThreadLimit: the thread has reached the limit
|
||||
- Use the comment id and parent id of this particular comment in this thread
|
||||
to see if it has the reply status,
|
||||
only one comment in a thread can have the status
|
||||
*/
|
||||
const commentHasReplyStatus = (commentId, commentParentId) =>
|
||||
hasReachedThreadLimit &&
|
||||
(this.state.threadLimitCommentId === commentId) &&
|
||||
(this.state.threadLimitParentId === commentParentId);
|
||||
|
||||
return (
|
||||
<FlexRow className="comment-container">
|
||||
<Comment
|
||||
highlighted={highlightedCommentId === id}
|
||||
postURI={postURI}
|
||||
onAddComment={this.handleAddComment}
|
||||
onReply={this.handleReplyStatus}
|
||||
{...{
|
||||
author,
|
||||
content,
|
||||
|
@ -108,6 +136,7 @@ class TopLevelComment extends React.Component {
|
|||
canReply,
|
||||
canReport,
|
||||
canRestore,
|
||||
hasReachedThreadLimit,
|
||||
id,
|
||||
onDelete,
|
||||
onReport,
|
||||
|
@ -115,6 +144,13 @@ class TopLevelComment extends React.Component {
|
|||
visibility
|
||||
}}
|
||||
/>
|
||||
{commentHasReplyStatus(id, id) &&
|
||||
<CommentingStatus className="thread-limit-status">
|
||||
<p>
|
||||
<FormattedMessage id="comments.reachedThreadLimit" />
|
||||
</p>
|
||||
</CommentingStatus>
|
||||
}
|
||||
{replies.length > 0 &&
|
||||
<FlexRow
|
||||
className={classNames(
|
||||
|
@ -125,27 +161,40 @@ class TopLevelComment extends React.Component {
|
|||
key={id}
|
||||
>
|
||||
{(this.state.expanded ? replies : replies.slice(0, 3)).map(reply => (
|
||||
<Comment
|
||||
author={reply.author}
|
||||
canDelete={canDelete}
|
||||
canDeleteWithoutConfirm={canDeleteWithoutConfirm}
|
||||
canReply={canReply}
|
||||
canReport={canReport}
|
||||
canRestore={canRestore && parentVisible}
|
||||
content={reply.content}
|
||||
datetimeCreated={reply.datetime_created}
|
||||
highlighted={highlightedCommentId === reply.id}
|
||||
id={reply.id}
|
||||
key={reply.id}
|
||||
parentId={id}
|
||||
postURI={postURI}
|
||||
replyUsername={this.authorUsername(reply.commentee_id)}
|
||||
visibility={reply.visibility}
|
||||
onAddComment={this.handleAddComment}
|
||||
onDelete={this.handleDeleteReply}
|
||||
onReport={this.handleReportReply}
|
||||
onRestore={this.handleRestoreReply}
|
||||
/>
|
||||
<React.Fragment
|
||||
key={`reply-and-status-${reply.id}`}
|
||||
>
|
||||
<Comment
|
||||
author={reply.author}
|
||||
canDelete={canDelete}
|
||||
canDeleteWithoutConfirm={canDeleteWithoutConfirm}
|
||||
canReply={canReply}
|
||||
canReport={canReport}
|
||||
canRestore={canRestore && parentVisible}
|
||||
content={reply.content}
|
||||
datetimeCreated={reply.datetime_created}
|
||||
hasReachedThreadLimit={hasReachedThreadLimit}
|
||||
highlighted={highlightedCommentId === reply.id}
|
||||
id={reply.id}
|
||||
key={reply.id}
|
||||
parentId={id}
|
||||
postURI={postURI}
|
||||
replyUsername={this.authorUsername(reply.commentee_id)}
|
||||
visibility={reply.visibility}
|
||||
onAddComment={this.handleAddComment}
|
||||
onDelete={this.handleDeleteReply}
|
||||
onReply={this.handleReplyStatus}
|
||||
onReport={this.handleReportReply}
|
||||
onRestore={this.handleRestoreReply}
|
||||
/>
|
||||
{commentHasReplyStatus(reply.id, id) &&
|
||||
<CommentingStatus className="thread-limit-status">
|
||||
<p>
|
||||
<FormattedMessage id="comments.reachedThreadLimit" />
|
||||
</p>
|
||||
</CommentingStatus>
|
||||
}
|
||||
</React.Fragment>
|
||||
))}
|
||||
{((!this.state.expanded && replies.length > 3) ||
|
||||
(this.state.expanded && moreRepliesToLoad)) &&
|
||||
|
@ -179,6 +228,7 @@ TopLevelComment.propTypes = {
|
|||
datetimeCreated: PropTypes.string,
|
||||
defaultExpanded: PropTypes.bool,
|
||||
deletable: PropTypes.bool,
|
||||
hasThreadLimit: PropTypes.bool,
|
||||
highlightedCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
|
||||
id: PropTypes.number,
|
||||
moreRepliesToLoad: PropTypes.bool,
|
||||
|
@ -196,6 +246,7 @@ TopLevelComment.propTypes = {
|
|||
TopLevelComment.defaultProps = {
|
||||
canDeleteWithoutConfirm: false,
|
||||
defaultExpanded: false,
|
||||
hasThreadLimit: false,
|
||||
moreRepliesToLoad: false
|
||||
};
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ const StudioComments = ({
|
|||
}
|
||||
{comments.map(comment => (
|
||||
<TopLevelComment
|
||||
hasThreadLimit
|
||||
author={comment.author}
|
||||
canDelete={canDeleteComment}
|
||||
canDeleteWithoutConfirm={canDeleteCommentWithoutConfirm}
|
||||
|
|
Loading…
Add table
Reference in a new issue