Skip to content

Commit

Permalink
ft(comments): Users should be able to comment on articles.
Browse files Browse the repository at this point in the history
- add test for the comments feature
- add respective ui desgins
- add actions for the feature
- add reducers for the action
- add the comments components to the feature
  [finishes #164798258]
  • Loading branch information
anyatibrian authored and anyatibrian committed May 23, 2019
1 parent 6c2aff9 commit 5fcfe2d
Show file tree
Hide file tree
Showing 17 changed files with 682 additions and 3 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"jest": "^24.8.0",
"jwt-decode": "^2.2.0",
"moxios": "^0.4.0",
"node-sass": "^4.12.0",
"prettier": "^1.17.0",
Expand Down
56 changes: 56 additions & 0 deletions src/actions/commentsActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
CREATE_COMMENTS,
CREATE_COMMENTS_ERRORS,
FETCH_COMMENTS,
FETCH_COMMENTS_ERRORS
} from "./types";
import axios from "axios";

export const articleComments = (data, slug) => async dispatch => {
return await axios
.post(
`https://ah-backend-prime-staging.herokuapp.com/api/v1/articles/${slug}/comments/0/`,
data,
{
headers: {
Authorization: "Bearer " + sessionStorage.token
}
}
)
.then(res => {
dispatch({
type: CREATE_COMMENTS,
payload: res.data
});
})
.catch(errors => {
console.log(errors.response.data);
dispatch({
type: CREATE_COMMENTS_ERRORS,
payload: errors.response.data
});
});
};
export const fetchComments = slug => async dispatch => {
return await axios
.get(
` https://ah-backend-prime-staging.herokuapp.com/api/v1/articles/${slug}/comments/0/`,
{
headers: {
Authorization: "Bearer " + sessionStorage.token
}
}
)
.then(res => {
dispatch({
type: FETCH_COMMENTS,
payload: res.data.message
});
})
.catch(error => {
dispatch({
type: FETCH_COMMENTS_ERRORS,
payload: { errors: "error fetching your comments" }
});
});
};
4 changes: 4 additions & 0 deletions src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ export const FOLLOWERS_LIST = "FOLLOWERS_LIST";
export const FOLLOWING_LIST = "FOLLOWING_LIST";
export const FOLLOWERS_LIST_FAILED = "FOLLOWERS_LIST_FAILED";
export const FOLLOWING_LIST_FAILED = "FOLLOWING_LIST_FAILED";
export const CREATE_COMMENTS = "CREATE_COMMENTS";
export const CREATE_COMMENTS_ERRORS = "CREATE_COMMENTS_ERRORS";
export const FETCH_COMMENTS = "FETCH_COMMENTS";
export const FETCH_COMMENTS_ERRORS = "FETCH_COMMENTS_ERRORS";
2 changes: 2 additions & 0 deletions src/components/articles/singleArticle.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { followUser, unfollowUser } from "../../actions/profileActions";

import "../../styles/singleArticle.scss";
import { getArticleAction } from "../../actions/getArticle";
import Comments from "../comments";

export class SingleArticleComponent extends Component {
componentDidMount() {
Expand Down Expand Up @@ -103,6 +104,7 @@ export class SingleArticleComponent extends Component {

<div className="article-description-body">
<p style={{ fontWeight: "light" }}>{oneArticle.body}</p>
<Comments articleSlug={oneArticle.slug} />
</div>
</div>
</div>
Expand Down
102 changes: 102 additions & 0 deletions src/components/comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { Component } from "react";
import { articleComments, fetchComments } from "../actions/commentsActions";
import { connect } from "react-redux";
import "../styles/comments.scss";

export class Comments extends Component {
constructor(props) {
super(props);
this.state = {
comments: ""
};
}

componentDidMount() {
const { fetchComments, articleSlug } = this.props;
fetchComments(articleSlug);
}

handleOnChange = event => {
event.preventDefault();
const { name, value } = event.target;
this.setState({ [name]: value });
};

handleOnSubmit = event => {
event.preventDefault();
const { articleSlug, articleComments, fetchComments } = this.props;
const { comments } = this.state;
const data = {
body: comments
};
articleComments(data, articleSlug);
fetchComments(articleSlug);
};

render() {
const { commentsMessage } = this.props;
console.log("comments", commentsMessage);
const comment = commentsMessage.map(message => (
<section className="comments" key={message.id}>
<article className="comment">
<a className="comment-img" href="#non">
<img src={message.author.image} alt="" width="50" height="50" />
</a>
<div className="comment-body">
<div className="text">
<p>{message.body}</p>
</div>
<p className="attribution">
by <a href="#non">{message.author.username}</a>
</p>
</div>
</article>
</section>
));
return (
<div className="row">
<div className="col col-lg-1" />
<div className="col col-lg-10">
<div className="row">
<div className="col col-lg-1" />
<div className="col col-lg-11">
<form
method="POST"
onSubmit={this.handleOnSubmit}
className="comment-forms"
>
<textarea
className="form-control"
name="comments"
value={this.state.comments}
onChange={this.handleOnChange}
>
comments here
</textarea>
<button
type="submit"
className="btn btn-comment"
style={{ marginTop: "10px" }}
>
comment
</button>
</form>
</div>
</div>
{comment}
</div>
<div className="col col-lg-1" />
</div>
);
}
}

export const mapStateToProps = state => ({
commentsSuccess: state.comments.success,
commentsErrors: state.comments.errors,
commentsMessage: state.getComments.comments
});
export default connect(
mapStateToProps,
{ articleComments, fetchComments }
)(Comments);
1 change: 0 additions & 1 deletion src/components/sideDrawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from "react";
import "../styles/sidedrawer.scss";

const SideDrawer = props => {

return (
<div className="sideDrawer">
<ul className="list-group">
Expand Down
24 changes: 24 additions & 0 deletions src/reducers/commentsReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CREATE_COMMENTS, CREATE_COMMENTS_ERRORS } from "../actions/types";

const initialState = {
success: {},
errors: {}
};

export default (state = initialState, action) => {
console.log("reducer");
switch (action.type) {
case CREATE_COMMENTS:
return {
...state,
success: action.payload
};
case CREATE_COMMENTS_ERRORS:
return {
...state,
errors: action.payload
};
default:
return state;
}
};
16 changes: 16 additions & 0 deletions src/reducers/getCommentsReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { FETCH_COMMENTS } from "../actions/types";

const initialState = {
comments: []
};
export default (state = initialState, action) => {
switch (action.type) {
case FETCH_COMMENTS:
return {
...state,
comments: action.payload
};
default:
return state;
}
};
7 changes: 6 additions & 1 deletion src/reducers/rootReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import profileReducer from "./profileReducer";
import getArticleReducer from "./getArticleReducer";
import createArticleReducer from "./createArticleReducer";
import followsUnfollowsReducer from "./followsUnfollowsReducer";
import commentsReducer from "./commentsReducer";
import getCommentsReducer from "./getCommentsReducer";

export default combineReducers({
auth_login: loginReducer,
Expand All @@ -20,5 +22,8 @@ export default combineReducers({
passReset: passwordResetReducer,
passChange: passwordChangeReducer,
profileReducer: profileReducer,
followsUnfollowsReducer: followsUnfollowsReducer
followsUnfollowsReducer: followsUnfollowsReducer,
profileReducer,
comments: commentsReducer,
getComments: getCommentsReducer
});
127 changes: 127 additions & 0 deletions src/styles/comments.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
article, aside, figure, footer, header, hgroup, menu, nav, section {
display: block
}

a {
color: #6d84b4;
text-decoration: none;
}

section {
background: rgba(255, 255, 255, 0.9);
padding-right: 85px;
padding-top: 20px;
padding-bottom: 20px;
height: auto;
width: 100%;
margin: auto;
margin-top: 10px;
border-radius: 5px;
}

.comment {
overflow: hidden;
padding: 0 0 1em;
border-bottom: 1px solid #ddd;
margin: 0 0 1em 40px;
*zoom: 1;
width: 100%;
}

.comment-img {
float: left;
margin-right: 33px;
border-radius: 50%;
border: 2px solid #e5e5e5;
overflow: hidden;
}

.comment-img img {
display: block;
}

.comment-body {
overflow: hidden
}

.comment .text {
padding: 10px;
border: 1px solid #e5e5e5;
border-radius: 5px;
background: #fff;
}

.comment .text p:last-child {
margin: 0
}

.comment .attribution {
margin: 0.5em 0 0;
font-size: 14px;
color: #666;
}

/* Decoration */

.comments, .comment {
position: relative
}

.comments:before, .comment:before, .comment .text:before {
content: "";
position: absolute;
top: 0;
left: 65px;
}

.comments:before {
width: 3px;
left: 105px;
bottom: 0px;
background: rgba(0, 0, 0, 0.1);
}

.comment:before {
width: 9px;
height: 9px;
border: 3px solid #fff;
border-radius: 100px;
margin: 16px 0 0 -6px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(0, 0, 0, 0.1);
background: #ccc;
}

.comment:hover {
cursor: cell;
}

.comment:hover:before {
background: #3b5998
}

.comment .text:before {
top: 18px;
left: 78px;
width: 9px;
height: 9px;
border-width: 0 0 1px 1px;
border-style: solid;
border-color: #e5e5e5;
background: #fff;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
}

.comment-forms {
textarea.form-control {
border: 1px solid #e5e5e5;
height: 100px;
}

.btn-comment {
border: 1px solid #ccc;
float: right;
}
}
Loading

0 comments on commit 5fcfe2d

Please sign in to comment.