fix(auth): Fix auth flow for the client app
This commit is contained in:
committed by
mrugesh mohapatra
parent
a656cbf98a
commit
c08bb95ea8
@@ -6,10 +6,12 @@ import { createSelector } from 'reselect';
|
||||
import { Grid, Button } from '@freecodecamp/react-bootstrap';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
import { apiLocation } from '../../config/env.json';
|
||||
import {
|
||||
signInLoadingSelector,
|
||||
userSelector,
|
||||
isSignedInSelector
|
||||
isSignedInSelector,
|
||||
hardGoTo
|
||||
} from '../redux';
|
||||
import { submitNewAbout, updateUserFlag, verifyCert } from '../redux/settings';
|
||||
import { createFlashMessage } from '../components/Flash/redux';
|
||||
@@ -29,6 +31,7 @@ import RedirectHome from '../components/RedirectHome';
|
||||
|
||||
const propTypes = {
|
||||
createFlashMessage: PropTypes.func.isRequired,
|
||||
hardGoTo: PropTypes.func.isRequired,
|
||||
isSignedIn: PropTypes.bool,
|
||||
showLoading: PropTypes.bool,
|
||||
submitNewAbout: PropTypes.func.isRequired,
|
||||
@@ -101,6 +104,7 @@ const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators(
|
||||
{
|
||||
createFlashMessage,
|
||||
hardGoTo,
|
||||
submitNewAbout,
|
||||
toggleNightMode: theme => updateUserFlag({ theme }),
|
||||
updateInternetSettings: updateUserFlag,
|
||||
@@ -112,9 +116,15 @@ const mapDispatchToProps = dispatch =>
|
||||
dispatch
|
||||
);
|
||||
|
||||
const createHandleSignoutClick = hardGoTo => e => {
|
||||
e.preventDefault();
|
||||
return hardGoTo(`${apiLocation}/signout`);
|
||||
};
|
||||
|
||||
function ShowSettings(props) {
|
||||
const {
|
||||
createFlashMessage,
|
||||
hardGoTo,
|
||||
isSignedIn,
|
||||
submitNewAbout,
|
||||
toggleNightMode,
|
||||
@@ -193,6 +203,7 @@ function ShowSettings(props) {
|
||||
bsStyle='primary'
|
||||
className='btn-invert'
|
||||
href={'/signout'}
|
||||
onClick={createHandleSignoutClick(hardGoTo)}
|
||||
>
|
||||
Sign me out of freeCodeCamp
|
||||
</Button>
|
||||
|
@@ -1,13 +1,27 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from '@freecodecamp/react-bootstrap';
|
||||
import { Link } from 'gatsby';
|
||||
|
||||
import { hardGoTo } from '../../../redux';
|
||||
import { apiLocation } from '../../../../config/env.json';
|
||||
|
||||
import './login.css';
|
||||
|
||||
function Login({ children, ...restProps }) {
|
||||
const mapStateToProps = () => ({});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
navigate: location => dispatch(hardGoTo(location))
|
||||
});
|
||||
|
||||
const createOnClick = navigate => e => {
|
||||
e.preventDefault();
|
||||
return navigate(`${apiLocation}/signin`);
|
||||
};
|
||||
|
||||
function Login(props) {
|
||||
const { children, navigate, ...restProps } = props;
|
||||
return (
|
||||
<Link to='/signin'>
|
||||
<a href='/signin' onClick={createOnClick(navigate)}>
|
||||
<Button
|
||||
{...restProps}
|
||||
bsStyle='default'
|
||||
@@ -17,13 +31,17 @@ function Login({ children, ...restProps }) {
|
||||
>
|
||||
{children || 'Sign In'}
|
||||
</Button>
|
||||
</Link>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
Login.displayName = 'Login';
|
||||
Login.propTypes = {
|
||||
children: PropTypes.any
|
||||
children: PropTypes.any,
|
||||
navigate: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default Login;
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Login);
|
||||
|
@@ -12,9 +12,9 @@ function Header({ disableSettings }) {
|
||||
return (
|
||||
<header>
|
||||
<nav id='top-nav'>
|
||||
<a className='home-link' href='https://www.freecodecamp.org'>
|
||||
<Link className='home-link' to='/'>
|
||||
<NavLogo />
|
||||
</a>
|
||||
</Link>
|
||||
{disableSettings ? null : <FCCSearch />}
|
||||
<ul id='top-right-nav'>
|
||||
<li>
|
||||
|
@@ -149,7 +149,7 @@ class DefaultLayout extends Component {
|
||||
]}
|
||||
/>
|
||||
<Header disableSettings={disableSettings} />
|
||||
<div className={landingPage && 'landing-page'}>
|
||||
<div className={`default-layout ${landingPage ? 'landing-page' : ''}`}>
|
||||
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
||||
{hasMessages ? (
|
||||
<Flash messages={flashMessages} onClose={removeFlashMessage} />
|
||||
|
@@ -622,6 +622,6 @@ pre tt:after {
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
main {
|
||||
.default-layout {
|
||||
margin-top: 38px;
|
||||
}
|
@@ -33,7 +33,7 @@ class NotFoundPage extends React.Component {
|
||||
<Layout>
|
||||
<div className='notfound-page-wrapper'>
|
||||
<Helmet title='Page Not Found | freeCodeCamp' />
|
||||
<img alt='learn to code at freeCodeCamp 404' src={notFoundLogo} />
|
||||
<img alt='404 Not Found' src={notFoundLogo} />
|
||||
<h1>NOT FOUND</h1>
|
||||
{this.state.randomQuote ? (
|
||||
<div>
|
||||
|
@@ -9,7 +9,11 @@ import Helmet from 'react-helmet';
|
||||
|
||||
import { Loader, Spacer } from '../components/helpers';
|
||||
import Layout from '../components/layouts/Default';
|
||||
import { userSelector, userFetchStateSelector } from '../redux';
|
||||
import {
|
||||
userSelector,
|
||||
userFetchStateSelector,
|
||||
isSignedInSelector
|
||||
} from '../redux';
|
||||
import { randomQuote } from '../utils/get-words';
|
||||
|
||||
import './welcome.css';
|
||||
@@ -20,6 +24,7 @@ const propTypes = {
|
||||
complete: PropTypes.bool,
|
||||
errored: PropTypes.bool
|
||||
}),
|
||||
isSignedIn: PropTypes.bool,
|
||||
user: PropTypes.shape({
|
||||
acceptedPrivacyTerms: PropTypes.bool,
|
||||
username: PropTypes.string,
|
||||
@@ -32,20 +37,22 @@ const propTypes = {
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
userFetchStateSelector,
|
||||
isSignedInSelector,
|
||||
userSelector,
|
||||
(fetchState, user) => ({ fetchState, user })
|
||||
(fetchState, isSignedIn, user) => ({ fetchState, isSignedIn, user })
|
||||
);
|
||||
const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);
|
||||
|
||||
function Welcome({
|
||||
fetchState: { pending, complete },
|
||||
isSignedIn,
|
||||
user: {
|
||||
acceptedPrivacyTerms,
|
||||
name = '',
|
||||
completedChallengeCount = 0,
|
||||
completedChallengeCount: completedChallenges = 0,
|
||||
completedProjectCount = 0,
|
||||
completedCertCount = 0,
|
||||
completedLegacyCertCount = 0
|
||||
completedLegacyCertCount: completedLegacyCerts = 0
|
||||
}
|
||||
}) {
|
||||
if (pending && !complete) {
|
||||
@@ -58,7 +65,12 @@ function Welcome({
|
||||
);
|
||||
}
|
||||
|
||||
if (!acceptedPrivacyTerms) {
|
||||
if (!isSignedIn) {
|
||||
navigate('/');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isSignedIn && !acceptedPrivacyTerms) {
|
||||
navigate('/accept-privacy-terms');
|
||||
return null;
|
||||
}
|
||||
@@ -70,72 +82,72 @@ function Welcome({
|
||||
<title>Welcome {name ? name : 'Camper'} | freeCodeCamp.org</title>
|
||||
</Helmet>
|
||||
<main>
|
||||
<Grid className='text-center'>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<Spacer />
|
||||
<h1 className='big-heading'>Welcome {name ? name : 'Camper'}!</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<a
|
||||
className='update-link'
|
||||
href='/n/7gR5pBM-K?refsource=userhome'
|
||||
target='_blank'
|
||||
>
|
||||
We're building a massive open dataset about new coders. Take the
|
||||
2018 New Coder Survey. It only takes 5 minutes.
|
||||
</a>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row className='quote-partial'>
|
||||
<Col sm={10} smOffset={1} xs={12}>
|
||||
<blockquote className='blockquote'>
|
||||
<span>
|
||||
<q>{quote}</q>
|
||||
<footer className='quote-author blockquote-footer'>
|
||||
<cite>{author}</cite>
|
||||
</footer>
|
||||
</span>
|
||||
</blockquote>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<p className='stats'>
|
||||
You have completed <span>{completedChallengeCount}</span> of{' '}
|
||||
<span>1408</span> coding challenges.
|
||||
</p>
|
||||
<p className='stats'>
|
||||
You have built <span>{completedProjectCount}</span> of{' '}
|
||||
<span>30</span> projects.
|
||||
</p>
|
||||
{completedLegacyCertCount ? (
|
||||
<Grid className='text-center'>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<Spacer />
|
||||
<h1 className='big-heading'>Welcome {name ? name : 'Camper'}!</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<a
|
||||
className='update-link'
|
||||
href='/n/7gR5pBM-K?refsource=userhome'
|
||||
target='_blank'
|
||||
>
|
||||
We're building a massive open dataset about new coders. Take the
|
||||
2018 New Coder Survey. It only takes 5 minutes.
|
||||
</a>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row className='quote-partial'>
|
||||
<Col sm={10} smOffset={1} xs={12}>
|
||||
<blockquote className='blockquote'>
|
||||
<span>
|
||||
<q>{quote}</q>
|
||||
<footer className='quote-author blockquote-footer'>
|
||||
<cite>{author}</cite>
|
||||
</footer>
|
||||
</span>
|
||||
</blockquote>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<p className='stats'>
|
||||
You have earned <span>{completedLegacyCertCount}</span> of{' '}
|
||||
<span>4</span> legacy certifications.
|
||||
You have completed <span>{completedChallenges}</span> of{' '}
|
||||
<span>1408</span> coding challenges.
|
||||
</p>
|
||||
) : null}
|
||||
<p className='stats'>
|
||||
You have earned <span>{completedCertCount}</span> of{' '}
|
||||
<span>6</span> certifications.
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<Button block={true} bsStyle='primary' className='btn-cta-big'>
|
||||
Go to my next challenge
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer size={4} />
|
||||
</Grid>
|
||||
<p className='stats'>
|
||||
You have built <span>{completedProjectCount}</span> of{' '}
|
||||
<span>30</span> projects.
|
||||
</p>
|
||||
{completedLegacyCerts ? (
|
||||
<p className='stats'>
|
||||
You have earned <span>{completedLegacyCerts}</span> of{' '}
|
||||
<span>4</span> legacy certifications.
|
||||
</p>
|
||||
) : null}
|
||||
<p className='stats'>
|
||||
You have earned <span>{completedCertCount}</span> of{' '}
|
||||
<span>6</span> certifications.
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<Button block={true} bsStyle='primary' className='btn-cta-big'>
|
||||
Go to my next challenge
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer size={4} />
|
||||
</Grid>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
|
@@ -11,7 +11,12 @@ import { ofType } from 'redux-observable';
|
||||
import store from 'store';
|
||||
import uuid from 'uuid/v4';
|
||||
|
||||
import { types, onlineStatusChange, isOnlineSelector } from './';
|
||||
import {
|
||||
types,
|
||||
onlineStatusChange,
|
||||
isOnlineSelector,
|
||||
isSignedInSelector
|
||||
} from './';
|
||||
import postUpdate$ from '../templates/Challenges/utils/postUpdate$';
|
||||
import { isGoodXHRStatus } from '../templates/Challenges/utils';
|
||||
|
||||
@@ -36,6 +41,7 @@ function failedUpdateEpic(action$, state$) {
|
||||
|
||||
const flushUpdates = action$.pipe(
|
||||
ofType(types.fetchUserComplete, types.updateComplete),
|
||||
filter(() => isSignedInSelector(state$.value)),
|
||||
filter(() => store.get(key)),
|
||||
filter(() => isOnlineSelector(state$.value)),
|
||||
tap(() => {
|
||||
|
@@ -2,8 +2,13 @@ import { call, put, takeEvery } from 'redux-saga/effects';
|
||||
|
||||
import { fetchUserComplete, fetchUserError } from './';
|
||||
import { getSessionUser } from '../utils/ajax';
|
||||
import { jwt } from './cookieValues';
|
||||
|
||||
function* fetchSessionUser() {
|
||||
if (!jwt) {
|
||||
yield put(fetchUserComplete({ user: {}, username: '' }));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const {
|
||||
data: { user = {}, result = '' }
|
||||
|
@@ -47,6 +47,7 @@ export const types = createTypes(
|
||||
[
|
||||
'appMount',
|
||||
'closeDonationModal',
|
||||
'hardGoTo',
|
||||
'openDonationModal',
|
||||
'onlineStatusChange',
|
||||
'updateComplete',
|
||||
@@ -81,6 +82,11 @@ export const openDonationModal = createAction(types.openDonationModal);
|
||||
|
||||
export const onlineStatusChange = createAction(types.onlineStatusChange);
|
||||
|
||||
// `hardGoTo` is used to hit the API server directly
|
||||
// without going through /internal
|
||||
// used for things like /signin and /signout
|
||||
export const hardGoTo = createAction(types.hardGoTo);
|
||||
|
||||
export const updateComplete = createAction(types.updateComplete);
|
||||
export const updateFailed = createAction(types.updateFailed);
|
||||
|
||||
|
Reference in New Issue
Block a user