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
107
app/javascript/components/Post/PostEditForm.tsx
Normal file
107
app/javascript/components/Post/PostEditForm.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import * as React from 'react';
|
||||
import I18n from 'i18n-js';
|
||||
|
||||
import PostBoardSelect from './PostBoardSelect';
|
||||
import PostStatusSelect from './PostStatusSelect';
|
||||
|
||||
import IPostStatus from '../../interfaces/IPostStatus';
|
||||
import IBoard from '../../interfaces/IBoard';
|
||||
import Button from '../common/Button';
|
||||
import Spinner from '../common/Spinner';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description?: string;
|
||||
boardId: number;
|
||||
postStatusId?: number;
|
||||
|
||||
isUpdating: boolean;
|
||||
error: string;
|
||||
|
||||
handleChangeTitle(title: string): void;
|
||||
handleChangeDescription(description: string): void;
|
||||
handleChangeBoard(boardId: number): void;
|
||||
handleChangePostStatus(postStatusId: number): void;
|
||||
|
||||
isPowerUser: boolean;
|
||||
boards: Array<IBoard>;
|
||||
postStatuses: Array<IPostStatus>;
|
||||
|
||||
toggleEditMode(): void;
|
||||
handleUpdatePost(
|
||||
title: string,
|
||||
description: string,
|
||||
boardId: number,
|
||||
postStatusId: number,
|
||||
): void;
|
||||
}
|
||||
|
||||
const PostEditForm = ({
|
||||
title,
|
||||
description,
|
||||
boardId,
|
||||
postStatusId,
|
||||
|
||||
isUpdating,
|
||||
error,
|
||||
|
||||
handleChangeTitle,
|
||||
handleChangeDescription,
|
||||
handleChangeBoard,
|
||||
handleChangePostStatus,
|
||||
|
||||
isPowerUser,
|
||||
boards,
|
||||
postStatuses,
|
||||
|
||||
toggleEditMode,
|
||||
handleUpdatePost,
|
||||
}: Props) => (
|
||||
<div className="postEditForm">
|
||||
<div className="postHeader">
|
||||
<input
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={e => handleChangeTitle(e.target.value)}
|
||||
className="form-control"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
isPowerUser ?
|
||||
<div className="postSettings">
|
||||
<PostBoardSelect
|
||||
boards={boards}
|
||||
selectedBoardId={boardId}
|
||||
handleChange={newBoardId => handleChangeBoard(newBoardId)}
|
||||
/>
|
||||
<PostStatusSelect
|
||||
postStatuses={postStatuses}
|
||||
selectedPostStatusId={postStatusId}
|
||||
handleChange={newPostStatusId => handleChangePostStatus(newPostStatusId)}
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
|
||||
<textarea
|
||||
value={description}
|
||||
onChange={e => handleChangeDescription(e.target.value)}
|
||||
rows={5}
|
||||
className="form-control"
|
||||
/>
|
||||
|
||||
<div className="postEditFormButtons">
|
||||
<a onClick={toggleEditMode}>
|
||||
{ I18n.t('common.buttons.cancel') }
|
||||
</a>
|
||||
|
||||
<Button onClick={() => handleUpdatePost(title, description, boardId, postStatusId)}>
|
||||
{ isUpdating ? <Spinner /> : I18n.t('common.buttons.update') }
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default PostEditForm;
|
||||
53
app/javascript/components/Post/PostFooter.tsx
Normal file
53
app/javascript/components/Post/PostFooter.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import * as React from 'react';
|
||||
import Gravatar from 'react-gravatar';
|
||||
import I18n from 'i18n-js';
|
||||
|
||||
import { MutedText } from '../common/CustomTexts';
|
||||
import friendlyDate from '../../helpers/datetime';
|
||||
import Separator from '../common/Separator';
|
||||
|
||||
interface Props {
|
||||
createdAt: string;
|
||||
toggleEditMode(): void;
|
||||
handleDeletePost(): void;
|
||||
isPowerUser: boolean;
|
||||
authorEmail: string;
|
||||
authorFullName: string;
|
||||
currentUserEmail: string;
|
||||
}
|
||||
|
||||
const PostFooter = ({
|
||||
createdAt,
|
||||
toggleEditMode,
|
||||
handleDeletePost,
|
||||
isPowerUser,
|
||||
authorEmail,
|
||||
authorFullName,
|
||||
currentUserEmail,
|
||||
}: Props) => (
|
||||
<div className="postFooter">
|
||||
<div className="postAuthor">
|
||||
<span>{ I18n.t('post.published_by').toLowerCase() } </span>
|
||||
<Gravatar email={authorEmail} size={24} className="postAuthorAvatar" />
|
||||
{authorFullName}
|
||||
</div>
|
||||
{
|
||||
isPowerUser || authorEmail === currentUserEmail ?
|
||||
<>
|
||||
<a onClick={toggleEditMode}>
|
||||
{ I18n.t('common.buttons.edit') }
|
||||
</a>
|
||||
<Separator />
|
||||
<a onClick={() => confirm(I18n.t('common.confirmation')) && handleDeletePost()}>
|
||||
{ I18n.t('common.buttons.delete') }
|
||||
</a>
|
||||
<Separator />
|
||||
</>
|
||||
:
|
||||
null
|
||||
}
|
||||
<MutedText>{friendlyDate(createdAt)}</MutedText>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default PostFooter;
|
||||
@@ -1,32 +1,34 @@
|
||||
import * as React from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import I18n from 'i18n-js';
|
||||
|
||||
import IPost from '../../interfaces/IPost';
|
||||
import IPostStatus from '../../interfaces/IPostStatus';
|
||||
import IBoard from '../../interfaces/IBoard';
|
||||
|
||||
import PostUpdateList from './PostUpdateList';
|
||||
import PostEditForm from './PostEditForm';
|
||||
import PostFooter from './PostFooter';
|
||||
import LikeList from './LikeList';
|
||||
import ActionBox from './ActionBox';
|
||||
import LikeButton from '../../containers/LikeButton';
|
||||
import PostBoardSelect from './PostBoardSelect';
|
||||
import PostStatusSelect from './PostStatusSelect';
|
||||
import PostBoardLabel from '../common/PostBoardLabel';
|
||||
import PostStatusLabel from '../common/PostStatusLabel';
|
||||
import Comments from '../../containers/Comments';
|
||||
import { MutedText } from '../common/CustomTexts';
|
||||
import Sidebar from '../common/Sidebar';
|
||||
|
||||
import { LikesState } from '../../reducers/likesReducer';
|
||||
import { CommentsState } from '../../reducers/commentsReducer';
|
||||
import { PostStatusChangesState } from '../../reducers/postStatusChangesReducer';
|
||||
import { PostEditFormState } from '../../reducers/currentPostReducer';
|
||||
|
||||
import friendlyDate, { fromRailsStringToJavascriptDate } from '../../helpers/datetime';
|
||||
import { fromRailsStringToJavascriptDate } from '../../helpers/datetime';
|
||||
import HttpStatus from '../../constants/http_status';
|
||||
|
||||
interface Props {
|
||||
postId: number;
|
||||
post: IPost;
|
||||
editMode: boolean;
|
||||
editForm: PostEditFormState;
|
||||
likes: LikesState;
|
||||
followed: boolean;
|
||||
comments: CommentsState;
|
||||
@@ -35,26 +37,38 @@ interface Props {
|
||||
postStatuses: Array<IPostStatus>;
|
||||
isLoggedIn: boolean;
|
||||
isPowerUser: boolean;
|
||||
userFullName: string;
|
||||
userEmail: string;
|
||||
currentUserFullName: string;
|
||||
currentUserEmail: string;
|
||||
authenticityToken: string;
|
||||
|
||||
requestPost(postId: number): void;
|
||||
updatePost(
|
||||
postId: number,
|
||||
title: string,
|
||||
description: string,
|
||||
boardId: number,
|
||||
postStatusId: number,
|
||||
authenticityToken: string,
|
||||
): Promise<any>;
|
||||
|
||||
requestLikes(postId: number): void;
|
||||
requestFollow(postId: number): void;
|
||||
requestPostStatusChanges(postId: number): void;
|
||||
changePostBoard(
|
||||
postId: number,
|
||||
newBoardId: number,
|
||||
authenticityToken: string,
|
||||
): void;
|
||||
changePostStatus(
|
||||
postId: number,
|
||||
|
||||
toggleEditMode(): void;
|
||||
handleChangeEditFormTitle(title: string): void;
|
||||
handleChangeEditFormDescription(description: string): void;
|
||||
handleChangeEditFormBoard(boardId: number): void;
|
||||
handleChangeEditFormPostStatus(postStatusId: number): void;
|
||||
|
||||
deletePost(postId: number, authenticityToken: string): Promise<any>;
|
||||
|
||||
postStatusChangeSubmitted(
|
||||
newPostStatusId: number,
|
||||
userFullName: string,
|
||||
userEmail: string,
|
||||
authenticityToken: string,
|
||||
): void;
|
||||
|
||||
submitFollow(
|
||||
postId: number,
|
||||
isFollow: boolean,
|
||||
@@ -63,8 +77,15 @@ interface Props {
|
||||
}
|
||||
|
||||
class PostP extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._handleUpdatePost = this._handleUpdatePost.bind(this);
|
||||
this._handleDeletePost = this._handleDeletePost.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {postId} = this.props;
|
||||
const { postId } = this.props;
|
||||
|
||||
this.props.requestPost(postId);
|
||||
this.props.requestLikes(postId);
|
||||
@@ -72,9 +93,51 @@ class PostP extends React.Component<Props> {
|
||||
this.props.requestPostStatusChanges(postId);
|
||||
}
|
||||
|
||||
_handleUpdatePost(title: string, description: string, boardId: number, postStatusId: number) {
|
||||
const {
|
||||
postId,
|
||||
post,
|
||||
currentUserFullName,
|
||||
currentUserEmail,
|
||||
authenticityToken,
|
||||
|
||||
updatePost,
|
||||
postStatusChangeSubmitted,
|
||||
} = this.props;
|
||||
|
||||
const oldPostStatusId = post.postStatusId;
|
||||
|
||||
updatePost(
|
||||
postId,
|
||||
title,
|
||||
description,
|
||||
boardId,
|
||||
postStatusId,
|
||||
authenticityToken,
|
||||
).then(res => {
|
||||
if (res?.status !== HttpStatus.OK) return;
|
||||
if (postStatusId === oldPostStatusId) return;
|
||||
|
||||
postStatusChangeSubmitted(
|
||||
postStatusId,
|
||||
currentUserFullName,
|
||||
currentUserEmail,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_handleDeletePost() {
|
||||
this.props.deletePost(
|
||||
this.props.postId,
|
||||
this.props.authenticityToken
|
||||
).then(() => window.location.href = `/boards/${this.props.post.boardId}`);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
post,
|
||||
editMode,
|
||||
editForm,
|
||||
likes,
|
||||
followed,
|
||||
comments,
|
||||
@@ -84,13 +147,15 @@ class PostP extends React.Component<Props> {
|
||||
|
||||
isLoggedIn,
|
||||
isPowerUser,
|
||||
userFullName,
|
||||
userEmail,
|
||||
currentUserEmail,
|
||||
authenticityToken,
|
||||
|
||||
changePostBoard,
|
||||
changePostStatus,
|
||||
submitFollow,
|
||||
toggleEditMode,
|
||||
handleChangeEditFormTitle,
|
||||
handleChangeEditFormDescription,
|
||||
handleChangeEditFormBoard,
|
||||
handleChangeEditFormPostStatus,
|
||||
} = this.props;
|
||||
|
||||
const postUpdates = [
|
||||
@@ -98,7 +163,7 @@ class PostP extends React.Component<Props> {
|
||||
...postStatusChanges.items,
|
||||
].sort(
|
||||
(a, b) =>
|
||||
fromRailsStringToJavascriptDate(a.updatedAt) < fromRailsStringToJavascriptDate(b.updatedAt) ? 1 : -1
|
||||
fromRailsStringToJavascriptDate(a.createdAt) < fromRailsStringToJavascriptDate(b.createdAt) ? 1 : -1
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -126,71 +191,72 @@ class PostP extends React.Component<Props> {
|
||||
</Sidebar>
|
||||
|
||||
<div className="postAndCommentsContainer">
|
||||
<>
|
||||
<div className="postHeader">
|
||||
<LikeButton
|
||||
postId={post.id}
|
||||
likesCount={likes.items.length}
|
||||
liked={likes.items.find(like => like.email === userEmail) ? 1 : 0}
|
||||
isLoggedIn={isLoggedIn}
|
||||
authenticityToken={authenticityToken}
|
||||
/>
|
||||
<h2>{post.title}</h2>
|
||||
{
|
||||
isPowerUser && post ?
|
||||
<a href={`/admin/posts/${post.id}`} data-turbolinks="false">
|
||||
{I18n.t('post.edit_button')}
|
||||
</a>
|
||||
:
|
||||
null
|
||||
}
|
||||
</div>
|
||||
{
|
||||
isPowerUser && post ?
|
||||
<div className="postSettings">
|
||||
<PostBoardSelect
|
||||
boards={boards}
|
||||
selectedBoardId={post.boardId}
|
||||
handleChange={
|
||||
newBoardId => changePostBoard(post.id, newBoardId, authenticityToken)
|
||||
}
|
||||
/>
|
||||
<PostStatusSelect
|
||||
postStatuses={postStatuses}
|
||||
selectedPostStatusId={post.postStatusId}
|
||||
handleChange={
|
||||
newPostStatusId =>
|
||||
changePostStatus(post.id, newPostStatusId, userFullName, userEmail, authenticityToken)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
<div className="postInfo">
|
||||
<PostBoardLabel
|
||||
{...boards.find(board => board.id === post.boardId)}
|
||||
/>
|
||||
<PostStatusLabel
|
||||
{...postStatuses.find(postStatus => postStatus.id === post.postStatusId)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
<ReactMarkdown
|
||||
className="postDescription"
|
||||
disallowedTypes={['heading', 'image', 'html']}
|
||||
unwrapDisallowed
|
||||
>
|
||||
{post.description}
|
||||
</ReactMarkdown>
|
||||
|
||||
<MutedText>{friendlyDate(post.createdAt)}</MutedText>
|
||||
</>
|
||||
{
|
||||
editMode ?
|
||||
<PostEditForm
|
||||
{...editForm}
|
||||
|
||||
handleChangeTitle={handleChangeEditFormTitle}
|
||||
handleChangeDescription={handleChangeEditFormDescription}
|
||||
handleChangeBoard={handleChangeEditFormBoard}
|
||||
handleChangePostStatus={handleChangeEditFormPostStatus}
|
||||
|
||||
isPowerUser={isPowerUser}
|
||||
boards={boards}
|
||||
postStatuses={postStatuses}
|
||||
|
||||
toggleEditMode={toggleEditMode}
|
||||
handleUpdatePost={this._handleUpdatePost}
|
||||
/>
|
||||
:
|
||||
<>
|
||||
<div className="postHeader">
|
||||
<LikeButton
|
||||
postId={post.id}
|
||||
likesCount={likes.items.length}
|
||||
liked={likes.items.find(like => like.email === currentUserEmail) ? 1 : 0}
|
||||
isLoggedIn={isLoggedIn}
|
||||
authenticityToken={authenticityToken}
|
||||
/>
|
||||
|
||||
<h3>{post.title}</h3>
|
||||
</div>
|
||||
|
||||
<div className="postInfo">
|
||||
<PostBoardLabel
|
||||
{...boards.find(board => board.id === post.boardId)}
|
||||
/>
|
||||
<PostStatusLabel
|
||||
{...postStatuses.find(postStatus => postStatus.id === post.postStatusId)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ReactMarkdown
|
||||
className="postDescription"
|
||||
disallowedTypes={['heading', 'image', 'html']}
|
||||
unwrapDisallowed
|
||||
>
|
||||
{post.description}
|
||||
</ReactMarkdown>
|
||||
|
||||
<PostFooter
|
||||
createdAt={post.createdAt}
|
||||
handleDeletePost={this._handleDeletePost}
|
||||
toggleEditMode={toggleEditMode}
|
||||
|
||||
isPowerUser={isPowerUser}
|
||||
authorEmail={post.userEmail}
|
||||
authorFullName={post.userFullName}
|
||||
currentUserEmail={currentUserEmail}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
<Comments
|
||||
postId={this.props.postId}
|
||||
isLoggedIn={isLoggedIn}
|
||||
isPowerUser={isPowerUser}
|
||||
userEmail={userEmail}
|
||||
userEmail={currentUserEmail}
|
||||
authenticityToken={authenticityToken}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,7 @@ const PostUpdateList = ({
|
||||
}
|
||||
</div>
|
||||
|
||||
<MutedText>{friendlyDate(postUpdate.updatedAt)}</MutedText>
|
||||
<MutedText>{friendlyDate(postUpdate.createdAt)}</MutedText>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ interface Props {
|
||||
postStatuses: Array<IPostStatus>;
|
||||
isLoggedIn: boolean;
|
||||
isPowerUser: boolean;
|
||||
userFullName: string;
|
||||
userEmail: string;
|
||||
currentUserFullName: string;
|
||||
currentUserEmail: string;
|
||||
authenticityToken: string;
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ class PostRoot extends React.Component<Props> {
|
||||
postStatuses,
|
||||
isLoggedIn,
|
||||
isPowerUser,
|
||||
userFullName,
|
||||
userEmail,
|
||||
currentUserFullName,
|
||||
currentUserEmail,
|
||||
authenticityToken
|
||||
} = this.props;
|
||||
|
||||
@@ -52,8 +52,8 @@ class PostRoot extends React.Component<Props> {
|
||||
|
||||
isLoggedIn={isLoggedIn}
|
||||
isPowerUser={isPowerUser}
|
||||
userFullName={userFullName}
|
||||
userEmail={userEmail}
|
||||
currentUserFullName={currentUserFullName}
|
||||
currentUserEmail={currentUserEmail}
|
||||
authenticityToken={authenticityToken}
|
||||
/>
|
||||
</Provider>
|
||||
|
||||
Reference in New Issue
Block a user