chore(learn): Merge learn in to the client app
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -14,3 +14,5 @@ node_modules
 | 
			
		||||
*.swp
 | 
			
		||||
 | 
			
		||||
.DS_Store
 | 
			
		||||
 | 
			
		||||
curriculum/dist/
 | 
			
		||||
@@ -1,12 +1,16 @@
 | 
			
		||||
const path = require('path');
 | 
			
		||||
 | 
			
		||||
const { buildChallenges$ } = require('./utils/buildChallenges');
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  siteMetadata: {
 | 
			
		||||
    title: 'freeCodeCamp',
 | 
			
		||||
    siteUrl: 'https://www.freecodecamp.org'
 | 
			
		||||
  },
 | 
			
		||||
  proxy: {
 | 
			
		||||
    prefix: '/internal',
 | 
			
		||||
    url: 'http://localhost:3000'
 | 
			
		||||
  },
 | 
			
		||||
  // proxy: {
 | 
			
		||||
  //   prefix: '/internal',
 | 
			
		||||
  //   url: 'http://localhost:3000'
 | 
			
		||||
  // },
 | 
			
		||||
  plugins: [
 | 
			
		||||
    'gatsby-plugin-react-helmet',
 | 
			
		||||
    {
 | 
			
		||||
@@ -20,16 +24,64 @@ module.exports = {
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      resolve: 'fcc-source-challenges',
 | 
			
		||||
      options: {
 | 
			
		||||
        name: 'challenges',
 | 
			
		||||
        source: buildChallenges$
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      resolve: 'gatsby-source-filesystem',
 | 
			
		||||
      options: {
 | 
			
		||||
        name: 'introductions',
 | 
			
		||||
        path: path.resolve(__dirname, './src/introductions')
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      resolve: 'gatsby-transformer-remark',
 | 
			
		||||
      options: {
 | 
			
		||||
        plugins: [
 | 
			
		||||
          {
 | 
			
		||||
            resolve: 'gatsby-remark-prismjs',
 | 
			
		||||
            options: {
 | 
			
		||||
              // Class prefix for <pre> tags containing syntax highlighting;
 | 
			
		||||
              // defaults to 'language-' (eg <pre class="language-js">).
 | 
			
		||||
              // If your site loads Prism into the browser at runtime,
 | 
			
		||||
              // (eg for use with libraries like react-live),
 | 
			
		||||
              // you may use this to prevent Prism from re-processing syntax.
 | 
			
		||||
              // This is an uncommon use-case though;
 | 
			
		||||
              // If you're unsure, it's best to use the default value.
 | 
			
		||||
              classPrefix: 'language-',
 | 
			
		||||
              // This is used to allow setting a language for inline code
 | 
			
		||||
              // (i.e. single backticks) by creating a separator.
 | 
			
		||||
              // This separator is a string and will do no white-space
 | 
			
		||||
              // stripping.
 | 
			
		||||
              // A suggested value for English speakers is the non-ascii
 | 
			
		||||
              // character '›'.
 | 
			
		||||
              inlineCodeMarker: null,
 | 
			
		||||
              // This lets you set up language aliases.  For example,
 | 
			
		||||
              // setting this to '{ sh: "bash" }' will let you use
 | 
			
		||||
              // the language "sh" which will highlight using the
 | 
			
		||||
              // bash highlighter.
 | 
			
		||||
              aliases: {}
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      resolve: 'gatsby-plugin-manifest',
 | 
			
		||||
      options: {
 | 
			
		||||
        name: 'freeCodeCamp',
 | 
			
		||||
        /* eslint-disable camelcase */
 | 
			
		||||
        short_name: 'fCC',
 | 
			
		||||
        start_url: '/',
 | 
			
		||||
        background_color: '#fff',
 | 
			
		||||
        theme_color: '#006400',
 | 
			
		||||
        background_color: '#fff',
 | 
			
		||||
        /* eslint-enable camelcase */
 | 
			
		||||
        display: 'minimal-ui',
 | 
			
		||||
        icon: 'src/images/square_puck.png' // This path is relative to the root of the site.
 | 
			
		||||
        icon: 'src/images/square_puck.png'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    'gatsby-plugin-sitemap'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,178 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Implement Gatsby's Node APIs in this file.
 | 
			
		||||
 *
 | 
			
		||||
 * See: https://www.gatsbyjs.org/docs/node-apis/
 | 
			
		||||
 */
 | 
			
		||||
require('dotenv').config();
 | 
			
		||||
 | 
			
		||||
// You can delete this file if you're not using it
 | 
			
		||||
const { dasherize } = require('./utils');
 | 
			
		||||
const { blockNameify } = require('./utils/blockNameify');
 | 
			
		||||
const { createChallengePages, createIntroPages } = require('./utils/gatsby');
 | 
			
		||||
 | 
			
		||||
exports.onCreateNode = function onCreateNode({ node, actions }) {
 | 
			
		||||
  const { createNodeField } = actions;
 | 
			
		||||
  if (node.internal.type === 'ChallengeNode') {
 | 
			
		||||
    const { tests = [], block, title, superBlock } = node;
 | 
			
		||||
 | 
			
		||||
    const slug = `/learn/${dasherize(superBlock)}/${dasherize(
 | 
			
		||||
      block
 | 
			
		||||
    )}/${dasherize(title)}`;
 | 
			
		||||
    createNodeField({ node, name: 'slug', value: slug });
 | 
			
		||||
    createNodeField({ node, name: 'blockName', value: blockNameify(block) });
 | 
			
		||||
    createNodeField({ node, name: 'tests', value: tests });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (node.internal.type === 'MarkdownRemark') {
 | 
			
		||||
    // console.log(node);
 | 
			
		||||
    const {
 | 
			
		||||
      frontmatter: { block, superBlock }
 | 
			
		||||
    } = node;
 | 
			
		||||
 | 
			
		||||
    let slug = `/${dasherize(superBlock)}`;
 | 
			
		||||
 | 
			
		||||
    // Without this condition the slug for superblocks ends up as something like
 | 
			
		||||
    // "/apis-and-microservice/undefined" and what we want instead is just
 | 
			
		||||
    // "/apis-and-microservice"
 | 
			
		||||
    if (typeof block !== 'undefined') {
 | 
			
		||||
      slug = slug + `/${dasherize(block)}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createNodeField({ node, name: 'slug', value: slug });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.createPages = ({ graphql, actions }) => {
 | 
			
		||||
  const { createPage } = actions;
 | 
			
		||||
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    // Query for all markdown 'nodes' and for the slug we previously created.
 | 
			
		||||
    resolve(
 | 
			
		||||
      graphql(`
 | 
			
		||||
        {
 | 
			
		||||
          allChallengeNode(sort: { fields: [superOrder, order, suborder] }) {
 | 
			
		||||
            edges {
 | 
			
		||||
              node {
 | 
			
		||||
                block
 | 
			
		||||
                challengeType
 | 
			
		||||
                fields {
 | 
			
		||||
                  slug
 | 
			
		||||
                }
 | 
			
		||||
                id
 | 
			
		||||
                order
 | 
			
		||||
                required {
 | 
			
		||||
                  link
 | 
			
		||||
                  raw
 | 
			
		||||
                  src
 | 
			
		||||
                }
 | 
			
		||||
                suborder
 | 
			
		||||
                superBlock
 | 
			
		||||
                superOrder
 | 
			
		||||
                template
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          allMarkdownRemark {
 | 
			
		||||
            edges {
 | 
			
		||||
              node {
 | 
			
		||||
                fields {
 | 
			
		||||
                  slug
 | 
			
		||||
                }
 | 
			
		||||
                frontmatter {
 | 
			
		||||
                  block
 | 
			
		||||
                  superBlock
 | 
			
		||||
                  title
 | 
			
		||||
                }
 | 
			
		||||
                html
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      `).then(result => {
 | 
			
		||||
        if (result.errors) {
 | 
			
		||||
          console.log(result.errors);
 | 
			
		||||
          reject(result.errors);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Create challenge pages.
 | 
			
		||||
        result.data.allChallengeNode.edges.forEach(
 | 
			
		||||
          createChallengePages(createPage)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Create intro pages
 | 
			
		||||
        result.data.allMarkdownRemark.edges.forEach(
 | 
			
		||||
          createIntroPages(createPage)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const RmServiceWorkerPlugin = require('webpack-remove-serviceworker-plugin');
 | 
			
		||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
 | 
			
		||||
 | 
			
		||||
exports.onCreateWebpackConfig = ({ stage, rules, plugins, actions }) => {
 | 
			
		||||
  actions.setWebpackConfig({
 | 
			
		||||
    module: {
 | 
			
		||||
      rules: [
 | 
			
		||||
        rules.js({
 | 
			
		||||
          /* eslint-disable max-len */
 | 
			
		||||
          exclude: modulePath => {
 | 
			
		||||
            return (
 | 
			
		||||
              (/node_modules/).test(modulePath) &&
 | 
			
		||||
              !(/(ansi-styles|chalk|strict-uri-encode|react-freecodecamp-search)/).test(
 | 
			
		||||
                modulePath
 | 
			
		||||
              )
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
          /* eslint-enable max-len*/
 | 
			
		||||
        })
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    node: {
 | 
			
		||||
      fs: 'empty'
 | 
			
		||||
    },
 | 
			
		||||
    plugins: [
 | 
			
		||||
      plugins.define({
 | 
			
		||||
        HOME_PATH: JSON.stringify(
 | 
			
		||||
          process.env.HOME_PATH || 'http://localhost:3000'
 | 
			
		||||
        ),
 | 
			
		||||
        STRIPE_PUBLIC_KEY: JSON.stringify(process.env.STRIPE_PUBLIC_KEY || '')
 | 
			
		||||
      }),
 | 
			
		||||
      new RmServiceWorkerPlugin()
 | 
			
		||||
    ]
 | 
			
		||||
  });
 | 
			
		||||
  if (stage !== 'build-html') {
 | 
			
		||||
    actions.setWebpackConfig({
 | 
			
		||||
      plugins: [new MonacoWebpackPlugin()]
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  if (stage === 'build-html') {
 | 
			
		||||
    actions.setWebpackConfig({
 | 
			
		||||
      plugins: [
 | 
			
		||||
        plugins.normalModuleReplacement(
 | 
			
		||||
          /react-monaco-editor/,
 | 
			
		||||
          require.resolve('./src/__mocks__/monacoEditorMock.js')
 | 
			
		||||
        )
 | 
			
		||||
      ]
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
exports.onCreateBabelConfig = ({ actions }) => {
 | 
			
		||||
  actions.setBabelPlugin({
 | 
			
		||||
    name: '@babel/plugin-proposal-function-bind'
 | 
			
		||||
  });
 | 
			
		||||
  actions.setBabelPlugin({
 | 
			
		||||
    name: '@babel/plugin-proposal-export-default-from'
 | 
			
		||||
  });
 | 
			
		||||
  actions.setBabelPlugin({
 | 
			
		||||
    name: 'babel-plugin-transform-imports',
 | 
			
		||||
    options: {
 | 
			
		||||
      '@freecodecamp/react-bootstrap': {
 | 
			
		||||
        transform: '@freecodecamp/react-bootstrap/lib/${member}',
 | 
			
		||||
        preventFullImport: true
 | 
			
		||||
      },
 | 
			
		||||
      lodash: {
 | 
			
		||||
        transform: 'lodash/${member}',
 | 
			
		||||
        preventFullImport: true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Provider } from 'react-redux';
 | 
			
		||||
 | 
			
		||||
import headComponents from './src/head';
 | 
			
		||||
import { createStore } from './src/redux/createStore';
 | 
			
		||||
 | 
			
		||||
const store = createStore();
 | 
			
		||||
@@ -13,3 +14,31 @@ export const wrapRootElement = ({ element }) => {
 | 
			
		||||
wrapRootElement.propTypes = {
 | 
			
		||||
  element: PropTypes.any
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const onRenderBody = ({ setHeadComponents, setPostBodyComponents }) => {
 | 
			
		||||
  setHeadComponents([...headComponents]);
 | 
			
		||||
  setPostBodyComponents([
 | 
			
		||||
    <script
 | 
			
		||||
      async={true}
 | 
			
		||||
      key='chai-CDN'
 | 
			
		||||
      src='https://cdnjs.cloudflare.com/ajax/libs/chai/4.1.2/chai.min.js'
 | 
			
		||||
    />,
 | 
			
		||||
    <script
 | 
			
		||||
      async={true}
 | 
			
		||||
      key='gtag-script'
 | 
			
		||||
      src='https://www.googletagmanager.com/gtag/js?id=AW-795617839'
 | 
			
		||||
    />,
 | 
			
		||||
    <script
 | 
			
		||||
      dangerouslySetInnerHTML={{
 | 
			
		||||
        __html: `
 | 
			
		||||
          window.dataLayer = window.dataLayer || [];
 | 
			
		||||
          function gtag(){dataLayer.push(arguments);}
 | 
			
		||||
          gtag('js', new Date());
 | 
			
		||||
          gtag('config', 'AW-795617839');
 | 
			
		||||
        `
 | 
			
		||||
      }}
 | 
			
		||||
      key='gtag-dataLayer'
 | 
			
		||||
    />,
 | 
			
		||||
    <script async={true} id='stripe-js' src='https://js.stripe.com/v3/' />
 | 
			
		||||
  ]);
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,8 @@ const babelOptions = {
 | 
			
		||||
    '@babel/plugin-proposal-function-bind',
 | 
			
		||||
    [
 | 
			
		||||
      'transform-imports', {
 | 
			
		||||
        'react-bootstrap': {
 | 
			
		||||
          transform: 'react-bootstrap/lib/${member}',
 | 
			
		||||
        '@freecodecamp/react-bootstrap': {
 | 
			
		||||
          transform: '@freecodecamp/react-bootstrap/lib/${member}',
 | 
			
		||||
          preventFullImport: true
 | 
			
		||||
        },
 | 
			
		||||
        lodash: {
 | 
			
		||||
							
								
								
									
										10081
									
								
								client/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10081
									
								
								client/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -4,58 +4,88 @@
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "author": "freeCodeCamp <team@freecodecamp.org>",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@babel/plugin-proposal-export-default-from": "^7.0.0",
 | 
			
		||||
    "@babel/plugin-proposal-function-bind": "^7.0.0",
 | 
			
		||||
    "@babel/standalone": "^7.1.0",
 | 
			
		||||
    "@fortawesome/fontawesome": "^1.1.8",
 | 
			
		||||
    "@fortawesome/fontawesome-svg-core": "^1.2.2",
 | 
			
		||||
    "@fortawesome/free-brands-svg-icons": "^5.2.0",
 | 
			
		||||
    "@fortawesome/free-regular-svg-icons": "^5.2.0",
 | 
			
		||||
    "@fortawesome/free-solid-svg-icons": "^5.2.0",
 | 
			
		||||
    "@fortawesome/react-fontawesome": "0.0.20",
 | 
			
		||||
    "@freecodecamp/curriculum": "^3.2.1",
 | 
			
		||||
    "@freecodecamp/react-bootstrap": "^0.32.3",
 | 
			
		||||
    "@reach/router": "^1.1.1",
 | 
			
		||||
    "axios": "^0.18.0",
 | 
			
		||||
    "browser-cookies": "^1.2.0",
 | 
			
		||||
    "chai": "^4.2.0",
 | 
			
		||||
    "enzyme": "^3.6.0",
 | 
			
		||||
    "enzyme-adapter-react-16": "^1.5.0",
 | 
			
		||||
    "fetchr": "^0.5.37",
 | 
			
		||||
    "gatsby": "^2.0.0-rc.15",
 | 
			
		||||
    "gatsby-link": "^2.0.0-rc.2",
 | 
			
		||||
    "gatsby-plugin-create-client-paths": "^2.0.0-rc.1",
 | 
			
		||||
    "gatsby-plugin-manifest": "next",
 | 
			
		||||
    "gatsby-plugin-react-helmet": "^3.0.0-rc.1",
 | 
			
		||||
    "gatsby-plugin-sitemap": "^2.0.0-rc.1",
 | 
			
		||||
    "gatsby-remark-prismjs": "^3.0.0-rc.2",
 | 
			
		||||
    "gatsby-source-filesystem": "^2.0.1-rc.6",
 | 
			
		||||
    "gatsby-transformer-remark": "^2.1.1-rc.5",
 | 
			
		||||
    "lodash": "^4.17.10",
 | 
			
		||||
    "loop-protect": "^2.1.6",
 | 
			
		||||
    "monaco-editor-webpack-plugin": "^1.5.4",
 | 
			
		||||
    "nanoid": "^1.2.2",
 | 
			
		||||
    "prismjs": "^1.15.0",
 | 
			
		||||
    "query-string": "^6.1.0",
 | 
			
		||||
    "react": "^16.4.2",
 | 
			
		||||
    "react-dom": "^16.4.2",
 | 
			
		||||
    "react-freecodecamp-search": "^2.0.2",
 | 
			
		||||
    "react-ga": "^2.5.3",
 | 
			
		||||
    "react-helmet": "^5.2.0",
 | 
			
		||||
    "react-media": "^1.8.0",
 | 
			
		||||
    "react-monaco-editor": "^0.18.0",
 | 
			
		||||
    "react-redux": "^5.0.7",
 | 
			
		||||
    "react-reflex": "^2.2.9",
 | 
			
		||||
    "react-spinkit": "^3.0.0",
 | 
			
		||||
    "react-stripe-elements": "^2.0.1",
 | 
			
		||||
    "redux": "^4.0.0",
 | 
			
		||||
    "redux-actions": "^2.6.1",
 | 
			
		||||
    "redux-devtools-extension": "^2.13.5",
 | 
			
		||||
    "redux-form": "^5.3.6",
 | 
			
		||||
    "redux-observable": "^1.0.0",
 | 
			
		||||
    "redux-saga": "^0.16.0",
 | 
			
		||||
    "reselect": "^3.0.1",
 | 
			
		||||
    "rxjs": "^6.3.3",
 | 
			
		||||
    "sinon": "^6.3.4",
 | 
			
		||||
    "store": "^2.0.12",
 | 
			
		||||
    "validator": "^10.7.0"
 | 
			
		||||
    "validator": "^10.7.0",
 | 
			
		||||
    "webpack-remove-serviceworker-plugin": "^1.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "gatsby"
 | 
			
		||||
  ],
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "build": "node ../config/ensure-env.js && gatsby build",
 | 
			
		||||
    "prebuild": "npm run build:frame-runner && node ../tools/scripts/ensure-env.js",
 | 
			
		||||
    "build": "gatsby build",
 | 
			
		||||
    "build:frame-runner": "webpack --env.production --config ./webpack-frame-runner.js",
 | 
			
		||||
    "predevelop": "npm run prebuild",
 | 
			
		||||
    "develop": "gatsby develop",
 | 
			
		||||
    "format:gatsby": "prettier-eslint --write --trailing-comma none --single-quote './gatsby-*.js'",
 | 
			
		||||
    "format:src": "prettier-eslint --write --trailing-comma none --single-quote './src/**/*.js'",
 | 
			
		||||
    "format:utils": "prettier-eslint --write --trailing-comma none --single-quote './utils/**/*.js'",
 | 
			
		||||
    "format": "npm run format:gatsby && npm run format:src && npm run format:utils",
 | 
			
		||||
    "test": "echo \"Error: no test specified\" && exit 1"
 | 
			
		||||
    "test": "jest"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "babel-plugin-transform-imports": "^1.5.0",
 | 
			
		||||
    "eslint": "^5.5.0",
 | 
			
		||||
    "eslint-config-freecodecamp": "^1.1.1",
 | 
			
		||||
    "jest": "^23.6.0",
 | 
			
		||||
    "prettier": "^1.14.2",
 | 
			
		||||
    "prettier-eslint-cli": "^4.7.1"
 | 
			
		||||
    "prettier-eslint-cli": "^4.7.1",
 | 
			
		||||
    "react-test-renderer": "^16.5.2",
 | 
			
		||||
    "webpack-cli": "^3.1.1"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								client/plugins/fcc-source-challenges/gatsby-node.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								client/plugins/fcc-source-challenges/gatsby-node.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
const { createChallengeNodes } = require('./create-Challenge-nodes');
 | 
			
		||||
 | 
			
		||||
exports.sourceNodes = ({ actions, reporter }, pluginOptions) => {
 | 
			
		||||
  if (typeof pluginOptions.source !== 'function') {
 | 
			
		||||
    reporter.panic(`
 | 
			
		||||
"source" is a required option for fcc-source-challenges. It must be a function
 | 
			
		||||
that delivers challenge files to the plugin
 | 
			
		||||
      `);
 | 
			
		||||
  }
 | 
			
		||||
  // TODO: Add live seed updates
 | 
			
		||||
  const { createNode } = actions;
 | 
			
		||||
 | 
			
		||||
  const { source } = pluginOptions;
 | 
			
		||||
  return source().subscribe(
 | 
			
		||||
    challenges =>
 | 
			
		||||
      challenges
 | 
			
		||||
        .filter(challenge => challenge.superBlock !== 'Certificates')
 | 
			
		||||
        .map(challenge => createChallengeNodes(challenge, reporter))
 | 
			
		||||
        .map(node => createNode(node)),
 | 
			
		||||
    e =>
 | 
			
		||||
      reporter.panic(`fcc-sourec-challenges
 | 
			
		||||
 | 
			
		||||
      ${e.message}
 | 
			
		||||
 | 
			
		||||
      `)
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1
									
								
								client/plugins/fcc-source-challenges/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								client/plugins/fcc-source-challenges/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
{"name": "fcc-source-challenges"}
 | 
			
		||||
@@ -6,11 +6,15 @@ import { createSelector } from 'reselect';
 | 
			
		||||
import { Grid, Button } from '@freecodecamp/react-bootstrap';
 | 
			
		||||
import Helmet from 'react-helmet';
 | 
			
		||||
 | 
			
		||||
import { signInLoadingSelector, userSelector } from '../redux';
 | 
			
		||||
import {
 | 
			
		||||
  signInLoadingSelector,
 | 
			
		||||
  userSelector,
 | 
			
		||||
  isSignedInSelector
 | 
			
		||||
} from '../redux';
 | 
			
		||||
import { submitNewAbout, updateUserFlag, verifyCert } from '../redux/settings';
 | 
			
		||||
import { createFlashMessage } from '../components/Flash/redux';
 | 
			
		||||
 | 
			
		||||
import Layout from '../components/Layout';
 | 
			
		||||
import Layout from '../components/layouts/Default';
 | 
			
		||||
import Spacer from '../components/helpers/Spacer';
 | 
			
		||||
import Loader from '../components/helpers/Loader';
 | 
			
		||||
import FullWidthRow from '../components/helpers/FullWidthRow';
 | 
			
		||||
@@ -21,9 +25,11 @@ import Internet from '../components/settings/Internet';
 | 
			
		||||
import Portfolio from '../components/settings/Portfolio';
 | 
			
		||||
import Honesty from '../components/settings/Honesty';
 | 
			
		||||
import Certification from '../components/settings/Certification';
 | 
			
		||||
import RedirectHome from '../components/RedirectHome';
 | 
			
		||||
 | 
			
		||||
const propTypes = {
 | 
			
		||||
  createFlashMessage: PropTypes.func.isRequired,
 | 
			
		||||
  isSignedIn: PropTypes.bool,
 | 
			
		||||
  showLoading: PropTypes.bool,
 | 
			
		||||
  submitNewAbout: PropTypes.func.isRequired,
 | 
			
		||||
  toggleNightMode: PropTypes.func.isRequired,
 | 
			
		||||
@@ -83,9 +89,11 @@ const propTypes = {
 | 
			
		||||
const mapStateToProps = createSelector(
 | 
			
		||||
  signInLoadingSelector,
 | 
			
		||||
  userSelector,
 | 
			
		||||
  (showLoading, user) => ({
 | 
			
		||||
  isSignedInSelector,
 | 
			
		||||
  (showLoading, user, isSignedIn) => ({
 | 
			
		||||
    showLoading,
 | 
			
		||||
    user
 | 
			
		||||
    user,
 | 
			
		||||
    isSignedIn
 | 
			
		||||
  })
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@@ -107,6 +115,7 @@ const mapDispatchToProps = dispatch =>
 | 
			
		||||
function ShowSettings(props) {
 | 
			
		||||
  const {
 | 
			
		||||
    createFlashMessage,
 | 
			
		||||
    isSignedIn,
 | 
			
		||||
    submitNewAbout,
 | 
			
		||||
    toggleNightMode,
 | 
			
		||||
    user: {
 | 
			
		||||
@@ -156,6 +165,10 @@ function ShowSettings(props) {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!showLoading && !isSignedIn) {
 | 
			
		||||
    return <RedirectHome />;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Layout>
 | 
			
		||||
      <Helmet>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import { Grid, Panel, Button } from '@freecodecamp/react-bootstrap';
 | 
			
		||||
import Helmet from 'react-helmet';
 | 
			
		||||
 | 
			
		||||
import env from '../../config/env.json';
 | 
			
		||||
import Layout from '../components/Layout';
 | 
			
		||||
import Layout from '../components/layouts/Default';
 | 
			
		||||
import FullWidthRow from '../components/helpers/FullWidthRow';
 | 
			
		||||
import { Spacer } from '../components/helpers';
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ import {
 | 
			
		||||
  userSelector,
 | 
			
		||||
  reportUser
 | 
			
		||||
} from '../redux';
 | 
			
		||||
import Layout from '../components/Layout';
 | 
			
		||||
import Layout from '../components/layouts/Default';
 | 
			
		||||
import { Spacer, Loader, FullWidthRow } from '../components/helpers';
 | 
			
		||||
 | 
			
		||||
const propTypes = {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										133
									
								
								client/src/client/frame-runner.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								client/src/client/frame-runner.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
document.addEventListener('DOMContentLoaded', function() {
 | 
			
		||||
  const {
 | 
			
		||||
    timeout,
 | 
			
		||||
    catchError,
 | 
			
		||||
    map,
 | 
			
		||||
    toArray,
 | 
			
		||||
    switchMap,
 | 
			
		||||
    of,
 | 
			
		||||
    from,
 | 
			
		||||
    throwError
 | 
			
		||||
  } = document.__deps__.rx;
 | 
			
		||||
  const frameReady = document.__frameReady;
 | 
			
		||||
  const chai = parent.chai;
 | 
			
		||||
  const source = document.__source;
 | 
			
		||||
  const __getUserInput = document.__getUserInput || (x => x);
 | 
			
		||||
  const checkChallengePayload = document.__checkChallengePayload;
 | 
			
		||||
 | 
			
		||||
  const fiveSeconds = 5000;
 | 
			
		||||
 | 
			
		||||
  function isPromise(value) {
 | 
			
		||||
    return (
 | 
			
		||||
      value &&
 | 
			
		||||
      typeof value.subscribe !== 'function' &&
 | 
			
		||||
      typeof value.then === 'function'
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  // Fake Deep Equal dependency
 | 
			
		||||
  /* eslint-disable no-unused-vars */
 | 
			
		||||
  const DeepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
 | 
			
		||||
 | 
			
		||||
  // Hardcode Deep Freeze dependency
 | 
			
		||||
  const DeepFreeze = o => {
 | 
			
		||||
    Object.freeze(o);
 | 
			
		||||
    Object.getOwnPropertyNames(o).forEach(function(prop) {
 | 
			
		||||
      if (
 | 
			
		||||
        o.hasOwnProperty(prop) &&
 | 
			
		||||
        o[prop] !== null &&
 | 
			
		||||
        (typeof o[prop] === 'object' || typeof o[prop] === 'function') &&
 | 
			
		||||
        !Object.isFrozen(o[prop])
 | 
			
		||||
      ) {
 | 
			
		||||
        DeepFreeze(o[prop]);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    return o;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (document.Enzyme) {
 | 
			
		||||
    window.Enzyme = document.Enzyme;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  document.__runTests = function runTests(tests = []) {
 | 
			
		||||
    /* eslint-disable no-unused-vars */
 | 
			
		||||
    const code = source.slice(0);
 | 
			
		||||
    const editor = {
 | 
			
		||||
      getValue() {
 | 
			
		||||
        return source;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    const userCode = document.createElement('script');
 | 
			
		||||
    userCode.type = 'text/javascript';
 | 
			
		||||
    userCode.text = code;
 | 
			
		||||
    document.body.appendChild(userCode);
 | 
			
		||||
    const assert = chai.assert;
 | 
			
		||||
    const getUserInput = __getUserInput;
 | 
			
		||||
    // Iterate through the test one at a time
 | 
			
		||||
    // on new stacks
 | 
			
		||||
    const results = from(tests).pipe(
 | 
			
		||||
      switchMap(function runOneTest({ text, testString }) {
 | 
			
		||||
        const newTest = { text, testString };
 | 
			
		||||
        let test;
 | 
			
		||||
        let __result;
 | 
			
		||||
        // uncomment the following line to inspect
 | 
			
		||||
        // the framerunner as it runs tests
 | 
			
		||||
        // make sure the dev tools console is open
 | 
			
		||||
        // debugger;
 | 
			
		||||
        try {
 | 
			
		||||
          /* eslint-disable no-eval */
 | 
			
		||||
          // eval test string to actual JavaScript
 | 
			
		||||
          // This return can be a function
 | 
			
		||||
          // i.e. function() { assert(true, 'happy coding'); }
 | 
			
		||||
          test = eval(testString);
 | 
			
		||||
          /* eslint-enable no-eval */
 | 
			
		||||
          if (typeof test === 'function') {
 | 
			
		||||
            // all async tests must return a promise or observable
 | 
			
		||||
            // sync tests can return Any type
 | 
			
		||||
            __result = test(getUserInput);
 | 
			
		||||
            if (isPromise(__result)) {
 | 
			
		||||
              // resolve the promise before continuing
 | 
			
		||||
              __result = from(__result);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (!__result || typeof __result.subscribe !== 'function') {
 | 
			
		||||
            // make sure result is an observable
 | 
			
		||||
            __result = of(null);
 | 
			
		||||
          }
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          __result = throwError(e);
 | 
			
		||||
        }
 | 
			
		||||
        return __result.pipe(
 | 
			
		||||
          timeout(fiveSeconds),
 | 
			
		||||
          map(() => {
 | 
			
		||||
            // if we are here, then the assert passed
 | 
			
		||||
            // mark test as passing
 | 
			
		||||
            newTest.pass = true;
 | 
			
		||||
            return newTest;
 | 
			
		||||
          }),
 | 
			
		||||
          catchError(err => {
 | 
			
		||||
            const { message, stack } = err;
 | 
			
		||||
            // we catch the error here to prevent the error from bubbling up
 | 
			
		||||
            // and collapsing the pipe
 | 
			
		||||
            let errMessage = message.slice(0) || '';
 | 
			
		||||
            const assertIndex = errMessage.indexOf(': expected');
 | 
			
		||||
            if (assertIndex !== -1) {
 | 
			
		||||
              errMessage = errMessage.slice(0, assertIndex);
 | 
			
		||||
            }
 | 
			
		||||
            errMessage = errMessage.replace(/<code>(.*?)<\/code>/g, '$1');
 | 
			
		||||
            newTest.err = errMessage + '\n' + stack;
 | 
			
		||||
            newTest.stack = stack;
 | 
			
		||||
            newTest.message = errMessage;
 | 
			
		||||
            // RxJS catch expects an observable as a return
 | 
			
		||||
            return of(newTest);
 | 
			
		||||
          })
 | 
			
		||||
        );
 | 
			
		||||
      }),
 | 
			
		||||
      toArray()
 | 
			
		||||
    );
 | 
			
		||||
    return results;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // notify that the window methods are ready to run
 | 
			
		||||
  frameReady.next({ checkChallengePayload });
 | 
			
		||||
});
 | 
			
		||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 | 
			
		||||
import { bindActionCreators } from 'redux';
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
import { createSelector } from 'reselect';
 | 
			
		||||
import { Modal } from 'react-bootstrap';
 | 
			
		||||
import { Modal } from '@freecodecamp/react-bootstrap';
 | 
			
		||||
import { StripeProvider, Elements } from 'react-stripe-elements';
 | 
			
		||||
 | 
			
		||||
import ga from '../../analytics';
 | 
			
		||||
@@ -13,10 +13,10 @@ import {
 | 
			
		||||
  userSelector,
 | 
			
		||||
  closeDonationModal,
 | 
			
		||||
  isDonationModalOpenSelector
 | 
			
		||||
} from '../../redux/app';
 | 
			
		||||
} from '../../redux';
 | 
			
		||||
 | 
			
		||||
import './donation.css';
 | 
			
		||||
import poweredByStripe from '../../../static/img/powered_by_stripe.svg';
 | 
			
		||||
import poweredByStripe from '../../images/powered_by_stripe.svg';
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = createSelector(
 | 
			
		||||
  userSelector,
 | 
			
		||||
@@ -82,7 +82,7 @@ class DonationModal extends PureComponent {
 | 
			
		||||
    };
 | 
			
		||||
    return (
 | 
			
		||||
      <div className='maybe-later-container'>
 | 
			
		||||
        <a onClick={handleClick}>Maybe later</a>
 | 
			
		||||
        <button onClick={handleClick}>Maybe later</button>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
@@ -109,7 +109,7 @@ class DonationModal extends PureComponent {
 | 
			
		||||
              />
 | 
			
		||||
            </Modal.Body>
 | 
			
		||||
            <Modal.Footer>
 | 
			
		||||
              <img src={poweredByStripe} />
 | 
			
		||||
              <img alt='powered by stripe' src={poweredByStripe} />
 | 
			
		||||
            </Modal.Footer>
 | 
			
		||||
          </Modal>
 | 
			
		||||
        </Elements>
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import React, { PureComponent } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Button } from 'react-bootstrap';
 | 
			
		||||
import { Button } from '@freecodecamp/react-bootstrap';
 | 
			
		||||
 | 
			
		||||
import StripCardForm from './StripeCardForm';
 | 
			
		||||
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import React, { Fragment } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Alert, Button } from 'react-bootstrap';
 | 
			
		||||
import { Alert, Button } from '@freecodecamp/react-bootstrap';
 | 
			
		||||
import Spinner from 'react-spinkit';
 | 
			
		||||
 | 
			
		||||
const propTypes = {
 | 
			
		||||
@@ -132,13 +132,12 @@ class DonateForm extends PureComponent {
 | 
			
		||||
  renderAmountButtons() {
 | 
			
		||||
    return this.buttonAmounts.map(amount => (
 | 
			
		||||
      <li key={'amount-' + amount}>
 | 
			
		||||
        <a
 | 
			
		||||
        <button
 | 
			
		||||
          className={`amount-value ${this.isActive(amount) ? 'active' : ''}`}
 | 
			
		||||
          href=''
 | 
			
		||||
          id={amount}
 | 
			
		||||
          onClick={this.handleAmountClick}
 | 
			
		||||
          tabIndex='-1'
 | 
			
		||||
          >{`$${amount / 100}`}</a>
 | 
			
		||||
          >{`$${amount / 100}`}</button>
 | 
			
		||||
      </li>
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
@@ -187,7 +186,9 @@ class DonateForm extends PureComponent {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { donationState: { processing, success, error } } = this.state;
 | 
			
		||||
    const {
 | 
			
		||||
      donationState: { processing, success, error }
 | 
			
		||||
    } = this.state;
 | 
			
		||||
    const { renderCompletion } = this.props;
 | 
			
		||||
    if (processing || success || error) {
 | 
			
		||||
      return renderCompletion({
 | 
			
		||||
@@ -5,7 +5,7 @@ import {
 | 
			
		||||
  CardExpiryElement,
 | 
			
		||||
  CardCVCElement
 | 
			
		||||
} from 'react-stripe-elements';
 | 
			
		||||
import { ControlLabel, FormGroup } from 'react-bootstrap';
 | 
			
		||||
import { ControlLabel, FormGroup } from '@freecodecamp/react-bootstrap';
 | 
			
		||||
 | 
			
		||||
const propTypes = {
 | 
			
		||||
  getValidationState: PropTypes.func.isRequired
 | 
			
		||||
@@ -18,7 +18,7 @@ function Header({ disableSettings }) {
 | 
			
		||||
        {disableSettings ? null : <FCCSearch />}
 | 
			
		||||
        <ul id='top-right-nav'>
 | 
			
		||||
          <li>
 | 
			
		||||
            <Link to='/'>Curriculum</Link>
 | 
			
		||||
            <Link to='/learn'>Curriculum</Link>
 | 
			
		||||
          </li>
 | 
			
		||||
          <li>
 | 
			
		||||
            <a
 | 
			
		||||
 
 | 
			
		||||
@@ -1,102 +0,0 @@
 | 
			
		||||
import React, { Fragment, Component } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
import { bindActionCreators } from 'redux';
 | 
			
		||||
import { createSelector } from 'reselect';
 | 
			
		||||
import Helmet from 'react-helmet';
 | 
			
		||||
import { StaticQuery, graphql } from 'gatsby';
 | 
			
		||||
 | 
			
		||||
import { fetchUser, isSignedInSelector } from '../redux';
 | 
			
		||||
import { flashMessagesSelector, removeFlashMessage } from './Flash/redux';
 | 
			
		||||
import Flash from './Flash';
 | 
			
		||||
import Header from './Header';
 | 
			
		||||
 | 
			
		||||
import './global.css';
 | 
			
		||||
import './layout.css';
 | 
			
		||||
import './night.css';
 | 
			
		||||
 | 
			
		||||
const propTypes = {
 | 
			
		||||
  children: PropTypes.node.isRequired,
 | 
			
		||||
  disableSettings: PropTypes.bool,
 | 
			
		||||
  fetchUser: PropTypes.func.isRequired,
 | 
			
		||||
  flashMessages: PropTypes.arrayOf(
 | 
			
		||||
    PropTypes.shape({
 | 
			
		||||
      id: PropTypes.string,
 | 
			
		||||
      type: PropTypes.string,
 | 
			
		||||
      message: PropTypes.string
 | 
			
		||||
    })
 | 
			
		||||
  ),
 | 
			
		||||
  hasMessages: PropTypes.bool,
 | 
			
		||||
  isSignedIn: PropTypes.bool,
 | 
			
		||||
  landingPage: PropTypes.bool,
 | 
			
		||||
  removeFlashMessage: PropTypes.func.isRequired
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = createSelector(
 | 
			
		||||
  isSignedInSelector,
 | 
			
		||||
  flashMessagesSelector,
 | 
			
		||||
  (isSignedIn, flashMessages) => ({
 | 
			
		||||
    isSignedIn,
 | 
			
		||||
    flashMessages,
 | 
			
		||||
    hasMessages: !!flashMessages.length
 | 
			
		||||
  })
 | 
			
		||||
);
 | 
			
		||||
const mapDispatchToProps = dispatch =>
 | 
			
		||||
  bindActionCreators({ fetchUser, removeFlashMessage }, dispatch);
 | 
			
		||||
 | 
			
		||||
class Layout extends Component {
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    if (!this.props.isSignedIn) {
 | 
			
		||||
      this.props.fetchUser();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      children,
 | 
			
		||||
      disableSettings,
 | 
			
		||||
      hasMessages,
 | 
			
		||||
      flashMessages = [],
 | 
			
		||||
      removeFlashMessage,
 | 
			
		||||
      landingPage
 | 
			
		||||
    } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <StaticQuery
 | 
			
		||||
        query={graphql`
 | 
			
		||||
          query SiteTitleQuery {
 | 
			
		||||
            site {
 | 
			
		||||
              siteMetadata {
 | 
			
		||||
                title
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        `}
 | 
			
		||||
        render={data => (
 | 
			
		||||
          <Fragment>
 | 
			
		||||
            <Helmet
 | 
			
		||||
              meta={[
 | 
			
		||||
                { name: 'description', content: 'Sample' },
 | 
			
		||||
                { name: 'keywords', content: 'sample, something' }
 | 
			
		||||
              ]}
 | 
			
		||||
              title={data.site.siteMetadata.title}
 | 
			
		||||
            />
 | 
			
		||||
            <Header disableSettings={disableSettings} />
 | 
			
		||||
            <main className={landingPage && 'landing-page'}>
 | 
			
		||||
              {hasMessages ? (
 | 
			
		||||
                <Flash messages={flashMessages} onClose={removeFlashMessage} />
 | 
			
		||||
              ) : null}
 | 
			
		||||
              {children}
 | 
			
		||||
            </main>
 | 
			
		||||
          </Fragment>
 | 
			
		||||
        )}
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Layout.propTypes = propTypes;
 | 
			
		||||
 | 
			
		||||
export default connect(
 | 
			
		||||
  mapStateToProps,
 | 
			
		||||
  mapDispatchToProps
 | 
			
		||||
)(Layout);
 | 
			
		||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 | 
			
		||||
import uniq from 'lodash/uniq';
 | 
			
		||||
 | 
			
		||||
import SuperBlock from './components/SuperBlock';
 | 
			
		||||
import Spacer from '../util/Spacer';
 | 
			
		||||
import Spacer from '../helpers/Spacer';
 | 
			
		||||
 | 
			
		||||
import './map.css';
 | 
			
		||||
import { ChallengeNode } from '../../redux/propTypes';
 | 
			
		||||
@@ -7,7 +7,7 @@ import { Link } from 'gatsby';
 | 
			
		||||
 | 
			
		||||
import ga from '../../../analytics';
 | 
			
		||||
import { makeExpandedBlockSelector, toggleBlock } from '../redux';
 | 
			
		||||
import { userSelector } from '../../../redux/app';
 | 
			
		||||
import { userSelector } from '../../../redux';
 | 
			
		||||
import Caret from '../../icons/Caret';
 | 
			
		||||
/* eslint-disable max-len */
 | 
			
		||||
import GreenPass from '../../../templates/Challenges/components/icons/GreenPass';
 | 
			
		||||
@@ -3,8 +3,7 @@ import PropTypes from 'prop-types';
 | 
			
		||||
import { bindActionCreators } from 'redux';
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
import { createSelector } from 'reselect';
 | 
			
		||||
import uniq from 'lodash/uniq';
 | 
			
		||||
import find from 'lodash/find';
 | 
			
		||||
import { uniq, find } from 'lodash';
 | 
			
		||||
 | 
			
		||||
import Block from './Block';
 | 
			
		||||
 | 
			
		||||
@@ -102,4 +101,7 @@ export class SuperBlock extends PureComponent {
 | 
			
		||||
SuperBlock.displayName = 'SuperBlock';
 | 
			
		||||
SuperBlock.propTypes = propTypes;
 | 
			
		||||
 | 
			
		||||
export default connect(mapStateToProps, mapDispatchToProps)(SuperBlock);
 | 
			
		||||
export default connect(
 | 
			
		||||
  mapStateToProps,
 | 
			
		||||
  mapDispatchToProps
 | 
			
		||||
)(SuperBlock);
 | 
			
		||||
@@ -2,7 +2,7 @@ import { createAction, handleActions } from 'redux-actions';
 | 
			
		||||
 | 
			
		||||
import { createTypes } from '../../../../utils/stateManagement';
 | 
			
		||||
 | 
			
		||||
const ns = 'map';
 | 
			
		||||
const ns = 'curriculumMap';
 | 
			
		||||
 | 
			
		||||
export const getNS = () => ns;
 | 
			
		||||
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
import { navigate } from 'gatsby';
 | 
			
		||||
 | 
			
		||||
const Redirecthome = () => {
 | 
			
		||||
const RedirectHome = () => {
 | 
			
		||||
  if (typeof window !== 'undefined') {
 | 
			
		||||
    navigate('/');
 | 
			
		||||
  }
 | 
			
		||||
  return null;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Redirecthome;
 | 
			
		||||
export default RedirectHome;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { Button } from 'react-bootstrap';
 | 
			
		||||
import { Button } from '@freecodecamp/react-bootstrap';
 | 
			
		||||
 | 
			
		||||
function BlockSaveButton(props) {
 | 
			
		||||
  return (
 | 
			
		||||
@@ -7,7 +7,7 @@ import {
 | 
			
		||||
  ControlLabel,
 | 
			
		||||
  FormControl,
 | 
			
		||||
  HelpBlock
 | 
			
		||||
} from 'react-bootstrap';
 | 
			
		||||
} from '@freecodecamp/react-bootstrap';
 | 
			
		||||
 | 
			
		||||
import './form-fields.css';
 | 
			
		||||
 | 
			
		||||
@@ -2,27 +2,27 @@ import React, { Fragment, Component } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
import { bindActionCreators } from 'redux';
 | 
			
		||||
import { createSelector } from 'reselect';
 | 
			
		||||
import Helmet from 'react-helmet';
 | 
			
		||||
 | 
			
		||||
import ga from '../analytics';
 | 
			
		||||
 | 
			
		||||
import Header from '../components/Header';
 | 
			
		||||
import DonationModal from '../components/Donation';
 | 
			
		||||
import OfflineWarning from '../components/OfflineWarning';
 | 
			
		||||
import ga from '../../analytics';
 | 
			
		||||
import {
 | 
			
		||||
  fetchUser,
 | 
			
		||||
  userSelector,
 | 
			
		||||
  isSignedInSelector,
 | 
			
		||||
  onlineStatusChange,
 | 
			
		||||
  isOnlineSelector,
 | 
			
		||||
  isSignedInSelector
 | 
			
		||||
} from '../redux/app';
 | 
			
		||||
  isOnlineSelector
 | 
			
		||||
} from '../../redux';
 | 
			
		||||
import { flashMessagesSelector, removeFlashMessage } from '../Flash/redux';
 | 
			
		||||
 | 
			
		||||
import { isBrowser } from '../../../utils';
 | 
			
		||||
 | 
			
		||||
import OfflineWarning from '../OfflineWarning';
 | 
			
		||||
import Flash from '../Flash';
 | 
			
		||||
import Header from '../Header';
 | 
			
		||||
 | 
			
		||||
import 'prismjs/themes/prism.css';
 | 
			
		||||
import 'react-reflex/styles.css';
 | 
			
		||||
import './global.css';
 | 
			
		||||
import './layout.css';
 | 
			
		||||
import { createSelector } from 'reselect';
 | 
			
		||||
import { isBrowser } from '../../utils';
 | 
			
		||||
import './night.css';
 | 
			
		||||
 | 
			
		||||
const metaKeywords = [
 | 
			
		||||
  'javascript',
 | 
			
		||||
@@ -48,57 +48,67 @@ const metaKeywords = [
 | 
			
		||||
  'programming'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const propTypes = {
 | 
			
		||||
  children: PropTypes.node.isRequired,
 | 
			
		||||
  disableSettings: PropTypes.bool,
 | 
			
		||||
  fetchUser: PropTypes.func.isRequired,
 | 
			
		||||
  flashMessages: PropTypes.arrayOf(
 | 
			
		||||
    PropTypes.shape({
 | 
			
		||||
      id: PropTypes.string,
 | 
			
		||||
      type: PropTypes.string,
 | 
			
		||||
      message: PropTypes.string
 | 
			
		||||
    })
 | 
			
		||||
  ),
 | 
			
		||||
  hasMessages: PropTypes.bool,
 | 
			
		||||
  isOnline: PropTypes.bool.isRequired,
 | 
			
		||||
  isSignedIn: PropTypes.bool,
 | 
			
		||||
  landingPage: PropTypes.bool,
 | 
			
		||||
  onlineStatusChange: PropTypes.func.isRequired,
 | 
			
		||||
  removeFlashMessage: PropTypes.func.isRequired
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = createSelector(
 | 
			
		||||
  userSelector,
 | 
			
		||||
  isSignedInSelector,
 | 
			
		||||
  flashMessagesSelector,
 | 
			
		||||
  isOnlineSelector,
 | 
			
		||||
  ({ theme = 'default' }, isSignedIn, isOnline) => ({
 | 
			
		||||
    theme,
 | 
			
		||||
  (isSignedIn, flashMessages, isOnline) => ({
 | 
			
		||||
    isSignedIn,
 | 
			
		||||
    flashMessages,
 | 
			
		||||
    hasMessages: !!flashMessages.length,
 | 
			
		||||
    isOnline
 | 
			
		||||
  })
 | 
			
		||||
);
 | 
			
		||||
const mapDispatchToProps = dispatch =>
 | 
			
		||||
  bindActionCreators({ fetchUser, onlineStatusChange }, dispatch);
 | 
			
		||||
  bindActionCreators(
 | 
			
		||||
    { fetchUser, removeFlashMessage, onlineStatusChange },
 | 
			
		||||
    dispatch
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
const propTypes = {
 | 
			
		||||
  children: PropTypes.object,
 | 
			
		||||
  fetchUser: PropTypes.func.isRequired,
 | 
			
		||||
  isOnline: PropTypes.bool.isRequired,
 | 
			
		||||
  isSignedIn: PropTypes.bool.isRequired,
 | 
			
		||||
  onlineStatusChange: PropTypes.func.isRequired,
 | 
			
		||||
  theme: PropTypes.string
 | 
			
		||||
};
 | 
			
		||||
class DefaultLayout extends Component {
 | 
			
		||||
  constructor(props) {
 | 
			
		||||
    super(props);
 | 
			
		||||
 | 
			
		||||
class Layout extends Component {
 | 
			
		||||
  state = {
 | 
			
		||||
    location: ''
 | 
			
		||||
  };
 | 
			
		||||
    this.location = '';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    if (!this.props.isSignedIn) {
 | 
			
		||||
      this.props.fetchUser();
 | 
			
		||||
    }
 | 
			
		||||
    const url = window.location.pathname + window.location.search;
 | 
			
		||||
    ga.pageview(url);
 | 
			
		||||
 | 
			
		||||
    window.addEventListener('online', this.updateOnlineStatus);
 | 
			
		||||
    window.addEventListener('offline', this.updateOnlineStatus);
 | 
			
		||||
 | 
			
		||||
    /* eslint-disable react/no-did-mount-set-state */
 | 
			
		||||
    // this is for local location tracking only, no re-rendering required
 | 
			
		||||
    this.setState({
 | 
			
		||||
      location: url
 | 
			
		||||
    });
 | 
			
		||||
    this.location = url;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentDidUpdate() {
 | 
			
		||||
    const url = window.location.pathname + window.location.search;
 | 
			
		||||
    if (url !== this.state.location) {
 | 
			
		||||
    if (url !== this.location) {
 | 
			
		||||
      ga.pageview(url);
 | 
			
		||||
      /* eslint-disable react/no-did-update-set-state */
 | 
			
		||||
      // this is for local location tracking only, no re-rendering required
 | 
			
		||||
      this.setState({
 | 
			
		||||
        location: url
 | 
			
		||||
      });
 | 
			
		||||
      this.location = url;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -115,7 +125,16 @@ class Layout extends Component {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { children, theme, isOnline, isSignedIn } = this.props;
 | 
			
		||||
    const {
 | 
			
		||||
      children,
 | 
			
		||||
      disableSettings,
 | 
			
		||||
      hasMessages,
 | 
			
		||||
      flashMessages = [],
 | 
			
		||||
      removeFlashMessage,
 | 
			
		||||
      landingPage,
 | 
			
		||||
      isOnline,
 | 
			
		||||
      isSignedIn
 | 
			
		||||
    } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <Fragment>
 | 
			
		||||
        <Helmet
 | 
			
		||||
@@ -129,17 +148,23 @@ class Layout extends Component {
 | 
			
		||||
            { name: 'keywords', content: metaKeywords.join(', ') }
 | 
			
		||||
          ]}
 | 
			
		||||
        />
 | 
			
		||||
        <Header />
 | 
			
		||||
        <div className={'app-wrapper ' + theme}>
 | 
			
		||||
        <Header disableSettings={disableSettings} />
 | 
			
		||||
        <main className={landingPage && 'landing-page'}>
 | 
			
		||||
          <OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
 | 
			
		||||
          <main>{children}</main>
 | 
			
		||||
        </div>
 | 
			
		||||
        <DonationModal />
 | 
			
		||||
          {hasMessages ? (
 | 
			
		||||
            <Flash messages={flashMessages} onClose={removeFlashMessage} />
 | 
			
		||||
          ) : null}
 | 
			
		||||
          {children}
 | 
			
		||||
        </main>
 | 
			
		||||
      </Fragment>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Layout.propTypes = propTypes;
 | 
			
		||||
DefaultLayout.displayName = 'DefaultLayout';
 | 
			
		||||
DefaultLayout.propTypes = propTypes;
 | 
			
		||||
 | 
			
		||||
export default connect(mapStateToProps, mapDispatchToProps)(Layout);
 | 
			
		||||
export default connect(
 | 
			
		||||
  mapStateToProps,
 | 
			
		||||
  mapDispatchToProps
 | 
			
		||||
)(DefaultLayout);
 | 
			
		||||
							
								
								
									
										25
									
								
								client/src/components/layouts/Learn.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								client/src/components/layouts/Learn.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import React, { Fragment } from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
 | 
			
		||||
import DefaultLayout from './Default';
 | 
			
		||||
import DonationModal from '../Donation';
 | 
			
		||||
 | 
			
		||||
import 'prismjs/themes/prism.css';
 | 
			
		||||
import 'react-reflex/styles.css';
 | 
			
		||||
import './learn.css';
 | 
			
		||||
 | 
			
		||||
function LearnLayout({ children }) {
 | 
			
		||||
  return (
 | 
			
		||||
    <Fragment>
 | 
			
		||||
      <DefaultLayout>
 | 
			
		||||
        <div id='learn-app-wrapper'>{children}</div>
 | 
			
		||||
      </DefaultLayout>
 | 
			
		||||
      <DonationModal />
 | 
			
		||||
    </Fragment>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LearnLayout.displayName = 'LearnLayout';
 | 
			
		||||
LearnLayout.propTypes = { children: PropTypes.any };
 | 
			
		||||
 | 
			
		||||
export default LearnLayout;
 | 
			
		||||
@@ -1,10 +1,5 @@
 | 
			
		||||
 | 
			
		||||
main {
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.reflex-layout.vertical > .reflex-splitter,
 | 
			
		||||
.reflex-layout > .reflex-element {
 | 
			
		||||
#learn-app-wrapper .reflex-layout.vertical > .reflex-splitter,
 | 
			
		||||
#learn-app-wrapper .reflex-layout > .reflex-element {
 | 
			
		||||
  /*
 | 
			
		||||
  Note(Bouncey): removing height: 100% makes the element 100% high ¯\_(ツ)_/¯
 | 
			
		||||
  */
 | 
			
		||||
@@ -12,21 +7,16 @@ main {
 | 
			
		||||
  /*
 | 
			
		||||
  Note(renojvarghese): had to put a max-height because max-height was removed from app-wrapper (has background of #333 in night mode)
 | 
			
		||||
  */
 | 
			
		||||
  max-height: calc(100vh - 36px);
 | 
			
		||||
  max-height: calc(100vh - 38px);
 | 
			
		||||
  overflow-y: scroll;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.reflex-layout.vertical .reflex-splitter:hover {
 | 
			
		||||
#learn-app-wrapper .reflex-layout.vertical .reflex-splitter:hover {
 | 
			
		||||
  border-left: 2px solid #006400 !important;
 | 
			
		||||
  border-right: 2px solid #006400 !important;
 | 
			
		||||
  background-color: #006400;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.reflex-layout.vertical > .reflex-splitter {
 | 
			
		||||
#learn-app-wrapper .reflex-layout.vertical > .reflex-splitter {
 | 
			
		||||
  width: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-wrapper {
 | 
			
		||||
  margin-top: 36px;
 | 
			
		||||
  min-height: calc(100vh - 36px);
 | 
			
		||||
}
 | 
			
		||||
@@ -5,6 +5,10 @@ import sassjs from './sassjs';
 | 
			
		||||
 | 
			
		||||
const metaAndStyleSheets = meta
 | 
			
		||||
  .concat(favicons, mathjax, sassjs)
 | 
			
		||||
  .map((element, i) => ({ ...element, key: `meta-stylesheet-${i}` }));
 | 
			
		||||
  .map((element, i) => ({
 | 
			
		||||
    ...element,
 | 
			
		||||
    key: `meta-stylesheet-${i}`,
 | 
			
		||||
    props: { ...element.props, key: `meta-stylesheet-${i}` }
 | 
			
		||||
  }));
 | 
			
		||||
 | 
			
		||||
export default metaAndStyleSheets;
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB  | 
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user