mirror of
https://github.com/astuto/astuto.git
synced 2025-12-16 03:37:56 +01:00
Add edit and delete actions to posts and comments (#125)
This commit is contained in:
committed by
GitHub
parent
07ca2a304a
commit
bc15140512
@@ -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;
|
||||
100
app/javascript/components/Comments/CommentEditForm.tsx
Normal file
100
app/javascript/components/Comments/CommentEditForm.tsx
Normal 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}
|
||||
/>
|
||||
|
||||
<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>
|
||||
|
||||
<Button
|
||||
onClick={() => handleUpdateComment(body, isPostUpdate)}
|
||||
>
|
||||
{ I18n.t('common.buttons.update') }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CommentEditForm;
|
||||
78
app/javascript/components/Comments/CommentFooter.tsx
Normal file
78
app/javascript/components/Comments/CommentFooter.tsx
Normal 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;
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
Reference in New Issue
Block a user