Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#165273540: Users should be able to follow each other #40

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/components/Apps/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Provider } from 'react-redux';
import { shallow, mount } from 'enzyme';
import store from 'store/store';
import { LandingPage } from 'pages/Landing';
import PageNotFound from 'pages/Error';
import { MemoryRouter } from 'react-router';
import App from './App';

Expand Down
65 changes: 65 additions & 0 deletions src/components/Follow/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// React libraries
import React, { Component } from 'react';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing error: 'import' and 'export' may appear only with 'sourceType: module'


// third-party libraries
import PropTypes from 'prop-types';

// components
import Button from 'components/Button';

// action creators
import {
fellowAndUnfollowAuthorActionCreator,
} from 'store/actions/followActions';

export default class FollowProfileButton extends Component {
followClick = () => {
const { onButtonClick, username } = this.props;
onButtonClick(fellowAndUnfollowAuthorActionCreator(username));
}

unfollowClick = () => {
const { onButtonClick, username } = this.props;
onButtonClick(fellowAndUnfollowAuthorActionCreator(username));
}

render() {
const { following } = this.props;
return (
<div>
{ following
? (
<Button
variant="raised"
color="secondary"
btnClass="edit-btn"
btnName="Unfollow"
btnEvent={this.unfollowClick}
onClick={this.unfollowClick}
>
Unfollow
</Button>
)
: (
<Button
variant="raised"
color="primary"
btnClass="edit-btn"
btnName="Follow"
btnEvent={this.followClick}
onClick={this.followClick}
>
Follow
</Button>
)
}
</div>
);
}
}

FollowProfileButton.propTypes = {
username: PropTypes.string.isRequired,
following: PropTypes.bool.isRequired,
onButtonClick: PropTypes.func.isRequired
};
29 changes: 25 additions & 4 deletions src/components/Profile/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import {
updateProfileRequest,
} from 'store/actions/profileActions';

// import components
import FollowProfileButton from 'components/Follow';

// import default image
import {
DEFAULT_IMAGE
Expand All @@ -28,6 +31,7 @@ class Sidebar extends Component {
super(props);
this.state = {
showResults: false,
followings: false,
};
}

Expand All @@ -44,6 +48,10 @@ class Sidebar extends Component {
this.setState({ [event.target.name]: event.target.value });
}

clickFollowButton = () => {
console.log('Follow button');
}

/**
* This is a function uses firebase config setting to upload
* image to our firebase storage
Expand Down Expand Up @@ -74,14 +82,14 @@ class Sidebar extends Component {

render() {
const {
authenticatedUsername, profile: { profile },
authenticatedUsername, following, followers, profile: { profile },
match: { params: { profileUser } }
} = this.props;
const {
firstname, lastname, username, email, image, bio,
} = profile;

const { showResults } = this.state;
const { showResults, followings } = this.state;
return (
<div className="sidebar">
<div>
Expand All @@ -108,6 +116,7 @@ class Sidebar extends Component {
</Form>
) }
</div>

<div className="username center-align">{username}</div>
<div className="fullname center-align">
{firstname}
Expand All @@ -116,16 +125,24 @@ class Sidebar extends Component {
</div>
<div className="email center-align">{email}</div>
<div className="bio center-align ">{bio}</div>
<div>
{' '}
<FollowProfileButton
username={username}
following={followings}
onButtonClick={this.clickFollowButton}
/>
</div>
<div className="follow-div">
<div className="follow-stats">
<div className="count center-align ">
0
{following}
</div>
<div className="title center-align">Followings</div>
</div>
<div>
<div className="count center-align">
0
{followers}
</div>
<div className="title center-align">Followers</div>
</div>
Expand All @@ -152,6 +169,8 @@ Sidebar.propTypes = {
bio: PropTypes.string,
}),
authenticatedUsername: PropTypes.string.isRequired,
followers: PropTypes.number,
following: PropTypes.number,
};

// Assigning default props
Expand All @@ -163,6 +182,8 @@ Sidebar.defaultProps = {
email: '',
bio: '',
},
followers: 0,
following: 0,
};

/**
Expand Down
4 changes: 2 additions & 2 deletions src/pages/PasswordReset/PasswordReset.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ describe('Password Reset Component', () => {
const buttonWrapper = wrapper.find('.password-reset__fields__field__button');

it('should render without exploding', () => {
const wrapper = shallow(<PasswordReset />);
expect(wrapper.length).toBe(1);
const wrapper1 = shallow(<PasswordReset />);
expect(wrapper1.length).toBe(1);
});

it('should handle change', () => {
Expand Down
4 changes: 4 additions & 0 deletions src/pages/Profile/__snapshots__/Profile.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ exports[`Profile should render 1`] = `
>
<[object Object]
fetchProfile={[Function]}
getFollowers={[Function]}
getFollowings={[Function]}
profile={
Object {
"bio": "this is my biodata",
Expand All @@ -45,6 +47,8 @@ exports[`Profile should render 1`] = `
<ProfileMain
authenticatedUser="lou"
fetchProfile={[Function]}
getFollowers={[Function]}
getFollowings={[Function]}
match={[Function]}
profile={
Object {
Expand Down
24 changes: 21 additions & 3 deletions src/pages/Profile/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import ProfileMain from 'components/Profile/Index';

// import actions creator
import { fetchProfileRequest } from 'store/actions/profileActions';
import {
getFollowersActionCreator,
getFollowingActionCreator,
} from 'store/actions/followActions';

// import styles
import './Profile.scss';
Expand All @@ -24,8 +28,10 @@ export class Profile extends Component {
const { match } = this.props;
if (match) {
const { params: { profileUser } } = match;
const { fetchProfile } = this.props;
const { fetchProfile, getFollowers, getFollowings } = this.props;
fetchProfile(profileUser);
getFollowers();
getFollowings();
}
}

Expand Down Expand Up @@ -57,6 +63,8 @@ Profile.propTypes = {
})
}).isRequired,
fetchProfile: PropTypes.func,
getFollowers: PropTypes.func,
getFollowings: PropTypes.func,
authenticatedUser: PropTypes.string.isRequired,
};

Expand All @@ -69,17 +77,27 @@ Profile.defaultProps = {
bio: '',
},
fetchProfile: () => { },
getFollowers: () => {},
getFollowings: () => {},
};

export const mapStateToProps = state => ({
profile: state.profile,
authenticatedUser: state.loginReducer.user.username
authenticatedUser: state.loginReducer.user.username,
followers: state.followReducer.followers.followers,
following: state.followReducer.following.following,
});

export const mapDispatchToProps = dispatch => ({
fetchProfile: (username) => {
dispatch(fetchProfileRequest(username));
}
},
getFollowers: () => {
dispatch(getFollowersActionCreator());
},
getFollowings: () => {
dispatch(getFollowingActionCreator());
},
});

export default connect(
Expand Down
106 changes: 106 additions & 0 deletions src/store/actions/followActions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// third party libraries
import { toast } from 'react-toastify';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing error: 'import' and 'export' may appear only with 'sourceType: module'


// import axios instance
import axios from 'utils/mainAxios';

// user constants
import {
FOLLOW_AUTHOR_ERROR,
FOLLOW_AUTHOR_SUCCESS,
UNFOLLOW_AUTHOR_FAILURE,
UNFOLLOW_AUTHOR_SUCCESS,
GET_FOLLOWERS_ERROR,
GET_FOLLOWERS_SUCCESS,
GET_FOLLOWING_ERROR,
GET_FOLLOWING_SUCCESS,
} from 'store/actions/followTypes';

/**
* follow author action function on success
* @param {string} response
*/
export const followAuthorSuccess = response => ({
type: FOLLOW_AUTHOR_SUCCESS,
response,
});

/**
* follow author action function on error
* @param {string} error
*/
export const followAuthorError = error => ({
type: FOLLOW_AUTHOR_ERROR,
error,
});

/**
* follow author action function on success
* @param {string} response
*/
export const getFollowersSuccess = response => ({
type: GET_FOLLOWERS_SUCCESS,
response,
});

/**
* get followers action function on error
* @param {string} error
*/
export const getFollowersError = error => ({
type: GET_FOLLOWERS_ERROR,
error,
});

/**
* get followers action function on success
* @param {string} response
*/
export const getFollowingSuccess = response => ({
type: GET_FOLLOWING_SUCCESS,
response,
});

/**
* follow author action function on error
* @param {string} error
*/
export const getFollowingError = error => ({
type: GET_FOLLOWING_ERROR,
error,
});

/**
* action creator function for following authors
* username as a parameter and dispatch as a function
* @param {string} username
*/
export const fellowAndUnfollowAuthorActionCreator = username => dispatch => axios
.post(`authors/${username}/follow/`)
.then(response => dispatch(followAuthorSuccess(response.data.profile)))
.catch(error => dispatch(followAuthorError(error)));


/**
* action creator function for getting author's followers
* username as a parameter and dispatch as a function
* @param {string} username
*/
export const getFollowersActionCreator = () => dispatch => axios
.get('authors/followers/')
.then((response) => {
dispatch(getFollowersSuccess(response.data));
})
.catch(error => dispatch(getFollowersError(error)));

/**
* action creator function for getting author's followers
* username as a parameter and dispatch as a function
* @param {string} username
*/
export const getFollowingActionCreator = () => dispatch => axios
.get('authors/following/')
.then((response) => {
dispatch(getFollowingSuccess(response.data));
})
.catch(error => dispatch(getFollowingError(error)));
8 changes: 8 additions & 0 deletions src/store/actions/followTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const FOLLOW_AUTHOR_ERROR = 'FOLLOW_AUTHOR_ERROR';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing error: 'import' and 'export' may appear only with 'sourceType: module'

export const FOLLOW_AUTHOR_SUCCESS = 'FOLLOW_AUTHOR_SUCCESS';
export const GET_FOLLOWERS_ERROR = 'GET_FOLLOWERS_ERROR';
export const GET_FOLLOWERS_SUCCESS = 'GET_FOLLOWERS_SUCCESS';
export const GET_FOLLOWING_ERROR = 'GET_FOLLOWING_ERROR';
export const GET_FOLLOWING_SUCCESS = 'GET_FOLLOWING_SUCCESS';
export const UNFOLLOW_AUTHOR_FAILURE = 'UNFOLLOW_AUTHOR_FAILURE';
export const UNFOLLOW_AUTHOR_SUCCESS = 'UNFOLLOW_AUTHOR_SUCCESS';
Loading