diff --git a/Intl/localizationData/en.js b/Intl/localizationData/en.js index 79b481d3c..8453c7e2b 100644 --- a/Intl/localizationData/en.js +++ b/Intl/localizationData/en.js @@ -17,6 +17,7 @@ export default { =1 {has # comment} other {has # comments} }`, + commentContent: 'Comment Content', HTMLComment: `user {name} {value, plural, =0 {does not have any comments} =1 {has # comment} diff --git a/client/modules/Post/PostActions.js b/client/modules/Post/PostActions.js index 7e5932fa5..8b5641d56 100644 --- a/client/modules/Post/PostActions.js +++ b/client/modules/Post/PostActions.js @@ -4,7 +4,10 @@ import callApi from '../../util/apiCaller'; export const ADD_POST = 'ADD_POST'; export const ADD_POSTS = 'ADD_POSTS'; export const DELETE_POST = 'DELETE_POST'; - +export const ADD_COMMENT = 'ADD_COMMENT'; +export const ADD_COMMENTS = 'ADD_COMMENTS'; +export const DELETE_COMMENT = 'DELETE_COMMENT'; +export const EDIT_COMMENT = 'EDIT_COMMENT'; // Export Actions export function addPost(post) { return { @@ -25,6 +28,25 @@ export function addPostRequest(post) { }; } +export function addComment(comment) { + return { + type: ADD_COMMENT, + comment, + }; +} + +export function addCommentRequest(comment) { + return (dispatch) => { + return callApi('comments', 'post', { + comment: { + name: comment.name, + content: comment.content, + postId: comment.postId, + }, + }).then(res => dispatch(addComment(res.comment))); + }; +} + export function addPosts(posts) { return { type: ADD_POSTS, @@ -40,12 +62,27 @@ export function fetchPosts() { }; } +export function addComments(comments) { + return { + type: ADD_COMMENTS, + comments, + }; +} +export function fetchComments(postId) { + return (dispatch) => { + return callApi(`comments/${postId}`).then(res => { + dispatch(addComments(res.comments)); + }); + }; +} + export function fetchPost(cuid) { return (dispatch) => { return callApi(`posts/${cuid}`).then(res => dispatch(addPost(res.post))); }; } + export function deletePost(cuid) { return { type: DELETE_POST, @@ -58,3 +95,33 @@ export function deletePostRequest(cuid) { return callApi(`posts/${cuid}`, 'delete').then(() => dispatch(deletePost(cuid))); }; } + +export function deleteComment(cuid) { + return { + type: DELETE_COMMENT, + cuid, + }; +} +export function deleteCommentRequest(cuid) { + return (dispatch) => { + return callApi(`comments/${cuid}`, 'delete').then(() => dispatch(deleteComment(cuid))); + }; +} + +export function editComment(comment) { + return { + type: EDIT_COMMENT, + comment, + }; +} + +export function editCommentRequest(comment) { + return (dispatch) => { + return callApi(`comments/${comment.postId}`, 'post', { + comment: { + name: comment.name, + content: comment.content, + }, + }).then(res => dispatch(editComment(res.comment))); + }; +} diff --git a/client/modules/Post/PostReducer.js b/client/modules/Post/PostReducer.js index 5a5054369..0004354b9 100644 --- a/client/modules/Post/PostReducer.js +++ b/client/modules/Post/PostReducer.js @@ -1,25 +1,47 @@ -import { ADD_POST, ADD_POSTS, DELETE_POST } from './PostActions'; +import { ADD_COMMENT, ADD_COMMENTS, ADD_POST, ADD_POSTS, DELETE_COMMENT, DELETE_POST, EDIT_COMMENT } from './PostActions'; // Initial State -const initialState = { data: [] }; +const initialState = { data: [], comments: [] }; 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 ADD_COMMENT : + return { + ...state, + comments: [action.comment, ...state.comments], + }; + case ADD_COMMENTS : + return { + ...state, + comments: action.comments, + }; case DELETE_POST : return { + ...state, data: state.data.filter(post => post.cuid !== action.cuid), }; - + case DELETE_COMMENT : + return { + ...state, + comments: state.comments.filter(comment => comment.cuid !== action.cuid), + }; + case EDIT_COMMENT : + return { + ...state, + comments: state.comments.map((comment) => (comment.cuid === action.comment.cuid ? action.comment : comment)), + }; default: return state; } diff --git a/client/modules/Post/components/PostCommentItem/PostCommentItem.css b/client/modules/Post/components/PostCommentItem/PostCommentItem.css new file mode 100644 index 000000000..a01c36828 --- /dev/null +++ b/client/modules/Post/components/PostCommentItem/PostCommentItem.css @@ -0,0 +1,41 @@ +.root { + border: 1px solid #ccc; + border-radius: 5px; + padding: 10px; + box-sizing: border-box; + -webkit-box-shadow: 0px 5px 5px -4px rgba(0,0,0,0.39); + -moz-box-shadow: 0px 5px 5px -4px rgba(0,0,0,0.39); + box-shadow: 0px 5px 5px -4px rgba(0,0,0,0.39); + margin-bottom: 10px; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; +} +.headerActions { + display: flex; + justify-content: space-between; + align-items: center; +} + +.headerActions p { + cursor: pointer; + margin-left: 10px; + transition: 0.3s; +} +.headerActions p:hover { + color: #212121; + +} + +.name { + font-size: 16px; + color: darkgray; +} + +.content { + font-style: italic; +} diff --git a/client/modules/Post/components/PostCommentItem/PostCommentItem.js b/client/modules/Post/components/PostCommentItem/PostCommentItem.js new file mode 100644 index 000000000..8b5af2711 --- /dev/null +++ b/client/modules/Post/components/PostCommentItem/PostCommentItem.js @@ -0,0 +1,34 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import styles from './PostCommentItem.css'; + +export class PostCommentItem extends PureComponent { + render() { + const { name, content, deleteComment, editComment, cuid } = this.props; + return ( +
+
+

{name}

+
+

editComment(cuid)}>edit

+

deleteComment(cuid)}>delete

+
+
+

{content}

+
+ ); + } +} + +PostCommentItem.propTypes = { + name: PropTypes.string, + content: PropTypes.string, + deleteComment: PropTypes.func, + editComment: PropTypes.func, +}; + +PostCommentItem.defaultProps = { + name: '', + content: '', +}; diff --git a/client/modules/Post/components/PostCommentWidget/PostCommentWidget.css b/client/modules/Post/components/PostCommentWidget/PostCommentWidget.css new file mode 100644 index 000000000..f0942b7e1 --- /dev/null +++ b/client/modules/Post/components/PostCommentWidget/PostCommentWidget.css @@ -0,0 +1,48 @@ +.form { + display: none; + background: #FAFAFA; + padding: 32px 0; + border: 1px solid #eee; + border-radius: 4px; +} + +.form-content{ + width: 100%; + max-width: 600px; + margin: auto; + font-size: 14px; +} + +.form-title{ + font-size: 16px; + font-weight: 700; + margin-bottom: 16px; + color: #757575; +} + +.form-field{ + width: 100%; + margin-bottom: 16px; + font-family: 'Lato', sans-serif; + font-size: 16px; + line-height: normal; + padding: 12px 16px; + border-radius: 4px; + border: 1px solid #ddd; + outline: none; + color: #212121; +} + +textarea { + min-height: 200px; +} + +.post-submit-button { + display: inline-block; + padding: 8px 16px; + font-size: 18px; + color: #FFF; + background: #03A9F4; + text-decoration: none; + border-radius: 4px; +} diff --git a/client/modules/Post/components/PostCommentWidget/PostCommentWidget.js b/client/modules/Post/components/PostCommentWidget/PostCommentWidget.js new file mode 100644 index 000000000..829fc3519 --- /dev/null +++ b/client/modules/Post/components/PostCommentWidget/PostCommentWidget.js @@ -0,0 +1,48 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +import styles from './PostCommentWidget.css'; +import { FormattedMessage, injectIntl } from 'react-intl'; +import { PostCreateWidget } from '../PostCreateWidget/PostCreateWidget'; + +export const isEmpty = obj => !Object.keys(obj).length; + +export class PostCommentWidget extends PureComponent { + componentDidUpdate() { + const comment = this.props.edit; + if (!isEmpty(comment)) { + this.refs.name.value = comment.name; + this.refs.content.value = comment.content; + } + } + + addComment = () => { + const nameRef = this.refs.name; + const contentRef = this.refs.content; + if (nameRef.value && contentRef.value) { + this.props.addComment(nameRef.value, contentRef.value, !isEmpty(this.props.edit)); + nameRef.value = contentRef.value = ''; + } + }; + + render() { + return ( +
+ + +