Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ public/*
dist
coverage/
.nyc_output/
yarn.lock
93 changes: 93 additions & 0 deletions client/modules/Post/CommentActions.js
Original file line number Diff line number Diff line change
@@ -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
};
}
64 changes: 64 additions & 0 deletions client/modules/Post/CommentReducer.js
Original file line number Diff line number Diff line change
@@ -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;
66 changes: 66 additions & 0 deletions client/modules/Post/components/Comments/CommentCreateWidget.css
Original file line number Diff line number Diff line change
@@ -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;
}
149 changes: 149 additions & 0 deletions client/modules/Post/components/Comments/CommentCreateWidget.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles["form"]}>
<div className={styles["form-content"]}>
<h2 className={styles["form-title"]}>Write a new comment</h2>
<input
placeholder="Author"
className={styles["form-field"]}
value={this.state.author}
onChange={e => this.setState({ author: e.target.value })}
/>

<textarea
placeholder="Write your comment here"
className={styles["form-field"]}
value={this.state.body}
onChange={e => this.setState({ body: e.target.value })}
/>
<a
className={styles["comment-submit-button"]}
href="#"
onClick={this.createComment}
>
Create
</a>
</div>
</div>
);
}

renderEditForm = () => {
return (
<div className={styles["form"]}>
<div className={styles["form-content"]}>
<h2 className={styles["form-title"]}>Edit existing comment</h2>
<h3 className={styles["comment-author"]}>By {this.state.author}</h3>

<textarea
placeholder="Write your comment here"
className={styles["form-field"]}
value={this.state.body}
onChange={e => this.setState({ body: e.target.value })}
/>
<a
className={styles["comment-submit-button"]}
href="#"
onClick={this.saveEditComment}
>
Save Edit
</a>
<a
className={styles["comment-cancel-button"]}
href="#"
onClick={this.cancelEditComment}
>
Cancel
</a>
</div>
</div>
);
};

render() {
if (this.props.editComment) {
return this.renderEditForm();
}
return this.renderCreateForm();
}
}

CommentCreateWidget.propTypes = {
ownerId: PropTypes.string.isRequired,
editComment: PropTypes.shape({
author: PropTypes.string.isRequired,
body: PropTypes.string.isRequired,
cuid: PropTypes.string.isRequired,
owner: PropTypes.string.isRequired,
dataAdded: PropTypes.instanceOf(Date)
}),
dispatch: PropTypes.func.isRequired
};

function mapStateToProps({ comments }) {
return {
editComment: comments.editComment
};
}

export default connect(mapStateToProps)(CommentCreateWidget);
4 changes: 4 additions & 0 deletions client/modules/Post/components/Comments/CommentList.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.comment-view {
width: 100%;
padding: 0 50px;
}
Loading