Skip to content

Commit

Permalink
[ft-bookmark-#164350372] implement bookmark functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Isaiah Afolayan authored and Isaiah Afolayan committed Mar 3, 2019
1 parent 27fee6d commit 6095460
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 48 deletions.
40 changes: 23 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,19 @@
"@babel/runtime": "^7.3.1",
"autoprefixer": "^9.4.7",
"axios": "^0.18.0",
"cloudinary-react": "^1.1.0",
"dotenv": "^6.2.0",
"dotenv-webpack": "^1.7.0",
"cloudinary-react": "^1.1.0",
"enzyme": "^3.8.0",
"express": "^4.16.4",
"faker": "^4.1.0",
"jwt-decode": "^2.2.0",
"medium-editor": "^5.23.3",
"normalize.css": "^8.0.1",
"postcss-loader": "^3.0.0",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"prop-types": "^15.7.2",
"react": "^16.8.3",
"react-dom": "^16.8.3",
"react-medium-editor": "^1.8.1",
"react-redux": "^6.0.0",
"react-responsive-carousel": "^3.1.47",
Expand Down
33 changes: 33 additions & 0 deletions src/actions/bookmarkAction/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { toast } from 'react-toastify';
import { createBookmark, removeBookmark } from '../../helpers/axiosHelper/bookmark';
import actionTypes from '../readArticleAction/actionTypes';

const { TOGGLE_ARTICLE_BOOKMARK } = actionTypes;

export const bookmarkSuccess = () => ({
type: TOGGLE_ARTICLE_BOOKMARK
});

export const bookmarkedAction = bookmarkDetail => async (dispatch) => {
try {
const response = await createBookmark(bookmarkDetail);
dispatch(bookmarkSuccess());
toast.success(response.data.message);
} catch (error) {
toast.error(error.response.data.message);
}
};

export const removeBookmarkSuccess = () => ({
type: TOGGLE_ARTICLE_BOOKMARK
});

export const removeBookmarkAction = bookmarkDetail => async (dispatch) => {
try {
const response = await removeBookmark(bookmarkDetail);
dispatch(removeBookmarkSuccess());
toast.success(response.data.message);
} catch (error) {
toast.error(error.response.data.message);
}
};
3 changes: 2 additions & 1 deletion src/actions/readArticleAction/actionTypes.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const actionTypes = {
READ_ARTICLE_LOADING: 'READ_ARTICLE_LOADING',
READ_ARTICLE_SUCCESS: 'READ_ARTICLE_SUCCESS',
READ_ARTICLE_FAILURE: 'READ_ARTICLE_FAILURE'
READ_ARTICLE_FAILURE: 'READ_ARTICLE_FAILURE',
TOGGLE_ARTICLE_BOOKMARK: 'TOGGLE_ARTICLE_BOOKMARK'
};

export default actionTypes;
53 changes: 38 additions & 15 deletions src/components/ReadArticle/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* eslint-disable no-unused-expressions */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Image, Container, Header, Comment, Divider } from 'semantic-ui-react';
import { Image, Container, Header, Comment, Divider, Icon } from 'semantic-ui-react';
import { connect } from 'react-redux';
import jwtDecode from 'jwt-decode';
import ShareArticle from '../ShareArticle';
import calendar from '../../images/calendar.svg';
import time from '../../images/time.svg';
Expand All @@ -10,10 +12,13 @@ import './styles/ReadArticle.scss';
import author from '../../images/avatar.png';
import { getArticle } from '../../actions/readArticleAction';
import { dateFormatter, readTimeFormatter } from '../../helpers/articleInfoFormatter';
import { bookmarkedAction, removeBookmarkAction } from '../../actions/bookmarkAction';

class ReadArticle extends Component {
componentDidMount = () => {
this.props.getArticle(this.props.selectedArticle);
const id = this.props.selectedArticle;
const { theArticle } = this.props;
theArticle(id);
};

getArticleContent = articleContent => (
Expand Down Expand Up @@ -49,15 +54,21 @@ class ReadArticle extends Component {
return {};
};

handleIconClick = () => {
const authorizedToken = localStorage.getItem('authentication');
const decoded = jwtDecode(authorizedToken);
const { createBookmark, removeBookmark } = this.props;
const { bookmark } = this.props.retrievedArticle.successResponse;
const detail = {
userId: decoded.payLoad,
articleId: this.props.selectedArticle
};
!bookmark ? createBookmark(detail) : removeBookmark(detail);
}

render() {
const { title,
content,
createdAt,
id,
description,
featuredImageUrl,
readTime,
articleAuthor } = this.props.retrievedArticle.successResponse;
const { title, content, createdAt, id, description, featuredImageUrl,
readTime, articleAuthor, bookmark } = this.props.retrievedArticle.successResponse;
const authorDetails = this.getAuthorDetails(articleAuthor);
return (
<Container className="readArticleContainer">
Expand All @@ -66,6 +77,7 @@ class ReadArticle extends Component {
<Comment.Group className="articleInfo">
<Image avatar as="a" src={author} />
{this.getArticleInfo(authorDetails.authorName, createdAt, readTime)}
<Icon name={bookmark ? 'bookmark' : 'bookmark outline'} size="large" className="bookmarkIcon" onClick={this.handleIconClick} />
</Comment.Group>
<Divider className="articleDivider" />
{this.getArticleContent(content)}
Expand All @@ -86,9 +98,12 @@ ReadArticle.propTypes = {
readTime: PropTypes.number,
featuredImageUrl: PropTypes.string,
articleAuthor: PropTypes.object,
getArticle: PropTypes.func.isRequired,
theArticle: PropTypes.func.isRequired,
retrievedArticle: PropTypes.object,
selectedArticle: PropTypes.string.isRequired
selectedArticle: PropTypes.string.isRequired,
bookmark: PropTypes.bool.isRequired,
createBookmark: PropTypes.func,
removeBookmark: PropTypes.func
};

ReadArticle.defaultProps = {
Expand All @@ -100,14 +115,22 @@ ReadArticle.defaultProps = {
readTime: 0,
featuredImageUrl: '',
articleAuthor: {},
retrievedArticle: {}
retrievedArticle: {},
createBookmark: null,
removeBookmark: null
};

const mapDispatchToProps = dispatch => ({
createBookmark: detail => dispatch(bookmarkedAction(detail)),
removeBookmark: detail => dispatch(removeBookmarkAction(detail)),
theArticle: id => dispatch(getArticle(id))
});

const mapStateToProps = state => ({
retrievedArticle: state.readArticle
retrievedArticle: state.readArticle,
});
export { ReadArticle as ReadArticlePage };
export default connect(
mapStateToProps,
{ getArticle }
mapDispatchToProps,
)(ReadArticle);
14 changes: 13 additions & 1 deletion src/components/ReadArticle/styles/ReadArticle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
.ui.comments .comment.infoContainer {
margin: -1% 1%;
padding: 0%;
width: 50%;
width: 100%;
}

img.ui.image {
Expand Down Expand Up @@ -93,6 +93,15 @@ img.ui.image {
width: 100%;
}

i {
cursor: pointer;

&.bookmarkIcon {
width: 2rem;
height: 2rem;
}
}

@media (max-width: 767px) {
.ui.header.articleTitle {
text-align: center;
Expand All @@ -119,6 +128,9 @@ img.ui.image {
.ui.massive.image {
height: 30rem;
}
.ui.comments.articleInfo {
max-width: 90%;
}
@media only screen and (max-width: 991px) {
.icon-bar {
top: 50%;
Expand Down
1 change: 1 addition & 0 deletions src/components/ReadArticle/test/ReadArticle.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('Read article component', () => {
selectedArticle={props.match.params.title}
retrievedArticle={retrievedArticle}
getArticle={getArticle}
bookmark
/>
);
expect(wrapper).toMatchSnapshot();
Expand Down
29 changes: 29 additions & 0 deletions src/helpers/axiosHelper/bookmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import axios from 'axios';
import { API_BASE_URL } from '../../constants';

const options = {
headers: {
authorization: localStorage.getItem('authentication')
}
};

const createBookmark = async (bookmarkDetail) => {
const response = await axios.post(`${API_BASE_URL}/articles/bookmark`, bookmarkDetail, options);
return response;
};

const removeBookmark = async (bookmarkDetail) => {
const response = await axios({
method: 'DELETE',
url: `${API_BASE_URL}/articles/bookmark`,
data: bookmarkDetail,
headers: options.headers
});
return response;
};


export {
createBookmark,
removeBookmark
};
29 changes: 29 additions & 0 deletions src/helpers/bookmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import axios from 'axios';
import { API_BASE_URL } from '../constants'

const options = {
headers: {
authorization: localStorage.getItem('authentication')
}
};

const createBookmark = async (bookmarkDetail) => {
const response = await axios.post(`${API_BASE_URL}/articles/bookmark`, bookmarkDetail, options);
return response;
};

const removeBookmark = async (bookmarkDetail) => {
const response = await axios({
method: 'DELETE',
url: `${API_BASE_URL}/articles/bookmark`,
data: bookmarkDetail,
headers: options.headers
});
return response;
};


export {
createBookmark,
removeBookmark
};
2 changes: 0 additions & 2 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import authReducer from './authReducer';
import socialLogin from './socialLoginReducer';
import readArticle from './readArticleReducer';
import totalArticleReactions from './totalArticleReactionReducer';
import userReducer from './userReducer';

const rootReducer = combineReducers({
trendingArticlesReducer,
Expand All @@ -13,7 +12,6 @@ const rootReducer = combineReducers({
CategoriesReducer,
TagsReducer,
authReducer,
userReducer,
socialLogin,
readArticle,
totalArticleReactions
Expand Down
8 changes: 2 additions & 6 deletions src/reducers/initialState.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,8 @@ const initialState = {
successResponse: {},
failureResponse: {}
},
user: {
fetchIsLoading: false,
updateIsLoading: false,
userId: '',
articles: [],
userprofile: {}
bookmark: {
bookmark: false
}
};

Expand Down
Loading

0 comments on commit 6095460

Please sign in to comment.