Add comment replies toggle and body to state

This commit is contained in:
riggraz
2019-09-17 19:09:38 +02:00
parent 0c0c6d4e30
commit ecfdc54100
8 changed files with 191 additions and 4 deletions

View File

@@ -0,0 +1,27 @@
export const TOGGLE_COMMENT_REPLY = 'TOGGLE_COMMENT_REPLY';
interface ToggleCommentReplyAction {
type: typeof TOGGLE_COMMENT_REPLY;
commentId: number;
}
export const SET_COMMENT_REPLY_BODY = 'SET_COMMENT_REPLY_BODY';
interface SetCommentReplyBodyAction {
type: typeof SET_COMMENT_REPLY_BODY;
commentId: number;
body: string;
}
export const toggleCommentReply = (commentId): ToggleCommentReplyAction => ({
type: TOGGLE_COMMENT_REPLY,
commentId,
});
export const setCommentReplyBody = (commentId, body): SetCommentReplyBodyAction => ({
type: SET_COMMENT_REPLY_BODY,
commentId,
body,
});
export type HandleCommentRepliesType =
ToggleCommentReplyAction |
SetCommentReplyBodyAction;

View File

@@ -1,7 +1,10 @@
import * as React from 'react';
import { FormEvent } from 'react';
import { MutedText } from '../shared/CustomTexts';
import { CommentRepliesState } from '../../reducers/commentRepliesReducer';
interface Props {
id: number;
body: string;
@@ -10,6 +13,9 @@ interface Props {
updatedAt: string;
level: number;
reply: CommentRepliesState;
handleToggleCommentReply(): void;
handleCommentReplyBodyChange(e: FormEvent): void;
}
const Comment = ({
@@ -20,6 +26,9 @@ const Comment = ({
updatedAt,
level,
reply,
handleToggleCommentReply,
handleCommentReplyBodyChange,
}: Props) => (
<div className="comment">
<div className="commentHeader">
@@ -27,9 +36,18 @@ const Comment = ({
</div>
<p className="commentBody">{body}</p>
<div className="commentFooter">
<a href="#">Reply</a>
<a onClick={handleToggleCommentReply}>Reply</a>
<MutedText>{updatedAt}</MutedText>
</div>
{
reply.isOpen ?
<textarea
value={reply.body}
onChange={handleCommentReplyBodyChange}
/>
:
null
}
</div>
);

View File

@@ -1,27 +1,55 @@
import * as React from 'react';
import { FormEvent } from 'react';
import Comment from './Comment';
import IComment from '../../interfaces/IComment';
import { CommentRepliesState } from '../../reducers/commentRepliesReducer';
interface Props {
comments: Array<IComment>;
replies: Array<CommentRepliesState>;
parentId: number;
level: number;
toggleCommentReply(commentId: number);
setCommentReplyBody(commentId: number, body: string);
}
const CommentList = ({ comments, parentId, level }: Props) => (
const CommentList = ({
comments,
replies,
parentId,
level,
toggleCommentReply,
setCommentReplyBody,
}: Props) => (
<React.Fragment>
{comments.map((comment, i) => {
if (comment.parentId === parentId) {
return (
<div className="commentList">
<Comment level={level} {...comment} />
<Comment
level={level}
reply={replies.find(reply => reply.commentId === comment.id)}
handleToggleCommentReply={() => toggleCommentReply(comment.id)}
handleCommentReplyBodyChange={
(e: FormEvent) => (
setCommentReplyBody(comment.id, (e.target as HTMLTextAreaElement).value)
)
}
{...comment}
/>
<CommentList
comments={comments}
replies={replies}
parentId={comment.id}
level={level+1}
toggleCommentReply={toggleCommentReply}
setCommentReplyBody={setCommentReplyBody}
/>
</div>
);

View File

@@ -5,15 +5,19 @@ import Spinner from '../shared/Spinner';
import { DangerText } from '../shared/CustomTexts';
import IComment from '../../interfaces/IComment';
import { CommentRepliesState } from '../../reducers/commentRepliesReducer';
interface Props {
postId: number;
comments: Array<IComment>;
replies: Array<CommentRepliesState>;
areLoading: boolean;
error: string;
requestComments(postId: number, page?: number);
toggleCommentReply(commentId: number);
setCommentReplyBody(commentId: number, body: string);
}
class CommentsP extends React.Component<Props> {
@@ -24,8 +28,12 @@ class CommentsP extends React.Component<Props> {
render() {
const {
comments,
replies,
areLoading,
error,
toggleCommentReply,
setCommentReplyBody,
} = this.props;
return (
@@ -37,6 +45,9 @@ class CommentsP extends React.Component<Props> {
<CommentList
comments={comments}
replies={replies}
toggleCommentReply={toggleCommentReply}
setCommentReplyBody={setCommentReplyBody}
parentId={null}
level={1}
/>

View File

@@ -1,6 +1,10 @@
import { connect } from 'react-redux';
import { requestComments } from '../actions/requestComments';
import {
toggleCommentReply,
setCommentReplyBody,
} from '../actions/handleCommentReplies';
import { State } from '../reducers/rootReducer';
@@ -8,6 +12,7 @@ import CommentsP from '../components/Comments/CommentsP';
const mapStateToProps = (state: State) => ({
comments: state.currentPost.comments.items,
replies: state.currentPost.comments.replies,
areLoading: state.currentPost.comments.areLoading,
error: state.currentPost.comments.error,
});
@@ -16,6 +21,14 @@ const mapDispatchToProps = (dispatch) => ({
requestComments(postId: number) {
dispatch(requestComments(postId));
},
toggleCommentReply(commentId: number) {
dispatch(toggleCommentReply(commentId));
},
setCommentReplyBody(commentId: number, body: string) {
dispatch(setCommentReplyBody(commentId, body));
},
});
export default connect(

View File

@@ -0,0 +1,50 @@
import {
COMMENT_REQUEST_SUCCESS,
} from '../actions/requestComment';
import {
HandleCommentRepliesType,
TOGGLE_COMMENT_REPLY,
SET_COMMENT_REPLY_BODY,
} from '../actions/handleCommentReplies';
export interface CommentRepliesState {
commentId: number;
isOpen: boolean;
body: string;
}
const initialState: CommentRepliesState = {
commentId: undefined,
isOpen: false,
body: '',
}
const commentRepliesReducer = (
state = initialState,
action,
) => {
switch (action.type) {
case COMMENT_REQUEST_SUCCESS:
return {
...initialState,
commentId: action.comment.id,
};
case TOGGLE_COMMENT_REPLY:
return {
...state,
isOpen: !state.isOpen,
};
case SET_COMMENT_REPLY_BODY:
return {
...state,
body: action.body,
};
default:
return state;
}
}
export default commentRepliesReducer;

View File

@@ -4,20 +4,31 @@ import {
COMMENTS_REQUEST_SUCCESS,
COMMENTS_REQUEST_FAILURE,
} from '../actions/requestComments';
import { commentRequestSuccess } from '../actions/requestComment';
import {
HandleCommentRepliesType,
TOGGLE_COMMENT_REPLY,
SET_COMMENT_REPLY_BODY,
} from '../actions/handleCommentReplies';
import commentReducer from './commentReducer';
import commentRepliesReducer from './commentRepliesReducer';
import IComment from '../interfaces/IComment';
import { CommentRepliesState } from './commentRepliesReducer';
export interface CommentsState {
items: Array<IComment>;
replies: Array<CommentRepliesState>;
areLoading: boolean;
error: string;
}
const initialState: CommentsState = {
items: [],
replies: [],
areLoading: false,
error: '',
};
@@ -39,6 +50,9 @@ const commentsReducer = (
items: action.comments.map(
comment => commentReducer(undefined, commentRequestSuccess(comment))
),
replies: action.comments.map(
comment => commentRepliesReducer(undefined, commentRequestSuccess(comment))
),
areLoading: false,
error: '',
};
@@ -50,6 +64,20 @@ const commentsReducer = (
error: action.error,
};
case TOGGLE_COMMENT_REPLY:
case SET_COMMENT_REPLY_BODY:
return {
...state,
replies: state.replies.map(
reply => (
reply.commentId === action.commentId ?
commentRepliesReducer(reply, action)
:
reply
)
),
};
default:
return state;
}

View File

@@ -17,6 +17,12 @@ import {
COMMENTS_REQUEST_FAILURE,
} from '../actions/requestComments';
import {
HandleCommentRepliesType,
TOGGLE_COMMENT_REPLY,
SET_COMMENT_REPLY_BODY,
} from '../actions/handleCommentReplies';
import postReducer from './postReducer';
import commentsReducer from './commentsReducer';
@@ -40,7 +46,11 @@ const initialState: CurrentPostState = {
const currentPostReducer = (
state = initialState,
action: PostRequestActionTypes | ChangePostStatusSuccessAction | CommentsRequestActionTypes,
action:
PostRequestActionTypes |
ChangePostStatusSuccessAction |
CommentsRequestActionTypes |
HandleCommentRepliesType
): CurrentPostState => {
switch (action.type) {
case POST_REQUEST_START:
@@ -73,6 +83,8 @@ const currentPostReducer = (
case COMMENTS_REQUEST_START:
case COMMENTS_REQUEST_SUCCESS:
case COMMENTS_REQUEST_FAILURE:
case TOGGLE_COMMENT_REPLY:
case SET_COMMENT_REPLY_BODY:
return {
...state,
comments: commentsReducer(state.comments, action),