From 6289d6d221f95ec9bbab4125b5cd4b31a43a466d Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 24 Jan 2017 13:44:42 -0500 Subject: [PATCH 01/17] Add google oauth using react component --- package.json | 1 + src/containers/BuildingList/index.js | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e7a62217..e6db53db 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "bpl": "git+https://7f8bbb4b0a383ad905fee5b0f7cbc0c22533b556:x-oauth-basic@github.com/Blocp/bpl.git", "react": "^15.3.2", "react-dom": "^15.3.2", + "react-google-login": "^2.7.1", "react-redux": "^4.4.5", "react-router": "^3.0.0", "react-router-redux": "^4.0.7", diff --git a/src/containers/BuildingList/index.js b/src/containers/BuildingList/index.js index 4b704072..9f9076bb 100644 --- a/src/containers/BuildingList/index.js +++ b/src/containers/BuildingList/index.js @@ -1,13 +1,14 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; +import GoogleLogin from 'react-google-login'; import { fetchBuildings, searchTerm } from './actions'; import BuildingListTable from '../../components/BuildingListTable'; import './styles.scss'; import { SearchSVG } from '../../components/bpl'; - +/* eslint-disable */ class BuildingList extends Component { constructor(props) { super(props); @@ -35,6 +36,10 @@ class BuildingList extends Component { this.props.fetchBuildings(this.state.term); } + responseGoogle(response) { + console.log(response); + } + render() { return (
@@ -94,6 +99,15 @@ class BuildingList extends Component {
+ + +
 
-- GitLab From a0e14590ec999a1fb681bb60a7a156a279c1a4c7 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Tue, 24 Jan 2017 22:05:58 -0500 Subject: [PATCH 02/17] Implement own google login from package --- package.json | 1 - src/components/GoogleLogin/index.js | 156 +++++++++++++++++++++++++++ src/containers/BuildingList/index.js | 16 +-- 3 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 src/components/GoogleLogin/index.js diff --git a/package.json b/package.json index e6db53db..e7a62217 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "bpl": "git+https://7f8bbb4b0a383ad905fee5b0f7cbc0c22533b556:x-oauth-basic@github.com/Blocp/bpl.git", "react": "^15.3.2", "react-dom": "^15.3.2", - "react-google-login": "^2.7.1", "react-redux": "^4.4.5", "react-router": "^3.0.0", "react-router-redux": "^4.0.7", diff --git a/src/components/GoogleLogin/index.js b/src/components/GoogleLogin/index.js new file mode 100644 index 00000000..61071ea2 --- /dev/null +++ b/src/components/GoogleLogin/index.js @@ -0,0 +1,156 @@ +import React, { PropTypes, Component } from 'react'; + +export default class GoogleLogin extends Component { + static onSuccess(response) { + localStorage.setItem('g_token_id', response.tokenId); + } + + static onFailure(response) { + console.log(response); + } + + static onRequest() { + console.log('loading...'); + } + + constructor(props) { + super(props); + this.signIn = this.signIn.bind(this); + + this.state = { + disabled: true, + loggedIn: false, + }; + } + + componentDidMount() { + const { scope, cookiePolicy, loginHint, hostedDomain, autoLoad } = this.props; + + // Include Google Platform Library + // https://developers.google.com/identity/sign-in/web/reference + const fjs = document.getElementsByTagName('script')[0]; + const js = document.createElement('script'); + js.id = 'google-login'; + js.src = '//apis.google.com/js/client:platform.js'; + fjs.parentNode.insertBefore(js, fjs); + + js.onload = () => { + const params = { + // TODO remove hardcoded client ID + client_id: '706519804793-420p5e2jk9fv6p46trd1ks2d46kg1na3.apps.googleusercontent.com', + cookiepolicy: cookiePolicy, + login_hint: loginHint, + hosted_domain: hostedDomain, + scope, + }; + + window.gapi.load('auth2', () => { + this.setState({ + disabled: false, + }); + if (!window.gapi.auth2.getAuthInstance()) { + window.gapi.auth2.init(params); + } + if (autoLoad) { + this.signIn(); + } + }); + }; + } + + signIn() { + if (!this.state.disabled) { + const auth2 = window.gapi.auth2.getAuthInstance(); + const { offline, redirectUri, approvalPrompt, prompt } = this.props; + const options = { + redirect_uri: redirectUri, + approval_prompt: approvalPrompt, + prompt, + }; + GoogleLogin.onRequest(); + if (offline) { + auth2.grantOfflineAccess(options) + .then( + res => GoogleLogin.onSuccess(res), + err => GoogleLogin.onFailure(err) + ); + } else { + /* eslint-disable no-param-reassign */ + auth2.signIn(options) + .then((res) => { + /* + offer renamed response keys to names that match use + */ + const basicProfile = res.getBasicProfile(); + const authResponse = res.getAuthResponse(); + res.googleId = basicProfile.getId(); + res.tokenObj = authResponse; + res.tokenId = authResponse.id_token; + res.accessToken = authResponse.access_token; + res.profileObj = { + googleId: basicProfile.getId(), + imageUrl: basicProfile.getImageUrl(), + email: basicProfile.getEmail(), + name: basicProfile.getName(), + givenName: basicProfile.getGivenName(), + familyName: basicProfile.getFamilyName(), + }; + GoogleLogin.onSuccess(res); + }, err => + this.onFailure(err) + ); + /* eslint-enable */ + } + } + } + + render() { + const initialStyle = { + color: 'white', + height: '40px', + marginTop: '15px', + }; + + const disabledStyle = { + opacity: 0.6, + }; + + const defaultStyle = (() => { + if (this.state.disabled) { + return { ...initialStyle, disabledStyle }; + } + return initialStyle; + })(); + + return ( + + ); + } +} + +GoogleLogin.propTypes = { + offline: PropTypes.bool, + scope: PropTypes.string, + redirectUri: PropTypes.string, + cookiePolicy: PropTypes.string, + loginHint: PropTypes.string, + hostedDomain: PropTypes.string, + approvalPrompt: PropTypes.string, + prompt: PropTypes.string, + autoLoad: React.PropTypes.bool, +}; + +GoogleLogin.defaultProps = { + scope: 'profile email', + redirectUri: 'postmessage', + prompt: '', + cookiePolicy: 'single_host_origin', + offline: false, +}; diff --git a/src/containers/BuildingList/index.js b/src/containers/BuildingList/index.js index 9f9076bb..3b449657 100644 --- a/src/containers/BuildingList/index.js +++ b/src/containers/BuildingList/index.js @@ -1,14 +1,14 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import GoogleLogin from 'react-google-login'; +import GoogleLogin from '../../components/GoogleLogin'; import { fetchBuildings, searchTerm } from './actions'; import BuildingListTable from '../../components/BuildingListTable'; import './styles.scss'; import { SearchSVG } from '../../components/bpl'; -/* eslint-disable */ + class BuildingList extends Component { constructor(props) { super(props); @@ -36,10 +36,6 @@ class BuildingList extends Component { this.props.fetchBuildings(this.state.term); } - responseGoogle(response) { - console.log(response); - } - render() { return (
@@ -100,13 +96,7 @@ class BuildingList extends Component {
- +
  -- GitLab From d1a327ba749d8d60c74921be63a03e3b39125c72 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 25 Jan 2017 00:27:09 -0500 Subject: [PATCH 03/17] Add sign out and refresh token functions --- .eslintrc.json | 1 + src/containers/BuildingList/index.js | 3 +- .../GoogleLogin/index.js | 59 +++++++++++++------ 3 files changed, 44 insertions(+), 19 deletions(-) rename src/{components => containers}/GoogleLogin/index.js (77%) diff --git a/.eslintrc.json b/.eslintrc.json index 8cb839b7..17a20919 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,5 +1,6 @@ { "extends": "airbnb", + "parser": "babel-eslint", "plugins": [ "react", "jsx-a11y", diff --git a/src/containers/BuildingList/index.js b/src/containers/BuildingList/index.js index 3b449657..6eea0ef8 100644 --- a/src/containers/BuildingList/index.js +++ b/src/containers/BuildingList/index.js @@ -1,7 +1,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import GoogleLogin from '../../components/GoogleLogin'; +import GoogleLogin from '../../containers/GoogleLogin'; import { fetchBuildings, searchTerm } from './actions'; import BuildingListTable from '../../components/BuildingListTable'; @@ -100,6 +100,7 @@ class BuildingList extends Component {
  + {localStorage.imageUrl && }
diff --git a/src/components/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js similarity index 77% rename from src/components/GoogleLogin/index.js rename to src/containers/GoogleLogin/index.js index 61071ea2..92e2593b 100644 --- a/src/components/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -1,10 +1,6 @@ import React, { PropTypes, Component } from 'react'; export default class GoogleLogin extends Component { - static onSuccess(response) { - localStorage.setItem('g_token_id', response.tokenId); - } - static onFailure(response) { console.log(response); } @@ -15,7 +11,6 @@ export default class GoogleLogin extends Component { constructor(props) { super(props); - this.signIn = this.signIn.bind(this); this.state = { disabled: true, @@ -23,6 +18,12 @@ export default class GoogleLogin extends Component { }; } + componentWillMount() { + if (localStorage.g_token_id) { + this.setState({ loggedIn: true }); + } + } + componentDidMount() { const { scope, cookiePolicy, loginHint, hostedDomain, autoLoad } = this.props; @@ -58,7 +59,21 @@ export default class GoogleLogin extends Component { }; } - signIn() { + onSuccess = (response) => { + localStorage.setItem('g_token_id', response.tokenId); + localStorage.setItem('givenName', response.profileObj.givenName); + localStorage.setItem('imageUrl', response.profileObj.imageUrl); + this.setState({ loggedIn: true }); + }; + + refreshToken = () => { + window.gapi.auth2.getAuthInstance().currentUser.get().reloadAuthResponse().then((res) => { + console.log(res); + this.onSuccess(); + }); + }; + + signIn = () => { if (!this.state.disabled) { const auth2 = window.gapi.auth2.getAuthInstance(); const { offline, redirectUri, approvalPrompt, prompt } = this.props; @@ -71,7 +86,7 @@ export default class GoogleLogin extends Component { if (offline) { auth2.grantOfflineAccess(options) .then( - res => GoogleLogin.onSuccess(res), + res => this.onSuccess(res), err => GoogleLogin.onFailure(err) ); } else { @@ -95,14 +110,20 @@ export default class GoogleLogin extends Component { givenName: basicProfile.getGivenName(), familyName: basicProfile.getFamilyName(), }; - GoogleLogin.onSuccess(res); + this.onSuccess(res); }, err => - this.onFailure(err) + GoogleLogin.onFailure(err) ); /* eslint-enable */ } } - } + }; + + signOut = () => { + window.gapi.auth2.getAuthInstance().signOut(); + localStorage.clear(); + this.setState({ loggedIn: false }); + }; render() { const initialStyle = { @@ -123,14 +144,16 @@ export default class GoogleLogin extends Component { })(); return ( - +
+ +
); } } -- GitLab From f328198a37d086086072a2856826a46fbc8d7c76 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 25 Jan 2017 13:19:30 -0500 Subject: [PATCH 04/17] Auto refresh token when the token has expired --- src/containers/GoogleLogin/index.js | 38 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index 92e2593b..15a1204c 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -1,11 +1,13 @@ import React, { PropTypes, Component } from 'react'; export default class GoogleLogin extends Component { - static onFailure(response) { - console.log(response); + static onFailure(err) { + // TODO handle error + console.log(err); } static onRequest() { + // TODO add spinner console.log('loading...'); } @@ -19,13 +21,13 @@ export default class GoogleLogin extends Component { } componentWillMount() { - if (localStorage.g_token_id) { + if (localStorage.g_access_token && new Date() < localStorage.expiration_date) { this.setState({ loggedIn: true }); } } componentDidMount() { - const { scope, cookiePolicy, loginHint, hostedDomain, autoLoad } = this.props; + const { scope, cookiePolicy, loginHint, hostedDomain } = this.props; // Include Google Platform Library // https://developers.google.com/identity/sign-in/web/reference @@ -49,28 +51,33 @@ export default class GoogleLogin extends Component { this.setState({ disabled: false, }); + if (!window.gapi.auth2.getAuthInstance()) { window.gapi.auth2.init(params); } - if (autoLoad) { - this.signIn(); + + if (localStorage.g_access_token && new Date() > localStorage.expiration_date) { + this.refreshToken(); } }); }; } - onSuccess = (response) => { - localStorage.setItem('g_token_id', response.tokenId); - localStorage.setItem('givenName', response.profileObj.givenName); - localStorage.setItem('imageUrl', response.profileObj.imageUrl); + onSuccess = (accessToken, expirationDate, response) => { + localStorage.setItem('g_access_token', accessToken); + localStorage.setItem('expiration_date', expirationDate); + if (response) { + localStorage.setItem('givenName', response.profileObj.givenName); + localStorage.setItem('imageUrl', response.profileObj.imageUrl); + } this.setState({ loggedIn: true }); }; refreshToken = () => { - window.gapi.auth2.getAuthInstance().currentUser.get().reloadAuthResponse().then((res) => { - console.log(res); - this.onSuccess(); - }); + window.gapi.auth2.getAuthInstance().currentUser.get() + .reloadAuthResponse().then((res) => { + this.onSuccess(res.access_tokenm, res.expires_at); + }); }; signIn = () => { @@ -110,7 +117,7 @@ export default class GoogleLogin extends Component { givenName: basicProfile.getGivenName(), familyName: basicProfile.getFamilyName(), }; - this.onSuccess(res); + this.onSuccess(authResponse.access_token, authResponse.expires_at, res); }, err => GoogleLogin.onFailure(err) ); @@ -167,7 +174,6 @@ GoogleLogin.propTypes = { hostedDomain: PropTypes.string, approvalPrompt: PropTypes.string, prompt: PropTypes.string, - autoLoad: React.PropTypes.bool, }; GoogleLogin.defaultProps = { -- GitLab From be2f3e21fdd9e87dcd74b55546085867306cb18d Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 25 Jan 2017 13:35:39 -0500 Subject: [PATCH 05/17] Remove hardcoded url --- src/containers/GoogleLogin/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index 15a1204c..7b67607d 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -39,8 +39,7 @@ export default class GoogleLogin extends Component { js.onload = () => { const params = { - // TODO remove hardcoded client ID - client_id: '706519804793-420p5e2jk9fv6p46trd1ks2d46kg1na3.apps.googleusercontent.com', + client_id: process.env.REACT_APP_GOOGLE_CLIENT, cookiepolicy: cookiePolicy, login_hint: loginHint, hosted_domain: hostedDomain, -- GitLab From 12810e233e9c4502752576560415ff9c261b17ee Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 25 Jan 2017 14:18:14 -0500 Subject: [PATCH 06/17] Validate token when user returns to site --- src/containers/GoogleLogin/index.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index 7b67607d..2780ba4a 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -1,4 +1,5 @@ import React, { PropTypes, Component } from 'react'; +import request from '../../utils/request'; export default class GoogleLogin extends Component { static onFailure(err) { @@ -22,7 +23,16 @@ export default class GoogleLogin extends Component { componentWillMount() { if (localStorage.g_access_token && new Date() < localStorage.expiration_date) { - this.setState({ loggedIn: true }); + const gUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo'; + const validate = request(`${gUrl}?access_token=${localStorage.g_access_token}`, { method: 'POST' }); + validate.then((res) => { + if (res.err) { + // TODO handle invalid token + console.log('Invalid token'); + } else { + this.setState({ loggedIn: true }); + } + }); } } -- GitLab From f90b9c825486c064c695e05031a0a9491e92a9bc Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 25 Jan 2017 16:41:48 -0500 Subject: [PATCH 07/17] Create functions for checkToken and loggedIn --- src/containers/GoogleLogin/index.js | 76 +++++++++++++++++------------ 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index 2780ba4a..cc876bc0 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -12,6 +12,28 @@ export default class GoogleLogin extends Component { console.log('loading...'); } + static checkToken() { + return localStorage.g_access_token && new Date() < localStorage.expiration_date; + } + + static loggedIn() { + return new Promise((resolve, reject) => { + if (GoogleLogin.checkToken()) { + const gUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo'; + request(`${gUrl}?access_token=${localStorage.g_access_token}`, { method: 'POST' }) + .then((res) => { + if (res.err) { + localStorage.clear(); + reject(Error('Invalid token')); + } + resolve(true); + }); + } else { + resolve(false); + } + }); + } + constructor(props) { super(props); @@ -22,18 +44,14 @@ export default class GoogleLogin extends Component { } componentWillMount() { - if (localStorage.g_access_token && new Date() < localStorage.expiration_date) { - const gUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo'; - const validate = request(`${gUrl}?access_token=${localStorage.g_access_token}`, { method: 'POST' }); - validate.then((res) => { - if (res.err) { - // TODO handle invalid token - console.log('Invalid token'); - } else { - this.setState({ loggedIn: true }); - } - }); - } + GoogleLogin.loggedIn().then((res) => { + if (res) { + this.setState({ loggedIn: true }); + } + }).catch((err) => { + // Invalid Token Error + console.log(err); + }); } componentDidMount() { @@ -65,19 +83,20 @@ export default class GoogleLogin extends Component { window.gapi.auth2.init(params); } - if (localStorage.g_access_token && new Date() > localStorage.expiration_date) { + if (localStorage.g_access_token + && new Date() > localStorage.expiration_date) { this.refreshToken(); } }); }; } - onSuccess = (accessToken, expirationDate, response) => { + onSuccess = (accessToken, expirationDate, user) => { localStorage.setItem('g_access_token', accessToken); localStorage.setItem('expiration_date', expirationDate); - if (response) { - localStorage.setItem('givenName', response.profileObj.givenName); - localStorage.setItem('imageUrl', response.profileObj.imageUrl); + if (user) { + localStorage.setItem('givenName', user.givenName); + localStorage.setItem('imageUrl', user.imageUrl); } this.setState({ loggedIn: true }); }; @@ -106,19 +125,16 @@ export default class GoogleLogin extends Component { err => GoogleLogin.onFailure(err) ); } else { - /* eslint-disable no-param-reassign */ auth2.signIn(options) .then((res) => { - /* - offer renamed response keys to names that match use - */ const basicProfile = res.getBasicProfile(); const authResponse = res.getAuthResponse(); - res.googleId = basicProfile.getId(); - res.tokenObj = authResponse; - res.tokenId = authResponse.id_token; - res.accessToken = authResponse.access_token; - res.profileObj = { + + const accessToken = authResponse.access_token; + const expirationDate = authResponse.expires_at; + + const user = { + tokenId: authResponse.id_token, googleId: basicProfile.getId(), imageUrl: basicProfile.getImageUrl(), email: basicProfile.getEmail(), @@ -126,11 +142,11 @@ export default class GoogleLogin extends Component { givenName: basicProfile.getGivenName(), familyName: basicProfile.getFamilyName(), }; - this.onSuccess(authResponse.access_token, authResponse.expires_at, res); + + this.onSuccess(accessToken, expirationDate, user); }, err => GoogleLogin.onFailure(err) ); - /* eslint-enable */ } } }; @@ -186,9 +202,9 @@ GoogleLogin.propTypes = { }; GoogleLogin.defaultProps = { + offline: false, scope: 'profile email', redirectUri: 'postmessage', - prompt: '', cookiePolicy: 'single_host_origin', - offline: false, + prompt: '', }; -- GitLab From 9840b6c562f04108802a27b2792729339ddc641e Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 25 Jan 2017 17:47:04 -0500 Subject: [PATCH 08/17] Separate NavBar and rename SearchBar --- src/components/NavBar/index.js | 54 +++++++ src/containers/BuildingList/index.js | 139 ------------------ src/containers/GoogleLogin/index.js | 18 +-- .../{BuildingList => SearchBar}/actions.js | 0 .../{BuildingList => SearchBar}/constants.js | 0 src/containers/SearchBar/index.js | 84 +++++++++++ .../{BuildingList => SearchBar}/reducer.js | 0 .../{BuildingList => SearchBar}/styles.scss | 0 src/reducers.js | 4 +- src/routes.js | 28 +++- src/screens/BuildingDetail/index.js | 5 +- src/screens/HomePage/index.js | 37 +++++ 12 files changed, 211 insertions(+), 158 deletions(-) create mode 100644 src/components/NavBar/index.js delete mode 100644 src/containers/BuildingList/index.js rename src/containers/{BuildingList => SearchBar}/actions.js (100%) rename src/containers/{BuildingList => SearchBar}/constants.js (100%) create mode 100644 src/containers/SearchBar/index.js rename src/containers/{BuildingList => SearchBar}/reducer.js (100%) rename src/containers/{BuildingList => SearchBar}/styles.scss (100%) create mode 100644 src/screens/HomePage/index.js diff --git a/src/components/NavBar/index.js b/src/components/NavBar/index.js new file mode 100644 index 00000000..6dcd2401 --- /dev/null +++ b/src/components/NavBar/index.js @@ -0,0 +1,54 @@ +import React, { PropTypes } from 'react'; +import { Link } from 'react-router'; + +import GoogleLogin from '../../containers/GoogleLogin'; + +export default function NavBar({ SearchBar }) { + return ( +
+
+
+ + + + + + + + + + +
+ + {SearchBar ? :
} + + + +
+   + {localStorage.imageUrl && } +
+
+
+ ); +} + +NavBar.propTypes = { + SearchBar: PropTypes.func, +}; diff --git a/src/containers/BuildingList/index.js b/src/containers/BuildingList/index.js deleted file mode 100644 index 6eea0ef8..00000000 --- a/src/containers/BuildingList/index.js +++ /dev/null @@ -1,139 +0,0 @@ -import React, { Component, PropTypes } from 'react'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import GoogleLogin from '../../containers/GoogleLogin'; - -import { fetchBuildings, searchTerm } from './actions'; -import BuildingListTable from '../../components/BuildingListTable'; -import './styles.scss'; -import { SearchSVG } from '../../components/bpl'; - - -class BuildingList extends Component { - constructor(props) { - super(props); - - this.state = { term: this.props.buildingList.term }; - this.onInputChange = this.onInputChange.bind(this); - this.onFormSubmit = this.onFormSubmit.bind(this); - } - - componentDidMount() { - this.getBuildings(); - } - - onInputChange(event) { - this.setState({ term: event.target.value }); - } - - onFormSubmit(event) { - event.preventDefault(); - this.getBuildings(); - } - - getBuildings() { - this.props.searchTerm(this.state.term); - this.props.fetchBuildings(this.state.term); - } - - render() { - return ( -
-
-
-
- - - - - - - - -
- -
- -
- - Search -
-
-
Address
- -
-
-
- -
- - - -
-   - {localStorage.imageUrl && } -
-
-
- -
- ); - } -} - -BuildingList.propTypes = { - buildingList: PropTypes.shape({ - buildings: PropTypes.arrayOf( - PropTypes.shape({ - address: PropTypes.string, - bbl: PropTypes.number, - building_id: PropTypes.number, - lot_id: PropTypes.number, - borough: PropTypes.string, - zipcode: PropTypes.number, - }) - ), - term: PropTypes.string, - }), - fetchBuildings: PropTypes.func, - searchTerm: PropTypes.func, -}; - -function mapDispatchToProps(dispatch) { - return bindActionCreators({ fetchBuildings, searchTerm }, dispatch); -} - -function mapStateToProps({ buildingList }) { - return { buildingList }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(BuildingList); diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index cc876bc0..d36cb6be 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -176,16 +176,14 @@ export default class GoogleLogin extends Component { })(); return ( -
- -
+ ); } } diff --git a/src/containers/BuildingList/actions.js b/src/containers/SearchBar/actions.js similarity index 100% rename from src/containers/BuildingList/actions.js rename to src/containers/SearchBar/actions.js diff --git a/src/containers/BuildingList/constants.js b/src/containers/SearchBar/constants.js similarity index 100% rename from src/containers/BuildingList/constants.js rename to src/containers/SearchBar/constants.js diff --git a/src/containers/SearchBar/index.js b/src/containers/SearchBar/index.js new file mode 100644 index 00000000..5000ed5d --- /dev/null +++ b/src/containers/SearchBar/index.js @@ -0,0 +1,84 @@ +import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import { fetchBuildings, searchTerm } from './actions'; +import './styles.scss'; +import { SearchSVG } from '../../components/bpl'; + + +class BuildingList extends Component { + constructor(props) { + super(props); + + this.state = { term: this.props.buildingList.term }; + this.onInputChange = this.onInputChange.bind(this); + this.onFormSubmit = this.onFormSubmit.bind(this); + } + + componentDidMount() { + this.getBuildings(); + } + + onInputChange(event) { + this.setState({ term: event.target.value }); + } + + onFormSubmit(event) { + event.preventDefault(); + this.getBuildings(); + } + + getBuildings() { + this.props.searchTerm(this.state.term); + this.props.fetchBuildings(this.state.term); + } + + render() { + return ( +
+
+ + Search +
+
+
Address
+ +
+
+
+
+ ); + } +} + +BuildingList.propTypes = { + buildingList: PropTypes.shape({ + term: PropTypes.string, + }), + fetchBuildings: PropTypes.func, + searchTerm: PropTypes.func, +}; + +function mapDispatchToProps(dispatch) { + return bindActionCreators({ fetchBuildings, searchTerm }, dispatch); +} + +function mapStateToProps({ buildingList }) { + return { buildingList }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(BuildingList); diff --git a/src/containers/BuildingList/reducer.js b/src/containers/SearchBar/reducer.js similarity index 100% rename from src/containers/BuildingList/reducer.js rename to src/containers/SearchBar/reducer.js diff --git a/src/containers/BuildingList/styles.scss b/src/containers/SearchBar/styles.scss similarity index 100% rename from src/containers/BuildingList/styles.scss rename to src/containers/SearchBar/styles.scss diff --git a/src/reducers.js b/src/reducers.js index 82192a2c..6a4ea606 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -1,11 +1,11 @@ import { combineReducers } from 'redux'; import { routerReducer } from 'react-router-redux'; -import BuildingListReducer from './containers/BuildingList/reducer'; +import SearchBarReducer from './containers/SearchBar/reducer'; import BuildingOverviewReducer from './containers/BuildingOverview/reducer'; export default combineReducers({ routing: routerReducer, - buildingList: BuildingListReducer, + buildingList: SearchBarReducer, buildingDetail: BuildingOverviewReducer, }); diff --git a/src/routes.js b/src/routes.js index 7e471c18..5fc818b3 100644 --- a/src/routes.js +++ b/src/routes.js @@ -1,18 +1,34 @@ import React from 'react'; import { Route, IndexRoute, IndexRedirect } from 'react-router'; +import HomePage from './screens/HomePage'; import BuildingDetail from './screens/BuildingDetail'; -import BuildingList from './containers/BuildingList'; -import BuildingOverview from './containers/BuildingOverview'; import NotFound from './screens/NotFound'; + +// import GoogleLogin from './containers/GoogleLogin'; +import BuildingOverview from './containers/BuildingOverview'; + import Dummy from './components/dummyComponent'; +// function requireAuth(nextState, replace) { +// // TODO Use GoogleLogin.loggedIn to verify token +// if (!GoogleLogin.checkToken()) { +// replace({ +// pathname: '/login', +// state: { nextPathname: nextState.location.pathname }, +// }); +// } +// } + +// onEnter={requireAuth} + export default ( - - - - + + + + + diff --git a/src/screens/BuildingDetail/index.js b/src/screens/BuildingDetail/index.js index 8c3f4d94..620e993e 100644 --- a/src/screens/BuildingDetail/index.js +++ b/src/screens/BuildingDetail/index.js @@ -1,10 +1,13 @@ import React, { PropTypes } from 'react'; import SideBarDetail from '../../components/SideBarDetail'; +import NavBar from '../../components/NavBar'; + export default function BuildingDetail(props) { return ( -
+
+
diff --git a/src/screens/HomePage/index.js b/src/screens/HomePage/index.js new file mode 100644 index 00000000..31aa3ccc --- /dev/null +++ b/src/screens/HomePage/index.js @@ -0,0 +1,37 @@ +import React, { PropTypes } from 'react'; +import { connect } from 'react-redux'; + +import NavBar from '../../components/NavBar'; +import BuildingList from '../../containers/SearchBar'; +import BuildingListTable from '../../components/BuildingListTable'; + +function HomePage({ buildingList }) { + return ( +
+ + +
+ ); +} + +HomePage.propTypes = { + buildingList: PropTypes.shape({ + buildings: PropTypes.arrayOf( + PropTypes.shape({ + address: PropTypes.string, + bbl: PropTypes.number, + building_id: PropTypes.number, + lot_id: PropTypes.number, + borough: PropTypes.string, + zipcode: PropTypes.number, + }) + ), + term: PropTypes.string, + }), +}; + +function mapStateToProps({ buildingList }) { + return { buildingList }; +} + +export default connect(mapStateToProps)(HomePage); -- GitLab From a1d5444fb9243758ca4a5f6532a10c286bb42513 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 25 Jan 2017 17:53:01 -0500 Subject: [PATCH 09/17] Remove links not being used --- src/components/SideBarDetail/index.js | 16 +++++++--------- src/routes.js | 3 ++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/components/SideBarDetail/index.js b/src/components/SideBarDetail/index.js index 6530ad2c..c801501d 100644 --- a/src/components/SideBarDetail/index.js +++ b/src/components/SideBarDetail/index.js @@ -6,19 +6,17 @@ export default function SideBarDetail({ buildingId }) { const rootURL = `/buildings/${buildingId}`; return (
- Home -
- Overview + Overview
Dimensions
- Weather -
Utilities -
- Occupancy -
- Financials + {/*
*/} + {/* Weather*/} + {/*
*/} + {/* Occupancy*/} + {/*
*/} + {/* Financials*/}
); } diff --git a/src/routes.js b/src/routes.js index 5fc818b3..0a0af57f 100644 --- a/src/routes.js +++ b/src/routes.js @@ -29,7 +29,8 @@ export default ( - + + -- GitLab From 7e365f0f4a51544444a28c326b71d5efb9ccd850 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Wed, 25 Jan 2017 18:00:30 -0500 Subject: [PATCH 10/17] Add login route --- src/routes.js | 27 +++++++++++++-------------- src/screens/Login/index.js | 11 +++++++++++ 2 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 src/screens/Login/index.js diff --git a/src/routes.js b/src/routes.js index 0a0af57f..853717fd 100644 --- a/src/routes.js +++ b/src/routes.js @@ -3,30 +3,29 @@ import { Route, IndexRoute, IndexRedirect } from 'react-router'; import HomePage from './screens/HomePage'; import BuildingDetail from './screens/BuildingDetail'; +import Login from './screens/Login'; import NotFound from './screens/NotFound'; -// import GoogleLogin from './containers/GoogleLogin'; +import GoogleLogin from './containers/GoogleLogin'; import BuildingOverview from './containers/BuildingOverview'; import Dummy from './components/dummyComponent'; -// function requireAuth(nextState, replace) { -// // TODO Use GoogleLogin.loggedIn to verify token -// if (!GoogleLogin.checkToken()) { -// replace({ -// pathname: '/login', -// state: { nextPathname: nextState.location.pathname }, -// }); -// } -// } - -// onEnter={requireAuth} +function requireAuth(nextState, replace) { + // TODO Use GoogleLogin.loggedIn to verify token + if (!GoogleLogin.checkToken()) { + replace({ + pathname: '/login', + state: { nextPathname: nextState.location.pathname }, + }); + } +} export default ( - - + + diff --git a/src/screens/Login/index.js b/src/screens/Login/index.js new file mode 100644 index 00000000..3cd55418 --- /dev/null +++ b/src/screens/Login/index.js @@ -0,0 +1,11 @@ +import React from 'react'; +import NavBar from '../../components/NavBar'; + +export default function Login() { + return ( +
+ + You must be logged in to view this page. +
+ ); +} -- GitLab From a04b599fc931199b251eca815b2e2846a3b3b4b0 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 26 Jan 2017 11:30:00 -0500 Subject: [PATCH 11/17] Redirect user to homepage once logged in --- src/containers/GoogleLogin/index.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index d36cb6be..c793b3e8 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -1,7 +1,8 @@ import React, { PropTypes, Component } from 'react'; +import { withRouter, routerShape } from 'react-router'; import request from '../../utils/request'; -export default class GoogleLogin extends Component { +class GoogleLogin extends Component { static onFailure(err) { // TODO handle error console.log(err); @@ -49,7 +50,7 @@ export default class GoogleLogin extends Component { this.setState({ loggedIn: true }); } }).catch((err) => { - // Invalid Token Error + // TODO handle invalid token error console.log(err); }); } @@ -91,7 +92,7 @@ export default class GoogleLogin extends Component { }; } - onSuccess = (accessToken, expirationDate, user) => { + onSuccess = (accessToken, expirationDate, user, goBack) => { localStorage.setItem('g_access_token', accessToken); localStorage.setItem('expiration_date', expirationDate); if (user) { @@ -99,6 +100,11 @@ export default class GoogleLogin extends Component { localStorage.setItem('imageUrl', user.imageUrl); } this.setState({ loggedIn: true }); + + if (goBack) { + // TODO go back to previous page + this.props.router.push('/'); + } }; refreshToken = () => { @@ -143,7 +149,7 @@ export default class GoogleLogin extends Component { familyName: basicProfile.getFamilyName(), }; - this.onSuccess(accessToken, expirationDate, user); + this.onSuccess(accessToken, expirationDate, user, true); }, err => GoogleLogin.onFailure(err) ); @@ -155,6 +161,7 @@ export default class GoogleLogin extends Component { window.gapi.auth2.getAuthInstance().signOut(); localStorage.clear(); this.setState({ loggedIn: false }); + this.props.router.push('/login'); }; render() { @@ -197,6 +204,7 @@ GoogleLogin.propTypes = { hostedDomain: PropTypes.string, approvalPrompt: PropTypes.string, prompt: PropTypes.string, + router: routerShape, }; GoogleLogin.defaultProps = { @@ -206,3 +214,5 @@ GoogleLogin.defaultProps = { cookiePolicy: 'single_host_origin', prompt: '', }; + +export default withRouter(GoogleLogin); -- GitLab From 6c53f5468a781fdf2589f70303d1da86870124e3 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 26 Jan 2017 13:57:16 -0500 Subject: [PATCH 12/17] Create actions and reducer for googleLogin --- src/components/NavBar/index.js | 1 - src/containers/GoogleLogin/actions.js | 15 ++++++++ src/containers/GoogleLogin/constants.js | 2 + src/containers/GoogleLogin/index.js | 51 ++++++++++++++++++++----- src/containers/GoogleLogin/reducer.js | 20 ++++++++++ src/reducers.js | 2 + src/screens/Login/index.js | 2 +- 7 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 src/containers/GoogleLogin/actions.js create mode 100644 src/containers/GoogleLogin/constants.js create mode 100644 src/containers/GoogleLogin/reducer.js diff --git a/src/components/NavBar/index.js b/src/components/NavBar/index.js index 6dcd2401..874bc241 100644 --- a/src/components/NavBar/index.js +++ b/src/components/NavBar/index.js @@ -41,7 +41,6 @@ export default function NavBar({ SearchBar }) {
-   {localStorage.imageUrl && }
diff --git a/src/containers/GoogleLogin/actions.js b/src/containers/GoogleLogin/actions.js new file mode 100644 index 00000000..65d3316d --- /dev/null +++ b/src/containers/GoogleLogin/actions.js @@ -0,0 +1,15 @@ +import { GOOGLE_NAME, GOOGLE_PHOTO } from './constants'; + +export function addName(name) { + return { + type: GOOGLE_NAME, + payload: name, + }; +} + +export function addPhoto(photoURL) { + return { + type: GOOGLE_PHOTO, + payload: photoURL, + }; +} diff --git a/src/containers/GoogleLogin/constants.js b/src/containers/GoogleLogin/constants.js new file mode 100644 index 00000000..8b4e33fd --- /dev/null +++ b/src/containers/GoogleLogin/constants.js @@ -0,0 +1,2 @@ +export const GOOGLE_NAME = 'ADD_NAME'; +export const GOOGLE_PHOTO = 'GOOGLE_PHOTO'; diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index c793b3e8..660a4a92 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -1,16 +1,20 @@ import React, { PropTypes, Component } from 'react'; import { withRouter, routerShape } from 'react-router'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; + +import { addName, addPhoto } from './actions'; import request from '../../utils/request'; class GoogleLogin extends Component { - static onFailure(err) { + static onFailure() { // TODO handle error - console.log(err); + // console.error(err); } static onRequest() { // TODO add spinner - console.log('loading...'); + // console.log('loading...'); } static checkToken() { @@ -49,14 +53,15 @@ class GoogleLogin extends Component { if (res) { this.setState({ loggedIn: true }); } - }).catch((err) => { + }).catch(() => { // TODO handle invalid token error - console.log(err); + // console.error(err); }); } componentDidMount() { const { scope, cookiePolicy, loginHint, hostedDomain } = this.props; + const { name, photoURL } = this.props.googleLogin; // Include Google Platform Library // https://developers.google.com/identity/sign-in/web/reference @@ -84,9 +89,15 @@ class GoogleLogin extends Component { window.gapi.auth2.init(params); } - if (localStorage.g_access_token - && new Date() > localStorage.expiration_date) { - this.refreshToken(); + if (localStorage.g_access_token) { + if (new Date() > localStorage.expiration_date) { + this.refreshToken(); + } + + if (name && photoURL) { + localStorage.setItem('givenName', name); + localStorage.setItem('imageUrl', photoURL); + } } }); }; @@ -98,6 +109,8 @@ class GoogleLogin extends Component { if (user) { localStorage.setItem('givenName', user.givenName); localStorage.setItem('imageUrl', user.imageUrl); + this.props.addName(user.givenName); + this.props.addPhoto(user.imageUrl); } this.setState({ loggedIn: true }); @@ -196,6 +209,10 @@ class GoogleLogin extends Component { } GoogleLogin.propTypes = { + googleLogin: PropTypes.shape({ + name: PropTypes.string, + photoURL: PropTypes.string, + }), offline: PropTypes.bool, scope: PropTypes.string, redirectUri: PropTypes.string, @@ -205,6 +222,8 @@ GoogleLogin.propTypes = { approvalPrompt: PropTypes.string, prompt: PropTypes.string, router: routerShape, + addName: PropTypes.func, + addPhoto: PropTypes.func, }; GoogleLogin.defaultProps = { @@ -215,4 +234,18 @@ GoogleLogin.defaultProps = { prompt: '', }; -export default withRouter(GoogleLogin); +function mapStateToProps({ googleLogin }) { + return { googleLogin }; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators({ + addName, + addPhoto, + }, dispatch); +} + +export default withRouter(connect( + mapStateToProps, + mapDispatchToProps +)(GoogleLogin)); diff --git a/src/containers/GoogleLogin/reducer.js b/src/containers/GoogleLogin/reducer.js new file mode 100644 index 00000000..32364735 --- /dev/null +++ b/src/containers/GoogleLogin/reducer.js @@ -0,0 +1,20 @@ +import { GOOGLE_NAME, GOOGLE_PHOTO } from './constants'; + +export default function (state = {}, action) { + switch (action.type) { + case GOOGLE_NAME: + return { + ...state, + name: action.payload, + }; + + case GOOGLE_PHOTO: + return { + ...state, + photoURL: action.payload, + }; + + default: + return state; + } +} diff --git a/src/reducers.js b/src/reducers.js index 6a4ea606..24879627 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -3,9 +3,11 @@ import { routerReducer } from 'react-router-redux'; import SearchBarReducer from './containers/SearchBar/reducer'; import BuildingOverviewReducer from './containers/BuildingOverview/reducer'; +import GoogleLoginReducer from './containers/GoogleLogin/reducer'; export default combineReducers({ routing: routerReducer, buildingList: SearchBarReducer, buildingDetail: BuildingOverviewReducer, + googleLogin: GoogleLoginReducer, }); diff --git a/src/screens/Login/index.js b/src/screens/Login/index.js index 3cd55418..edddf946 100644 --- a/src/screens/Login/index.js +++ b/src/screens/Login/index.js @@ -5,7 +5,7 @@ export default function Login() { return (
- You must be logged in to view this page. +

Login to get started.

); } -- GitLab From f39ea3047f1a437c73033d83c14f24593c23a2e1 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Thu, 26 Jan 2017 17:54:10 -0500 Subject: [PATCH 13/17] Add getHeaders method and url constants for service --- src/containers/BuildingOverview/sagas.js | 22 +++++------- src/containers/GoogleLogin/index.js | 46 +++++++++--------------- src/containers/SearchBar/actions.js | 17 ++++----- src/utils/rest_services.js | 9 +++++ 4 files changed, 42 insertions(+), 52 deletions(-) create mode 100644 src/utils/rest_services.js diff --git a/src/containers/BuildingOverview/sagas.js b/src/containers/BuildingOverview/sagas.js index 2aa9622c..2a410499 100644 --- a/src/containers/BuildingOverview/sagas.js +++ b/src/containers/BuildingOverview/sagas.js @@ -1,5 +1,6 @@ import { call, put, takeEvery } from 'redux-saga/effects'; import request from '../../utils/request'; +import { getHeaders, turkURL, buildingsURL } from '../../utils/rest_services'; import { LOAD_BUILDING_DETAIL, @@ -19,11 +20,6 @@ import { hitDecisionError, } from './actions'; -const BUILDING_SERVICE_URL = `${process.env.REACT_APP_BUILDING_SERVICE}`; -const HEADERS = new Headers({ - 'x-blocpower-app-key': process.env.REACT_APP_KEY, -}); - /** * Detail page request/response handler * @@ -33,9 +29,9 @@ function* getBuildingDetail(action) { const buildingId = action.payload; const data = yield call( request, - `${BUILDING_SERVICE_URL}/building/${buildingId}`, { + `${buildingsURL}${buildingId}`, { method: 'GET', - headers: HEADERS, + headers: getHeaders(), } ); @@ -48,9 +44,9 @@ function* getBuildingDetail(action) { function* loadHit(action) { const buildingId = action.payload; - const data = yield call(request, `${BUILDING_SERVICE_URL}/turkhit/${buildingId}`, { + const data = yield call(request, `${turkURL}${buildingId}`, { method: 'GET', - headers: HEADERS, + headers: getHeaders(), }); if (!data.err) { @@ -67,9 +63,9 @@ function* loadHit(action) { */ function* createHit(action) { const turkHitFormData = action.payload; - const data = yield call(request, `${BUILDING_SERVICE_URL}/turkhit/`, { + const data = yield call(request, turkURL, { method: 'POST', - headers: HEADERS, + headers: getHeaders(), body: JSON.stringify(turkHitFormData), }); @@ -86,9 +82,9 @@ function* createHit(action) { * @param {number} action 1 or 0 representing approve or reject */ function* approveHit(action) { - const data = yield call(request, `${BUILDING_SERVICE_URL}/turkhit/${action.buildingId}`, { + const data = yield call(request, `${turkURL}${action.buildingId}`, { method: 'PUT', - headers: HEADERS, + headers: getHeaders(), body: JSON.stringify({ accept: action.decision }), }); diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index 660a4a92..80eaf450 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -18,14 +18,14 @@ class GoogleLogin extends Component { } static checkToken() { - return localStorage.g_access_token && new Date() < localStorage.expiration_date; + return localStorage.gat && new Date() < localStorage.ed; } static loggedIn() { return new Promise((resolve, reject) => { if (GoogleLogin.checkToken()) { const gUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo'; - request(`${gUrl}?access_token=${localStorage.g_access_token}`, { method: 'POST' }) + request(`${gUrl}?access_token=${localStorage.gat}`, { method: 'POST' }) .then((res) => { if (res.err) { localStorage.clear(); @@ -89,13 +89,13 @@ class GoogleLogin extends Component { window.gapi.auth2.init(params); } - if (localStorage.g_access_token) { - if (new Date() > localStorage.expiration_date) { + if (localStorage.gat) { + if (new Date() > localStorage.ed) { this.refreshToken(); } if (name && photoURL) { - localStorage.setItem('givenName', name); + localStorage.setItem('name', name); localStorage.setItem('imageUrl', photoURL); } } @@ -103,14 +103,16 @@ class GoogleLogin extends Component { }; } - onSuccess = (accessToken, expirationDate, user, goBack) => { - localStorage.setItem('g_access_token', accessToken); - localStorage.setItem('expiration_date', expirationDate); + onSuccess = (authResponse, user, goBack) => { + localStorage.setItem('git', authResponse.id_token); + localStorage.setItem('gat', authResponse.access_token); + localStorage.setItem('ed', authResponse.expires_at); + if (user) { - localStorage.setItem('givenName', user.givenName); - localStorage.setItem('imageUrl', user.imageUrl); - this.props.addName(user.givenName); - this.props.addPhoto(user.imageUrl); + localStorage.setItem('name', user.getGivenName()); + localStorage.setItem('imageUrl', user.getImageUrl()); + this.props.addName(user.getGivenName()); + this.props.addPhoto(user.getImageUrl()); } this.setState({ loggedIn: true }); @@ -122,8 +124,8 @@ class GoogleLogin extends Component { refreshToken = () => { window.gapi.auth2.getAuthInstance().currentUser.get() - .reloadAuthResponse().then((res) => { - this.onSuccess(res.access_tokenm, res.expires_at); + .reloadAuthResponse().then((authResponse) => { + this.onSuccess(authResponse); }); }; @@ -148,21 +150,7 @@ class GoogleLogin extends Component { .then((res) => { const basicProfile = res.getBasicProfile(); const authResponse = res.getAuthResponse(); - - const accessToken = authResponse.access_token; - const expirationDate = authResponse.expires_at; - - const user = { - tokenId: authResponse.id_token, - googleId: basicProfile.getId(), - imageUrl: basicProfile.getImageUrl(), - email: basicProfile.getEmail(), - name: basicProfile.getName(), - givenName: basicProfile.getGivenName(), - familyName: basicProfile.getFamilyName(), - }; - - this.onSuccess(accessToken, expirationDate, user, true); + this.onSuccess(authResponse, basicProfile, true); }, err => GoogleLogin.onFailure(err) ); diff --git a/src/containers/SearchBar/actions.js b/src/containers/SearchBar/actions.js index 0768281f..0fd5e8a0 100644 --- a/src/containers/SearchBar/actions.js +++ b/src/containers/SearchBar/actions.js @@ -1,18 +1,15 @@ import 'whatwg-fetch'; import { FETCH_BUILDINGS, SEARCH_TERM } from './constants'; +import { getHeaders, buildingsURL } from '../../utils/rest_services'; -const ROOT_URL = `${process.env.REACT_APP_BUILDING_SERVICE}/building/`; -const HEADERS = new Headers({ 'x-blocpower-app-key': process.env.REACT_APP_KEY }); +function fetchBuildings(address) { + const url = `${buildingsURL}?address=${address}`; -const init = { - method: 'GET', - headers: HEADERS, - mode: 'cors', - cache: 'default', -}; + const init = { + method: 'GET', + headers: getHeaders(), + }; -function fetchBuildings(address) { - const url = `${ROOT_URL}?address=${address}`; return { type: FETCH_BUILDINGS, payload: fetch(url, init).then(response => diff --git a/src/utils/rest_services.js b/src/utils/rest_services.js new file mode 100644 index 00000000..04fb1634 --- /dev/null +++ b/src/utils/rest_services.js @@ -0,0 +1,9 @@ +export function getHeaders() { + return new Headers({ + 'x-blocpower-app-key': process.env.REACT_APP_KEY, + 'x-blocpower-google-token': localStorage.git, + }); +} + +export const buildingsURL = `${process.env.REACT_APP_BUILDING_SERVICE}/building/`; +export const turkURL = `${process.env.REACT_APP_BUILDING_SERVICE}/turkhit/`; -- GitLab From f2e27e2d3cd7057f18769c024cadb583988a9ca2 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Sat, 28 Jan 2017 11:56:23 -0500 Subject: [PATCH 14/17] Remove caret from dependencies --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e7a62217..f887a5cf 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "chalk": "1.1.3", "connect-history-api-fallback": "1.3.0", "cross-spawn": "4.0.2", - "css-loader": "^0.25.0", + "css-loader": "0.25.0", "detect-port": "1.0.1", "dotenv": "2.0.0", "eslint": "3.9.1", @@ -35,19 +35,19 @@ "http-proxy-middleware": "0.17.2", "jest": "16.0.2", "json-loader": "0.5.4", - "node-sass": "^4.1.1", - "nodemon": "^1.11.0", + "node-sass": "4.1.1", + "nodemon": "1.11.0", "object-assign": "4.1.0", "path-exists": "2.1.0", "postcss-loader": "1.0.0", "promise": "7.1.1", "react-dev-utils": "^0.3.0", "recursive-readdir": "2.1.0", - "resolve-url-loader": "^1.6.1", + "resolve-url-loader": "1.6.1", "rimraf": "2.5.4", - "sass-loader": "^4.1.1", + "sass-loader": "4.1.1", "strip-ansi": "3.0.1", - "style-loader": "^0.13.1", + "style-loader": "0.13.1", "url-loader": "0.5.7", "webpack": "1.13.2", "webpack-dev-server": "1.16.2", -- GitLab From 0da1b5dd11cfa19e7b0ab71a390cd961d6c270dc Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Sun, 29 Jan 2017 17:01:11 -0500 Subject: [PATCH 15/17] Allow only blocpower accounts to login --- src/containers/GoogleLogin/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index 80eaf450..ef111eb6 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -60,7 +60,7 @@ class GoogleLogin extends Component { } componentDidMount() { - const { scope, cookiePolicy, loginHint, hostedDomain } = this.props; + const { scope, cookiePolicy, loginHint } = this.props; const { name, photoURL } = this.props.googleLogin; // Include Google Platform Library @@ -76,7 +76,7 @@ class GoogleLogin extends Component { client_id: process.env.REACT_APP_GOOGLE_CLIENT, cookiepolicy: cookiePolicy, login_hint: loginHint, - hosted_domain: hostedDomain, + hosted_domain: 'blocpower.org', scope, }; @@ -206,7 +206,6 @@ GoogleLogin.propTypes = { redirectUri: PropTypes.string, cookiePolicy: PropTypes.string, loginHint: PropTypes.string, - hostedDomain: PropTypes.string, approvalPrompt: PropTypes.string, prompt: PropTypes.string, router: routerShape, -- GitLab From e6d3bf97a8af7993579a05685b8e8dee125c7aff Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Sun, 29 Jan 2017 17:46:13 -0500 Subject: [PATCH 16/17] Make logout button disabled until gapi library is loaded --- src/containers/GoogleLogin/index.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/containers/GoogleLogin/index.js b/src/containers/GoogleLogin/index.js index ef111eb6..61b69cf2 100644 --- a/src/containers/GoogleLogin/index.js +++ b/src/containers/GoogleLogin/index.js @@ -159,10 +159,14 @@ class GoogleLogin extends Component { }; signOut = () => { - window.gapi.auth2.getAuthInstance().signOut(); - localStorage.clear(); - this.setState({ loggedIn: false }); - this.props.router.push('/login'); + // TODO prevent logout button from being pressed before gapi.auth2.init + // has loaded + if (!this.state.disabled) { + window.gapi.auth2.getAuthInstance().signOut(); + localStorage.clear(); + this.setState({ loggedIn: false }); + this.props.router.push('/login'); + } }; render() { -- GitLab From 40316e645f8ae696086ee8aa5d28fb184f69b872 Mon Sep 17 00:00:00 2001 From: Alessandro DiMarco Date: Sun, 29 Jan 2017 17:59:09 -0500 Subject: [PATCH 17/17] Redirect to homepage if at /login and signed in --- src/routes.js | 22 ++++++---------------- src/utils/auth.js | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 src/utils/auth.js diff --git a/src/routes.js b/src/routes.js index 853717fd..ef2a8816 100644 --- a/src/routes.js +++ b/src/routes.js @@ -1,30 +1,20 @@ import React from 'react'; import { Route, IndexRoute, IndexRedirect } from 'react-router'; -import HomePage from './screens/HomePage'; -import BuildingDetail from './screens/BuildingDetail'; +import { requireAuth, redirectIfLoggedIn } from './utils/auth'; + import Login from './screens/Login'; import NotFound from './screens/NotFound'; - -import GoogleLogin from './containers/GoogleLogin'; +import HomePage from './screens/HomePage'; +import BuildingDetail from './screens/BuildingDetail'; import BuildingOverview from './containers/BuildingOverview'; import Dummy from './components/dummyComponent'; -function requireAuth(nextState, replace) { - // TODO Use GoogleLogin.loggedIn to verify token - if (!GoogleLogin.checkToken()) { - replace({ - pathname: '/login', - state: { nextPathname: nextState.location.pathname }, - }); - } -} - export default ( - + @@ -36,7 +26,7 @@ export default ( - + ); diff --git a/src/utils/auth.js b/src/utils/auth.js new file mode 100644 index 00000000..60a89414 --- /dev/null +++ b/src/utils/auth.js @@ -0,0 +1,20 @@ +import GoogleLogin from '../containers/GoogleLogin'; + +export function requireAuth(nextState, replace) { + // TODO Use GoogleLogin.loggedIn to verify token + if (!GoogleLogin.checkToken()) { + replace({ + pathname: '/login', + state: { nextPathname: nextState.location.pathname }, + }); + } +} + +export function redirectIfLoggedIn(nextState, replace) { + if (GoogleLogin.checkToken()) { + replace({ + pathname: '/', + state: { nextPathname: nextState.location.pathname }, + }); + } +} -- GitLab