diff --git a/.gitignore b/.gitignore index d41d351f8..6ab7cf84b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ public/* dist coverage/ .nyc_output/ +yarn.lock \ No newline at end of file diff --git a/client/modules/Post/CommentActions.js b/client/modules/Post/CommentActions.js new file mode 100644 index 000000000..b585dc192 --- /dev/null +++ b/client/modules/Post/CommentActions.js @@ -0,0 +1,93 @@ +import callApi from "../../util/apiCaller"; + +export const ADD_COMMENTS = "ADD_COMMENTS"; +export const ADD_COMMENT = "ADD_COMMENT"; +export const EDIT_COMMENT = "EDIT_COMMENT"; +export const EDIT_COMMENT_MODE = "EDIT_COMMENT_MODE"; +export const DELETE_COMMENT = "DELETE_COMMENT"; +export const CANCEL_EDIT_COMMENT = "CANCEL_EDIT_COMMENT"; +export const CLEAR_COMMENTS = "CLEAR_COMMENTS"; + +export function addComments(comments) { + return { + type: ADD_COMMENTS, + comments + }; +} + +export function fetchComments(cuid) { + return dispatch => { + return callApi(`posts/${cuid}/comments`).then(res => { + dispatch(addComments(res.comments)); + }); + }; +} + +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 addComment(comment) { + return { + type: ADD_COMMENT, + comment + }; +} + +export function addCommentRequest(comment, ownerId) { + return dispatch => { + return callApi(`posts/${ownerId}/comments`, "post", { + comment: { + author: comment.author, + body: comment.body + } + }).then(res => dispatch(addComment(res.comment))); + }; +} + +export function enableEditMode(comment) { + return { + type: EDIT_COMMENT_MODE, + comment + }; +} + +export function cancelEditMode() { + return { + type: CANCEL_EDIT_COMMENT + }; +} + +export function saveEdit(comment) { + return { + type: EDIT_COMMENT, + comment + }; +} + +export function updateCommentRequest(commentBody, cuid) { + return dispatch => { + return callApi(`comments/${cuid}`, "put", { + comment: { + body: commentBody + } + }).then(res => dispatch(saveEdit(res.comment))); + }; +} + +export function clearComments() { + return { + type: CLEAR_COMMENTS + }; +} diff --git a/client/modules/Post/CommentReducer.js b/client/modules/Post/CommentReducer.js new file mode 100644 index 000000000..fafc6d0ad --- /dev/null +++ b/client/modules/Post/CommentReducer.js @@ -0,0 +1,64 @@ +import { + ADD_COMMENTS, + ADD_COMMENT, + EDIT_COMMENT, + EDIT_COMMENT_MODE, + CANCEL_EDIT_COMMENT, + DELETE_COMMENT, + CLEAR_COMMENTS +} from "./CommentActions"; + +const initialState = { + comments: [], + editComment: null +}; + +const CommentReducer = (state = initialState, action) => { + switch (action.type) { + case ADD_COMMENTS: + return { ...state, comments: action.comments }; + + case ADD_COMMENT: { + return { ...state, comments: [...state.comments, action.comment] }; + } + + case EDIT_COMMENT: { + let index = state.comments.findIndex(comment => { + if (comment.cuid === action.comment.cuid) { + return true; + } + return false; + }); + + let startCommentArray = state.comments.slice(0, index); + let endCommentArray = state.comments.slice( + index + 1, + state.comments.length + ); + return { + ...state, + comments: [...startCommentArray, action.comment, ...endCommentArray] + }; + } + case EDIT_COMMENT_MODE: + return { ...state, editComment: action.comment }; + case CANCEL_EDIT_COMMENT: + return { ...state, editComment: null }; + case DELETE_COMMENT: + return { + editComment: null, + comments: state.comments.filter(comment => comment.cuid !== action.cuid) + }; + case CLEAR_COMMENTS: + return initialState; + default: + return state; + } +}; + +/* Selectors */ + +// Get corresponding comments +export const getComments = state => state.comments.comments; + +export default CommentReducer; diff --git a/client/modules/Post/components/Comments/CommentCreateWidget.css b/client/modules/Post/components/Comments/CommentCreateWidget.css new file mode 100644 index 000000000..02cd84e18 --- /dev/null +++ b/client/modules/Post/components/Comments/CommentCreateWidget.css @@ -0,0 +1,66 @@ +.form { + 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; +} + +.comment-cancel-button { + display: inline-block; + padding: 8px 16px; + font-size: 18px; + color: #fff; + background: #f4c842; + text-decoration: none; + border-radius: 4px; +} + +.comment-submit-button { + display: inline-block; + padding: 8px 16px; + margin-right: 10px; + font-size: 18px; + color: #fff; + background: #03a9f4; + text-decoration: none; + border-radius: 4px; +} + +.comment-author { + margin-bottom: 20px; +} + +.appear { + display: block; +} diff --git a/client/modules/Post/components/Comments/CommentCreateWidget.js b/client/modules/Post/components/Comments/CommentCreateWidget.js new file mode 100644 index 000000000..2a8658c20 --- /dev/null +++ b/client/modules/Post/components/Comments/CommentCreateWidget.js @@ -0,0 +1,149 @@ +import React, { Component, PropTypes } from "react"; +import { connect } from "react-redux"; + +import styles from "./CommentCreateWidget.css"; +import { + addCommentRequest, + cancelEditMode, + updateCommentRequest +} from "../../CommentActions"; + +class CommentCreateWidget extends Component { + state = { + author: "", + body: "" + }; + + componentWillReceiveProps(nextProps) { + if (nextProps.editComment) { + this.setState({ + author: nextProps.editComment.author, + body: nextProps.editComment.body + }); + } + } + + createComment = e => { + e.preventDefault(); + if (this.state.author && this.state.body) { + this.props.dispatch( + addCommentRequest( + { author: this.state.author, body: this.state.body }, + this.props.ownerId + ) + ); + this.setState({ author: "", body: "" }); + } + }; + + saveEditComment = e => { + e.preventDefault(); + if (!this.state.body) { + return; + } + if (this.props.editComment.body === this.state.body) { + this.cancelEditComment(e); + return; + } + + this.props.dispatch( + updateCommentRequest(this.state.body, this.props.editComment.cuid) + ); + this.cancelEditComment(e); + }; + + cancelEditComment = e => { + e.preventDefault(); + this.props.dispatch(cancelEditMode()); + this.setState({ author: "", body: "" }); + }; + + renderCreateForm() { + return ( +
+
+

Write a new comment

+ this.setState({ author: e.target.value })} + /> + +