mirror of
https://github.com/wassname/talk.git
synced 2026-07-14 11:18:50 +08:00
227 lines
6.6 KiB
JavaScript
227 lines
6.6 KiB
JavaScript
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import LoadMore from './LoadMore';
|
|
import NewCount from './NewCount';
|
|
import {TransitionGroup} from 'react-transition-group';
|
|
import {forEachError} from 'coral-framework/utils';
|
|
import Comment from '../containers/Comment';
|
|
import NoComments from './NoComments';
|
|
|
|
const hasComment = (nodes, id) => nodes.some((node) => node.id === id);
|
|
|
|
// resetCursors will return the id cursors of the first and second comment of
|
|
// the current comment list. The cursors are used to dertermine which
|
|
// comments to show. The spare cursor functions as a backup in case one
|
|
// of the comments gets deleted.
|
|
function resetCursors(state, props) {
|
|
const comments = props.root.asset.comments;
|
|
if (comments && comments.nodes.length) {
|
|
const idCursors = [comments.nodes[0].id];
|
|
if (comments.nodes[1]) {
|
|
idCursors.push(comments.nodes[1].id);
|
|
}
|
|
return {idCursors};
|
|
}
|
|
return {idCursors: []};
|
|
}
|
|
|
|
// invalidateCursor is called whenever a comment is removed which is referenced
|
|
// by one of the 2 id cursors. It returns a new set of id cursors calculated
|
|
// using the help of the backup cursor.
|
|
function invalidateCursor(invalidated, state, props) {
|
|
const alt = invalidated === 1 ? 0 : 1;
|
|
const comments = props.root.asset.comments;
|
|
const idCursors = [];
|
|
if (state.idCursors[alt]) {
|
|
idCursors.push(state.idCursors[alt]);
|
|
const index = comments.nodes.findIndex((node) => node.id === idCursors[0]);
|
|
const nextInLine = comments.nodes[index + 1];
|
|
if (nextInLine) {
|
|
idCursors.push(nextInLine.id);
|
|
}
|
|
}
|
|
return {idCursors};
|
|
}
|
|
|
|
class AllCommentsPane extends React.Component {
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
...resetCursors(this.state, props),
|
|
loadingState: '',
|
|
};
|
|
}
|
|
|
|
componentWillReceiveProps(next) {
|
|
const {comments: prevComments} = this.props;
|
|
const {comments: nextComments} = next;
|
|
|
|
if (!prevComments && nextComments) {
|
|
this.setState(resetCursors);
|
|
return;
|
|
}
|
|
|
|
if (
|
|
prevComments && nextComments &&
|
|
nextComments.nodes.length < prevComments.nodes.length
|
|
) {
|
|
|
|
// Invalidate first cursor if referenced comment was removed.
|
|
if (this.state.idCursors[0] && !hasComment(nextComments.nodes, this.state.idCursors[0])) {
|
|
this.setState(invalidateCursor(0, this.state, next));
|
|
}
|
|
|
|
// Invalidate second cursor if referenced comment was removed.
|
|
if (this.state.idCursors[1] && !hasComment(nextComments.nodes, this.state.idCursors[1])) {
|
|
this.setState(invalidateCursor(1, this.state, next));
|
|
}
|
|
}
|
|
}
|
|
|
|
loadMore = () => {
|
|
this.setState({loadingState: 'loading'});
|
|
this.props.loadMore()
|
|
.then(() => {
|
|
this.setState({loadingState: 'success'});
|
|
})
|
|
.catch((error) => {
|
|
this.setState({loadingState: 'error'});
|
|
forEachError(error, ({msg}) => {this.props.notify('error', msg);});
|
|
});
|
|
}
|
|
|
|
viewNewComments = () => {
|
|
this.setState(resetCursors);
|
|
this.props.emit('ui.AllCommentsPane.viewNewComments');
|
|
};
|
|
|
|
// getVisibileComments returns a list containing comments
|
|
// which were authored by current user or comes after the `idCursor`.
|
|
getVisibleComments() {
|
|
const {comments, currentUser: user} = this.props;
|
|
const idCursor = this.state.idCursors[0];
|
|
const userId = user ? user.id : null;
|
|
|
|
if (!comments) {
|
|
return [];
|
|
}
|
|
|
|
const view = [];
|
|
let pastCursor = false;
|
|
comments.nodes.forEach((comment) => {
|
|
if (comment.id === idCursor) {
|
|
pastCursor = true;
|
|
}
|
|
if (pastCursor || comment.user.id === userId) {
|
|
view.push(comment);
|
|
}
|
|
});
|
|
return view;
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
data,
|
|
root,
|
|
comments,
|
|
commentClassNames,
|
|
setActiveReplyBox,
|
|
activeReplyBox,
|
|
notify,
|
|
disableReply,
|
|
postComment,
|
|
asset,
|
|
currentUser,
|
|
postFlag,
|
|
postDontAgree,
|
|
loadNewReplies,
|
|
deleteAction,
|
|
showSignInDialog,
|
|
charCountEnable,
|
|
maxCharCount,
|
|
editComment,
|
|
emit,
|
|
} = this.props;
|
|
|
|
const {loadingState} = this.state;
|
|
const visibleComments = this.getVisibleComments();
|
|
|
|
return (
|
|
<div>
|
|
{visibleComments.length ? (
|
|
<div className="talk-stream-comments-container">
|
|
<NewCount
|
|
count={comments.nodes.length - visibleComments.length}
|
|
loadMore={this.viewNewComments}
|
|
/>
|
|
<TransitionGroup component='div' className="embed__stream">
|
|
{visibleComments.map((comment) => {
|
|
return (
|
|
<Comment
|
|
commentClassNames={commentClassNames}
|
|
data={data}
|
|
root={root}
|
|
disableReply={disableReply}
|
|
setActiveReplyBox={setActiveReplyBox}
|
|
activeReplyBox={activeReplyBox}
|
|
notify={notify}
|
|
depth={0}
|
|
postComment={postComment}
|
|
asset={asset}
|
|
currentUser={currentUser}
|
|
postFlag={postFlag}
|
|
postDontAgree={postDontAgree}
|
|
loadMore={loadNewReplies}
|
|
deleteAction={deleteAction}
|
|
showSignInDialog={showSignInDialog}
|
|
key={comment.id}
|
|
comment={comment}
|
|
charCountEnable={charCountEnable}
|
|
maxCharCount={maxCharCount}
|
|
editComment={editComment}
|
|
emit={emit}
|
|
/>
|
|
);
|
|
})}
|
|
</TransitionGroup>
|
|
<LoadMore
|
|
topLevel={true}
|
|
moreComments={asset.comments.hasNextPage}
|
|
loadMore={this.loadMore}
|
|
loadingState={loadingState}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<NoComments assetClosed={asset.isClosed} />
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
AllCommentsPane.propTypes = {
|
|
data: PropTypes.object,
|
|
root: PropTypes.object,
|
|
comments: PropTypes.object,
|
|
commentClassNames: PropTypes.array,
|
|
setActiveReplyBox: PropTypes.func,
|
|
activeReplyBox: PropTypes.string,
|
|
notify: PropTypes.func,
|
|
disableReply: PropTypes.bool,
|
|
postComment: PropTypes.func,
|
|
asset: PropTypes.object,
|
|
currentUser: PropTypes.object,
|
|
postFlag: PropTypes.func,
|
|
postDontAgree: PropTypes.func,
|
|
loadNewReplies: PropTypes.func,
|
|
deleteAction: PropTypes.func,
|
|
showSignInDialog: PropTypes.func,
|
|
charCountEnable: PropTypes.bool,
|
|
maxCharCount: PropTypes.number,
|
|
editComment: PropTypes.func,
|
|
emit: PropTypes.func,
|
|
};
|
|
|
|
export default AllCommentsPane;
|