mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-03-31 15:21:34 -04:00
Merge pull request #5536 from LLK/develop
Stage additional changes for release.
This commit is contained in:
commit
dab89d0a02
5 changed files with 124 additions and 25 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.replyLimitReached": "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,
|
||||
|
|
|
@ -284,6 +284,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,12 @@ class TopLevelComment extends React.Component {
|
|||
this.props.onAddComment(comment, this.props.id);
|
||||
}
|
||||
|
||||
handleReplyStatus (id, parentId) {
|
||||
// Send the parentId up to track which thread got "reply" clicked on
|
||||
if (this.props.onReply) this.props.onReply(parentId);
|
||||
this.setState({threadLimitCommentId: id, threadLimitParentId: parentId});
|
||||
}
|
||||
|
||||
authorUsername (authorId) {
|
||||
if (this.authorUsernameCache[authorId]) return this.authorUsernameCache[authorId];
|
||||
|
||||
|
@ -80,6 +93,7 @@ class TopLevelComment extends React.Component {
|
|||
canRestore,
|
||||
content,
|
||||
datetimeCreated,
|
||||
hasThreadLimit,
|
||||
highlightedCommentId,
|
||||
id,
|
||||
moreRepliesToLoad,
|
||||
|
@ -88,17 +102,39 @@ class TopLevelComment extends React.Component {
|
|||
onRestore,
|
||||
replies,
|
||||
postURI,
|
||||
threadHasReplyStatus,
|
||||
visibility
|
||||
} = this.props;
|
||||
|
||||
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
|
||||
- threadHasReplyStatus: this thread should be showing the status
|
||||
(false, if the user just clicked reply elsewhere and another thread/comment stole the status message)
|
||||
- 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
|
||||
|
||||
All of these conditions together ensure that the user only sees one status message on the comments page.
|
||||
*/
|
||||
const commentHasReplyStatus = (commentId, commentParentId) =>
|
||||
hasReachedThreadLimit &&
|
||||
threadHasReplyStatus &&
|
||||
(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 +144,7 @@ class TopLevelComment extends React.Component {
|
|||
canReply,
|
||||
canReport,
|
||||
canRestore,
|
||||
hasReachedThreadLimit,
|
||||
id,
|
||||
onDelete,
|
||||
onReport,
|
||||
|
@ -115,6 +152,13 @@ class TopLevelComment extends React.Component {
|
|||
visibility
|
||||
}}
|
||||
/>
|
||||
{commentHasReplyStatus(id, id) &&
|
||||
<CommentingStatus className="thread-limit-status">
|
||||
<p>
|
||||
<FormattedMessage id="comments.replyLimitReached" />
|
||||
</p>
|
||||
</CommentingStatus>
|
||||
}
|
||||
{replies.length > 0 &&
|
||||
<FlexRow
|
||||
className={classNames(
|
||||
|
@ -125,27 +169,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,24 +236,29 @@ 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,
|
||||
onAddComment: PropTypes.func,
|
||||
onDelete: PropTypes.func,
|
||||
onLoadMoreReplies: PropTypes.func,
|
||||
onReply: PropTypes.func,
|
||||
onReport: PropTypes.func,
|
||||
onRestore: PropTypes.func,
|
||||
parentId: PropTypes.number,
|
||||
postURI: PropTypes.string,
|
||||
replies: PropTypes.arrayOf(PropTypes.object),
|
||||
threadHasReplyStatus: PropTypes.bool,
|
||||
visibility: PropTypes.string
|
||||
};
|
||||
|
||||
TopLevelComment.defaultProps = {
|
||||
canDeleteWithoutConfirm: false,
|
||||
defaultExpanded: false,
|
||||
moreRepliesToLoad: false
|
||||
hasThreadLimit: false,
|
||||
moreRepliesToLoad: false,
|
||||
threadHasReplyStatus: false
|
||||
};
|
||||
|
||||
module.exports = TopLevelComment;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, {useEffect, useRef} from 'react';
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
@ -56,6 +56,18 @@ const StudioComments = ({
|
|||
if (isAdmin !== wasAdmin) handleResetComments();
|
||||
}, [isAdmin]);
|
||||
|
||||
const [replyStatusCommentId, setReplyStatusCommentId] = useState('');
|
||||
|
||||
const hasReplyStatus = function (comment) {
|
||||
return (
|
||||
comment.parent_id && comment.parent_id === replyStatusCommentId
|
||||
) || (comment.id === replyStatusCommentId);
|
||||
};
|
||||
|
||||
const handleReplyStatusChange = function (id) {
|
||||
setReplyStatusCommentId(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="studio-compose-container">
|
||||
<div className="studio-header-container">
|
||||
|
@ -74,6 +86,7 @@ const StudioComments = ({
|
|||
}
|
||||
{comments.map(comment => (
|
||||
<TopLevelComment
|
||||
hasThreadLimit
|
||||
author={comment.author}
|
||||
canDelete={canDeleteComment}
|
||||
canDeleteWithoutConfirm={canDeleteCommentWithoutConfirm}
|
||||
|
@ -88,10 +101,13 @@ const StudioComments = ({
|
|||
parentId={comment.parent_id}
|
||||
postURI={postURI}
|
||||
replies={replies && replies[comment.id] ? replies[comment.id] : []}
|
||||
threadHasReplyStatus={hasReplyStatus(comment)}
|
||||
visibility={comment.visibility}
|
||||
onAddComment={handleNewComment}
|
||||
onDelete={handleDeleteComment}
|
||||
onRestore={handleRestoreComment}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onReply={handleReplyStatusChange}
|
||||
onReport={handleReportComment}
|
||||
onLoadMoreReplies={handleLoadMoreReplies}
|
||||
/>
|
||||
|
|
Loading…
Add table
Reference in a new issue