Skip to content

Commit

Permalink
feat(like-dislike): Like and dislike articles
Browse files Browse the repository at this point in the history
- add like actions
- add like reducers
- add dislike actions
- add dislike reducers
- add tests

[Finishes #165305230]
  • Loading branch information
ElishaMisoi committed Jun 20, 2019
1 parent 621e1cf commit 10d95a1
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 24 deletions.
19 changes: 19 additions & 0 deletions src/assets/styles/articles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,25 @@
width: 4.7%;
}
}

.likeDislike{
display: flex;
flex-direction: column;
margin-left: 2em !important;
&-row {
display: flex;
flex-direction: row;
}
&-text{
margin-top: 13px;
margin-left: 13px;
}
}

.thumbSize{
font-size: 30px;
}

.pagination {
display: flex;
justify-content: center;
Expand Down
23 changes: 21 additions & 2 deletions src/components/articles/OneArticle.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import React, { Component } from 'react';
import { Image, Row, Col, Button, Popover, OverlayTrigger } from 'react-bootstrap';
import {
Image, Row, Col, Button, Popover, OverlayTrigger,
} from 'react-bootstrap';
import { NavLink, Link } from 'react-router-dom';
import parse from 'html-react-parser';
import PropTypes from 'prop-types';
Expand All @@ -28,6 +30,10 @@ class OneArticle extends Component {
history,
id,
reportClick,
numVoteDown,
numVoteUp,
likeClicked,
dislikeClicked,
} = this.props;
const popover = (
<Popover id="popover-basic">
Expand Down Expand Up @@ -155,8 +161,17 @@ class OneArticle extends Component {
<Row>
<Col sm={2}>
<Bookmarks slug={slug} bookmarks={bookmarks} history={history} />
<div className="likeDislike">
<div className="likeDislike-row">
<span className="mdi mdi-thumb-up-outline thumbSize" onClick={likeClicked} />
<h5 className="likeDislike-text">{numVoteUp}</h5>
</div>
<div className="likeDislike-row">
<span className="mdi mdi-thumb-down-outline thumbSize" onClick={dislikeClicked} />
<h5 className="likeDislike-text">{numVoteDown}</h5>
</div>
</div>
</Col>

<Col sm={10} className="body-text read-one-image">
{parse(body)}
<ReportChevron />
Expand Down Expand Up @@ -219,6 +234,10 @@ OneArticle.propTypes = {
tags: PropTypes.arrayOf(PropTypes.string),
id: PropTypes.number.isRequired,
reportClick: PropTypes.func.isRequired,
numVoteDown: PropTypes.number.isRequired,
numVoteUp: PropTypes.number.isRequired,
likeClicked: PropTypes.func.isRequired,
dislikeClicked: PropTypes.func.isRequired,
};

OneArticle.defaultProps = {
Expand Down
25 changes: 23 additions & 2 deletions src/components/articles/ReadArticle.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import { connect } from 'react-redux';
import { Loader } from '../layout/Loader';
import OneArticle from './OneArticle';
import { isTokenExpired, errorToast } from '../../helpers';
import { fetchOneArticle, deleteArticle } from '../../redux/actions/FetchArticlesActions';
import { fetchBookmark } from '../../redux/actions/bookmarkActions';
import { createReport } from '../../redux/actions/ArticlesReportsActions';
import {
fetchOneArticle, deleteArticle, likeArticle, dislikeArticle,
} from '../../redux/actions/FetchArticlesActions';

class ReadArticle extends Component {
state = {
Expand Down Expand Up @@ -97,6 +99,18 @@ class ReadArticle extends Component {
});
}

likeClicked = () => {
const { history } = this.props;
const { data } = this.props.article.article.data;
this.props.likeArticle(data.slug, history);
}

dislikeClicked = () => {
const { history } = this.props;
const { data } = this.props.article.article.data;
this.props.dislikeArticle(data.slug, history);
}

render() {
let article;
const {
Expand Down Expand Up @@ -136,23 +150,30 @@ class ReadArticle extends Component {
history={history}
reportClick={this.reportClick}
reason={this.state.reason}
numVoteDown={data.num_vote_down}
numVoteUp={data.num_vote_up}
likeClicked={this.likeClicked}
dislikeClicked={this.dislikeClicked}
/>
);
}
return <div id="view-article">{article}</div>;
}
}

export const mapStateToProps = state => ({
export const mapStateToProps = (state, { FetchArticlesReducer }) => ({
article: state.FetchArticlesReducer,
bookmark: state.bookmarkReducer,
FetchArticlesReducer,
});

export const mapDispatchToProps = () => ({
fetchOneArticle,
deleteArticle,
fetchBookmark,
createReport,
likeArticle,
dislikeArticle,
});

ReadArticle.propTypes = {
Expand Down
21 changes: 10 additions & 11 deletions src/helpers/image_uploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
import axios from 'axios';

const uploadImageCallBack = (file) => {
try {
const url = 'https://cors-anywhere.herokuapp.com/https://api.cloudinary.com/v1_1/do8v0ew77/image/upload';
const formData = new FormData();
formData.append('file', file);
formData.append('upload_preset', 'sobu9leq');
return axios.post(url, formData);
}
catch (err) {
return err;
}
}
try {
const url = 'https://cors-anywhere.herokuapp.com/https://api.cloudinary.com/v1_1/do8v0ew77/image/upload';
const formData = new FormData();
formData.append('file', file);
formData.append('upload_preset', 'sobu9leq');
return axios.post(url, formData);
} catch (err) {
return err;
}
};

export default uploadImageCallBack;
1 change: 0 additions & 1 deletion src/redux/actions/CreateCommentAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const createComment = (data, slug, history) => async dispatch => {
.then(response => {
dispatch(createCommentSuccess(response.data));
successToast('Comment posted successfully');

dispatch(fetchCommentsAction(slug, history));
})
.catch(error => {
Expand Down
82 changes: 76 additions & 6 deletions src/redux/actions/FetchArticlesActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from 'axios';
import { FETCH_ARTICLES, DELETE_ARTICLE, BASE_URL } from '../constants';
import {
FETCH_ARTICLES, DELETE_ARTICLE, BASE_URL, LIKE_ARTICLE, DISLIKE_ARTICLE,
} from '../constants';
import { successToast } from '../../helpers';

/*
Expand Down Expand Up @@ -39,6 +41,38 @@ export const fetchByAuthorSuccess = response => ({
response,
});

/*
*Defines the action types for successful like of articles
*/
export const likeArticleSuccess = response => ({
type: `${LIKE_ARTICLE}_SUCCESS`,
response,
});

/*
*Defines the action types for failure like of articles
*/
export const likeArticleFailure = error => ({
type: `${LIKE_ARTICLE}_FAILURE`,
error,
});

/*
*Defines the action types for successful dislike of article
*/
export const dislikeArticleSuccess = response => ({
type: `${DISLIKE_ARTICLE}_SUCCESS`,
response,
});

/*
*Defines the action types for failure dislike of article
*/
export const dislikeArticleFailure = error => ({
type: `${DISLIKE_ARTICLE}_FAILURE`,
error,
});

/*
*Defines the action types for unsuccessful fetch article for author
*/
Expand Down Expand Up @@ -80,6 +114,10 @@ export const fetchArticles = url => dispatch => {
});
};

const config = {
headers: { Authorization: localStorage.getItem('token') },
};

/*
*Defines the fetchOneArticle actions and dispatches the right
*action for either success
Expand All @@ -88,7 +126,7 @@ export const fetchArticles = url => dispatch => {
export const fetchOneArticle = (slug, history) => dispatch => {
dispatch({ type: FETCH_ARTICLES });
return axios
.get(`${BASE_URL}/articles/${slug}/`)
.get(`${BASE_URL}/articles/${slug}/`, config)
.then(response => {
dispatch(fetchOneSuccess(response));
})
Expand All @@ -115,10 +153,6 @@ export const fetchByAuthor = author => dispatch => {
});
};

const config = {
headers: { Authorization: localStorage.getItem('token') },
};

/*
*Defines the deleteArticle actions and dispatches the right
*action for either success
Expand All @@ -137,3 +171,39 @@ export const deleteArticle = (slug, history) => dispatch => {
dispatch(deleteArticleFailure(error));
});
};

/*
*Defines the deleteArticle actions and dispatches corresponding
*action for either success
*failure
*/
export const likeArticle = (slug, history) => async dispatch => {
return axios
.post(`${BASE_URL}/articles/${slug}/like/`, null, config)
.then(response => {
dispatch(likeArticleSuccess(response));
successToast(response.data.message);
dispatch(fetchOneArticle(slug, history));
})
.catch(error => {
dispatch(likeArticleFailure(error));
});
};

/*
*Defines the dislikeArticle actions and dispatches corresponding
*action for either success
*failure
*/
export const dislikeArticle = (slug, history) => async dispatch => {
return axios
.post(`${BASE_URL}/articles/${slug}/dislike/`, null, config)
.then(response => {
dispatch(dislikeArticleSuccess(response));
successToast(response.data.message);
dispatch(fetchOneArticle(slug, history));
})
.catch(error => {
dispatch(dislikeArticleFailure(error));
});
};
2 changes: 2 additions & 0 deletions src/redux/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ export const CREATE_REPORT = 'CREATE_REPORT';
export const FETCH_REPORT = 'FETCH_REPORT';
export const DELETE_REPORT = 'DELETE_REPORT';
export const UPDATE_REPORT = 'UPDATE_REPORT';
export const LIKE_ARTICLE = 'LIKE_ARTICLE';
export const DISLIKE_ARTICLE = 'DISLIKE_ARTICLE';
29 changes: 28 additions & 1 deletion src/redux/reducers/FetchArticlesReducer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { FETCH_ARTICLES, DELETE_ARTICLE } from '../constants';
import {
FETCH_ARTICLES, DELETE_ARTICLE, LIKE_ARTICLE, DISLIKE_ARTICLE,
} from '../constants';

const initialState = {
isLoading: false,
articles: {},
error: {},
article: {},
likeDislike: {},
};

const FetchArticlesReducer = (state = initialState, action) => {
Expand Down Expand Up @@ -68,6 +71,30 @@ const FetchArticlesReducer = (state = initialState, action) => {
isLoading: false,
authorArticleDelete: action.error,
};
case `${LIKE_ARTICLE}_SUCCESS`:
return {
...state,
isLoading: false,
likeDislike: action.response,
};
case `${LIKE_ARTICLE}_FAILURE`:
return {
...state,
isLoading: false,
likeDislike: action.error,
};
case `${DISLIKE_ARTICLE}_SUCCESS`:
return {
...state,
isLoading: false,
likeDislike: action.response,
};
case `${DISLIKE_ARTICLE}_FAILURE`:
return {
...state,
isLoading: false,
likeDislike: action.error,
};

default:
return { ...state };
Expand Down
1 change: 1 addition & 0 deletions src/redux/reducers/loginReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const initialState = {
data: {},
error: {},
isLoading: false,

};

/*
Expand Down
Loading

0 comments on commit 10d95a1

Please sign in to comment.