2018-03-26 13:01:24 +01:00
|
|
|
import React from 'react';
|
2018-04-06 14:51:52 +01:00
|
|
|
import PropTypes from 'prop-types';
|
2019-09-19 19:45:01 +03:00
|
|
|
import { createSelector } from 'reselect';
|
2019-02-21 20:55:36 +05:30
|
|
|
import { graphql } from 'gatsby';
|
2018-04-06 14:51:52 +01:00
|
|
|
import Helmet from 'react-helmet';
|
2018-05-18 14:54:21 +01:00
|
|
|
import { connect } from 'react-redux';
|
2019-09-19 19:45:01 +03:00
|
|
|
import { Row, Col } from '@freecodecamp/react-bootstrap';
|
2018-03-26 13:01:24 +01:00
|
|
|
|
2019-09-19 19:45:01 +03:00
|
|
|
import { userFetchStateSelector, isSignedInSelector } from '../redux';
|
2018-09-30 11:37:19 +01:00
|
|
|
|
|
|
|
import LearnLayout from '../components/layouts/Learn';
|
2019-09-19 19:45:01 +03:00
|
|
|
import Login from '../components/Header/components/Login';
|
|
|
|
import { Link, Spacer, Loader } from '../components/helpers';
|
2018-06-01 03:36:42 +05:30
|
|
|
import Map from '../components/Map';
|
2018-04-06 14:51:52 +01:00
|
|
|
|
2018-09-30 11:37:19 +01:00
|
|
|
import './learn.css';
|
2018-04-06 14:51:52 +01:00
|
|
|
|
2019-09-19 19:45:01 +03:00
|
|
|
import {
|
|
|
|
ChallengeNode,
|
|
|
|
AllChallengeNode,
|
|
|
|
AllMarkdownRemark
|
|
|
|
} from '../redux/propTypes';
|
|
|
|
|
|
|
|
const mapStateToProps = createSelector(
|
|
|
|
userFetchStateSelector,
|
|
|
|
isSignedInSelector,
|
|
|
|
(fetchState, isSignedIn) => ({
|
|
|
|
fetchState,
|
|
|
|
isSignedIn
|
|
|
|
})
|
|
|
|
);
|
2018-05-18 14:54:21 +01:00
|
|
|
|
2018-04-06 14:51:52 +01:00
|
|
|
const propTypes = {
|
|
|
|
data: PropTypes.shape({
|
2018-06-01 03:36:42 +05:30
|
|
|
challengeNode: ChallengeNode,
|
|
|
|
allChallengeNode: AllChallengeNode,
|
|
|
|
allMarkdownRemark: AllMarkdownRemark
|
2019-09-19 19:45:01 +03:00
|
|
|
}),
|
|
|
|
fetchState: PropTypes.shape({
|
|
|
|
pending: PropTypes.bool,
|
|
|
|
complete: PropTypes.bool,
|
|
|
|
errored: PropTypes.bool
|
|
|
|
}),
|
|
|
|
isSignedIn: PropTypes.bool
|
|
|
|
};
|
|
|
|
|
|
|
|
const BigCallToAction = isSignedIn => {
|
|
|
|
if (!isSignedIn) {
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Spacer size={2} />
|
|
|
|
<Row>
|
|
|
|
<Col sm={8} smOffset={2} xs={12}>
|
|
|
|
<Login className={'text-center'}>Sign in to save progress.</Login>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return '';
|
2018-04-06 14:51:52 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
const IndexPage = ({
|
2019-09-19 19:45:01 +03:00
|
|
|
fetchState: { pending, complete },
|
|
|
|
isSignedIn,
|
2018-06-01 03:36:42 +05:30
|
|
|
data: {
|
2018-09-30 11:37:19 +01:00
|
|
|
challengeNode: {
|
|
|
|
fields: { slug }
|
|
|
|
},
|
2018-06-01 03:36:42 +05:30
|
|
|
allChallengeNode: { edges },
|
|
|
|
allMarkdownRemark: { edges: mdEdges }
|
|
|
|
}
|
2019-09-19 19:45:01 +03:00
|
|
|
}) => {
|
|
|
|
if (pending && !complete) {
|
|
|
|
return <Loader fullScreen={true} />;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<LearnLayout>
|
|
|
|
<div className='learn-page-wrapper'>
|
|
|
|
<Helmet title='Learn | freeCodeCamp.org' />
|
|
|
|
{BigCallToAction(isSignedIn)}
|
|
|
|
<Spacer size={2} />
|
|
|
|
<h1 className='text-center'>Welcome to the freeCodeCamp curriculum</h1>
|
|
|
|
<p>
|
|
|
|
We have thousands of coding lessons to help you improve your skills.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
You can earn each certification by completing its 5 final projects.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
And yes - all of this is 100% free, thanks to the thousands of campers
|
|
|
|
who{' '}
|
|
|
|
<Link external={true} to='/donate'>
|
|
|
|
donate
|
|
|
|
</Link>{' '}
|
|
|
|
to our nonprofit.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
If you are new to coding, we recommend you{' '}
|
|
|
|
<Link to={slug}>start at the beginning</Link>.
|
|
|
|
</p>
|
|
|
|
<Map
|
|
|
|
introNodes={mdEdges.map(({ node }) => node)}
|
|
|
|
nodes={edges
|
|
|
|
.map(({ node }) => node)
|
|
|
|
.filter(({ isPrivate }) => !isPrivate)}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</LearnLayout>
|
|
|
|
);
|
|
|
|
};
|
2018-03-26 13:01:24 +01:00
|
|
|
|
2018-04-06 14:51:52 +01:00
|
|
|
IndexPage.displayName = 'IndexPage';
|
|
|
|
IndexPage.propTypes = propTypes;
|
|
|
|
|
2018-06-07 23:13:33 +01:00
|
|
|
export default connect(mapStateToProps)(IndexPage);
|
2018-04-06 14:51:52 +01:00
|
|
|
|
|
|
|
export const query = graphql`
|
|
|
|
query FirstChallenge {
|
2018-10-04 14:47:55 +01:00
|
|
|
challengeNode(order: { eq: 0 }, challengeOrder: { eq: 0 }) {
|
2018-04-06 14:51:52 +01:00
|
|
|
fields {
|
|
|
|
slug
|
2018-06-01 03:36:42 +05:30
|
|
|
}
|
|
|
|
}
|
2019-02-18 19:32:49 +00:00
|
|
|
allChallengeNode(sort: { fields: [superOrder, order, challengeOrder] }) {
|
2018-06-01 03:36:42 +05:30
|
|
|
edges {
|
|
|
|
node {
|
|
|
|
fields {
|
|
|
|
slug
|
|
|
|
blockName
|
|
|
|
}
|
|
|
|
id
|
|
|
|
block
|
|
|
|
title
|
|
|
|
isRequired
|
|
|
|
superBlock
|
|
|
|
dashedName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
allMarkdownRemark(filter: { frontmatter: { block: { ne: null } } }) {
|
|
|
|
edges {
|
|
|
|
node {
|
|
|
|
frontmatter {
|
|
|
|
title
|
|
|
|
block
|
|
|
|
}
|
|
|
|
fields {
|
|
|
|
slug
|
|
|
|
}
|
|
|
|
}
|
2018-04-06 14:51:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|