Skip to content
This repository has been archived by the owner on Jun 28, 2021. It is now read-only.

Commit

Permalink
User authentication and profile (#460)
Browse files Browse the repository at this point in the history
* wip

* user stuff

* Profile page!

* Unneeded calls

* PR feedback

* go stateless

* dup route

* oops decorators
  • Loading branch information
mmahalwy authored Nov 13, 2016
1 parent 4f450ff commit 12e87d6
Show file tree
Hide file tree
Showing 26 changed files with 507 additions and 61 deletions.
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
NODE_ENV=development
PORT=8000
API_URL=http://quran.com:3000
ONE_QURAN_URL=http://localhost:3030
SEGMENTS_KEY=
SENTRY_KEY_CLIENT=
SENTRY_KEY_SERVER=
# Quran.com - development app, no need to worry!
FACEBOOK_APP_ID=1599019233731707
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
Raven: true,
mixpanel: true,
"expect": true,
"browser": true
"browser": true,
"FB": true
}
}
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ ENV NODE_ENV production
ENV API_URL http://api.quran.com:3000
ENV SENTRY_KEY_CLIENT https://44c105328ae544ae9928f9eb74b40061@app.getsentry.com/80639
ENV SENTRY_KEY_SERVER https://44c105328ae544ae9928f9eb74b40061:41ca814d33124e04ab450104c3938cb1@app.getsentry.com/80639
# It's okay because it's only the APP ID
ENV FACEBOOK_APP_ID 1557596491207315
ENV ONE_QURAN_URL https://one.quran.com
ENV PORT 8000
ENV NODE_PATH "./src"

Expand Down
2 changes: 1 addition & 1 deletion src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ window.clearCookies = () => {
reactCookie.remove('isFirstTime');
};

match({ history, routes: routes() }, (error, redirectLocation, renderProps) => {
match({ history, routes: routes(store) }, (error, redirectLocation, renderProps) => {
const component = (
<Router
{...renderProps}
Expand Down
110 changes: 110 additions & 0 deletions src/components/FacebookButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import { facebook } from 'redux/actions/auth';

import config from 'config';

@connect(
null,
{ facebook, push }
)
export default class FacebookLogin extends Component {
static propTypes = {
callback: PropTypes.func.isRequired,
appId: PropTypes.string.isRequired,
xfbml: PropTypes.bool,
cookie: PropTypes.bool,
scope: PropTypes.string,
textButton: PropTypes.string,
autoLoad: PropTypes.bool,
size: PropTypes.string,
fields: PropTypes.string,
cssClass: PropTypes.string,
version: PropTypes.string,
icon: PropTypes.string,
push: PropTypes.func,
facebook: PropTypes.func
};

static defaultProps = {
callback: () => {},
appId: config.facebookAppId,
textButton: 'Connect with Facebook',
icon: 'fa-facebook',
scope: 'email,public_profile,user_location',
xfbml: true,
cookie: true,
autoLoad: false,
size: 'md',
fields: 'first_name,name,picture',
cssClass: 'btn btn-facebook btn-',
version: '2.7'
};

componentDidMount() {
window.fbAsyncInit = () => {
FB.init({
appId: this.props.appId,
xfbml: this.props.xfbml,
cookie: this.props.cookie,
version: `v${this.props.version}`,
});

if (this.props.autoLoad) {
FB.getLoginStatus(this.checkLoginState);
}
};

// Load the SDK asynchronously
(function(d, s, id) { // eslint-disable-line
const element = d.getElementsByTagName(s)[0];
const fjs = element;
let js = element;
if (d.getElementById(id)) { return; }
js = d.createElement(s); js.id = id;
js.src = '//connect.facebook.net/en_US/sdk.js';
fjs.parentNode.insertBefore(js, fjs);
}(window.document, 'script', 'facebook-jssdk')); // eslint-disable-line
}

responseApi = (authResponse) => {
const { callback, facebook, push } = this.props; // eslint-disable-line no-shadow

return FB.api('/me', { fields: this.props.fields }, (me) => {
me.accessToken = authResponse.accessToken; // eslint-disable-line
callback(me);

return facebook(authResponse.accessToken).then(action => !action.error && push('/'));
});
};

checkLoginState = (response) => {
if (response.authResponse) {
this.responseApi(response.authResponse);
} else {
if (this.props.callback) {
this.props.callback({ status: response.status });
}
}
};

handleClick = () => {
FB.login(this.checkLoginState, { scope: this.props.scope });
};

render() {
return (
<div>
<button
className={`${this.props.cssClass}${this.props.size}`}
onClick={this.handleClick}
>
{this.props.icon && <i className={`margin-md-right fa ${this.props.icon}`} />}
{this.props.textButton}
</button>
<div id="fb-root"></div>
</div>
);
}
}
51 changes: 20 additions & 31 deletions src/components/IndexHeader/Nav/index.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,25 @@
import React, { Component, PropTypes } from 'react';
import Link from 'react-router/lib/Link';
import { connect } from 'react-redux';

class IndexHeaderNav extends Component {
export class IndexHeaderNav extends Component {
static propTypes = {
navlink: PropTypes.bool
user: PropTypes.object
};

state = {
open: false
};

openNav(e) {
e.preventDefault();
openNav(event) {
event.preventDefault();

this.setState({open: !this.state.open});
}

links() {
let classNames = `links ${this.state.open ? 'open' : ''}`;

if (this.props.navlink === false) {
return (
<ul className={classNames}>
<li>
<Link to="/apps" data-metrics-event-name="IndexHeader:Link:Mobile">
Mobile
</Link>
</li>
<li>
<a href="https://quran.zendesk.com/hc/en-us/articles/210090626-Development-help" target="_blank" data-metrics-event-name="IndexHeader:Link:Developer">
Developers
</a>
</li>
<li>
<a href="http://legacy.quran.com" data-metrics-event-name="IndexHeader:Link:Legacy">Legacy Quran.com</a>
</li>
<li>
<a href="https://quran.zendesk.com/hc/en-us" data-metrics-event-name="IndexHeader:Link:Contact">
Contact us
</a>
</li>
</ul>
);
}
const { user } = this.props;
const classNames = `links ${this.state.open ? 'open' : ''}`;

return (
<ul className={classNames}>
Expand All @@ -69,6 +46,14 @@ class IndexHeaderNav extends Component {
Contact us
</a>
</li>
{
user &&
<li>
<Link to="/profile" data-metrics-event-name="IndexHeader:Link:Profile">
{user.firstName}
</Link>
</li>
}
</ul>
);
}
Expand All @@ -82,4 +67,8 @@ class IndexHeaderNav extends Component {
}
}

export default IndexHeaderNav;
export default connect(
state => ({
user: state.auth.user
})
)(IndexHeaderNav);
7 changes: 3 additions & 4 deletions src/components/IndexHeader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import React, { Component, PropTypes } from 'react';
import Link from 'react-router/lib/Link';

import SearchInput from '../SearchInput';
import IndexHeaderNav from './Nav';
import Nav from './Nav';

import debug from '../../helpers/debug';

const logo = require('../../../static/images/logo-lg-w.png');

export default class IndexHeader extends Component {
static propTypes = {
noSearch: PropTypes.bool,
navlink: PropTypes.any
noSearch: PropTypes.bool
};

renderSearch() {
Expand All @@ -29,7 +28,7 @@ export default class IndexHeader extends Component {

return (
<div className="index-header" style={{backgroundColor: '#2CA4AB'}}>
<IndexHeaderNav navlink={this.props.navlink} />
<Nav />
<div className="container">
<div className="row">
<div className="col-md-10 col-md-offset-1 text-center">
Expand Down
78 changes: 78 additions & 0 deletions src/components/QuranNav/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React, { PropTypes } from 'react';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import Navbar from 'react-bootstrap/lib/Navbar';
import Nav from 'react-bootstrap/lib/Nav';
import NavDropdown from 'react-bootstrap/lib/NavDropdown';
import MenuItem from 'react-bootstrap/lib/MenuItem';
import NavItem from 'react-bootstrap/lib/NavItem';
import Image from 'react-bootstrap/lib/Image';
const Header = Navbar.Header;
const Collapse = Navbar.Collapse;
const Toggle = Navbar.Toggle;
import { connect } from 'react-redux';

import userType from 'types/userType';

const styles = require('./style.scss');

export const QuranNav = ({ user }) => (
<Navbar inverse fluid className={styles.nav}>
<Header>
<Toggle />
</Header>
<Collapse>
<Nav />
<Nav pullRight>
<LinkContainer to="/apps" data-metrics-event-name="IndexHeader:Link:Mobile">
<NavItem>
Mobile
</NavItem>
</LinkContainer>
<NavItem href="https://quran.zendesk.com/hc/en-us/articles/210090626-Development-help" target="_blank" data-metrics-event-name="IndexHeader:Link:Developer">
Developers
</NavItem>
<NavItem href="http://legacy.quran.com" data-metrics-event-name="IndexHeader:Link:Legacy">
Legacy Quran.com
</NavItem>
<LinkContainer to="/donations" data-metrics-event-name="IndexHeader:Link:Contribute">
<NavItem>
Contribute
</NavItem>
</LinkContainer>
<NavItem href="https://quran.zendesk.com/hc/en-us" data-metrics-event-name="IndexHeader:Link:Contact">
Contact us
</NavItem>
{
user &&
<NavDropdown
title={
<span className={styles.name}>
<Image src={user.image} className={styles.image} circle />
{user.firstName}
</span>
}
id="user-dropdown"
>
<LinkContainer
to="/profile"
data-metrics-event-name="IndexHeader:Link:Profile"
>
<MenuItem eventKey={3.1}>Profile</MenuItem>
</LinkContainer>
<MenuItem eventKey={3.3}>Logout</MenuItem>
</NavDropdown>
}
</Nav>
</Collapse>
</Navbar>
);

QuranNav.propTypes = {
user: PropTypes.shape(userType)
};

export default connect(
state => ({
user: state.auth.user
})
)(QuranNav);
27 changes: 27 additions & 0 deletions src/components/QuranNav/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
$navbar-height: 40px;
.nav {
min-height: $navbar-height;
border-radius: 0px;
margin-bottom: 0px;

:global(.navbar-nav > li > a) {
padding-top: ($navbar-height - 20) / 2;
padding-bottom: ($navbar-height - 20) / 2;
}

&.transparent{
background: transparent;
}
}

.name{
padding-left: 25px;
}

.image{
width: 30px;
position: absolute;
left: 5px;
top: 50%;
transform: translateY(-50%);
}
2 changes: 2 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ module.exports = Object.assign({
host: process.env.HOST || 'localhost',
port: process.env.PORT,
api: process.env.API_URL,
oneQuran: process.env.ONE_QURAN_URL,
sentryClient: process.env.SENTRY_KEY_CLIENT,
sentryServer: process.env.SENTRY_KEY_SERVER,
facebookAppId: process.env.FACEBOOK_APP_ID,
app: {
head: {
titleTemplate: `%s - ${title}`,
Expand Down
9 changes: 9 additions & 0 deletions src/containers/Login/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

import FacebookButton from 'components/FacebookButton';

export default () => (
<div>
<FacebookButton />
</div>
);
Loading

0 comments on commit 12e87d6

Please sign in to comment.