Skip to content

Commit

Permalink
feature(comments): update comments
Browse files Browse the repository at this point in the history
- update comments on articles

[Delivers #161959254]
  • Loading branch information
MuhanguziDavid committed Nov 21, 2018
1 parent 7a73f18 commit c60d186
Show file tree
Hide file tree
Showing 15 changed files with 414 additions and 14 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"moment": "^2.22.2",
"node-sass": "^4.9.4",
"prop-types": "^15.6.2",
"react": "^16.6.0",
"react-dom": "^16.6.0",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-facebook-login": "^4.1.1",
"react-google-login": "^3.2.1",
"react-loader": "^2.4.5",
Expand All @@ -26,6 +26,7 @@
"react-router-dom": "^4.3.1",
"react-scripts": "2.0.5",
"react-toastify": "^4.4.0",
"reactstrap": "^6.5.0",
"redux": "^4.0.1",
"redux-mock-store": "^1.5.3",
"redux-thunk": "^2.3.0"
Expand Down
16 changes: 16 additions & 0 deletions src/actions/actionCreators.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import {
LIKE_DISLIKE_SUCCESS,
LIKE_DISLIKE_ERROR,
DELETE_ARTICLE_SUCCESS,
UPDATE_COMMENT_INITIATED,
UPDATE_COMMENT_SUCCESS,
UPDATE_COMMENT_ERROR,
} from './types';

export const socialLoginInitiated = () => ({
Expand Down Expand Up @@ -87,3 +90,16 @@ export const deleteArticleSuccess = payload => ({
type: DELETE_ARTICLE_SUCCESS,
payload,
});

export const updateCommentInitiated = payload => ({
type: UPDATE_COMMENT_INITIATED,
payload,
});
export const updateCommentSuccess = payload => ({
type: UPDATE_COMMENT_SUCCESS,
payload,
});
export const updateCommentError = payload => ({
type: UPDATE_COMMENT_ERROR,
payload,
});
30 changes: 29 additions & 1 deletion src/actions/articleActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import {
likeDislikeSuccess,
likeDislikeError,
deleteArticleSuccess,
updateCommentInitiated,
updateCommentSuccess,
updateCommentError,
} from './actionCreators';

export const postArticle = postData => dispatch => {
Expand Down Expand Up @@ -57,7 +60,7 @@ export const fetchComments = article => dispatch => {
.catch(() => {
localStorage.removeItem('auth_token');
dispatch(logoutUser(false));
toast.error('Please login to view comments', { autoClose: false, hideProgressBar: true });
toast.error('Please login', { autoClose: false, hideProgressBar: true });
});
};

Expand Down Expand Up @@ -125,3 +128,28 @@ export const deleteArticle = slug => dispatch => {
);
});
};

export const editComment = (payload, slug, comment) => dispatch => {
toast.dismiss();
dispatch(updateCommentInitiated(true));
axiosInstance
.put(`/api/articles/${slug}/comments/${comment}/`, payload)
.then(() => {
const success_message = 'Comment updated successfully';
dispatch(updateCommentSuccess(true));
toast.success(
success_message,
{ autoClose: 3500, hideProgressBar: true },
);
})
.catch((error) => {
let errorMessage = '';
if (error.response.status === 403) {
errorMessage = 'Re-login and try again';
} else {
errorMessage = 'This comment is non existent';
}
dispatch(updateCommentError(errorMessage));
toast.error(errorMessage, { autoClose: false, hideProgressBar: true });
});
};
3 changes: 3 additions & 0 deletions src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ export const GET_ALL_ARTICLES_INITIATED = 'GET_ALL_ARTICLES_INITIATED';
export const LIKE_DISLIKE_SUCCESS = 'LIKE_DISLIKE_SUCCESS';
export const LIKE_DISLIKE_ERROR = 'LIKE_DISLIKE_ERROR';
export const DELETE_ARTICLE_SUCCESS = 'DELETE_ARTICLE_SUCCESS';
export const UPDATE_COMMENT_INITIATED = 'UPDATE_COMMENT_INITIATED';
export const UPDATE_COMMENT_SUCCESS = 'UPDATE_COMMENT_SUCCESS';
export const UPDATE_COMMENT_ERROR = 'UPDATE_COMMENT_ERROR';
2 changes: 1 addition & 1 deletion src/components/Articles/SingleArticle.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const SingleArticle = ({ article }) => {
<i className="reaction far fa-star icon" />
<i className="reaction far fa-bookmark icon" />
<i className="reaction fas fa-share-alt icon" />
<Link to={returnArticleURL(article.slug)}><i className="far fa-comment icon" /></Link>
<Link exact to={returnArticleURL(article.slug)}><i className="far fa-comment icon" /></Link>
</div>
</div>
</div>
Expand Down
6 changes: 4 additions & 2 deletions src/components/comments/CommentList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@ import React from 'react';
import PropTypes from 'prop-types';
import RetrieveCommentForm from './RetrieveCommentForm';

export const CommentList = ({ comments }) => (
export const CommentList = ({ comments, article }) => (
<div>
{comments.map(comment => (
<RetrieveCommentForm key={comment.id} comment={comment} />
<RetrieveCommentForm key={comment.id} comment={comment} article={article} />
))}
</div>
);

CommentList.propTypes = {
comments: PropTypes.array,
article: PropTypes.string,
};

CommentList.defaultProps = {
comments: [],
article: '',
};

export default CommentList;
2 changes: 1 addition & 1 deletion src/components/comments/Comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class Comments extends Component {
/>
<Loader loaded={!loading}>
{Object.keys(commentsPayload).length > 0
&& <CommentList comments={commentsPayload.comments} />
&& <CommentList comments={commentsPayload.comments} article={article} />
}
</Loader>
</div>
Expand Down
5 changes: 4 additions & 1 deletion src/components/comments/RetrieveCommentForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import Moment from 'react-moment';
import user from '../../assets/images/user.png';
import UpdateComment from './UpdateComment';

export const RetrieveCommentForm = ({ comment }) => (
export const RetrieveCommentForm = ({ comment, article }) => (
<div className="row comment">
<div className="col-12">
<div className="row entire-article">
Expand Down Expand Up @@ -34,6 +35,7 @@ export const RetrieveCommentForm = ({ comment }) => (
</div>
</div>
<div className="card-footer w-100 text-muted">
<UpdateComment slug={article} comment={comment} />
<i className="far fa-thumbs-down icon" />
<i className="far fa-thumbs-up icon" />
<i className="far fa-star icon" />
Expand All @@ -58,6 +60,7 @@ export const RetrieveCommentForm = ({ comment }) => (

RetrieveCommentForm.propTypes = {
comment: PropTypes.object.isRequired,
article: PropTypes.string.isRequired,
};

export default RetrieveCommentForm;
129 changes: 129 additions & 0 deletions src/components/comments/UpdateComment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ReactQuill from 'react-quill';
import PropTypes from 'prop-types';
import Loader from 'react-loader';
import {
Modal,
ModalHeader,
ModalBody,
ModalFooter,
} from 'reactstrap';
import { editComment } from '../../actions/articleActions';

export class UpdateComment extends Component {
constructor(props) {
super(props);
this.state = {
body: '',
modal: false,
};
}

componentDidMount = () => {
const { comment } = this.props;
this.setState({ body: comment.body });
}

componentWillReceiveProps(nextProps) {
if (nextProps.updateCommentSuccess === true) {
window.location.reload();
}
if (nextProps.isLoggedIn === false) {
const { history } = this.props;
history.push('/login');
}
}

toggle = () => {
const { modal } = this.state;
this.setState({
modal: !modal,
});
}

handleChange = value => {
this.setState({ body: value });
}

handleSubmit = event => {
event.preventDefault();
const { editComment, slug, comment: { id } } = this.props;
const { body, modal } = this.state;
const payload = {
comment: {
body,
},
};
editComment(payload, slug, id);
this.setState({
modal: !modal,
});
}

render() {
const {
body,
modal,
} = this.state;
const {
loading,
className,
buttonLabel,
} = this.props;
return (
<React.Fragment>
<Loader loaded={!loading}>
<i className="far fa-edit icon" onClick={this.toggle} role="button" tabIndex={0}>{buttonLabel}</i>
<Modal isOpen={modal} toggle={this.toggle} className={className}>
<ModalHeader>Edit comment</ModalHeader>
<ModalBody>
<ReactQuill
id="text-editor"
theme=""
placeholder="Please add a comment"
className="quill-height-comments"
value={body}
onChange={this.handleChange}
required
/>
</ModalBody>
<ModalFooter>
<button type="button" className="btn btn-outline-primary m-r-10" onClick={this.handleSubmit}>Submit</button>
<button type="button" className="btn btn-outline-warning m-r-10" onClick={this.toggle}>Close</button>
</ModalFooter>
</Modal>
</Loader>
</React.Fragment>
);
}
}

const mapStateToProps = (state) => ({
isLoggedIn: state.user.isLoggedIn,
loading: state.user.loading,
updateCommentSuccess: state.article.updateCommentSuccess,
});

const matchDispatchToProps = (dispatch) => bindActionCreators({
editComment,
}, dispatch);

UpdateComment.propTypes = {
updateCommentSuccess: PropTypes.bool,
comment: PropTypes.object.isRequired,
slug: PropTypes.string.isRequired,
editComment: PropTypes.func.isRequired,
className: PropTypes.string.isRequired,
buttonLabel: PropTypes.string.isRequired,
history: PropTypes.object.isRequired,
loading: PropTypes.bool.isRequired,
isLoggedIn: PropTypes.object.isRequired,
};

UpdateComment.defaultProps = {
updateCommentSuccess: false,
};

export default connect(mapStateToProps, matchDispatchToProps)(UpdateComment);
10 changes: 5 additions & 5 deletions src/components/landingPage/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ export class Navbar extends Component {
<i className="fas fa-user" />
</a>
<div className="dropdown-menu" aria-labelledby="navbarDropdown">
<NavLink className="dropdown-item" to="/create-article/">New Article</NavLink>
<NavLink className="dropdown-item" to={`/profiles/${username}`}>Profile</NavLink>
<NavLink className="dropdown-item" exact to="/create-article/">New Article</NavLink>
<NavLink className="dropdown-item" exact to={`/profiles/${username}`}>Profile</NavLink>
<div className="dropdown-divider" />
<NavLink className="dropdown-item" to="#">Logout</NavLink>
<NavLink className="dropdown-item" exact to="#">Logout</NavLink>
</div>
</div>
</li>
Expand All @@ -71,12 +71,12 @@ export class Navbar extends Component {
: (
<React.Fragment>
<li className="nav-item active">
<NavLink className="btn ah-btn ah-btn-nav js-scroll-trigger nav-link nav-text" to="signup">
<NavLink className="btn ah-btn ah-btn-nav js-scroll-trigger nav-link nav-text" exact to="/signup">
Signup
</NavLink>
</li>
<li className="nav-item">
<NavLink className="btn ah-btn ah-btn-nav js-scroll-trigger nav-link nav-text" to="login">Login</NavLink>
<NavLink className="btn ah-btn ah-btn-nav js-scroll-trigger nav-link nav-text" exact to="/login">Login</NavLink>
</li>
</React.Fragment>
)
Expand Down
2 changes: 2 additions & 0 deletions src/components/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Dashboard from '../dashboard/Dashboard';
import Profile from '../profiles/Profile';
import NewArticle from '../Articles/NewArticle';
import Comments from '../comments/Comments';
import UpdateComment from '../comments/UpdateComment';
import ViewArticle from '../Articles/ViewArticle';

const Routes = () => (
Expand All @@ -19,6 +20,7 @@ const Routes = () => (
<Route exact path="/profiles/:username" component={Profile} />
<Route exact path="/create-article" component={NewArticle} />
<Route exact path="/articles/:article/comments/" component={Comments} />
<Route exact path="/articles/:article/comments/:id/" component={UpdateComment} />
<Route exact path="/article/:slug" component={ViewArticle} />
<Route exact component={NotFound} />
</Switch>
Expand Down
22 changes: 22 additions & 0 deletions src/reducers/articlesReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import {
LIKE_DISLIKE_ERROR,
LIKE_DISLIKE_SUCCESS,
DELETE_ARTICLE_SUCCESS,
UPDATE_COMMENT_INITIATED,
UPDATE_COMMENT_SUCCESS,
UPDATE_COMMENT_ERROR,
} from '../actions/types';

const initialState = {
Expand All @@ -26,6 +29,8 @@ const initialState = {
likeDislikeSuccess: false,
likeDislikeError: {},
confirmDelete: false,
updateCommentError: {},
updateCommentSuccess: false,
};

export const articlesReducer = (state = initialState, action) => {
Expand Down Expand Up @@ -102,6 +107,23 @@ export const articlesReducer = (state = initialState, action) => {
loading: false,
confirmDelete: action.payload,
};
case UPDATE_COMMENT_INITIATED:
return {
...state,
loading: action.payload,
};
case UPDATE_COMMENT_SUCCESS:
return {
...state,
updateCommentSuccess: action.payload,
loading: false,
};
case UPDATE_COMMENT_ERROR:
return {
...state,
updateCommentError: action.payload,
loading: false,
};
default:
return state;
}
Expand Down
Loading

0 comments on commit c60d186

Please sign in to comment.