Skip to content

Commit

Permalink
feat(login): implement social login
Browse files Browse the repository at this point in the history
- implement functionality to get access token from facebook/ google
- include social media icons on both signup and sign in pages
- send login request to ah backend using access token and provider details
- show spinner when logging in
- disable buttons after response
- update failing tests
- add tests for social login
- add documentation
[Delivers #165305224]
  • Loading branch information
jkamz committed Jun 7, 2019
1 parent 5165046 commit 7247c34
Show file tree
Hide file tree
Showing 18 changed files with 556 additions and 26 deletions.
15 changes: 10 additions & 5 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
"sourceType": "module",
"allowImportExportEverywhere": true
},
"parserOptions": {
"sourceType": "module",
"allowImportExportEverywhere": true
},
"extends": "airbnb",
"plugins": ["babel", "import", "jsx-a11y", "react", "prettier"],
"rules": {
Expand All @@ -30,6 +26,15 @@
{
"extensions": [".js", ".jsx"]
}
]
],
"react/button-has-type": [
0,
{
"button": false,
"submit": false,
"reset": false
}
],
"arrow-parens": 0
}
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"start:dev": "webpack-dev-server --mode development --open --hot",
"start:dev": "webpack-dev-server --mode development --open --https --hot",
"build": "webpack --mode production",
"serve": "node server.js",
"test": "jest",
Expand Down Expand Up @@ -48,6 +48,9 @@
"react-bootstrap": "^1.0.0-beta.9",
"react-dom": "^16.8.6",
"react-inline-editable-field": "^1.0.6",
"react-facebook-login": "^4.1.1",
"react-google-login": "^5.0.4",
"react-icons": "^3.7.0",
"react-redux": "^7.0.3",
"react-router-dom": "^5.0.0",
"react-toastify": "^5.2.0",
Expand Down
1 change: 1 addition & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ module.exports = {
jsxBracketSameLine: false,
tabWidth: 2,
semi: true,
// arrowParens: 'always',
};
62 changes: 62 additions & 0 deletions src/assets/styles/social.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* Shared */
.loginBtn {
box-sizing: border-box;
position: relative;
margin: 0.2em;
padding: 0 15px 0 46px;
border: none;
text-align: left;
line-height: 34px;
white-space: nowrap;
border-radius: 0.2em;
font-size: 16px;
color: #fff;
width: -webkit-fill-available;
text-align: -webkit-center;
}
.loginBtn:before {
content: '';
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
width: 34px;
height: 100%;
}
.loginBtn:focus {
outline: none;
}
.loginBtn:active {
box-shadow: inset 0 0 0 32px rgba(0, 0, 0, 0.1);
}

/* Facebook */
.loginBtn--facebook {
background-color: #4c69ba;
background-image: linear-gradient(#4c69ba, #3b55a0);
text-shadow: 0 -1px 0 #354c8c;
}
.loginBtn--facebook:before {
border-right: #364e92 1px solid;
background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/14082/icon_facebook.png') 6px 6px
no-repeat;
}
.loginBtn--facebook:hover,
.loginBtn--facebook:focus {
background-color: #5b7bd5;
background-image: linear-gradient(#5b7bd5, #4864b1);
}

/* Google */
.loginBtn--google {
background: #dd4b39;
}
.loginBtn--google:before {
border-right: #bb3f30 1px solid;
background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/14082/icon_google.png') 6px 6px
no-repeat;
}
.loginBtn--google:hover,
.loginBtn--google:focus {
background: #e74b37;
}
5 changes: 3 additions & 2 deletions src/components/auth/LoginForm.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Button, Form, Spinner } from 'react-bootstrap';
import SocialLogin from './Social';
import '../../assets/styles/login.scss';

class LoginForm extends Component {
Expand Down Expand Up @@ -74,8 +75,8 @@ class LoginForm extends Component {
<p className="separator-text">OR</p>
<div className="separator-line" />
</div>
<div className="social">
<p>Social login icons here</p>
<div className="rounded-social-buttons">
<SocialLogin />
</div>
<div className="login">
<p>No account?</p>
Expand Down
7 changes: 6 additions & 1 deletion src/components/auth/Signup.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Form, Button, Spinner } from 'react-bootstrap';
import { Link } from 'react-router-dom';
import SocialLogin from './Social';
import '../../assets/styles/signup.scss';
import '../../assets/styles/social.scss';

class SignupForm extends Component {
render() {
const { onSubmit, onChange, error, handleConfirmPassword, isLoading } = this.props;
const { onSubmit, onChange, error, handleConfirmPassword, isLoading, social } = this.props;
return (
<div className="signup-page">
<h1>Authors Haven</h1>
Expand Down Expand Up @@ -90,6 +92,9 @@ class SignupForm extends Component {
<p className="separator-text">OR</p>
<div className="separator-line" />
</div>
<div className="rounded-social-buttons">
<SocialLogin />
</div>
<div className="login">
<p>Already have an account?</p>
<Link className="login-text" to="/Login">
Expand Down
110 changes: 110 additions & 0 deletions src/components/auth/Social.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props';
import { GoogleLogin } from 'react-google-login';
import { UserSocialLogin } from '../../redux/actions/loginActions';
import { errorToast } from '../../helpers';
import '../../assets/styles/social.scss';

class SocialLogin extends Component {
state = {
isClicked: false,
};

/*
* componentClicked:
* changes state to avoid double button click
*/
componentClicked = () => {
this.setState({
isClicked: true,
});
};

/*
* responseFacebook:
* gets response from Facebook and calls login reducer
*/
responseFacebook = response => {
this.setState({
isClicked: true,
});
const { UserSocialLogin, history } = this.props;
UserSocialLogin({ provider: 'facebook', access_token: response.accessToken }, history);
};

/*
* googleResponse:
* gets response from Google and calls login reducer
*/
googleResponse = response => {
this.setState({
isClicked: true,
});
const { UserSocialLogin, history } = this.props;
UserSocialLogin({ provider: 'google-oauth2', access_token: response.accessToken }, history);
};

/*
* onFailure:
* Displays error message in case of failure
*/
onFailure = error => {
errorToast('Oops! something went wrong, please try again');
};

render() {
return (
<div data-set="SocialLoginDiv" className="sld">
<FacebookLogin
appId="409869986516596"
autoLoad={false}
fields="name,email,picture"
callback={this.responseFacebook}
onClick={this.componentClicked}
isDisabled={this.state.isClicked}
render={renderProps => (
<button
className="loginBtn loginBtn--facebook"
id="facebookLogin"
onClick={renderProps.onClick}
>
Login with Facebook
</button>
)}
/>

<GoogleLogin
clientId="857285911538-6jchs8qlak94ek13roke22h2vncbvn5a.apps.googleusercontent.com"
render={renderProps => (
<button
className="loginBtn loginBtn--google"
id="googleLogin"
onClick={renderProps.onClick}
disabled={renderProps.disabled}
>
Login with Google
</button>
)}
onSuccess={this.googleResponse}
onFailure={this.onFailure}
disabled={this.state.isClicked}
/>
</div>
);
}
}

const mapStateToProps = state => ({
myState: state,
});

const mapDispatchToProps = () => ({
UserSocialLogin,
});

export default connect(
mapStateToProps,
mapDispatchToProps(),
)(withRouter(SocialLogin));
13 changes: 11 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link
rel="stylesheet"
Expand All @@ -13,7 +16,13 @@
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<title>Author's Haven</title>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css" />
<script type="text/javascript">
/* this is dummy */
</script>
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css"
/>
</head>

<body>
Expand Down
5 changes: 5 additions & 0 deletions src/redux/actions/SignUpAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,8 @@ export const verificationFailure = error => ({
type: `${SIGNUP_USER}_SEND_VERIFICATION_FAILURE`,
error,
});

export const socialAction = data => ({
type: `${SIGNUP_USER}_SOCIAL`,
data,
});
33 changes: 32 additions & 1 deletion src/redux/actions/loginActions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import axios from 'axios';
import { LOGIN_USER } from '../constants';
import { LOGIN_USER, SIGNUP_USER } from '../constants';
import { successToast, errorToast } from '../../helpers';
import { BASE_URL } from '../constants/index';
import { socialAction } from './SignUpAction';

/*
*Defines the loginUser actions and dispatches the right
Expand All @@ -24,6 +25,28 @@ export const loginUser = (data, history) => dispatch => {
});
};

/*
*Defines the actions for social login and dispatches the right
*action for either success >>loginSuccessSocial() or
*failure >>loginfailure() or
*/
export const UserSocialLogin = (data, history) => dispatch => {
dispatch({ type: LOGIN_USER });
dispatch({ type: SIGNUP_USER });
axios
.post(`${BASE_URL}/users/social/login/`, data)
.then(response => {
dispatch(loginSuccessSocial(response.data));
dispatch(socialAction(response.data));
successToast(`Welcome ${response.data.username}`);
history.push('/');
})
.catch(err => {
dispatch(loginFailure(err));
errorToast('Oops! something went wrong, please try again');
dispatch(socialAction(err));
});
};
/*
*Defines the logout actions and dispatches the
*logout user action
Expand All @@ -40,6 +63,14 @@ export const loginSuccess = response => ({
response,
});

/*
*Defines the action types for successful social login
*/
export const loginSuccessSocial = response => ({
type: `${LOGIN_USER}_SUCCESS_SOCIAL`,
response,
});

/*
*Defines the action types for unsuccessful login
*/
Expand Down
6 changes: 6 additions & 0 deletions src/redux/reducers/SignUpReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export default (state = initialState, action) => {
isLoading: false,
error: action.error.response.data,
};
case `${SIGNUP_USER}_SOCIAL`:
return {
...state,
isLoading: false,
};

default:
return { ...state };
}
Expand Down
Loading

0 comments on commit 7247c34

Please sign in to comment.