Skip to content

Commit

Permalink
Merge pull request #313 from nukeop/feature/github-login
Browse files Browse the repository at this point in the history
Github login
  • Loading branch information
nukeop authored Apr 22, 2019
2 parents becd7e5 + f414a00 commit 89a4d4e
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 30 deletions.
98 changes: 98 additions & 0 deletions app/actions/github.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {
GITHUB_OAUTH_ACCESS_TOKEN_URL,
GITHUB_API_USER_ENDPOINT
} from '../rest/Github';

import {
githubClientId,
githubSecret
} from '../globals';

export const GITHUB_OAUTH_CODE_SUCCESS = 'GITHUB_OAUTH_CODE_SUCCESS';
export const GITHUB_OAUTH_ACCESS_TOKEN_SUCCESS = 'GITHUB_OAUTH_ACCESS_TOKEN_SUCCESS';
export const GITHUB_LOG_OUT = 'GITHUB_LOG_OUT';

export const GITHUB_GET_USER_START = 'GITHUB_GET_USER_START';
export const GITHUB_GET_USER_SUCCESS = 'GITHUB_GET_USER_SUCCESS';
export const GITHUB_GET_USER_ERROR = 'GITHUB_GET_USER_ERROR';

function githubOauthCodeSuccess(code) {
return {
type: GITHUB_OAUTH_CODE_SUCCESS,
payload: { code }
};
}

function githubOauthAccessTokenSuccess(accessToken) {
return {
type: GITHUB_OAUTH_ACCESS_TOKEN_SUCCESS,
payload: { accessToken }
};
}

export function githubOauth(code) {
return dispatch => {
dispatch(githubOauthCodeSuccess(code));
fetch(
'https://cors-anywhere.herokuapp.com/' +
GITHUB_OAUTH_ACCESS_TOKEN_URL +
'?client_id=' +
githubClientId +
'&client_secret=' +
githubSecret +
'&code=' +
code,
{
method: 'POST',
headers: { 'Accept': 'application/json' }
}
)
.then(response => response.json())
.then(data => {
dispatch(githubOauthAccessTokenSuccess(data.access_token));
dispatch(githubGetUser(data.access_token));
});
};
}

export function githubLogOut() {
return {
type: GITHUB_LOG_OUT
};
}

function githubGetUserStart() {
return {
type: GITHUB_GET_USER_START
};
}

function githubGetUserSuccess(data) {
return {
type: GITHUB_GET_USER_SUCCESS,
payload: { data }
};
}

function githubGetUserError(error) {
return {
type: GITHUB_GET_USER_ERROR,
payload: { error }
};
}

export function githubGetUser(token) {
return dispatch => {
dispatch(githubGetUserStart());
fetch(GITHUB_API_USER_ENDPOINT, {
headers: { 'Authorization': `token ${token}` }
})
.then(response => response.json())
.then(data => {
dispatch(githubGetUserSuccess(data));
})
.catch(error => {
dispatch(githubGetUserError(error));
});
};
}
77 changes: 77 additions & 0 deletions app/components/OauthPopup/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';
import PropTypes from 'prop-types';

class OauthPopup extends React.Component {
constructor(props) {
super(props);
}

createPopup() {
const {
url,
width,
height,
onCode,
onClose,
onError
} = this.props;

const left = window.screenX + (window.outerWidth - width) / 2;
const top = window.screenY + (window.outerHeight - height) / 2.5;
this.externalWindow = window.open(
url,
'',
`width=${width},height=${height},left=${left},top=${top}`
);

this.codeCheck = setInterval(() => {
try {
const params = new URL(this.externalWindow.location).searchParams;
const code = params.get('code');
if (!code) {
return;
}
clearInterval(this.codeCheck);
onCode(code, params);
this.externalWindow.close();
} catch (e) {
onError(e);
}
}, 20);

this.externalWindow.onbeforeunload = () => {
onClose();
clearInterval(this.codeCheck);
};
}

render() {
const {
render
} = this.props;

return render({ onClick: this.createPopup.bind(this) });
}
}

OauthPopup.propTypes = {
url: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
render: PropTypes.func,
onCode: PropTypes.func,
onClose: PropTypes.func,
onError: PropTypes.func
};

OauthPopup.defaultProps = {
url: '',
width: 500,
height: 500,
render: () => null,
onCode: () => {},
onClose: () => {},
onError: () => {}
};

export default OauthPopup;
71 changes: 71 additions & 0 deletions app/components/Settings/GithubSettings/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import {
Button,
Icon
} from 'semantic-ui-react';

import Spacer from '../../Spacer';
import OauthPopup from '../../OauthPopup';
import { getGithubOauthUrl } from '../../../rest/Github';

import settingsStyles from '../styles.scss';

const GithubSettings = props => {
const {
username,
loading,
logIn,
logOut
} = props;

return (
<div className={ settingsStyles.settings_item } >
<span>User: <strong>{ _.defaultTo(username, 'Not logged in') }</strong></span>
<Spacer />

{
_.isNil(username) &&
<OauthPopup
url={ getGithubOauthUrl() }
onCode={ code => logIn(code) }
render={ oauthProps =>
<Button
color='black'
loading={ loading }
onClick={ oauthProps.onClick }
>
<Icon name='github'/>
Log in with Github
</Button>
} />
}
{
!_.isNil(username) &&
<Button
inverted
onClick={ logOut }
>
Log out
</Button>
}
</div>
);
};

GithubSettings.propTypes = {
username: PropTypes.string,
loading: PropTypes.bool,
logIn: PropTypes.func,
logOut: PropTypes.func
};

GithubSettings.defaultProps = {
username: null,
loading: false,
logIn: () => {},
logOut: () => {}
};

export default GithubSettings;
61 changes: 55 additions & 6 deletions app/components/Settings/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import React from 'react';
import FontAwesome from 'react-fontawesome';
import PropTypes from 'prop-types';
import { Button, Input, Radio, Segment } from 'semantic-ui-react';
import Range from 'react-range-progress';
import cx from 'classnames';
import _ from 'lodash';
import {
Divider,
Icon
} from 'semantic-ui-react';

import Header from '../Header';
import Spacer from '../Spacer';
import SocialIntegration from './SocialIntegration';
import GithubSettings from './GithubSettings';
import settingsEnum from '../../constants/settingsEnum';

import styles from './styles.scss';
Expand Down Expand Up @@ -50,11 +52,11 @@ class Settings extends React.Component {
<SocialIntegration
logo={
<Icon.Group size='big'>
<Icon name='square' className={ styles.lastfm_icon_bg }/>
<Icon name='square' className={ styles.social_icon_bg }/>
<Icon name='lastfm square' className={ styles.lastfm_icon }/>
</Icon.Group>
}
title='Last.fm integration'
title='Last.fm'
description={
'In order to enable scrobbling, you first have to'
+ ' connect and authorize Nuclear on Last.fm, then click log in.'
Expand Down Expand Up @@ -100,14 +102,14 @@ class Settings extends React.Component {
{
lastFmSessionKey &&
<Button onClick={ lastFmLogOut } inverted>
Log out
Log out
</Button>
}
</div>
);
}

renderLastFmOptionRadio () {
renderLastFmOptionRadio() {
let { lastFmScrobblingEnabled } = this.props.scrobbling;
const { enableScrobbling, disableScrobbling } = this.props.actions;
return (
Expand All @@ -129,13 +131,44 @@ class Settings extends React.Component {
);
}

renderGithubSocialIntegration() {
const {
actions,
github
} = this.props;

return (
<SocialIntegration
logo={
<Icon.Group size='big'>
<Icon name='square' className={ styles.social_icon_bg } />
<Icon color='black' name='github square' />
</Icon.Group>
}
title='Github'
description={
'Log in via Github to be able to create and share your playlists online (upcoming feature).'
}
>
<GithubSettings
logIn={ actions.githubOauth }
logOut={ actions.githubLogOut }
loading={ _.get(github, 'loading') }
username={ _.get(github, 'login') }
/>
</SocialIntegration>
);
}

renderSocialSettings () {
return (
<div className={styles.settings_section}>
<Header>Social</Header>
<hr />
<Segment>
{this.renderLastFmSocialIntegration()}
{ this.renderLastFmSocialIntegration() }
<Divider />
{ this.renderGithubSocialIntegration() }
</Segment>
</div>
);
Expand Down Expand Up @@ -250,4 +283,20 @@ class Settings extends React.Component {
}
}

Settings.propTypes = {
actions: PropTypes.object,
github: PropTypes.object,
scrobbling: PropTypes.object,
settings: PropTypes.object,
options: PropTypes.array
};

Settings.defaultProps = {
actions: {},
github: {},
scrobbling: {},
settings: {},
options: []
};

export default Settings;
2 changes: 1 addition & 1 deletion app/components/Settings/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
color: $red;
}

.lastfm_icon_bg {
.social_icon_bg {
color: $white;
font-size: 24px;
}
Expand Down
Loading

0 comments on commit 89a4d4e

Please sign in to comment.