Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add/delete comments #415

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ FROM node:8 as base
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
EXPOSE 8000
EXPOSE 27017

FROM base as development
ENV NODE_ENV development
Expand Down
2 changes: 2 additions & 0 deletions Intl/localizationData/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export default {
twitterMessage: 'We are on Twitter',
by: 'By',
deletePost: 'Delete Post',
addComment: 'Add comment',
commentAuthor: 'Author',
createNewPost: 'Create new post',
authorName: 'Author\'s Name',
postTitle: 'Post Title',
Expand Down
2 changes: 2 additions & 0 deletions Intl/localizationData/fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export default {
twitterMessage: 'Nous sommes sur Twitter',
by: 'Par',
deletePost: 'Supprimer le message',
addComment: 'Ajouter un commentaire',
commentAuthor: 'Auteur',
createNewPost: 'Créer un nouveau message',
authorName: 'Nom de l\'auteur',
postTitle: 'Titre de l\'article',
Expand Down
72 changes: 72 additions & 0 deletions client/modules/Post/PostActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ export const ADD_POST = 'ADD_POST';
export const ADD_POSTS = 'ADD_POSTS';
export const DELETE_POST = 'DELETE_POST';

export const COMMENT_FORM_OPEN = 'COMMENT_FORM_OPEN';
export const COMMENT_FORM_CLOSE = 'COMMENT_FORM_CLOSE';
export const COMMENT_ADD = 'COMMENT_ADD';
export const COMMENT_REMOVE = 'COMMENT_REMOVE';

// Export Actions
export function addPost(post) {
return {
Expand Down Expand Up @@ -58,3 +63,70 @@ export function deletePostRequest(cuid) {
return callApi(`posts/${cuid}`, 'delete').then(() => dispatch(deletePost(cuid)));
};
}

export function commentFormForPostClose(postId) {
return {
type: COMMENT_FORM_OPEN,
payload: {
postId,
},
};
}

export function commentAdd(authorName, comment, postId, _id) {
return {
type: COMMENT_ADD,
payload: {
authorName,
comment,
postId,
_id,
},
};
}

export function commentRemove(commentId, postId) {
return {
type: COMMENT_REMOVE,
payload: {
commentId,
postId,
},
};
}

export function commentRemoveRequest(commentId, postId) {
return (dispatch) => {
return callApi('comment', 'delete', { commentId, postId })
.then((response) => {
dispatch(commentRemove(
response.commentId,
response.postId
));
});
};
}

export function commentRequestAdd(authorName, comment, postId) {
return (dispatch) => {
return callApi('comment', 'post', { authorName, comment, postId })
.then((response) => {
dispatch(commentFormForPostClose());
dispatch(commentAdd(
response.authorName,
response.comment,
response.postId,
response._id
));
});
};
}

export function commentFormForPostOpen(postId) {
return {
type: COMMENT_FORM_OPEN,
payload: {
postId,
},
};
}
81 changes: 78 additions & 3 deletions client/modules/Post/PostReducer.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,98 @@
import { ADD_POST, ADD_POSTS, DELETE_POST } from './PostActions';
import {
ADD_POST,
ADD_POSTS,
DELETE_POST,
COMMENT_ADD,
COMMENT_REMOVE,
COMMENT_FORM_OPEN,
} from './PostActions';

// Post
// {
// _id,
// name,
// title,
// slug,
// cuid,
// content,
// comments: [],
// }
// Initial State
const initialState = { data: [] };
const initialState = {
data: [],
addCommentForm: {
isVisible: false,
postId: '',
},
};

const PostReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_POST :
return {
...state,
data: [action.post, ...state.data],
};

case ADD_POSTS :
return {
...state,
data: action.posts,
};

case DELETE_POST :
return {
...state,
data: state.data.filter(post => post.cuid !== action.cuid),
};

case COMMENT_FORM_OPEN :
return {
...state,
addCommentForm: {
isVisible: true,
postId: action.payload.postId,
},
};

case COMMENT_ADD :
return {
...state,
data: state.data.map(post => {
if (post._id === action.payload.postId) {
return {
...post,
comments: [
...post.comments,
{
authorName: action.payload.authorName,
comment: action.payload.comment,
postId: action.payload.postId,
_id: action.payload._id,
},
],
};
}
return post;
}),
};

case COMMENT_REMOVE :
return {
...state,
data: state.data.map(post => {
if (post._id === action.payload.postId) {
return {
...post,
comments: post.comments.filter(
({ _id }) => _id !== action.payload.commentId
),
};
}
return post;
}),
};

default:
return state;
}
Expand All @@ -31,7 +104,9 @@ const PostReducer = (state = initialState, action) => {
export const getPosts = state => state.posts.data;

// Get post by cuid
export const getPost = (state, cuid) => state.posts.data.filter(post => post.cuid === cuid)[0];
export const getPost = (state, cuid) => state.posts.data.filter(
post => post.cuid === cuid
)[0];

// Export Reducer
export default PostReducer;
37 changes: 37 additions & 0 deletions client/modules/Post/PostSelectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createSelector } from 'reselect';

export const commentFormValuesSelector = state =>
state.form.addCommentForm && state.form.addCommentForm.values;

export const authorNameSelector = createSelector(
commentFormValuesSelector,
values => {
return values && values.authorName ? values.authorName : '';
}
);

export const commentSelector = createSelector(
commentFormValuesSelector,
values => {
return values && values.comment ? values.comment : '';
}
);

export const isCommentFormVisibleSelector = state => {
// render on server error ".addCommentForm is undefined"
return !!state.posts.addCommentForm && state.posts.addCommentForm.isVisible;
};

export const isCommentFormVisiblePostIdSelector = (state, props) => {
// render on server error ".addCommentForm is undefined"
return !!state.posts.addCommentForm
&&
props.post._id === state.posts.addCommentForm.postId;
};

export const isVommentFormVisibleInPostSelector = createSelector(
isCommentFormVisibleSelector,
isCommentFormVisiblePostIdSelector,
(isCommentFormVisible, isCommentFormVisiblePostId) =>
isCommentFormVisible && isCommentFormVisiblePostId
);
6 changes: 5 additions & 1 deletion client/modules/Post/__tests__/components/PostList.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ const posts = [

test('renders the list', t => {
const wrapper = shallow(
<PostList posts={posts} handleShowPost={() => {}} handleDeletePost={() => {}} />
<PostList
posts={posts}
handleShowPost={() => {}}
handleDeletePost={() => {}}
/>
);

t.is(wrapper.find('PostListItem').length, 2);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.close-comment{
margin-left: 100px;
cursor: pointer;
}
79 changes: 79 additions & 0 deletions client/modules/Post/components/CommentAddForm/CommentAddForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { Field } from 'redux-form';

import styles from './CommentAddForm.css';

import {
commentFormForPostClose,
commentRequestAdd,
} from '../../PostActions';

const CommentAddForm = (props) => {
function addCommentHandler(event) {
const {
authorName,
comment,
commentAdd,
postId,
} = props;

event.preventDefault();
commentAdd(authorName, comment, postId);
}

return (
<form onSubmit={addCommentHandler}>
<div>
<label>Author name</label>
<span
className={styles['close-comment']}
onClick={props.addFormClose}
>
&times;
</span>
<div>
<Field
name="authorName"
component="input"
type="text"
placeholder="Author name"
/>
</div>
</div>
<div>
<label>Comment</label>
<div>
<Field
name="comment"
component="textarea"
type="text"
placeholder="Comment"
/>
</div>
</div>
<div>
<button type="submit">
Submit
</button>
</div>
</form>
);
};

CommentAddForm.propTypes = {
postId: PropTypes.string.isRequired,
authorName: PropTypes.string.isRequired,
comment: PropTypes.string.isRequired,
commentAdd: PropTypes.func.isRequired,
addFormClose: PropTypes.func.isRequired,
};

const mapDispatchToProps = dispatch => ({
addFormClose: () => dispatch(commentFormForPostClose()),
commentAdd: (authorName, comment, postId) =>
dispatch(commentRequestAdd(authorName, comment, postId)),
});

export default connect(null, mapDispatchToProps)(CommentAddForm);
22 changes: 22 additions & 0 deletions client/modules/Post/components/CommentAddForm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { connect } from 'react-redux';
import { reduxForm } from 'redux-form';

import CommentAddForm from './CommentAddForm'
import { authorNameSelector, commentSelector } from '../../PostSelectors';
import { addCommentRequest } from '../../PostActions';

const mapStateToProps = (state, props) => ({
authorName: authorNameSelector(state),
comment: commentSelector(state),
});

const mapDispatchToProps = (dispatch, props) => ({
addComment: (authorName, comment) =>
dispatch(addCommentRequest(authorName, comment, props.postId))
});

const Connected = connect(mapStateToProps, mapDispatchToProps)(CommentAddForm)

export default reduxForm({
form: 'addCommentForm', // a unique identifier for this form
})(Connected);
6 changes: 6 additions & 0 deletions client/modules/Post/components/PostList.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ PostList.propTypes = {
content: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
cuid: PropTypes.string.isRequired,
comments: PropTypes.arrayOf(PropTypes.shape({
authorName: PropTypes.string.isRequired,
comment: PropTypes.string.isRequired,
postId: PropTypes.string.isRequired,
_id: PropTypes.string.isRequired,
})).isRequired,
})).isRequired,
handleDeletePost: PropTypes.func.isRequired,
};
Expand Down
Loading