Skip to content

Commit

Permalink
add bookmark
Browse files Browse the repository at this point in the history
  • Loading branch information
raymond42 committed Nov 8, 2019
1 parent b765002 commit 5ba1967
Show file tree
Hide file tree
Showing 20 changed files with 661 additions and 162 deletions.
264 changes: 132 additions & 132 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@
"sass-loader": "^8.0.0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.3.2",
"@typescript-eslint/parser": "^2.3.2",
"coveralls": "^3.0.6",
"cypress": "^3.4.1",
"eslint": "^6.1.0",
"@typescript-eslint/eslint-plugin": "^2.3.2",
"@typescript-eslint/parser": "^2.3.2",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.14.3",
"moxios": "^0.4.0",
"eslint-plugin-react-hooks": "^1.7.0",
"moxios": "^0.4.0",
"react-test-renderer": "^16.10.2",
"redux-mock-store": "^1.5.3",
"redux-promise-middleware": "^6.1.1",
Expand Down
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.
17 changes: 17 additions & 0 deletions src/app/middlewares/checkToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import jwdDecoded from 'jwt-decode';

const checkToken = () => next => action => {
try {
const decoded = jwdDecoded(localStorage.token);
console.log('decoded', decoded);
} catch (error) {
if (localStorage.token) {
window.location.reload();
}
localStorage.token = '';
localStorage.username = '';
}
next(action);
};

export default checkToken;
4 changes: 3 additions & 1 deletion src/app/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import resetPasswordReducer from '../../feature/Reset Password/reset password/re
import article from '../../feature/articles/createArticle/createArticleReducer';
import getAllArticles from '../../feature/articles/getArticles/GetAllArticleReducer';
import followReducer from '../../feature/followUnfollow/followUnfollowReducer';
import getSingleArticle from '../../feature/articles/getSingleArticle/GetSingleArticleReducer';
import socialReducer from '../../feature/auth/socialLogin/SocialReducer';
import bookmarkReducer from '../../feature/bookmark/bookmarkReducer';
import getSingleArticle from '../../feature/articles/getSingleArticle/GetSingleArticleReducer';

export default combineReducers({
login: loginReducer,
Expand All @@ -23,4 +24,5 @@ export default combineReducers({
followAuthor: followReducer,
getSingleArticle,
social: socialReducer,
bookmarking: bookmarkReducer
});
5 changes: 3 additions & 2 deletions src/app/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
import checkToken from '../middlewares/checkToken';

const initialState = {};

const middlewares = [checkToken, thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(thunk)),
composeWithDevTools(applyMiddleware(...middlewares)),
);

export default store;
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
101 changes: 81 additions & 20 deletions src/feature/articles/getSingleArticle/GetSingleArticleComponent.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/* 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 { Link, BrowserRouter } from 'react-router-dom';
Expand All @@ -10,18 +16,64 @@ 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 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 = {
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 { 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);
}

render() {
Expand All @@ -33,7 +85,6 @@ export class ViewSingleArticle extends Component {
title,
description,
body,
slug,
likes,
dislikes,
createdAt,
Expand All @@ -43,13 +94,15 @@ export class ViewSingleArticle extends Component {
}
} = this.props.article;
const { userName, image } = author;
const { user } = this.props.currentUser;
const { currentUser: { user }, bookmarkLoading } = this.props;
const ownArticle = userName === user.username;
const { bookmarks } = this.state;

return (
<div className="wrapper">
<div className="heading">
<div className="heading__left">
<span>
<span>
<div className="heading__follow">
<FollowUnfollowComponent
authorId={authorId}
Expand All @@ -74,7 +127,9 @@ export class ViewSingleArticle extends Component {
.fromNow()}
</span>
{' '}
<span className="heading__munite">{readTime}.</span>
<span className="heading__munite">
{readTime}
</span>
<span>
<div className="heading__avarageRating">
<AverageRating avarageRatings={averageRatings} />
Expand All @@ -84,10 +139,14 @@ export class ViewSingleArticle extends Component {
</div>
<div className="heading__right">
<div className="heading__right-item">
<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}
/>
<span className="menu">
<img src={ellipsis} className="heading__menu" alt=" " />
</span>
Expand Down Expand Up @@ -137,7 +196,6 @@ export class ViewSingleArticle extends Component {
<StarRating
articleId={id}
pathname={this.props.location.pathname}
slug={slug}
/>
</span>
)}
Expand All @@ -156,24 +214,27 @@ export class ViewSingleArticle extends Component {
);
}
}
const mapStateToProps = ({ getSingleArticle, login }) => ({
const mapStateToProps = ({ getSingleArticle, login, bookmarking }) => ({
article: getSingleArticle,
currentUser: login,
isAuthenticated: login
isAuthenticated: login,
bookmarks: bookmarking.bookmarks,
bookmarkLoading: bookmarking.loading,
});

const mapDispatchToProps = {
GetSingleArticle
GetSingleArticle,
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 @@ -11,6 +11,8 @@ const renderViewSingleArticle = args => {
likeArticle: () => {},
dislikeArticle: () => {},
handleDislike: () => {},
handleBookmarkClick: () => {},
submitBookmark: () => {},
GetSingleArticle: () => {},
article: {
article: {
Expand All @@ -31,7 +33,7 @@ describe('Get All Articles Components tests', () => {
window.scrollTo = jest.fn();
const wrapper = renderViewSingleArticle();
expect(wrapper.find('div').length).toBe(21);
expect(wrapper.find('img').length).toBe(3);
expect(wrapper.find('img').length).toBe(2);
expect(window.scrollTo).toBeCalledWith(0, 0);
});
});
36 changes: 36 additions & 0 deletions src/feature/bookmark/BookmarkComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* 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: '33px' }}
className={isBookmarked ? 'fa fa-bookmark' : 'fa fa-bookmark-o'}
onClick={() => {
onClick(isBookmarked, articleId);
setIsBookmarked(!isBookmarked);
}}
/>
</span>
);
};

export default (BookmarkComponent);
Loading

0 comments on commit 5ba1967

Please sign in to comment.