2021-02-13 05:05:07 +00:00
|
|
|
import React, { Component, Fragment } from 'react';
|
2018-05-18 11:59:12 +01:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import Helmet from 'react-helmet';
|
2018-09-11 16:10:21 +03:00
|
|
|
import { graphql } from 'gatsby';
|
2021-01-13 07:09:45 -06:00
|
|
|
import { uniq } from 'lodash';
|
2020-12-28 20:52:41 -06:00
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { createSelector } from 'reselect';
|
|
|
|
import { bindActionCreators } from 'redux';
|
2021-01-13 07:09:45 -06:00
|
|
|
import { withTranslation } from 'react-i18next';
|
2021-01-26 02:55:07 +09:00
|
|
|
import { Grid, Row, Col } from '@freecodecamp/react-bootstrap';
|
2021-02-11 12:22:04 -06:00
|
|
|
import { configureAnchors } from 'react-scrollable-anchor';
|
2021-01-13 07:09:45 -06:00
|
|
|
|
|
|
|
import Login from '../../components/Header/components/Login';
|
|
|
|
import Map from '../../components/Map';
|
|
|
|
import CertChallenge from './components/CertChallenge';
|
|
|
|
import SuperBlockIntro from './components/SuperBlockIntro';
|
|
|
|
import Block from './components/Block';
|
2021-02-11 13:40:32 +01:00
|
|
|
import { Spacer } from '../../components/helpers';
|
2020-12-28 20:52:41 -06:00
|
|
|
import {
|
2021-01-13 07:09:45 -06:00
|
|
|
currentChallengeIdSelector,
|
|
|
|
userFetchStateSelector,
|
|
|
|
isSignedInSelector
|
|
|
|
} from '../../redux';
|
|
|
|
import { resetExpansion, toggleBlock } from './redux';
|
|
|
|
import { MarkdownRemark, AllChallengeNode } from '../../redux/propTypes';
|
2020-12-28 20:52:41 -06:00
|
|
|
|
|
|
|
import './intro.css';
|
2018-05-18 11:59:12 +01:00
|
|
|
|
|
|
|
const propTypes = {
|
2020-12-28 20:52:41 -06:00
|
|
|
currentChallengeId: PropTypes.string,
|
2018-05-18 11:59:12 +01:00
|
|
|
data: PropTypes.shape({
|
2020-12-28 20:52:41 -06:00
|
|
|
markdownRemark: MarkdownRemark,
|
2021-01-13 07:09:45 -06:00
|
|
|
allChallengeNode: AllChallengeNode
|
2020-12-28 20:52:41 -06:00
|
|
|
}),
|
|
|
|
expandedState: PropTypes.object,
|
2021-01-13 07:09:45 -06:00
|
|
|
fetchState: PropTypes.shape({
|
|
|
|
pending: PropTypes.bool,
|
|
|
|
complete: PropTypes.bool,
|
|
|
|
errored: PropTypes.bool
|
|
|
|
}),
|
2020-12-28 20:52:41 -06:00
|
|
|
isSignedIn: PropTypes.bool,
|
2021-01-13 07:09:45 -06:00
|
|
|
location: PropTypes.shape({
|
2021-02-11 12:22:04 -06:00
|
|
|
hash: PropTypes.string,
|
2021-01-13 07:09:45 -06:00
|
|
|
state: PropTypes.shape({
|
|
|
|
breadcrumbBlockClick: PropTypes.string
|
|
|
|
})
|
|
|
|
}),
|
2020-12-28 20:52:41 -06:00
|
|
|
resetExpansion: PropTypes.func,
|
2021-01-13 07:09:45 -06:00
|
|
|
t: PropTypes.func,
|
2020-12-28 20:52:41 -06:00
|
|
|
toggleBlock: PropTypes.func
|
|
|
|
};
|
|
|
|
|
2021-02-11 12:22:04 -06:00
|
|
|
configureAnchors({ offset: -40, scrollDuration: 0 });
|
|
|
|
|
2020-12-28 20:52:41 -06:00
|
|
|
const mapStateToProps = state => {
|
|
|
|
return createSelector(
|
|
|
|
currentChallengeIdSelector,
|
|
|
|
isSignedInSelector,
|
2021-01-13 07:09:45 -06:00
|
|
|
userFetchStateSelector,
|
|
|
|
(currentChallengeId, isSignedIn, fetchState) => ({
|
2020-12-28 20:52:41 -06:00
|
|
|
currentChallengeId,
|
2021-01-13 07:09:45 -06:00
|
|
|
isSignedIn,
|
|
|
|
fetchState
|
2020-12-28 20:52:41 -06:00
|
|
|
})
|
|
|
|
)(state);
|
2018-05-18 11:59:12 +01:00
|
|
|
};
|
|
|
|
|
2020-12-28 20:52:41 -06:00
|
|
|
const mapDispatchToProps = dispatch =>
|
|
|
|
bindActionCreators(
|
|
|
|
{ resetExpansion, toggleBlock: b => toggleBlock(b) },
|
|
|
|
dispatch
|
|
|
|
);
|
|
|
|
|
|
|
|
export class SuperBlockIntroductionPage extends Component {
|
2021-01-13 07:09:45 -06:00
|
|
|
componentDidMount() {
|
2020-12-28 20:52:41 -06:00
|
|
|
this.initializeExpandedState();
|
2021-02-11 12:22:04 -06:00
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
configureAnchors({ offset: -40, scrollDuration: 400 });
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
configureAnchors({ offset: -40, scrollDuration: 0 });
|
2021-01-13 07:09:45 -06:00
|
|
|
}
|
|
|
|
|
2021-01-29 07:21:52 -06:00
|
|
|
getChosenBlock() {
|
2020-12-28 20:52:41 -06:00
|
|
|
const {
|
|
|
|
data: {
|
|
|
|
allChallengeNode: { edges }
|
|
|
|
},
|
|
|
|
isSignedIn,
|
|
|
|
currentChallengeId,
|
2021-01-13 07:09:45 -06:00
|
|
|
location
|
2020-12-28 20:52:41 -06:00
|
|
|
} = this.props;
|
|
|
|
|
2021-01-13 07:09:45 -06:00
|
|
|
// if coming from breadcrumb click
|
2021-02-11 12:22:04 -06:00
|
|
|
if (location.state && location.state.breadcrumbBlockClick) {
|
2021-02-23 05:22:48 +01:00
|
|
|
return location.state.breadcrumbBlockClick;
|
2021-02-11 12:22:04 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// if the URL includes a hash
|
|
|
|
if (location.hash) {
|
|
|
|
const dashedBlock = location.hash.replace('#', '').replace('/', '');
|
|
|
|
return dashedBlock;
|
|
|
|
}
|
2020-12-28 20:52:41 -06:00
|
|
|
|
2021-01-13 07:09:45 -06:00
|
|
|
let edge = edges[0];
|
2020-12-28 20:52:41 -06:00
|
|
|
|
|
|
|
if (isSignedIn) {
|
|
|
|
// see if currentChallenge is in this superBlock
|
2021-01-13 07:09:45 -06:00
|
|
|
const currentChallengeEdge = edges.find(
|
|
|
|
edge => edge.node.id === currentChallengeId
|
|
|
|
);
|
2020-12-28 20:52:41 -06:00
|
|
|
|
2021-01-13 07:09:45 -06:00
|
|
|
return currentChallengeEdge
|
|
|
|
? currentChallengeEdge.node.block
|
|
|
|
: edge.node.block;
|
2020-12-28 20:52:41 -06:00
|
|
|
}
|
|
|
|
|
2021-01-29 07:21:52 -06:00
|
|
|
return edge.node.block;
|
2021-01-13 07:09:45 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
initializeExpandedState() {
|
|
|
|
const { resetExpansion, toggleBlock } = this.props;
|
|
|
|
|
|
|
|
resetExpansion();
|
|
|
|
return toggleBlock(this.getChosenBlock());
|
2020-12-28 20:52:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const {
|
|
|
|
data: {
|
|
|
|
markdownRemark: {
|
|
|
|
frontmatter: { superBlock }
|
|
|
|
},
|
2021-01-13 07:09:45 -06:00
|
|
|
allChallengeNode: { edges }
|
|
|
|
},
|
|
|
|
isSignedIn,
|
|
|
|
t
|
2020-12-28 20:52:41 -06:00
|
|
|
} = this.props;
|
|
|
|
|
2021-01-13 07:09:45 -06:00
|
|
|
const nodesForSuperBlock = edges.map(({ node }) => node);
|
|
|
|
const blockDashedNames = uniq(nodesForSuperBlock.map(({ block }) => block));
|
|
|
|
|
2021-02-13 06:06:04 +01:00
|
|
|
const i18nSuperBlock = t(`intro:${superBlock}.title`);
|
2021-01-13 07:09:45 -06:00
|
|
|
|
2020-12-28 20:52:41 -06:00
|
|
|
return (
|
2021-01-13 07:09:45 -06:00
|
|
|
<>
|
2020-12-28 20:52:41 -06:00
|
|
|
<Helmet>
|
2021-01-13 07:09:45 -06:00
|
|
|
<title>{i18nSuperBlock} | freeCodeCamp.org</title>
|
2020-12-28 20:52:41 -06:00
|
|
|
</Helmet>
|
2021-01-26 02:55:07 +09:00
|
|
|
<Grid>
|
2021-01-29 07:21:52 -06:00
|
|
|
<Row className='super-block-intro-page'>
|
2021-01-26 02:55:07 +09:00
|
|
|
<Col md={8} mdOffset={2} sm={10} smOffset={1} xs={12}>
|
2021-02-11 13:40:32 +01:00
|
|
|
<Spacer size={2} />
|
2021-01-26 02:55:07 +09:00
|
|
|
<SuperBlockIntro superBlock={superBlock} />
|
|
|
|
<Spacer size={2} />
|
|
|
|
<h2 className='text-center big-subheading'>
|
|
|
|
{t(`intro:misc-text.courses`)}
|
|
|
|
</h2>
|
|
|
|
<Spacer />
|
|
|
|
<div className='block-ui'>
|
|
|
|
{blockDashedNames.map(blockDashedName => (
|
2021-02-13 05:05:07 +00:00
|
|
|
<Fragment key={blockDashedName}>
|
2021-01-26 02:55:07 +09:00
|
|
|
<Block
|
|
|
|
blockDashedName={blockDashedName}
|
|
|
|
challenges={nodesForSuperBlock.filter(
|
|
|
|
node => node.block === blockDashedName
|
|
|
|
)}
|
2021-02-13 06:06:04 +01:00
|
|
|
superBlockDashedName={superBlock}
|
2021-01-26 02:55:07 +09:00
|
|
|
/>
|
2021-02-11 12:22:04 -06:00
|
|
|
{blockDashedName !== 'project-euler' ? <Spacer /> : null}
|
2021-02-13 05:05:07 +00:00
|
|
|
</Fragment>
|
2021-01-26 02:55:07 +09:00
|
|
|
))}
|
2021-02-13 06:06:04 +01:00
|
|
|
{superBlock !== 'coding-interview-prep' && (
|
2021-01-26 02:55:07 +09:00
|
|
|
<div>
|
|
|
|
<CertChallenge superBlock={superBlock} />
|
|
|
|
</div>
|
|
|
|
)}
|
2021-01-13 07:09:45 -06:00
|
|
|
</div>
|
2021-01-26 02:55:07 +09:00
|
|
|
{!isSignedIn && (
|
|
|
|
<div>
|
|
|
|
<Spacer size={2} />
|
|
|
|
<Login block={true}>{t('buttons.logged-out-cta-btn')}</Login>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
<Spacer size={2} />
|
|
|
|
<h3
|
|
|
|
className='text-center big-block-title'
|
|
|
|
style={{ whiteSpace: 'pre-line' }}
|
|
|
|
>
|
|
|
|
{t(`intro:misc-text.browse-other`)}
|
|
|
|
</h3>
|
|
|
|
<Spacer />
|
|
|
|
<Map currentSuperBlock={superBlock} />
|
|
|
|
<Spacer size={2} />
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
</Grid>
|
2021-01-13 07:09:45 -06:00
|
|
|
</>
|
2020-12-28 20:52:41 -06:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-18 11:59:12 +01:00
|
|
|
SuperBlockIntroductionPage.displayName = 'SuperBlockIntroductionPage';
|
|
|
|
SuperBlockIntroductionPage.propTypes = propTypes;
|
|
|
|
|
2020-12-28 20:52:41 -06:00
|
|
|
export default connect(
|
|
|
|
mapStateToProps,
|
|
|
|
mapDispatchToProps
|
2021-01-13 07:09:45 -06:00
|
|
|
)(withTranslation()(SuperBlockIntroductionPage));
|
2018-05-18 11:59:12 +01:00
|
|
|
|
|
|
|
export const query = graphql`
|
2020-12-28 20:52:41 -06:00
|
|
|
query SuperBlockIntroPageBySlug($slug: String!, $superBlock: String!) {
|
2018-05-18 11:59:12 +01:00
|
|
|
markdownRemark(fields: { slug: { eq: $slug } }) {
|
|
|
|
frontmatter {
|
|
|
|
superBlock
|
|
|
|
}
|
2020-12-28 20:52:41 -06:00
|
|
|
}
|
|
|
|
allChallengeNode(
|
|
|
|
sort: { fields: [superOrder, order, challengeOrder] }
|
|
|
|
filter: { superBlock: { eq: $superBlock } }
|
|
|
|
) {
|
|
|
|
edges {
|
|
|
|
node {
|
|
|
|
fields {
|
|
|
|
slug
|
|
|
|
blockName
|
|
|
|
}
|
|
|
|
id
|
|
|
|
block
|
2021-01-13 07:09:45 -06:00
|
|
|
challengeType
|
2020-12-28 20:52:41 -06:00
|
|
|
title
|
|
|
|
order
|
|
|
|
superBlock
|
|
|
|
dashedName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-18 11:59:12 +01:00
|
|
|
}
|
|
|
|
`;
|