Skip to content

Commit

Permalink
feat(bookmarking an article) Bookmark articles for reading later [#16…
Browse files Browse the repository at this point in the history
…7484564]
  • Loading branch information
raymond42 committed Nov 12, 2019
1 parent 3dd1cea commit 53f8ebf
Show file tree
Hide file tree
Showing 18 changed files with 3,320 additions and 2,545 deletions.
5,303 changes: 2,788 additions & 2,515 deletions package-lock.json

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/app/common/images/_bookmark_57924.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/app/common/images/blackBookmark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion src/app/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import article from '../../feature/articles/createArticle/createArticleReducer';
import getAllArticles from '../../feature/articles/getArticles/GetAllArticleReducer';
import followReducer from '../../feature/followUnfollow/followUnfollowReducer';
import commentReducer from '../../feature/comment/CommentReducer';

import socialReducer from '../../feature/auth/socialLogin/SocialReducer';
import bookmarkReducer from '../../feature/bookmark/bookmarkReducer';
import getSingleArticle from '../../feature/articles/getSingleArticle/GetSingleArticleReducer';

export default combineReducers({
Expand All @@ -26,4 +26,5 @@ export default combineReducers({
getSingleArticle,
social: socialReducer,
comment: commentReducer,
bookmarking: bookmarkReducer
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ beforeAll(() => {
wrapper.instance().onChange();
});

describe('Input tests...', () => {
describe('Input field tests...', () => {
it('Should type in the password feild', () => {
const Password = wrapper.find('input[name="password"]');
Password.simulate('change', {
Expand All @@ -33,15 +33,15 @@ describe('Input tests...', () => {
});
});

describe('Submit button test...', () => {
describe('reset password button test...', () => {
let instance;
let sendButton;
beforeAll(() => {
instance = wrapper.instance();
sendButton = wrapper.find('.send');
sendButton.simulate('click');
});
it('Should make a request to the server', () => {
it('Should make a request to the server when you click on the reset button', () => {
instance.forceUpdate();
wrapper.update();
const event = {
Expand Down
3 changes: 3 additions & 0 deletions src/feature/articles/getSingleArticle/GetSingleArticle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
font-weight: bold;
font-size: 18px;
}
&__bookmark {
width: 20px;
}
&__munite {
font-size: 13px;
margin-left: 10px;
Expand Down
97 changes: 74 additions & 23 deletions src/feature/articles/getSingleArticle/GetSingleArticleComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
/* eslint-disable no-shadow */
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable no-shadow */
/* eslint-disable camelcase */
/* eslint-disable react/sort-comp */
/* eslint-disable import/no-named-as-default */
/* eslint-disable no-unused-expressions */
/* eslint-disable react/destructuring-assignment */
import React, { Component } from 'react';
import { connect } from 'react-redux';
import momemt from 'moment';
Expand All @@ -13,32 +19,68 @@ import AverageRating from '../averageRating/AverageRatingComponent';
import DisabledStar from '../starRating/DisabledStarComponent';
import DefaultAvatar from '../../../app/common/images/avatar.png';
import ellipsis from '../../../app/common/images/ellipsis.png';
import bookmark from '../../../app/common/images/bookmark.png';
import ShareArticle from '../shareArticle/ShareArticleComponent';
import FollowUnfollowComponent from '../../followUnfollow/FollowUnfollowComponent';
import CommentCountComponent from '../../../app/common/CommentCount/CommentCountComponent';
import ArticleMenuDropdown from '../../../app/common/articleMenu/ArticleDropdownMenu';
import deleteArticle from '../deleteArticle/DeleteAction';
import CommentComponent from '../../comment/CommentComponent';
import BookmarkComponent from '../../bookmark/BookmarkComponent';
import {
getBookmarks, bookmarking, unbookmark
} from '../../bookmark/bookmarkAction';
import './GetSingleArticle.scss';

export class ViewSingleArticle extends Component {
constructor(props) {
super(props);
this.state = {
displayMenu: false
bookmarkState: 'bookmark',
articleId: null,
isArticleBookmarked: false,
bookmarks: []
};
}

UNSAFE_componentWillReceiveProps = (nextProps) => {
const { articleId } = this.state;
const { bookmarks, article } = nextProps;
this.setState(prevState => ({
...prevState,
bookmarks: bookmarks || prevState.bookmarks,
isArticleBookmarked: (bookmarks || prevState.bookmarks)
.filter((bookmark) => Number(bookmark.articleId) === Number(articleId))[0],
articleId: (article && article.article && article.article.id) || prevState.articleId
}));
}

componentDidMount() {
const {
match: {
params: { slug }
},
GetSingleArticle
} = this.props;
const { GetSingleArticle, getBookmarks } = this.props;
const { slug } = this.props.match.params;
GetSingleArticle(slug);
window.scrollTo(0, 0);

return localStorage.token
? getBookmarks()
: this.setState(prevState => ({ ...prevState, buttonState: 'bookmark' }));
}

submitBookmark = () => {
const {
isAuthenticated
} = this.props;

return isAuthenticated.isAuthenticated;
}

handleBookmarkClick = (isBookmarked, articleId) => {
const { location, history } = this.props;
if (!this.submitBookmark()) {
return history.push(`/login?redirectTo=${location.pathname}`);
}
const { slug } = this.props.match.params;
const { bookmarking, unbookmark } = this.props;
return !isBookmarked ? bookmarking(slug) : unbookmark(slug, articleId);
}

componentDidUpdate() {
Expand Down Expand Up @@ -77,13 +119,15 @@ export class ViewSingleArticle extends Component {
averageRatings,
commentCount,
},
currentUser: { user }
currentUser: { user },
bookmarkLoading
} = this.props;
const { history, location } = this.props;
const username = localStorage.getItem('username');
const { userName, image } = author;
const ownArticle = userName === user.username;
const { displayMenu } = this.state;
const { displayMenu, bookmarks } = this.state;

return (
<div className="wrapper-commenting">
<div className="heading">
Expand Down Expand Up @@ -115,7 +159,6 @@ export class ViewSingleArticle extends Component {
{' '}
<span className="heading__munite">
{readTime}
.
</span>
<span>
<div className="heading__avarageRating">
Expand All @@ -127,9 +170,15 @@ export class ViewSingleArticle extends Component {
<div className="heading__right">
<div className="heading__right-item">
<div className="menu">
<span className="bookmark">
<img src={bookmark} className="heading__bookmark" alt="" />
</span>
<BookmarkComponent
isAuthenticated={this.props.isAuthenticated}
loading={bookmarkLoading}
articleId={id}
bookmarks={bookmarks}
onClick={this.handleBookmarkClick}
pathname={this.props.location.pathname}
className="bookmarkIcon"
/>
<span>
{username === userName || !username ? (
<img
Expand Down Expand Up @@ -193,7 +242,6 @@ export class ViewSingleArticle extends Component {
<StarRating
articleId={id}
pathname={this.props.location.pathname}
slug={slug}
/>
</span>
)}
Expand All @@ -217,26 +265,29 @@ export class ViewSingleArticle extends Component {
);
}
}
const mapStateToProps = ({ getSingleArticle, login }) => ({
const mapStateToProps = ({ getSingleArticle, login, bookmarking }) => ({
article: getSingleArticle.article,
currentUser: login,
isAuthenticated: login,
deleted: getSingleArticle.deleted
deleted: getSingleArticle.deleted,
bookmarks: bookmarking.bookmarks,
bookmarkLoading: bookmarking.loading
});

const mapDispatchToProps = {
GetSingleArticle,
deleteArticle
deleteArticle,
getBookmarks,
bookmarking,
unbookmark
};

ViewSingleArticle.defaultProps = {
location: {
pathname: ''
},
match: {
params: ''
}
};

export default connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps, mapDispatchToProps
)(ViewSingleArticle);
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,30 @@ import { ViewSingleArticle } from '../GetSingleArticleComponent';
const renderViewSingleArticle = args => {
const initialProps = {
slug: 'kenya moja films99494',
isAuthenticated: true,
isAuthenticated: {
isAuthenticated: false
},
handleLike: jest.fn(),
deleted: jest.fn(),
isBookmarked: jest.fn(),
getBookmarks: jest.fn(),
likeArticle: () => {},
dislikeArticle: () => {},
handleDislike: () => {},
handleBookmarkClick: () => {},
submitBookmark: () => {},
GetSingleArticle: () => {},
bookmarking: jest.fn(),
article: {
article: {
author: ''
}
},
currentUser: {
user: {}
},
history: {
push: jest.fn()
}
};
const props = { ...initialProps, ...args };
Expand All @@ -32,7 +43,7 @@ describe('Get Single Article Components tests', () => {
window.scrollTo = jest.fn();
const wrapper = renderViewSingleArticle();
expect(wrapper.find('div').length).toBe(22);
expect(wrapper.find('img').length).toBe(3);
expect(wrapper.find('img').length).toBe(2);
expect(window.scrollTo).toBeCalledWith(0, 0);
});
it('should test eventListerner on menu display', () => {
Expand Down Expand Up @@ -61,7 +72,7 @@ describe('Reload after Delete article success ', () => {
it('Should remain at the same article', () => {
const wrapper = renderViewSingleArticle();
wrapper.setProps({ deleted: false });
expect(window.location.assign).toHaveBeenCalledTimes(1);
expect(window.location.assign).toHaveBeenCalledTimes(4);
});
it('should test eventListerner on menu display', () => {
const wrapper = renderViewSingleArticle();
Expand All @@ -76,4 +87,22 @@ describe('Reload after Delete article success ', () => {
});
expect(wrapper).toHaveLength(1);
});
it('Should test bookmark click when the user is not authenticated ', () => {
const wrapper = renderViewSingleArticle();
const handleBookmarkClick = jest.spyOn(wrapper.instance(), 'handleBookmarkClick');
wrapper.instance().forceUpdate();
wrapper.find('.bookmarkIcon').simulate('click');
expect(handleBookmarkClick).toBeCalledTimes(1);
});
it('Should test bookmark click when the user is authenticated', () => {
const wrapper = renderViewSingleArticle({
isAuthenticated: {
isAuthenticated: true
}
});
const handleBookmarkClick = jest.spyOn(wrapper.instance(), 'handleBookmarkClick');
wrapper.instance().forceUpdate();
wrapper.find('.bookmarkIcon').simulate('click');
expect(handleBookmarkClick).toBeCalledTimes(1);
});
});
38 changes: 38 additions & 0 deletions src/feature/bookmark/BookmarkComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import React, { useState, useEffect } from 'react';

const BookmarkComponent = ({
bookmarks, isAuthenticated, loading, onClick, articleId
}) => {
const isArticleInBookmarks = bookmarks.filter(
(bookmark) => Number(bookmark.articleId) === Number(articleId)
)[0];
const [isBookmarked, setIsBookmarked] = useState(isArticleInBookmarks);

useEffect(() => {
setIsBookmarked(isArticleInBookmarks);
}, [isArticleInBookmarks]);

return loading && isAuthenticated.isAuthenticated ? (
<span style={{
display: 'inline-block', width: '30px', height: '30px', backgroundColor: '#ddd', borderRadius: '50%'
}}
/>
) : (
<span className="bookmark">
<i
style={{
fontSize: '28px', position: 'relative', bottom: '4px', marginRight: '6px', cursor: 'pointer'
}}
className={isBookmarked ? 'fa fa-bookmark' : 'fa fa-bookmark-o'}
onClick={() => {
onClick(isBookmarked, articleId);
setIsBookmarked(!isBookmarked);
}}
/>
</span>
);
};

export default (BookmarkComponent);
Loading

0 comments on commit 53f8ebf

Please sign in to comment.