chore(learn): Merge learn in to the client app
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -13,4 +13,6 @@ node_modules | |||||||
| *.gz | *.gz | ||||||
| *.swp | *.swp | ||||||
|  |  | ||||||
| .DS_Store | .DS_Store | ||||||
|  |  | ||||||
|  | curriculum/dist/ | ||||||
| @@ -1,12 +1,16 @@ | |||||||
|  | const path = require('path'); | ||||||
|  |  | ||||||
|  | const { buildChallenges$ } = require('./utils/buildChallenges'); | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   siteMetadata: { |   siteMetadata: { | ||||||
|     title: 'freeCodeCamp', |     title: 'freeCodeCamp', | ||||||
|     siteUrl: 'https://www.freecodecamp.org' |     siteUrl: 'https://www.freecodecamp.org' | ||||||
|   }, |   }, | ||||||
|   proxy: { |   // proxy: { | ||||||
|     prefix: '/internal', |   //   prefix: '/internal', | ||||||
|     url: 'http://localhost:3000' |   //   url: 'http://localhost:3000' | ||||||
|   }, |   // }, | ||||||
|   plugins: [ |   plugins: [ | ||||||
|     'gatsby-plugin-react-helmet', |     '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', |       resolve: 'gatsby-plugin-manifest', | ||||||
|       options: { |       options: { | ||||||
|         name: 'freeCodeCamp', |         name: 'freeCodeCamp', | ||||||
|  |         /* eslint-disable camelcase */ | ||||||
|         short_name: 'fCC', |         short_name: 'fCC', | ||||||
|         start_url: '/', |         start_url: '/', | ||||||
|         background_color: '#fff', |  | ||||||
|         theme_color: '#006400', |         theme_color: '#006400', | ||||||
|  |         background_color: '#fff', | ||||||
|  |         /* eslint-enable camelcase */ | ||||||
|         display: 'minimal-ui', |         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' |     'gatsby-plugin-sitemap' | ||||||
|   | |||||||
| @@ -1,7 +1,178 @@ | |||||||
| /** | require('dotenv').config(); | ||||||
|  * Implement Gatsby's Node APIs in this file. |  | ||||||
|  * |  | ||||||
|  * See: https://www.gatsbyjs.org/docs/node-apis/ |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| // 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 PropTypes from 'prop-types'; | ||||||
| import { Provider } from 'react-redux'; | import { Provider } from 'react-redux'; | ||||||
|  |  | ||||||
|  | import headComponents from './src/head'; | ||||||
| import { createStore } from './src/redux/createStore'; | import { createStore } from './src/redux/createStore'; | ||||||
|  |  | ||||||
| const store = createStore(); | const store = createStore(); | ||||||
| @@ -13,3 +14,31 @@ export const wrapRootElement = ({ element }) => { | |||||||
| wrapRootElement.propTypes = { | wrapRootElement.propTypes = { | ||||||
|   element: PropTypes.any |   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', |     '@babel/plugin-proposal-function-bind', | ||||||
|     [ |     [ | ||||||
|       'transform-imports', { |       'transform-imports', { | ||||||
|         'react-bootstrap': { |         '@freecodecamp/react-bootstrap': { | ||||||
|           transform: 'react-bootstrap/lib/${member}', |           transform: '@freecodecamp/react-bootstrap/lib/${member}', | ||||||
|           preventFullImport: true |           preventFullImport: true | ||||||
|         }, |         }, | ||||||
|         lodash: { |         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", |   "version": "1.0.0", | ||||||
|   "author": "freeCodeCamp <team@freecodecamp.org>", |   "author": "freeCodeCamp <team@freecodecamp.org>", | ||||||
|   "dependencies": { |   "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": "^1.1.8", | ||||||
|     "@fortawesome/fontawesome-svg-core": "^1.2.2", |     "@fortawesome/fontawesome-svg-core": "^1.2.2", | ||||||
|     "@fortawesome/free-brands-svg-icons": "^5.2.0", |     "@fortawesome/free-brands-svg-icons": "^5.2.0", | ||||||
|     "@fortawesome/free-regular-svg-icons": "^5.2.0", |     "@fortawesome/free-regular-svg-icons": "^5.2.0", | ||||||
|     "@fortawesome/free-solid-svg-icons": "^5.2.0", |     "@fortawesome/free-solid-svg-icons": "^5.2.0", | ||||||
|     "@fortawesome/react-fontawesome": "0.0.20", |     "@fortawesome/react-fontawesome": "0.0.20", | ||||||
|  |     "@freecodecamp/curriculum": "^3.2.1", | ||||||
|     "@freecodecamp/react-bootstrap": "^0.32.3", |     "@freecodecamp/react-bootstrap": "^0.32.3", | ||||||
|     "@reach/router": "^1.1.1", |     "@reach/router": "^1.1.1", | ||||||
|     "axios": "^0.18.0", |     "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": "^2.0.0-rc.15", | ||||||
|     "gatsby-link": "^2.0.0-rc.2", |     "gatsby-link": "^2.0.0-rc.2", | ||||||
|     "gatsby-plugin-create-client-paths": "^2.0.0-rc.1", |     "gatsby-plugin-create-client-paths": "^2.0.0-rc.1", | ||||||
|     "gatsby-plugin-manifest": "next", |     "gatsby-plugin-manifest": "next", | ||||||
|     "gatsby-plugin-react-helmet": "^3.0.0-rc.1", |     "gatsby-plugin-react-helmet": "^3.0.0-rc.1", | ||||||
|     "gatsby-plugin-sitemap": "^2.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", |     "lodash": "^4.17.10", | ||||||
|  |     "loop-protect": "^2.1.6", | ||||||
|  |     "monaco-editor-webpack-plugin": "^1.5.4", | ||||||
|     "nanoid": "^1.2.2", |     "nanoid": "^1.2.2", | ||||||
|     "prismjs": "^1.15.0", |     "prismjs": "^1.15.0", | ||||||
|     "query-string": "^6.1.0", |     "query-string": "^6.1.0", | ||||||
|     "react": "^16.4.2", |     "react": "^16.4.2", | ||||||
|     "react-dom": "^16.4.2", |     "react-dom": "^16.4.2", | ||||||
|     "react-freecodecamp-search": "^2.0.2", |     "react-freecodecamp-search": "^2.0.2", | ||||||
|  |     "react-ga": "^2.5.3", | ||||||
|     "react-helmet": "^5.2.0", |     "react-helmet": "^5.2.0", | ||||||
|     "react-media": "^1.8.0", |     "react-media": "^1.8.0", | ||||||
|  |     "react-monaco-editor": "^0.18.0", | ||||||
|     "react-redux": "^5.0.7", |     "react-redux": "^5.0.7", | ||||||
|  |     "react-reflex": "^2.2.9", | ||||||
|     "react-spinkit": "^3.0.0", |     "react-spinkit": "^3.0.0", | ||||||
|  |     "react-stripe-elements": "^2.0.1", | ||||||
|     "redux": "^4.0.0", |     "redux": "^4.0.0", | ||||||
|     "redux-actions": "^2.6.1", |     "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", |     "redux-saga": "^0.16.0", | ||||||
|     "reselect": "^3.0.1", |     "reselect": "^3.0.1", | ||||||
|  |     "rxjs": "^6.3.3", | ||||||
|  |     "sinon": "^6.3.4", | ||||||
|     "store": "^2.0.12", |     "store": "^2.0.12", | ||||||
|     "validator": "^10.7.0" |     "validator": "^10.7.0", | ||||||
|  |     "webpack-remove-serviceworker-plugin": "^1.0.0" | ||||||
|   }, |   }, | ||||||
|   "keywords": [ |   "keywords": [ | ||||||
|     "gatsby" |     "gatsby" | ||||||
|   ], |   ], | ||||||
|   "license": "MIT", |   "license": "MIT", | ||||||
|   "scripts": { |   "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", |     "develop": "gatsby develop", | ||||||
|     "format:gatsby": "prettier-eslint --write --trailing-comma none --single-quote './gatsby-*.js'", |     "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:src": "prettier-eslint --write --trailing-comma none --single-quote './src/**/*.js'", | ||||||
|     "format:utils": "prettier-eslint --write --trailing-comma none --single-quote './utils/**/*.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", |     "format": "npm run format:gatsby && npm run format:src && npm run format:utils", | ||||||
|     "test": "echo \"Error: no test specified\" && exit 1" |     "test": "jest" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "babel-plugin-transform-imports": "^1.5.0", |     "babel-plugin-transform-imports": "^1.5.0", | ||||||
|     "eslint": "^5.5.0", |     "eslint": "^5.5.0", | ||||||
|     "eslint-config-freecodecamp": "^1.1.1", |     "eslint-config-freecodecamp": "^1.1.1", | ||||||
|  |     "jest": "^23.6.0", | ||||||
|     "prettier": "^1.14.2", |     "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": { |   "repository": { | ||||||
|     "type": "git", |     "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 { Grid, Button } from '@freecodecamp/react-bootstrap'; | ||||||
| import Helmet from 'react-helmet'; | import Helmet from 'react-helmet'; | ||||||
|  |  | ||||||
| import { signInLoadingSelector, userSelector } from '../redux'; | import { | ||||||
|  |   signInLoadingSelector, | ||||||
|  |   userSelector, | ||||||
|  |   isSignedInSelector | ||||||
|  | } from '../redux'; | ||||||
| import { submitNewAbout, updateUserFlag, verifyCert } from '../redux/settings'; | import { submitNewAbout, updateUserFlag, verifyCert } from '../redux/settings'; | ||||||
| import { createFlashMessage } from '../components/Flash/redux'; | import { createFlashMessage } from '../components/Flash/redux'; | ||||||
|  |  | ||||||
| import Layout from '../components/Layout'; | import Layout from '../components/layouts/Default'; | ||||||
| import Spacer from '../components/helpers/Spacer'; | import Spacer from '../components/helpers/Spacer'; | ||||||
| import Loader from '../components/helpers/Loader'; | import Loader from '../components/helpers/Loader'; | ||||||
| import FullWidthRow from '../components/helpers/FullWidthRow'; | import FullWidthRow from '../components/helpers/FullWidthRow'; | ||||||
| @@ -21,9 +25,11 @@ import Internet from '../components/settings/Internet'; | |||||||
| import Portfolio from '../components/settings/Portfolio'; | import Portfolio from '../components/settings/Portfolio'; | ||||||
| import Honesty from '../components/settings/Honesty'; | import Honesty from '../components/settings/Honesty'; | ||||||
| import Certification from '../components/settings/Certification'; | import Certification from '../components/settings/Certification'; | ||||||
|  | import RedirectHome from '../components/RedirectHome'; | ||||||
|  |  | ||||||
| const propTypes = { | const propTypes = { | ||||||
|   createFlashMessage: PropTypes.func.isRequired, |   createFlashMessage: PropTypes.func.isRequired, | ||||||
|  |   isSignedIn: PropTypes.bool, | ||||||
|   showLoading: PropTypes.bool, |   showLoading: PropTypes.bool, | ||||||
|   submitNewAbout: PropTypes.func.isRequired, |   submitNewAbout: PropTypes.func.isRequired, | ||||||
|   toggleNightMode: PropTypes.func.isRequired, |   toggleNightMode: PropTypes.func.isRequired, | ||||||
| @@ -83,9 +89,11 @@ const propTypes = { | |||||||
| const mapStateToProps = createSelector( | const mapStateToProps = createSelector( | ||||||
|   signInLoadingSelector, |   signInLoadingSelector, | ||||||
|   userSelector, |   userSelector, | ||||||
|   (showLoading, user) => ({ |   isSignedInSelector, | ||||||
|  |   (showLoading, user, isSignedIn) => ({ | ||||||
|     showLoading, |     showLoading, | ||||||
|     user |     user, | ||||||
|  |     isSignedIn | ||||||
|   }) |   }) | ||||||
| ); | ); | ||||||
|  |  | ||||||
| @@ -107,6 +115,7 @@ const mapDispatchToProps = dispatch => | |||||||
| function ShowSettings(props) { | function ShowSettings(props) { | ||||||
|   const { |   const { | ||||||
|     createFlashMessage, |     createFlashMessage, | ||||||
|  |     isSignedIn, | ||||||
|     submitNewAbout, |     submitNewAbout, | ||||||
|     toggleNightMode, |     toggleNightMode, | ||||||
|     user: { |     user: { | ||||||
| @@ -156,6 +165,10 @@ function ShowSettings(props) { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (!showLoading && !isSignedIn) { | ||||||
|  |     return <RedirectHome />; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Layout> |     <Layout> | ||||||
|       <Helmet> |       <Helmet> | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ import { Grid, Panel, Button } from '@freecodecamp/react-bootstrap'; | |||||||
| import Helmet from 'react-helmet'; | import Helmet from 'react-helmet'; | ||||||
|  |  | ||||||
| import env from '../../config/env.json'; | import env from '../../config/env.json'; | ||||||
| import Layout from '../components/Layout'; | import Layout from '../components/layouts/Default'; | ||||||
| import FullWidthRow from '../components/helpers/FullWidthRow'; | import FullWidthRow from '../components/helpers/FullWidthRow'; | ||||||
| import { Spacer } from '../components/helpers'; | import { Spacer } from '../components/helpers'; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ import { | |||||||
|   userSelector, |   userSelector, | ||||||
|   reportUser |   reportUser | ||||||
| } from '../redux'; | } from '../redux'; | ||||||
| import Layout from '../components/Layout'; | import Layout from '../components/layouts/Default'; | ||||||
| import { Spacer, Loader, FullWidthRow } from '../components/helpers'; | import { Spacer, Loader, FullWidthRow } from '../components/helpers'; | ||||||
|  |  | ||||||
| const propTypes = { | 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 { bindActionCreators } from 'redux'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { createSelector } from 'reselect'; | import { createSelector } from 'reselect'; | ||||||
| import { Modal } from 'react-bootstrap'; | import { Modal } from '@freecodecamp/react-bootstrap'; | ||||||
| import { StripeProvider, Elements } from 'react-stripe-elements'; | import { StripeProvider, Elements } from 'react-stripe-elements'; | ||||||
| 
 | 
 | ||||||
| import ga from '../../analytics'; | import ga from '../../analytics'; | ||||||
| @@ -13,10 +13,10 @@ import { | |||||||
|   userSelector, |   userSelector, | ||||||
|   closeDonationModal, |   closeDonationModal, | ||||||
|   isDonationModalOpenSelector |   isDonationModalOpenSelector | ||||||
| } from '../../redux/app'; | } from '../../redux'; | ||||||
| 
 | 
 | ||||||
| import './donation.css'; | import './donation.css'; | ||||||
| import poweredByStripe from '../../../static/img/powered_by_stripe.svg'; | import poweredByStripe from '../../images/powered_by_stripe.svg'; | ||||||
| 
 | 
 | ||||||
| const mapStateToProps = createSelector( | const mapStateToProps = createSelector( | ||||||
|   userSelector, |   userSelector, | ||||||
| @@ -82,7 +82,7 @@ class DonationModal extends PureComponent { | |||||||
|     }; |     }; | ||||||
|     return ( |     return ( | ||||||
|       <div className='maybe-later-container'> |       <div className='maybe-later-container'> | ||||||
|         <a onClick={handleClick}>Maybe later</a> |         <button onClick={handleClick}>Maybe later</button> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| @@ -109,7 +109,7 @@ class DonationModal extends PureComponent { | |||||||
|               /> |               /> | ||||||
|             </Modal.Body> |             </Modal.Body> | ||||||
|             <Modal.Footer> |             <Modal.Footer> | ||||||
|               <img src={poweredByStripe} /> |               <img alt='powered by stripe' src={poweredByStripe} /> | ||||||
|             </Modal.Footer> |             </Modal.Footer> | ||||||
|           </Modal> |           </Modal> | ||||||
|         </Elements> |         </Elements> | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| import React, { PureComponent } from 'react'; | import React, { PureComponent } from 'react'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import { Button } from 'react-bootstrap'; | import { Button } from '@freecodecamp/react-bootstrap'; | ||||||
| 
 | 
 | ||||||
| import StripCardForm from './StripeCardForm'; | import StripCardForm from './StripeCardForm'; | ||||||
| 
 | 
 | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| import React, { Fragment } from 'react'; | import React, { Fragment } from 'react'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import { Alert, Button } from 'react-bootstrap'; | import { Alert, Button } from '@freecodecamp/react-bootstrap'; | ||||||
| import Spinner from 'react-spinkit'; | import Spinner from 'react-spinkit'; | ||||||
| 
 | 
 | ||||||
| const propTypes = { | const propTypes = { | ||||||
| @@ -132,13 +132,12 @@ class DonateForm extends PureComponent { | |||||||
|   renderAmountButtons() { |   renderAmountButtons() { | ||||||
|     return this.buttonAmounts.map(amount => ( |     return this.buttonAmounts.map(amount => ( | ||||||
|       <li key={'amount-' + amount}> |       <li key={'amount-' + amount}> | ||||||
|         <a |         <button | ||||||
|           className={`amount-value ${this.isActive(amount) ? 'active' : ''}`} |           className={`amount-value ${this.isActive(amount) ? 'active' : ''}`} | ||||||
|           href='' |  | ||||||
|           id={amount} |           id={amount} | ||||||
|           onClick={this.handleAmountClick} |           onClick={this.handleAmountClick} | ||||||
|           tabIndex='-1' |           tabIndex='-1' | ||||||
|           >{`$${amount / 100}`}</a> |           >{`$${amount / 100}`}</button> | ||||||
|       </li> |       </li> | ||||||
|     )); |     )); | ||||||
|   } |   } | ||||||
| @@ -187,7 +186,9 @@ class DonateForm extends PureComponent { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   render() { |   render() { | ||||||
|     const { donationState: { processing, success, error } } = this.state; |     const { | ||||||
|  |       donationState: { processing, success, error } | ||||||
|  |     } = this.state; | ||||||
|     const { renderCompletion } = this.props; |     const { renderCompletion } = this.props; | ||||||
|     if (processing || success || error) { |     if (processing || success || error) { | ||||||
|       return renderCompletion({ |       return renderCompletion({ | ||||||
| @@ -5,7 +5,7 @@ import { | |||||||
|   CardExpiryElement, |   CardExpiryElement, | ||||||
|   CardCVCElement |   CardCVCElement | ||||||
| } from 'react-stripe-elements'; | } from 'react-stripe-elements'; | ||||||
| import { ControlLabel, FormGroup } from 'react-bootstrap'; | import { ControlLabel, FormGroup } from '@freecodecamp/react-bootstrap'; | ||||||
| 
 | 
 | ||||||
| const propTypes = { | const propTypes = { | ||||||
|   getValidationState: PropTypes.func.isRequired |   getValidationState: PropTypes.func.isRequired | ||||||
| @@ -18,7 +18,7 @@ function Header({ disableSettings }) { | |||||||
|         {disableSettings ? null : <FCCSearch />} |         {disableSettings ? null : <FCCSearch />} | ||||||
|         <ul id='top-right-nav'> |         <ul id='top-right-nav'> | ||||||
|           <li> |           <li> | ||||||
|             <Link to='/'>Curriculum</Link> |             <Link to='/learn'>Curriculum</Link> | ||||||
|           </li> |           </li> | ||||||
|           <li> |           <li> | ||||||
|             <a |             <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 uniq from 'lodash/uniq'; | ||||||
| 
 | 
 | ||||||
| import SuperBlock from './components/SuperBlock'; | import SuperBlock from './components/SuperBlock'; | ||||||
| import Spacer from '../util/Spacer'; | import Spacer from '../helpers/Spacer'; | ||||||
| 
 | 
 | ||||||
| import './map.css'; | import './map.css'; | ||||||
| import { ChallengeNode } from '../../redux/propTypes'; | import { ChallengeNode } from '../../redux/propTypes'; | ||||||
| @@ -7,7 +7,7 @@ import { Link } from 'gatsby'; | |||||||
| 
 | 
 | ||||||
| import ga from '../../../analytics'; | import ga from '../../../analytics'; | ||||||
| import { makeExpandedBlockSelector, toggleBlock } from '../redux'; | import { makeExpandedBlockSelector, toggleBlock } from '../redux'; | ||||||
| import { userSelector } from '../../../redux/app'; | import { userSelector } from '../../../redux'; | ||||||
| import Caret from '../../icons/Caret'; | import Caret from '../../icons/Caret'; | ||||||
| /* eslint-disable max-len */ | /* eslint-disable max-len */ | ||||||
| import GreenPass from '../../../templates/Challenges/components/icons/GreenPass'; | import GreenPass from '../../../templates/Challenges/components/icons/GreenPass'; | ||||||
| @@ -3,8 +3,7 @@ import PropTypes from 'prop-types'; | |||||||
| import { bindActionCreators } from 'redux'; | import { bindActionCreators } from 'redux'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { createSelector } from 'reselect'; | import { createSelector } from 'reselect'; | ||||||
| import uniq from 'lodash/uniq'; | import { uniq, find } from 'lodash'; | ||||||
| import find from 'lodash/find'; |  | ||||||
| 
 | 
 | ||||||
| import Block from './Block'; | import Block from './Block'; | ||||||
| 
 | 
 | ||||||
| @@ -102,4 +101,7 @@ export class SuperBlock extends PureComponent { | |||||||
| SuperBlock.displayName = 'SuperBlock'; | SuperBlock.displayName = 'SuperBlock'; | ||||||
| SuperBlock.propTypes = propTypes; | 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'; | import { createTypes } from '../../../../utils/stateManagement'; | ||||||
| 
 | 
 | ||||||
| const ns = 'map'; | const ns = 'curriculumMap'; | ||||||
| 
 | 
 | ||||||
| export const getNS = () => ns; | export const getNS = () => ns; | ||||||
| 
 | 
 | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| import { navigate } from 'gatsby'; | import { navigate } from 'gatsby'; | ||||||
|  |  | ||||||
| const Redirecthome = () => { | const RedirectHome = () => { | ||||||
|   if (typeof window !== 'undefined') { |   if (typeof window !== 'undefined') { | ||||||
|     navigate('/'); |     navigate('/'); | ||||||
|   } |   } | ||||||
|   return null; |   return null; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export default Redirecthome; | export default RedirectHome; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import { Button } from 'react-bootstrap'; | import { Button } from '@freecodecamp/react-bootstrap'; | ||||||
| 
 | 
 | ||||||
| function BlockSaveButton(props) { | function BlockSaveButton(props) { | ||||||
|   return ( |   return ( | ||||||
| @@ -7,7 +7,7 @@ import { | |||||||
|   ControlLabel, |   ControlLabel, | ||||||
|   FormControl, |   FormControl, | ||||||
|   HelpBlock |   HelpBlock | ||||||
| } from 'react-bootstrap'; | } from '@freecodecamp/react-bootstrap'; | ||||||
| 
 | 
 | ||||||
| import './form-fields.css'; | import './form-fields.css'; | ||||||
| 
 | 
 | ||||||
| @@ -2,27 +2,27 @@ import React, { Fragment, Component } from 'react'; | |||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { bindActionCreators } from 'redux'; | import { bindActionCreators } from 'redux'; | ||||||
|  | import { createSelector } from 'reselect'; | ||||||
| import Helmet from 'react-helmet'; | import Helmet from 'react-helmet'; | ||||||
| 
 | 
 | ||||||
| import ga from '../analytics'; | import ga from '../../analytics'; | ||||||
| 
 |  | ||||||
| import Header from '../components/Header'; |  | ||||||
| import DonationModal from '../components/Donation'; |  | ||||||
| import OfflineWarning from '../components/OfflineWarning'; |  | ||||||
| import { | import { | ||||||
|   fetchUser, |   fetchUser, | ||||||
|   userSelector, |   isSignedInSelector, | ||||||
|   onlineStatusChange, |   onlineStatusChange, | ||||||
|   isOnlineSelector, |   isOnlineSelector | ||||||
|   isSignedInSelector | } from '../../redux'; | ||||||
| } from '../redux/app'; | 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 './global.css'; | ||||||
| import './layout.css'; | import './layout.css'; | ||||||
| import { createSelector } from 'reselect'; | import './night.css'; | ||||||
| import { isBrowser } from '../../utils'; |  | ||||||
| 
 | 
 | ||||||
| const metaKeywords = [ | const metaKeywords = [ | ||||||
|   'javascript', |   'javascript', | ||||||
| @@ -48,57 +48,67 @@ const metaKeywords = [ | |||||||
|   'programming' |   '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( | const mapStateToProps = createSelector( | ||||||
|   userSelector, |  | ||||||
|   isSignedInSelector, |   isSignedInSelector, | ||||||
|  |   flashMessagesSelector, | ||||||
|   isOnlineSelector, |   isOnlineSelector, | ||||||
|   ({ theme = 'default' }, isSignedIn, isOnline) => ({ |   (isSignedIn, flashMessages, isOnline) => ({ | ||||||
|     theme, |  | ||||||
|     isSignedIn, |     isSignedIn, | ||||||
|  |     flashMessages, | ||||||
|  |     hasMessages: !!flashMessages.length, | ||||||
|     isOnline |     isOnline | ||||||
|   }) |   }) | ||||||
| ); | ); | ||||||
| const mapDispatchToProps = dispatch => | const mapDispatchToProps = dispatch => | ||||||
|   bindActionCreators({ fetchUser, onlineStatusChange }, dispatch); |   bindActionCreators( | ||||||
|  |     { fetchUser, removeFlashMessage, onlineStatusChange }, | ||||||
|  |     dispatch | ||||||
|  |   ); | ||||||
| 
 | 
 | ||||||
| const propTypes = { | class DefaultLayout extends Component { | ||||||
|   children: PropTypes.object, |   constructor(props) { | ||||||
|   fetchUser: PropTypes.func.isRequired, |     super(props); | ||||||
|   isOnline: PropTypes.bool.isRequired, |  | ||||||
|   isSignedIn: PropTypes.bool.isRequired, |  | ||||||
|   onlineStatusChange: PropTypes.func.isRequired, |  | ||||||
|   theme: PropTypes.string |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| class Layout extends Component { |     this.location = ''; | ||||||
|   state = { |   } | ||||||
|     location: '' |  | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
|   componentDidMount() { |   componentDidMount() { | ||||||
|     this.props.fetchUser(); |     if (!this.props.isSignedIn) { | ||||||
|  |       this.props.fetchUser(); | ||||||
|  |     } | ||||||
|     const url = window.location.pathname + window.location.search; |     const url = window.location.pathname + window.location.search; | ||||||
|     ga.pageview(url); |     ga.pageview(url); | ||||||
| 
 | 
 | ||||||
|     window.addEventListener('online', this.updateOnlineStatus); |     window.addEventListener('online', this.updateOnlineStatus); | ||||||
|     window.addEventListener('offline', this.updateOnlineStatus); |     window.addEventListener('offline', this.updateOnlineStatus); | ||||||
| 
 | 
 | ||||||
|     /* eslint-disable react/no-did-mount-set-state */ |     this.location = url; | ||||||
|     // this is for local location tracking only, no re-rendering required
 |  | ||||||
|     this.setState({ |  | ||||||
|       location: url |  | ||||||
|     }); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   componentDidUpdate() { |   componentDidUpdate() { | ||||||
|     const url = window.location.pathname + window.location.search; |     const url = window.location.pathname + window.location.search; | ||||||
|     if (url !== this.state.location) { |     if (url !== this.location) { | ||||||
|       ga.pageview(url); |       ga.pageview(url); | ||||||
|       /* eslint-disable react/no-did-update-set-state */ |       this.location = url; | ||||||
|       // this is for local location tracking only, no re-rendering required
 |  | ||||||
|       this.setState({ |  | ||||||
|         location: url |  | ||||||
|       }); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -115,7 +125,16 @@ class Layout extends Component { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render() { |   render() { | ||||||
|     const { children, theme, isOnline, isSignedIn } = this.props; |     const { | ||||||
|  |       children, | ||||||
|  |       disableSettings, | ||||||
|  |       hasMessages, | ||||||
|  |       flashMessages = [], | ||||||
|  |       removeFlashMessage, | ||||||
|  |       landingPage, | ||||||
|  |       isOnline, | ||||||
|  |       isSignedIn | ||||||
|  |     } = this.props; | ||||||
|     return ( |     return ( | ||||||
|       <Fragment> |       <Fragment> | ||||||
|         <Helmet |         <Helmet | ||||||
| @@ -129,17 +148,23 @@ class Layout extends Component { | |||||||
|             { name: 'keywords', content: metaKeywords.join(', ') } |             { name: 'keywords', content: metaKeywords.join(', ') } | ||||||
|           ]} |           ]} | ||||||
|         /> |         /> | ||||||
|         <Header /> |         <Header disableSettings={disableSettings} /> | ||||||
|         <div className={'app-wrapper ' + theme}> |         <main className={landingPage && 'landing-page'}> | ||||||
|           <OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} /> |           <OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} /> | ||||||
|           <main>{children}</main> |           {hasMessages ? ( | ||||||
|         </div> |             <Flash messages={flashMessages} onClose={removeFlashMessage} /> | ||||||
|         <DonationModal /> |           ) : null} | ||||||
|  |           {children} | ||||||
|  |         </main> | ||||||
|       </Fragment> |       </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 @@ | |||||||
| 
 | #learn-app-wrapper .reflex-layout.vertical > .reflex-splitter, | ||||||
| main { | #learn-app-wrapper .reflex-layout > .reflex-element { | ||||||
|   height: 100%; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .reflex-layout.vertical > .reflex-splitter, |  | ||||||
| .reflex-layout > .reflex-element { |  | ||||||
|   /* |   /* | ||||||
|   Note(Bouncey): removing height: 100% makes the element 100% high ¯\_(ツ)_/¯ |   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) |   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; |   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-left: 2px solid #006400 !important; | ||||||
|   border-right: 2px solid #006400 !important; |   border-right: 2px solid #006400 !important; | ||||||
|   background-color: #006400; |   background-color: #006400; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .reflex-layout.vertical > .reflex-splitter { | #learn-app-wrapper .reflex-layout.vertical > .reflex-splitter { | ||||||
|   width: 5px; |   width: 5px; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| .app-wrapper { |  | ||||||
|   margin-top: 36px; |  | ||||||
|   min-height: calc(100vh - 36px); |  | ||||||
| } |  | ||||||
| @@ -5,6 +5,10 @@ import sassjs from './sassjs'; | |||||||
| 
 | 
 | ||||||
| const metaAndStyleSheets = meta | const metaAndStyleSheets = meta | ||||||
|   .concat(favicons, mathjax, sassjs) |   .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; | 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