Add edit and delete actions to posts and comments (#125)

This commit is contained in:
Riccardo Graziosi
2022-06-22 10:17:42 +02:00
committed by GitHub
parent 07ca2a304a
commit bc15140512
52 changed files with 1495 additions and 481 deletions

View File

@@ -4,12 +4,11 @@ import Gravatar from 'react-gravatar';
import I18n from 'i18n-js';
import NewComment from './NewComment';
import Separator from '../common/Separator';
import { MutedText } from '../common/CustomTexts';
import { ReplyFormState } from '../../reducers/replyFormReducer';
import friendlyDate from '../../helpers/datetime';
import CommentEditForm from './CommentEditForm';
import CommentFooter from './CommentFooter';
interface Props {
id: number;
@@ -17,119 +16,147 @@ interface Props {
isPostUpdate: boolean;
userFullName: string;
userEmail: string;
createdAt: string;
updatedAt: string;
replyForm: ReplyFormState;
handleToggleCommentReply(): void;
handleCommentReplyBodyChange(e: React.FormEvent): void;
handleToggleIsCommentUpdate(commentId: number, currentIsPostUpdate: boolean): void;
handleSubmitComment(body: string, parentId: number, isPostUpdate: boolean): void;
handleUpdateComment(commentId: number, body: string, isPostUpdate: boolean, onSuccess: Function): void;
handleDeleteComment(id: number): void;
isLoggedIn: boolean;
isPowerUser: boolean;
currentUserEmail: string;
}
const Comment = ({
id,
body,
isPostUpdate,
userFullName,
userEmail,
updatedAt,
interface State {
editMode: boolean;
}
replyForm,
handleToggleCommentReply,
handleCommentReplyBodyChange,
handleToggleIsCommentUpdate,
handleSubmitComment,
class Comment extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
isLoggedIn,
isPowerUser,
currentUserEmail,
}: Props) => (
<div className="comment">
<div className="commentHeader">
<Gravatar email={userEmail} size={28} className="gravatar" />
<span className="commentAuthor">{userFullName}</span>
{
isPostUpdate ?
<span className="postUpdateBadge">
{I18n.t('post.comments.post_update_badge')}
</span>
:
null
}
</div>
this.state = {
editMode: false,
};
<ReactMarkdown
className="commentBody"
disallowedTypes={['heading', 'image', 'html']}
unwrapDisallowed
>
{body}
</ReactMarkdown>
this.toggleEditMode = this.toggleEditMode.bind(this);
this._handleUpdateComment = this._handleUpdateComment.bind(this);
}
<div className="commentFooter">
<a className="commentReplyButton commentLink" onClick={handleToggleCommentReply}>
toggleEditMode() {
this.setState({editMode: !this.state.editMode});
}
_handleUpdateComment(body: string, isPostUpdate: boolean) {
this.props.handleUpdateComment(
this.props.id,
body,
isPostUpdate,
this.toggleEditMode,
);
}
render() {
const {
id,
body,
isPostUpdate,
userFullName,
userEmail,
createdAt,
updatedAt,
replyForm,
handleToggleCommentReply,
handleCommentReplyBodyChange,
handleSubmitComment,
handleDeleteComment,
isLoggedIn,
isPowerUser,
currentUserEmail,
} = this.props;
return (
<div className="comment">
<div className="commentHeader">
<Gravatar email={userEmail} size={28} className="gravatar" />
<span className="commentAuthor">{userFullName}</span>
{
isPostUpdate ?
<span className="postUpdateBadge">
{I18n.t('post.comments.post_update_badge')}
</span>
:
null
}
</div>
{
this.state.editMode ?
<CommentEditForm
id={id}
initialBody={body}
initialIsPostUpdate={isPostUpdate}
isPowerUser={isPowerUser}
handleUpdateComment={this._handleUpdateComment}
toggleEditMode={this.toggleEditMode}
/>
:
<>
<ReactMarkdown
className="commentBody"
disallowedTypes={['heading', 'image', 'html']}
unwrapDisallowed
>
{body}
</ReactMarkdown>
<CommentFooter
id={id}
createdAt={createdAt}
updatedAt={updatedAt}
replyForm={replyForm}
isPowerUser={isPowerUser}
currentUserEmail={currentUserEmail}
commentAuthorEmail={userEmail}
handleDeleteComment={handleDeleteComment}
handleToggleCommentReply={handleToggleCommentReply}
toggleEditMode={this.toggleEditMode}
/>
</>
}
{
replyForm.isOpen ?
I18n.t('common.buttons.cancel')
:
I18n.t('post.comments.reply_button')
<NewComment
body={replyForm.body}
parentId={id}
postUpdateFlagValue={replyForm.isPostUpdate}
isSubmitting={replyForm.isSubmitting}
error={replyForm.error}
handleChange={handleCommentReplyBodyChange}
handlePostUpdateFlag={() => null}
handleSubmit={handleSubmitComment}
isLoggedIn={isLoggedIn}
isPowerUser={isPowerUser}
userEmail={currentUserEmail}
/>
:
null
}
</a>
{
isPowerUser ?
<>
<Separator />
<a
onClick={() => handleToggleIsCommentUpdate(id, isPostUpdate)}
className="commentLink"
>
{ 'Post update: ' + (isPostUpdate ? 'yes' : 'no') }
</a>
<Separator />
<a href={`/admin/comments/${id}/edit`} className="commentLink" data-turbolinks="false">
{I18n.t('common.buttons.edit')}
</a>
<Separator />
<a
href={`/admin/comments/${id}`}
className="commentLink"
data-method="delete"
data-confirm="Are you sure?"
data-turbolinks="false">
{I18n.t('common.buttons.delete')}
</a>
</>
:
null
}
<Separator />
<MutedText>{friendlyDate(updatedAt)}</MutedText>
</div>
{
replyForm.isOpen ?
<NewComment
body={replyForm.body}
parentId={id}
postUpdateFlagValue={replyForm.isPostUpdate}
isSubmitting={replyForm.isSubmitting}
error={replyForm.error}
handleChange={handleCommentReplyBodyChange}
handlePostUpdateFlag={() => null}
handleSubmit={handleSubmitComment}
isLoggedIn={isLoggedIn}
isPowerUser={isPowerUser}
userEmail={currentUserEmail}
/>
:
null
}
</div>
);
</div>
);
}
}
export default Comment;

View File

@@ -0,0 +1,100 @@
import * as React from 'react';
import I18n from 'i18n-js';
import Button from '../common/Button';
interface Props {
id: number;
initialBody: string;
initialIsPostUpdate: boolean;
isPowerUser: boolean;
handleUpdateComment(body: string, isPostUpdate: boolean): void;
toggleEditMode(): void;
}
interface State {
body: string;
isPostUpdate: boolean;
}
class CommentEditForm extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
body: '',
isPostUpdate: false,
};
this.handleCommentBodyChange = this.handleCommentBodyChange.bind(this);
this.handleCommentIsPostUpdateChange = this.handleCommentIsPostUpdateChange.bind(this);
}
componentDidMount() {
this.setState({
body: this.props.initialBody,
isPostUpdate: this.props.initialIsPostUpdate,
});
}
handleCommentBodyChange(newCommentBody: string) {
this.setState({ body: newCommentBody });
}
handleCommentIsPostUpdateChange(newIsPostUpdate: boolean) {
this.setState({ isPostUpdate: newIsPostUpdate });
}
render() {
const { id, isPowerUser, handleUpdateComment, toggleEditMode } = this.props;
const { body, isPostUpdate } = this.state;
return (
<div className="editCommentForm">
<textarea
value={body}
onChange={e => this.handleCommentBodyChange(e.target.value)}
className="commentForm"
/>
<div>
<div>
{
isPowerUser ?
<>
<input
id={`isPostUpdateFlagComment${id}`}
type="checkbox"
onChange={e => this.handleCommentIsPostUpdateChange(e.target.checked)}
checked={isPostUpdate || false}
/>
&nbsp;
<label htmlFor={`isPostUpdateFlagComment${id}`}>
{I18n.t('post.new_comment.is_post_update')}
</label>
</>
:
null
}
</div>
<div>
<a className="commentLink" onClick={toggleEditMode}>
{ I18n.t('common.buttons.cancel') }
</a>
&nbsp;
<Button
onClick={() => handleUpdateComment(body, isPostUpdate)}
>
{ I18n.t('common.buttons.update') }
</Button>
</div>
</div>
</div>
);
}
}
export default CommentEditForm;

View File

@@ -0,0 +1,78 @@
import * as React from 'react';
import I18n from 'i18n-js';
import Separator from '../common/Separator';
import { MutedText } from '../common/CustomTexts';
import friendlyDate from '../../helpers/datetime';
import { ReplyFormState } from '../../reducers/replyFormReducer';
interface Props {
id: number;
createdAt: string;
updatedAt: string;
replyForm: ReplyFormState;
isPowerUser: boolean;
currentUserEmail: string;
commentAuthorEmail: string;
handleDeleteComment(id: number): void;
handleToggleCommentReply(): void;
toggleEditMode(): void;
}
const CommentFooter = ({
id,
createdAt,
updatedAt,
replyForm,
isPowerUser,
currentUserEmail,
commentAuthorEmail,
handleDeleteComment,
handleToggleCommentReply,
toggleEditMode,
}: Props) => (
<div className="commentFooter">
<a className="commentReplyButton commentLink" onClick={handleToggleCommentReply}>
{
replyForm.isOpen ?
I18n.t('common.buttons.cancel')
:
I18n.t('post.comments.reply_button')
}
</a>
{
isPowerUser || currentUserEmail === commentAuthorEmail ?
<>
<Separator />
<a onClick={toggleEditMode} className="commentLink">
{I18n.t('common.buttons.edit')}
</a>
<Separator />
<a
onClick={() => confirm(I18n.t('common.confirmation')) && handleDeleteComment(id)}
className="commentLink">
{I18n.t('common.buttons.delete')}
</a>
</>
:
null
}
<Separator />
<MutedText>{friendlyDate(createdAt)}</MutedText>
{
createdAt !== updatedAt ?
<>
<Separator />
<MutedText>{ I18n.t('common.edited').toLowerCase() }</MutedText>
</>
:
null
}
</div>
);
export default CommentFooter;

View File

@@ -13,8 +13,10 @@ interface Props {
toggleCommentReply(commentId: number): void;
setCommentReplyBody(commentId: number, body: string): void;
handleToggleIsCommentUpdate(commentId: number, currentIsPostUpdate: boolean): void;
handleSubmitComment(body: string, parentId: number, isPostUpdate: boolean): void;
handleUpdateComment(commentId: number, body: string, isPostUpdate: boolean, onSuccess: Function): void;
handleDeleteComment(id: number): void;
isLoggedIn: boolean;
isPowerUser: boolean;
@@ -29,8 +31,9 @@ const CommentList = ({
toggleCommentReply,
setCommentReplyBody,
handleToggleIsCommentUpdate,
handleSubmitComment,
handleUpdateComment,
handleDeleteComment,
isLoggedIn,
isPowerUser,
@@ -49,8 +52,11 @@ const CommentList = ({
setCommentReplyBody(comment.id, (e.target as HTMLTextAreaElement).value)
)
}
handleToggleIsCommentUpdate={handleToggleIsCommentUpdate}
handleSubmitComment={handleSubmitComment}
handleUpdateComment={handleUpdateComment}
handleDeleteComment={handleDeleteComment}
{...comment}
isLoggedIn={isLoggedIn}
@@ -66,8 +72,10 @@ const CommentList = ({
toggleCommentReply={toggleCommentReply}
setCommentReplyBody={setCommentReplyBody}
handleToggleIsCommentUpdate={handleToggleIsCommentUpdate}
handleSubmitComment={handleSubmitComment}
handleUpdateComment={handleUpdateComment}
handleDeleteComment={handleDeleteComment}
isLoggedIn={isLoggedIn}
isPowerUser={isPowerUser}

View File

@@ -26,12 +26,7 @@ interface Props {
toggleCommentReply(commentId: number): void;
setCommentReplyBody(commentId: number, body: string): void;
toggleCommentIsPostUpdateFlag(): void;
toggleCommentIsPostUpdate(
postId: number,
commentId: number,
currentIsPostUpdate: boolean,
authenticityToken: string,
): void;
submitComment(
postId: number,
body: string,
@@ -39,6 +34,19 @@ interface Props {
isPostUpdate: boolean,
authenticityToken: string,
): void;
updateComment(
postId: number,
commentId: number,
body: string,
isPostUpdate: boolean,
onSuccess: Function,
authenticityToken: string,
): void;
deleteComment(
postId: number,
commentId: number,
authenticityToken: string,
): void;
}
class CommentsP extends React.Component<Props> {
@@ -46,15 +54,6 @@ class CommentsP extends React.Component<Props> {
this.props.requestComments(this.props.postId);
}
_handleToggleIsCommentUpdate = (commentId: number, currentIsPostUpdate: boolean) => {
this.props.toggleCommentIsPostUpdate(
this.props.postId,
commentId,
currentIsPostUpdate,
this.props.authenticityToken,
);
}
_handleSubmitComment = (body: string, parentId: number, isPostUpdate: boolean) => {
this.props.submitComment(
this.props.postId,
@@ -65,6 +64,25 @@ class CommentsP extends React.Component<Props> {
);
}
_handleUpdateComment = (commentId: number, body: string, isPostUpdate: boolean, onSuccess: Function) => {
this.props.updateComment(
this.props.postId,
commentId,
body,
isPostUpdate,
onSuccess,
this.props.authenticityToken,
);
}
_handleDeleteComment = (commentId: number) => {
this.props.deleteComment(
this.props.postId,
commentId,
this.props.authenticityToken,
);
}
render() {
const {
isLoggedIn,
@@ -118,8 +136,9 @@ class CommentsP extends React.Component<Props> {
replyForms={replyForms}
toggleCommentReply={toggleCommentReply}
setCommentReplyBody={setCommentReplyBody}
handleToggleIsCommentUpdate={this._handleToggleIsCommentUpdate}
handleSubmitComment={this._handleSubmitComment}
handleUpdateComment={this._handleUpdateComment}
handleDeleteComment={this._handleDeleteComment}
parentId={null}
level={1}
isLoggedIn={isLoggedIn}

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import I18n from 'i18n-js';
import Gravatar from 'react-gravatar';
import I18n from 'i18n-js';
import NewCommentUpdateSection from './NewCommentUpdateSection';
@@ -52,7 +52,7 @@ const NewComment = ({
value={body}
onChange={handleChange}
placeholder={I18n.t('post.new_comment.body_placeholder')}
className="newCommentBody"
className="commentForm"
/>
<Button
onClick={() => handleSubmit(body, parentId, postUpdateFlagValue)}