feat: remove portfolio and add conditional intro
This commit is contained in:
committed by
Mrugesh Mohapatra
parent
3483a04ba1
commit
2352c0b1d9
@ -83,9 +83,9 @@ export const loginRedirect = () => {
|
|||||||
const successRedirect = req => {
|
const successRedirect = req => {
|
||||||
if (!!req && req.session && req.session.returnTo) {
|
if (!!req && req.session && req.session.returnTo) {
|
||||||
delete req.session.returnTo;
|
delete req.session.returnTo;
|
||||||
return `${homeLocation}/`;
|
return `${homeLocation}/learn`;
|
||||||
}
|
}
|
||||||
return `${homeLocation}/`;
|
return `${homeLocation}/learn`;
|
||||||
};
|
};
|
||||||
|
|
||||||
let redirect = url.parse(successRedirect(req), true);
|
let redirect = url.parse(successRedirect(req), true);
|
||||||
|
@ -55,7 +55,7 @@ export function ifNotVerifiedRedirectToUpdateEmail(req, res, next) {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ifUserRedirectTo(path = `${homeLocation}/`, status) {
|
export function ifUserRedirectTo(path = `${homeLocation}/learn`, status) {
|
||||||
status = status === 301 ? 301 : 302;
|
status = status === 301 ? 301 : 302;
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
const { accessToken } = getAccessTokenFromRequest(req);
|
const { accessToken } = getAccessTokenFromRequest(req);
|
||||||
|
@ -23,13 +23,13 @@ describe('<NavLinks />', () => {
|
|||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const expectedLinks = ['/learn', '/portfolio', '/news', '/forum'];
|
const expectedLinks = ['/learn', '/news', '/forum'];
|
||||||
|
|
||||||
it('renders to the DOM', () => {
|
it('renders to the DOM', () => {
|
||||||
expect(root).toBeTruthy();
|
expect(root).toBeTruthy();
|
||||||
});
|
});
|
||||||
it('has 2 links', () => {
|
it('has 3 links', () => {
|
||||||
expect(aTags.length === 4).toBeTruthy();
|
expect(aTags.length === 3).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has links to news, forum, learn and portfolio', () => {
|
it('has links to news, forum, learn and portfolio', () => {
|
||||||
|
@ -25,10 +25,7 @@ function NavLinks({ displayMenu }) {
|
|||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className='nav-projects' role='menuitem'>
|
<li className='nav-projects' role='menuitem'>
|
||||||
<Link to='/learn'>Projects</Link>
|
<Link to='/learn'>Learn</Link>
|
||||||
</li>
|
|
||||||
<li className='nav-portfolio' role='menuitem'>
|
|
||||||
<Link to='/portfolio'>Portfolio</Link>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
42
client/src/components/Intro/Intro.test.js
Normal file
42
client/src/components/Intro/Intro.test.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* global expect */
|
||||||
|
import React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
// import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
|
|
||||||
|
import 'jest-dom/extend-expect';
|
||||||
|
|
||||||
|
import Intro from './';
|
||||||
|
|
||||||
|
describe('<Intro />', () => {
|
||||||
|
it('has no blockquotes when loggedOut', () => {
|
||||||
|
const container = renderer.create(<Intro {...loggedOutProps} />).root;
|
||||||
|
expect(container.findAllByType('blockquote').length === 0).toBeTruthy();
|
||||||
|
expect(container.findAllByType('h1').length === 1).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a blockquote when loggedIn', () => {
|
||||||
|
const container = renderer.create(<Intro {...loggedInProps} />).root;
|
||||||
|
expect(container.findAllByType('blockquote').length === 1).toBeTruthy();
|
||||||
|
expect(container.findAllByType('h1').length === 1).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const loggedInProps = {
|
||||||
|
complete: true,
|
||||||
|
isSignedIn: true,
|
||||||
|
name: 'Developement User',
|
||||||
|
navigate: () => {},
|
||||||
|
pending: false,
|
||||||
|
slug: '/',
|
||||||
|
username: 'DevelopmentUser'
|
||||||
|
};
|
||||||
|
|
||||||
|
const loggedOutProps = {
|
||||||
|
complete: true,
|
||||||
|
isSignedIn: false,
|
||||||
|
name: '',
|
||||||
|
navigate: () => {},
|
||||||
|
pending: false,
|
||||||
|
slug: '/',
|
||||||
|
username: ''
|
||||||
|
};
|
130
client/src/components/Intro/index.js
Normal file
130
client/src/components/Intro/index.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Link, Spacer, Loader } from '../helpers';
|
||||||
|
import { Row, Col } from '@freecodecamp/react-bootstrap';
|
||||||
|
import { navigate as gatsbyNavigate } from 'gatsby';
|
||||||
|
import { apiLocation } from '../../../config/env.json';
|
||||||
|
import { randomQuote } from '../../utils/get-words';
|
||||||
|
|
||||||
|
import './intro.css';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
complete: PropTypes.bool,
|
||||||
|
isSignedIn: PropTypes.bool,
|
||||||
|
name: PropTypes.string,
|
||||||
|
navigate: PropTypes.func,
|
||||||
|
pending: PropTypes.bool,
|
||||||
|
slug: PropTypes.string,
|
||||||
|
username: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
function Intro({
|
||||||
|
isSignedIn,
|
||||||
|
username,
|
||||||
|
name,
|
||||||
|
navigate,
|
||||||
|
pending,
|
||||||
|
complete,
|
||||||
|
slug
|
||||||
|
}) {
|
||||||
|
if (pending && !complete) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Spacer />
|
||||||
|
<Loader />
|
||||||
|
<Spacer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (isSignedIn) {
|
||||||
|
const { quote, author } = randomQuote();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<Spacer />
|
||||||
|
<h1 className='text-center big-heading'>
|
||||||
|
{name
|
||||||
|
? 'Welcome back, ' + name + '.'
|
||||||
|
: 'Welcome to freeCodeCamp.org'}
|
||||||
|
</h1>
|
||||||
|
</Col>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<Spacer />
|
||||||
|
<button
|
||||||
|
className={'btn-primary btn center-block'}
|
||||||
|
onClick={() => gatsbyNavigate(`/${username}`)}
|
||||||
|
>
|
||||||
|
View your Portfolio and Settings
|
||||||
|
</button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spacer />
|
||||||
|
<Row className='text-center 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>
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<Spacer />
|
||||||
|
<h4>
|
||||||
|
If you are new to coding, we recommend you{' '}
|
||||||
|
<Link to={slug}>start at the beginning</Link>.
|
||||||
|
</h4>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<Spacer />
|
||||||
|
<h1 className='big-heading text-center'>
|
||||||
|
Welcome to freeCodeCamp.org
|
||||||
|
</h1>
|
||||||
|
<Spacer />
|
||||||
|
<h2 className='medium-heading'>Learn to code.</h2>
|
||||||
|
<h2 className='medium-heading'>Build projects.</h2>
|
||||||
|
<h2 className='medium-heading'>Earn certifications.</h2>
|
||||||
|
<h2 className='medium-heading'>
|
||||||
|
Since 2014, more than 40,000 freeCodeCamp.org graduates have
|
||||||
|
gotten jobs at tech companies including:
|
||||||
|
</h2>
|
||||||
|
<div className='logo-row'>
|
||||||
|
<h2 className='medium-heading'>Apple</h2>
|
||||||
|
<h2 className='medium-heading'>Google</h2>
|
||||||
|
<h2 className='medium-heading'>Amazon</h2>
|
||||||
|
<h2 className='medium-heading'>Microsoft</h2>
|
||||||
|
<h2 className='medium-heading'>Spotify</h2>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<button
|
||||||
|
className={'btn-cta-big signup-btn btn-cta center-block'}
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`${apiLocation}/signin`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sign in to save your progress (it's free)
|
||||||
|
</button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spacer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Intro.propTypes = propTypes;
|
||||||
|
Intro.displayName = 'Intro';
|
||||||
|
|
||||||
|
export default Intro;
|
49
client/src/components/Intro/intro.css
Normal file
49
client/src/components/Intro/intro.css
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
.large-p {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons with a lot of text can overflow and mess up formatting on small
|
||||||
|
screens, this stops that unless the word itself is too large. */
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-row h2 {
|
||||||
|
height: 35px;
|
||||||
|
padding: 0 10px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-page ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#learn-app-wrapper h2.medium-heading {
|
||||||
|
margin-bottom: 50px;
|
||||||
|
margin-top: 0px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote-partial .blockquote {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.quote-partial .blockquote {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.quote-author {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
@ -27,6 +27,7 @@ const propTypes = {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
isSignedIn: PropTypes.bool,
|
||||||
nodes: PropTypes.arrayOf(ChallengeNode),
|
nodes: PropTypes.arrayOf(ChallengeNode),
|
||||||
resetExpansion: PropTypes.func,
|
resetExpansion: PropTypes.func,
|
||||||
toggleBlock: PropTypes.func.isRequired,
|
toggleBlock: PropTypes.func.isRequired,
|
||||||
@ -68,9 +69,11 @@ export class Map extends Component {
|
|||||||
nodes,
|
nodes,
|
||||||
resetExpansion,
|
resetExpansion,
|
||||||
toggleBlock,
|
toggleBlock,
|
||||||
toggleSuperBlock
|
toggleSuperBlock,
|
||||||
|
isSignedIn
|
||||||
} = this.props;
|
} = this.props;
|
||||||
resetExpansion();
|
resetExpansion();
|
||||||
|
|
||||||
let node;
|
let node;
|
||||||
|
|
||||||
// find the challenge that has the same superblock with hash
|
// find the challenge that has the same superblock with hash
|
||||||
@ -78,13 +81,17 @@ export class Map extends Component {
|
|||||||
node = nodes.find(node => dasherize(node.superBlock) === hash);
|
node = nodes.find(node => dasherize(node.superBlock) === hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is no hash or the hash did not match any challenge superblock
|
// whitout hash only expand when signed in
|
||||||
// and there was a currentChallengeId
|
if (isSignedIn) {
|
||||||
if (!node && currentChallengeId) {
|
// if there is no hash or the hash did not match any challenge superblock
|
||||||
node = nodes.find(node => node.id === currentChallengeId);
|
// and there was a currentChallengeId
|
||||||
|
if (!node && currentChallengeId) {
|
||||||
|
node = nodes.find(node => node.id === currentChallengeId);
|
||||||
|
}
|
||||||
|
if (!node) node = nodes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node) node = nodes[0];
|
if (!node) return;
|
||||||
|
|
||||||
toggleBlock(node.block);
|
toggleBlock(node.block);
|
||||||
toggleSuperBlock(node.superBlock);
|
toggleSuperBlock(node.superBlock);
|
||||||
|
@ -10,7 +10,7 @@ body {
|
|||||||
|
|
||||||
.btn-cta-big {
|
.btn-cta-big {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
font-size: 40px;
|
font-size: 27px;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
/* global expect */
|
|
||||||
import React from 'react';
|
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
|
||||||
|
|
||||||
import 'jest-dom/extend-expect';
|
|
||||||
import { LearnPage } from '../../pages/learn';
|
|
||||||
|
|
||||||
import Welcome from './';
|
|
||||||
|
|
||||||
import mockChallengeNodes from '../../__mocks__/challenge-nodes';
|
|
||||||
import mockIntroNodes from '../../__mocks__/intro-nodes';
|
|
||||||
|
|
||||||
describe('<Welcome />', () => {
|
|
||||||
it('renders when visiting index page and logged in', () => {
|
|
||||||
const shallow = new ShallowRenderer();
|
|
||||||
shallow.render(<LearnPage {...loggedInProps} />);
|
|
||||||
const result = shallow.getRenderOutput();
|
|
||||||
expect(
|
|
||||||
result.type.WrappedComponent.displayName === 'LearnLayout'
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a header', () => {
|
|
||||||
const container = renderer.create(<Welcome name={'Development User'} />)
|
|
||||||
.root;
|
|
||||||
expect(container.findAllByType('h1').length === 1).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has a blockquote', () => {
|
|
||||||
const container = renderer.create(<Welcome name={'Development User'} />)
|
|
||||||
.root;
|
|
||||||
expect(container.findAllByType('blockquote').length === 1).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const nodes = mockChallengeNodes.map(node => {
|
|
||||||
return { node };
|
|
||||||
});
|
|
||||||
const loggedInProps = {
|
|
||||||
fetchState: {
|
|
||||||
complete: true,
|
|
||||||
error: null,
|
|
||||||
errored: false,
|
|
||||||
pending: false
|
|
||||||
},
|
|
||||||
isSignedIn: true,
|
|
||||||
user: {
|
|
||||||
name: 'Development User'
|
|
||||||
},
|
|
||||||
location: { hash: '' },
|
|
||||||
data: {
|
|
||||||
challengeNode: {
|
|
||||||
fields: {
|
|
||||||
slug:
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
allChallengeNode: { edges: nodes },
|
|
||||||
allMarkdownRemark: { edges: [{ mdEdges: mockIntroNodes }] }
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,52 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import { Row, Col } from '@freecodecamp/react-bootstrap';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { Spacer } from '../helpers';
|
|
||||||
import { randomQuote } from '../../utils/get-words';
|
|
||||||
|
|
||||||
import './welcome.css';
|
|
||||||
|
|
||||||
// created outside the function, so that the quotes don't change unless the
|
|
||||||
// page is reloaded.
|
|
||||||
|
|
||||||
const { quote, author } = randomQuote();
|
|
||||||
|
|
||||||
function Welcome({ name }) {
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<Row>
|
|
||||||
<Col sm={10} smOffset={1} xs={12}>
|
|
||||||
<Spacer />
|
|
||||||
<h1 className='text-center big-heading'>
|
|
||||||
{name
|
|
||||||
? 'Welcome back, ' + name + '.'
|
|
||||||
: 'Welcome to freeCodeCamp.org'}
|
|
||||||
</h1>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Spacer />
|
|
||||||
<Row className='text-center 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>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
name: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
Welcome.propTypes = propTypes;
|
|
||||||
Welcome.displayName = 'Welcome';
|
|
||||||
|
|
||||||
export default Welcome;
|
|
@ -1,14 +0,0 @@
|
|||||||
.quote-partial .blockquote {
|
|
||||||
font-size: 1.3rem;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
|
||||||
.quote-partial .blockquote {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.quote-author {
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
@ -1,24 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Grid, Row, Col } from '@freecodecamp/react-bootstrap';
|
import { Grid } from '@freecodecamp/react-bootstrap';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { graphql } from 'gatsby';
|
import { graphql } from 'gatsby';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import LearnLayout from '../components/layouts/Learn';
|
||||||
|
import { dasherize } from '../../../utils/slugs';
|
||||||
|
import Map from '../components/Map';
|
||||||
|
import Intro from '../components/Intro';
|
||||||
import {
|
import {
|
||||||
userFetchStateSelector,
|
userFetchStateSelector,
|
||||||
isSignedInSelector,
|
isSignedInSelector,
|
||||||
userSelector
|
userSelector,
|
||||||
|
hardGoTo as navigate
|
||||||
} from '../redux';
|
} from '../redux';
|
||||||
|
|
||||||
import LearnLayout from '../components/layouts/Learn';
|
|
||||||
import Login from '../components/Header/components/Login';
|
|
||||||
import { Link, Spacer } from '../components/helpers';
|
|
||||||
import Map from '../components/Map';
|
|
||||||
import Welcome from '../components/welcome';
|
|
||||||
import { dasherize } from '../../../utils/slugs';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChallengeNode,
|
ChallengeNode,
|
||||||
AllChallengeNode,
|
AllChallengeNode,
|
||||||
@ -50,39 +47,30 @@ const propTypes = {
|
|||||||
hash: PropTypes.string,
|
hash: PropTypes.string,
|
||||||
isSignedIn: PropTypes.bool,
|
isSignedIn: PropTypes.bool,
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
|
navigate: PropTypes.func.isRequired,
|
||||||
state: PropTypes.object,
|
state: PropTypes.object,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
name: PropTypes.string
|
name: PropTypes.string,
|
||||||
|
username: PropTypes.string
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
const BigCallToAction = isSignedIn => {
|
|
||||||
if (!isSignedIn) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Row>
|
|
||||||
<Col sm={10} smOffset={1} xs={12}>
|
|
||||||
<Spacer />
|
|
||||||
<Login>Sign in to save your progress.</Login>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
};
|
|
||||||
|
|
||||||
// choose between the state from landing page and hash from url.
|
// choose between the state from landing page and hash from url.
|
||||||
const hashValueSelector = (state, hash) => {
|
const hashValueSelector = (state, hash) => {
|
||||||
if (state && state.superBlock) return dasherize(state.superBlock);
|
if (state && state.superBlock) return dasherize(state.superBlock);
|
||||||
else if (hash) return hash.substr(1);
|
else if (hash) return hash.substr(1);
|
||||||
else return null;
|
else return null;
|
||||||
};
|
};
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
navigate
|
||||||
|
};
|
||||||
|
|
||||||
export const LearnPage = ({
|
export const LearnPage = ({
|
||||||
location: { hash = '', state = '' },
|
location: { hash = '', state = '' },
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
user: { name = '' },
|
navigate,
|
||||||
|
fetchState: { pending, complete },
|
||||||
|
user: { name = '', username = '' },
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
fields: { slug }
|
fields: { slug }
|
||||||
@ -96,20 +84,19 @@ export const LearnPage = ({
|
|||||||
<LearnLayout>
|
<LearnLayout>
|
||||||
<Helmet title='Learn | freeCodeCamp.org' />
|
<Helmet title='Learn | freeCodeCamp.org' />
|
||||||
<Grid>
|
<Grid>
|
||||||
<Welcome name={name} />
|
<Intro
|
||||||
<Row className='text-center'>
|
complete={complete}
|
||||||
<Col sm={10} smOffset={1} xs={12}>
|
isSignedIn={isSignedIn}
|
||||||
{BigCallToAction(isSignedIn)}
|
name={name}
|
||||||
<Spacer />
|
navigate={navigate}
|
||||||
<h3>
|
pending={pending}
|
||||||
If you are new to coding, we recommend you{' '}
|
slug={slug}
|
||||||
<Link to={slug}>start at the beginning</Link>.
|
username={username}
|
||||||
</h3>
|
/>
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Map
|
<Map
|
||||||
hash={hashValue}
|
hash={hashValue}
|
||||||
introNodes={mdEdges.map(({ node }) => node)}
|
introNodes={mdEdges.map(({ node }) => node)}
|
||||||
|
isSignedIn={isSignedIn}
|
||||||
nodes={edges
|
nodes={edges
|
||||||
.map(({ node }) => node)
|
.map(({ node }) => node)
|
||||||
.filter(({ isPrivate }) => !isPrivate)}
|
.filter(({ isPrivate }) => !isPrivate)}
|
||||||
@ -122,7 +109,10 @@ export const LearnPage = ({
|
|||||||
LearnPage.displayName = 'LearnPage';
|
LearnPage.displayName = 'LearnPage';
|
||||||
LearnPage.propTypes = propTypes;
|
LearnPage.propTypes = propTypes;
|
||||||
|
|
||||||
export default connect(mapStateToProps)(LearnPage);
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(LearnPage);
|
||||||
|
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query FirstChallenge {
|
query FirstChallenge {
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import createRedirect from '../components/createRedirect';
|
|
||||||
import { apiLocation } from '../../config/env.json';
|
|
||||||
import {
|
|
||||||
signInLoadingSelector,
|
|
||||||
userSelector,
|
|
||||||
isSignedInSelector,
|
|
||||||
hardGoTo as navigate
|
|
||||||
} from '../redux';
|
|
||||||
|
|
||||||
import Loader from '../components/helpers/Loader';
|
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
|
||||||
signInLoadingSelector,
|
|
||||||
userSelector,
|
|
||||||
isSignedInSelector,
|
|
||||||
(showLoading, user, isSignedIn) => ({
|
|
||||||
showLoading,
|
|
||||||
user,
|
|
||||||
isSignedIn
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
navigate
|
|
||||||
};
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
isSignedIn: PropTypes.bool.isRequired,
|
|
||||||
navigate: PropTypes.func.isRequired,
|
|
||||||
showLoading: PropTypes.bool.isRequired,
|
|
||||||
user: PropTypes.shape({
|
|
||||||
username: PropTypes.string
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
function ProfilePage(props) {
|
|
||||||
const {
|
|
||||||
showLoading,
|
|
||||||
isSignedIn,
|
|
||||||
user: { username },
|
|
||||||
navigate
|
|
||||||
} = props;
|
|
||||||
if (showLoading) {
|
|
||||||
return <Loader fullScreen={true} />;
|
|
||||||
}
|
|
||||||
if (!showLoading && !isSignedIn) {
|
|
||||||
navigate(`${apiLocation}/signin?returnTo=portfolio`);
|
|
||||||
return <Loader fullScreen={true} />;
|
|
||||||
}
|
|
||||||
const RedirecUser = createRedirect('/' + username);
|
|
||||||
return <RedirecUser />;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProfilePage.displayName = 'profilePage';
|
|
||||||
ProfilePage.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(ProfilePage);
|
|
Reference in New Issue
Block a user