From 156ea1af766bd34d08d132e819e90807a97b30c8 Mon Sep 17 00:00:00 2001 From: Stuart Taylor Date: Tue, 15 May 2018 06:12:05 +0100 Subject: [PATCH] Feat: Welcome Page (#17150) --- client/index.js | 18 +- client/less/main.less | 18 + client/utils/use-lang-routes.js | 44 - common/app/App.jsx | 7 +- common/app/Map/Block.jsx | 105 --- common/app/Map/Blocks.jsx | 39 - common/app/Map/Challenge.jsx | 155 ---- common/app/Map/Challenges.jsx | 35 - common/app/Map/Map.jsx | 127 --- common/app/Map/Super-Block.jsx | 96 -- common/app/Map/index.js | 1 - common/app/Map/map.less | 144 --- common/app/Map/ns.json | 1 - common/app/Map/redux/fetch-map-ui-epic.js | 12 +- common/app/Map/redux/index.js | 3 +- common/app/Nav/LargeNav.jsx | 13 +- common/app/Nav/MediumNav.jsx | 27 +- common/app/Nav/Nav.jsx | 61 +- common/app/Nav/SmallNav.jsx | 25 +- common/app/Nav/components/NavLinks.jsx | 20 - common/app/Nav/components/NavLogo.jsx | 6 +- common/app/Nav/links.json | 43 +- common/app/Nav/navPropTypes.js | 5 +- common/app/Nav/redux/index.js | 5 +- .../Nav/redux/load-current-challenge-epic.js | 58 -- common/app/Panes/Divider.jsx | 51 -- common/app/Panes/Pane.jsx | 41 - common/app/Panes/Panes.jsx | 127 --- common/app/Panes/index.js | 1 - common/app/Panes/ns.json | 1 - common/app/Panes/panes.less | 42 - common/app/Panes/quotes.js | 71 -- common/app/Panes/redux/divider-epic.js | 40 - common/app/Panes/redux/index.js | 213 ----- common/app/Panes/redux/utils.js | 62 -- common/app/Panes/redux/window-epic.js | 19 - common/app/Router/Link.jsx | 9 +- common/app/Router/redux/add-lang-enhancer.js | 28 - common/app/Router/redux/index.js | 1 - common/app/Router/to-url.js | 10 +- common/app/Toasts/Toasts.jsx | 9 +- common/app/create-app.jsx | 15 +- common/app/create-panes-map.js | 1 - common/app/entities/index.js | 54 +- common/app/epics.js | 4 - common/app/files/index.js | 53 -- common/app/index.less | 2 - common/app/reducer.js | 5 +- common/app/redux-form-reducer.js | 37 - common/app/redux/fetch-challenges-epic.js | 118 --- common/app/redux/index.js | 150 +--- common/app/redux/update-my-challenge-epic.js | 51 -- common/app/redux/utils.js | 45 - common/app/redux/utils.test.js | 152 ---- common/app/routes-map.js | 8 +- .../Challenges/Challenge-Description.jsx | 51 -- .../app/routes/Challenges/Challenge-Title.jsx | 52 -- .../app/routes/Challenges/Child-Container.jsx | 23 - .../Challenges/Code-Mirror-Skeleton.jsx | 42 - .../routes/Challenges/Completion-Modal.jsx | 116 --- common/app/routes/Challenges/Help-Modal.jsx | 75 -- common/app/routes/Challenges/Output.jsx | 46 - common/app/routes/Challenges/Preview.jsx | 20 - common/app/routes/Challenges/Show.jsx | 127 --- common/app/routes/Challenges/Side-Panel.jsx | 182 ---- .../app/routes/Challenges/Solution-Input.jsx | 46 - common/app/routes/Challenges/Test-Suite.jsx | 80 -- common/app/routes/Challenges/Tool-Panel.jsx | 179 ---- common/app/routes/Challenges/challenges.less | 201 ----- common/app/routes/Challenges/index.js | 65 -- common/app/routes/Challenges/ns.json | 1 - .../routes/Challenges/rechallenge/builders.js | 118 --- .../routes/Challenges/rechallenge/throwers.js | 124 --- .../Challenges/rechallenge/transformers.js | 162 ---- .../routes/Challenges/redux/challenge-epic.js | 80 -- .../Challenges/redux/code-storage-epic.js | 196 ---- .../Challenges/redux/completion-epic.js | 175 ---- .../redux/execute-challenge-epic.js | 125 --- common/app/routes/Challenges/redux/index.js | 351 -------- .../app/routes/Challenges/redux/modal-epic.js | 64 -- common/app/routes/Challenges/utils/build.js | 85 -- .../app/routes/Challenges/utils/code-uri.js | 79 -- .../Challenges/utils/fetch-and-cache.js | 74 -- common/app/routes/Challenges/utils/frame.js | 145 --- common/app/routes/Challenges/utils/index.js | 348 ------- .../app/routes/Challenges/utils/index.test.js | 848 ------------------ .../routes/Challenges/views/Modern/Editor.jsx | 155 ---- .../routes/Challenges/views/Modern/Show.jsx | 111 --- .../routes/Challenges/views/Modern/index.js | 1 - .../routes/Challenges/views/Modern/ns.json | 1 - .../Challenges/views/backend/Back-End.jsx | 184 ---- .../routes/Challenges/views/backend/Show.jsx | 39 - .../routes/Challenges/views/backend/index.js | 1 - .../Challenges/views/classic/Editor.jsx | 162 ---- .../routes/Challenges/views/classic/Show.jsx | 51 -- .../Challenges/views/classic/classic.less | 21 - .../routes/Challenges/views/classic/index.js | 1 - .../routes/Challenges/views/classic/ns.json | 1 - common/app/routes/Challenges/views/index.less | 3 - .../routes/Challenges/views/project/Forms.jsx | 158 ---- .../Challenges/views/project/Project.jsx | 58 -- .../routes/Challenges/views/project/Show.jsx | 39 - .../Challenges/views/project/Side-Panel.jsx | 39 - .../Challenges/views/project/Tool-Panel.jsx | 131 --- .../routes/Challenges/views/project/index.js | 1 - .../routes/Challenges/views/project/ns.json | 1 - .../Challenges/views/project/redux/index.js | 29 - .../views/project/redux/project-normalizer.js | 11 - .../routes/Challenges/views/quiz/Choice.jsx | 83 -- .../app/routes/Challenges/views/quiz/Quiz.jsx | 241 ----- .../app/routes/Challenges/views/quiz/Show.jsx | 39 - .../app/routes/Challenges/views/quiz/index.js | 1 - .../app/routes/Challenges/views/quiz/ns.json | 1 - .../routes/Challenges/views/quiz/quiz.less | 93 -- .../Challenges/views/quiz/redux/index.js | 81 -- .../app/routes/Challenges/views/step/Show.jsx | 39 - .../app/routes/Challenges/views/step/Step.jsx | 293 ------ .../app/routes/Challenges/views/step/index.js | 1 - .../app/routes/Challenges/views/step/ns.json | 1 - .../Challenges/views/step/redux/index.js | 92 -- .../views/step/redux/step-challenge-epic.js | 52 -- .../step/redux/step-challenge-epic.test.js | 146 --- .../routes/Challenges/views/step/step.less | 60 -- common/app/routes/Map/index.js | 10 - common/app/routes/Profile/Profile.jsx | 7 +- .../routes/Profile/components/Timeline.jsx | 13 +- common/app/routes/Settings/Settings.jsx | 18 +- .../Settings/components/Cert-Settings.jsx | 10 - .../Settings/components/Language-Settings.jsx | 141 --- common/app/routes/Settings/redux/index.js | 5 - .../routes/Settings/redux/update-user-epic.js | 56 +- common/app/routes/index.js | 27 - common/app/routes/index.less | 1 - common/app/routes/redux.js | 7 - common/app/utils/lang.js | 39 - common/app/utils/quotes.js | 143 +++ common/app/utils/redux-first-router.js | 26 - common/models/user.json | 19 +- common/utils/supported-languages.js | 5 - package-lock.json | 16 + package.json | 1 + server/boot/authentication.js | 2 +- server/boot/challenge.js | 8 +- server/boot/commit.js | 2 +- server/boot/home.js | 89 +- server/boot/randomAPIs.js | 22 +- server/boot/react.js | 5 +- server/boot/settings.js | 29 - server/boot/user.js | 2 +- server/boot/z-lang-redirect.js | 37 - server/boot/z-not-found.js | 22 + server/component-passport.js | 36 +- server/middleware.json | 1 - server/middlewares/add-lang.js | 68 -- server/middlewares/add-return-to.js | 4 +- server/models/about.js | 9 +- server/rss/index.js | 86 ++ server/rss/lybsyn.js | 47 + server/rss/medium.js | 39 + server/services/challenge.js | 8 +- server/services/mapUi.js | 13 +- server/utils/map.js | 43 +- server/utils/publicUserProps.js | 1 - .../views/homePartials/activeUsersScript.jade | 14 + .../views/homePartials/activeUsersView.jade | 4 + server/views/homePartials/newsFeed.jade | 18 + server/views/homePartials/quote.jade | 6 + server/views/homePartials/scripts.jade | 3 + server/views/homePartials/stats.jade | 26 + server/views/{home.jade => noUserHome.jade} | 9 + server/views/partials/navbar.jade | 23 +- server/views/userHome.jade | 22 + 172 files changed, 693 insertions(+), 9738 deletions(-) delete mode 100644 client/utils/use-lang-routes.js delete mode 100644 common/app/Map/Block.jsx delete mode 100644 common/app/Map/Blocks.jsx delete mode 100644 common/app/Map/Challenge.jsx delete mode 100644 common/app/Map/Challenges.jsx delete mode 100644 common/app/Map/Map.jsx delete mode 100644 common/app/Map/Super-Block.jsx delete mode 100644 common/app/Map/index.js delete mode 100644 common/app/Map/map.less delete mode 100644 common/app/Map/ns.json delete mode 100644 common/app/Nav/redux/load-current-challenge-epic.js delete mode 100644 common/app/Panes/Divider.jsx delete mode 100644 common/app/Panes/Pane.jsx delete mode 100644 common/app/Panes/Panes.jsx delete mode 100644 common/app/Panes/index.js delete mode 100644 common/app/Panes/ns.json delete mode 100644 common/app/Panes/panes.less delete mode 100644 common/app/Panes/quotes.js delete mode 100644 common/app/Panes/redux/divider-epic.js delete mode 100644 common/app/Panes/redux/index.js delete mode 100644 common/app/Panes/redux/utils.js delete mode 100644 common/app/Panes/redux/window-epic.js delete mode 100644 common/app/Router/redux/add-lang-enhancer.js delete mode 100644 common/app/create-panes-map.js delete mode 100644 common/app/files/index.js delete mode 100644 common/app/redux-form-reducer.js delete mode 100644 common/app/redux/fetch-challenges-epic.js delete mode 100644 common/app/redux/update-my-challenge-epic.js delete mode 100644 common/app/redux/utils.test.js delete mode 100644 common/app/routes/Challenges/Challenge-Description.jsx delete mode 100644 common/app/routes/Challenges/Challenge-Title.jsx delete mode 100644 common/app/routes/Challenges/Child-Container.jsx delete mode 100644 common/app/routes/Challenges/Code-Mirror-Skeleton.jsx delete mode 100644 common/app/routes/Challenges/Completion-Modal.jsx delete mode 100644 common/app/routes/Challenges/Help-Modal.jsx delete mode 100644 common/app/routes/Challenges/Output.jsx delete mode 100644 common/app/routes/Challenges/Preview.jsx delete mode 100644 common/app/routes/Challenges/Show.jsx delete mode 100644 common/app/routes/Challenges/Side-Panel.jsx delete mode 100644 common/app/routes/Challenges/Solution-Input.jsx delete mode 100644 common/app/routes/Challenges/Test-Suite.jsx delete mode 100644 common/app/routes/Challenges/Tool-Panel.jsx delete mode 100644 common/app/routes/Challenges/challenges.less delete mode 100644 common/app/routes/Challenges/index.js delete mode 100644 common/app/routes/Challenges/ns.json delete mode 100644 common/app/routes/Challenges/rechallenge/builders.js delete mode 100644 common/app/routes/Challenges/rechallenge/throwers.js delete mode 100644 common/app/routes/Challenges/rechallenge/transformers.js delete mode 100644 common/app/routes/Challenges/redux/challenge-epic.js delete mode 100644 common/app/routes/Challenges/redux/code-storage-epic.js delete mode 100644 common/app/routes/Challenges/redux/completion-epic.js delete mode 100644 common/app/routes/Challenges/redux/execute-challenge-epic.js delete mode 100644 common/app/routes/Challenges/redux/index.js delete mode 100644 common/app/routes/Challenges/redux/modal-epic.js delete mode 100644 common/app/routes/Challenges/utils/build.js delete mode 100644 common/app/routes/Challenges/utils/code-uri.js delete mode 100644 common/app/routes/Challenges/utils/fetch-and-cache.js delete mode 100644 common/app/routes/Challenges/utils/frame.js delete mode 100644 common/app/routes/Challenges/utils/index.js delete mode 100644 common/app/routes/Challenges/utils/index.test.js delete mode 100644 common/app/routes/Challenges/views/Modern/Editor.jsx delete mode 100644 common/app/routes/Challenges/views/Modern/Show.jsx delete mode 100644 common/app/routes/Challenges/views/Modern/index.js delete mode 100644 common/app/routes/Challenges/views/Modern/ns.json delete mode 100644 common/app/routes/Challenges/views/backend/Back-End.jsx delete mode 100644 common/app/routes/Challenges/views/backend/Show.jsx delete mode 100644 common/app/routes/Challenges/views/backend/index.js delete mode 100644 common/app/routes/Challenges/views/classic/Editor.jsx delete mode 100644 common/app/routes/Challenges/views/classic/Show.jsx delete mode 100644 common/app/routes/Challenges/views/classic/classic.less delete mode 100644 common/app/routes/Challenges/views/classic/index.js delete mode 100644 common/app/routes/Challenges/views/classic/ns.json delete mode 100644 common/app/routes/Challenges/views/index.less delete mode 100644 common/app/routes/Challenges/views/project/Forms.jsx delete mode 100644 common/app/routes/Challenges/views/project/Project.jsx delete mode 100644 common/app/routes/Challenges/views/project/Show.jsx delete mode 100644 common/app/routes/Challenges/views/project/Side-Panel.jsx delete mode 100644 common/app/routes/Challenges/views/project/Tool-Panel.jsx delete mode 100644 common/app/routes/Challenges/views/project/index.js delete mode 100644 common/app/routes/Challenges/views/project/ns.json delete mode 100644 common/app/routes/Challenges/views/project/redux/index.js delete mode 100644 common/app/routes/Challenges/views/project/redux/project-normalizer.js delete mode 100644 common/app/routes/Challenges/views/quiz/Choice.jsx delete mode 100644 common/app/routes/Challenges/views/quiz/Quiz.jsx delete mode 100644 common/app/routes/Challenges/views/quiz/Show.jsx delete mode 100644 common/app/routes/Challenges/views/quiz/index.js delete mode 100644 common/app/routes/Challenges/views/quiz/ns.json delete mode 100644 common/app/routes/Challenges/views/quiz/quiz.less delete mode 100644 common/app/routes/Challenges/views/quiz/redux/index.js delete mode 100644 common/app/routes/Challenges/views/step/Show.jsx delete mode 100644 common/app/routes/Challenges/views/step/Step.jsx delete mode 100644 common/app/routes/Challenges/views/step/index.js delete mode 100644 common/app/routes/Challenges/views/step/ns.json delete mode 100644 common/app/routes/Challenges/views/step/redux/index.js delete mode 100644 common/app/routes/Challenges/views/step/redux/step-challenge-epic.js delete mode 100644 common/app/routes/Challenges/views/step/redux/step-challenge-epic.test.js delete mode 100644 common/app/routes/Challenges/views/step/step.less delete mode 100644 common/app/routes/Map/index.js delete mode 100644 common/app/routes/Settings/components/Language-Settings.jsx delete mode 100644 common/app/utils/lang.js create mode 100644 common/app/utils/quotes.js delete mode 100644 common/app/utils/redux-first-router.js delete mode 100644 common/utils/supported-languages.js delete mode 100644 server/boot/z-lang-redirect.js create mode 100644 server/boot/z-not-found.js delete mode 100644 server/middlewares/add-lang.js create mode 100644 server/rss/index.js create mode 100644 server/rss/lybsyn.js create mode 100644 server/rss/medium.js create mode 100644 server/views/homePartials/activeUsersScript.jade create mode 100644 server/views/homePartials/activeUsersView.jade create mode 100644 server/views/homePartials/newsFeed.jade create mode 100644 server/views/homePartials/quote.jade create mode 100644 server/views/homePartials/scripts.jade create mode 100644 server/views/homePartials/stats.jade rename server/views/{home.jade => noUserHome.jade} (95%) create mode 100644 server/views/userHome.jade diff --git a/client/index.js b/client/index.js index 969dab1366..514fb1a6f0 100644 --- a/client/index.js +++ b/client/index.js @@ -4,20 +4,13 @@ import Rx from 'rx'; import debug from 'debug'; import { render } from 'redux-epic'; import createHistory from 'history/createBrowserHistory'; -import useLangRoutes from './utils/use-lang-routes'; import sendPageAnalytics from './utils/send-page-analytics'; import { App, createApp, provideStore } from '../common/app'; -import { getLangFromPath } from '../common/app/utils/lang'; // client specific epics import epics from './epics'; -import { - isColdStored, - getColdStorage, - saveToColdStorage -} from './cold-reload'; const { __OPBEAT__ORG_ID, @@ -47,7 +40,7 @@ const { document, ga, __fcc__: { - data: ssrState = {}, + data: defaultState = {}, csrf: { token: csrfToken } = {} @@ -63,10 +56,6 @@ const epicOptions = { const DOMContainer = document.getElementById('fcc'); -const defaultState = isColdStored() ? - getColdStorage() : - ssrState; -const primaryLang = getLangFromPath(location.pathname); defaultState.app.csrfToken = csrfToken; @@ -76,7 +65,7 @@ const serviceOptions = { xhrTimeout: 15000 }; -const history = useLangRoutes(createHistory, primaryLang)(); +const history = createHistory(); sendPageAnalytics(history, ga); createApp({ @@ -88,14 +77,13 @@ createApp({ enhancers: isDev && devToolsExtension && [ devToolsExtension() ], middlewares: enableOpbeat && [ createOpbeatMiddleware() ] }) - .doOnNext(({ store }) => { + .doOnNext(() => { if (module.hot && typeof module.hot.accept === 'function') { module.hot.accept(() => { // note(berks): not sure this ever runs anymore after adding // RHR? log('saving state and refreshing.'); log('ignore react ssr warning.'); - saveToColdStorage(store.getState()); setTimeout(() => location.reload(), hotReloadTimeout); }); } diff --git a/client/less/main.less b/client/less/main.less index f1653e0759..db9a804e05 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -152,6 +152,24 @@ h1, h2, h3, h4, h5, h6, p, li { width: 100px; } +p.stats { + font-size: 2rem; +} + +.green-text { + color: #006400; +} + +.more-button-container { + display: flex; + justify-content: center; + align-items: center; + + div { + flex-grow: 1; + } +} + .completion-icon { font-size: 150px; } diff --git a/client/utils/use-lang-routes.js b/client/utils/use-lang-routes.js deleted file mode 100644 index d254e1ac09..0000000000 --- a/client/utils/use-lang-routes.js +++ /dev/null @@ -1,44 +0,0 @@ -import { addLang, getLangFromPath } from '../../common/app/utils/lang.js'; - -function addLangToLocation(location, lang) { - if (!location) { - return location; - } - if (typeof location === 'string') { - return addLang(location, lang); - } - return { - ...location, - pathname: addLang(location.pathname, lang) - }; -} - -function getLangFromLocation(location) { - if (!location) { - return location; - } - if (typeof location === 'string') { - return getLangFromPath(location); - } - return getLangFromPath(location.pathname); -} - -export default function useLangRoutes(createHistory, primaryLang) { - return (options = {}) => { - let lang = primaryLang || 'en'; - const history = createHistory(options); - const unsubscribeFromHistory = history.listen(nextLocation => { - lang = getLangFromLocation(nextLocation); - }); - const push = location => history.push(addLangToLocation(location, lang)); - const replace = location => history.replace( - addLangToLocation(location, lang) - ); - return { - ...history, - push, - replace, - unsubscribe() { unsubscribeFromHistory(); } - }; - }; -} diff --git a/common/app/App.jsx b/common/app/App.jsx index f4e17b61f6..7d32cdc6e6 100644 --- a/common/app/App.jsx +++ b/common/app/App.jsx @@ -10,17 +10,19 @@ import { isSignedInSelector } from './redux'; +import { fetchMapUi } from './Map/redux'; + import Flash from './Flash'; import Nav from './Nav'; import Toasts from './Toasts'; import NotFound from './NotFound'; import { mainRouteSelector } from './routes/redux'; -import Challenges from './routes/Challenges'; import Profile from './routes/Profile'; import Settings from './routes/Settings'; const mapDispatchToProps = { appMounted, + fetchMapUi, fetchUser }; @@ -37,6 +39,7 @@ const mapStateToProps = state => { const propTypes = { appMounted: PropTypes.func.isRequired, children: PropTypes.node, + fetchMapUi: PropTypes.func.isRequired, fetchUser: PropTypes.func, isSignedIn: PropTypes.bool, route: PropTypes.string, @@ -44,7 +47,6 @@ const propTypes = { }; const routes = { - challenges: Challenges, profile: Profile, settings: Settings }; @@ -53,6 +55,7 @@ const routes = { export class FreeCodeCamp extends React.Component { componentDidMount() { this.props.appMounted(); + this.props.fetchMapUi(); if (!this.props.isSignedIn) { this.props.fetchUser(); } diff --git a/common/app/Map/Block.jsx b/common/app/Map/Block.jsx deleted file mode 100644 index 1387faa0bd..0000000000 --- a/common/app/Map/Block.jsx +++ /dev/null @@ -1,105 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import FA from 'react-fontawesome'; -import { Panel } from 'react-bootstrap'; - -import ns from './ns.json'; -import Challenges from './Challenges.jsx'; -import { - toggleThisPanel, - makePanelOpenSelector -} from './redux'; -import { fetchNewBlock } from '../redux'; - -import { makeBlockSelector } from '../entities'; - -const mapDispatchToProps = { - fetchNewBlock, - toggleThisPanel -}; -function makeMapStateToProps(_, { dashedName }) { - return createSelector( - makeBlockSelector(dashedName), - makePanelOpenSelector(dashedName), - (block, isOpen) => { - return { - isOpen, - dashedName, - title: block.title, - time: block.time, - challenges: block.challenges || [] - }; - } - ); -} -const propTypes = { - challenges: PropTypes.array, - dashedName: PropTypes.string, - fetchNewBlock: PropTypes.func.isRequired, - isOpen: PropTypes.bool, - time: PropTypes.string, - title: PropTypes.string, - toggleThisPanel: PropTypes.func -}; - -export class Block extends PureComponent { - constructor(...props) { - super(...props); - this.handleSelect = this.handleSelect.bind(this); - } - handleSelect(eventKey, e) { - e.preventDefault(); - this.props.toggleThisPanel(eventKey); - } - - renderHeader(isOpen, title, time, isCompleted) { - return ( -
- - - { title } - - { - time && ({ time }) - } -
- ); - } - - render() { - const { - title, - time, - dashedName, - isOpen, - challenges, - fetchNewBlock - } = this.props; - return ( - fetchNewBlock(dashedName) } - onSelect={ this.handleSelect } - > - { isOpen && } - - ); - } -} - -Block.displayName = 'Block'; -Block.propTypes = propTypes; - -export default connect(makeMapStateToProps, mapDispatchToProps)(Block); diff --git a/common/app/Map/Blocks.jsx b/common/app/Map/Blocks.jsx deleted file mode 100644 index 879a928334..0000000000 --- a/common/app/Map/Blocks.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -import ns from './ns.json'; -import Block from './Block.jsx'; - -const propTypes = { - blocks: PropTypes.array.isRequired -}; - -export default class Blocks extends Component { - shouldComponentUpdate(nextProps) { - return this.props.blocks !== nextProps.blocks; - } - - render() { - const { - blocks - } = this.props; - if (blocks.length <= 0) { - return null; - } - return ( -
- { - blocks.map(dashedName => ( - - )) - } -
- ); - } -} - -Blocks.displayName = 'Blocks'; -Blocks.propTypes = propTypes; diff --git a/common/app/Map/Challenge.jsx b/common/app/Map/Challenge.jsx deleted file mode 100644 index ccc4d18401..0000000000 --- a/common/app/Map/Challenge.jsx +++ /dev/null @@ -1,155 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import classnames from 'classnames'; -import debug from 'debug'; - -import { clickOnChallenge } from './redux'; -import { userSelector } from '../redux'; -import { paramsSelector } from '../Router/redux'; -import { challengeMapSelector } from '../entities'; -import { Link } from '../Router'; -import { onRouteChallenges } from '../routes/Challenges/redux'; - -const propTypes = { - block: PropTypes.string, - challenge: PropTypes.object, - clickOnChallenge: PropTypes.func.isRequired, - dashedName: PropTypes.string, - isComingSoon: PropTypes.bool, - isCompleted: PropTypes.bool, - isDev: PropTypes.bool, - isLocked: PropTypes.bool, - selected: PropTypes.bool, - title: PropTypes.string -}; -const mapDispatchToProps = { clickOnChallenge }; - -function makeMapStateToProps(_, { dashedName }) { - return createSelector( - userSelector, - challengeMapSelector, - paramsSelector, - ( - { challengeMap: userChallengeMap }, - challengeMap, - params - ) => { - const { - id, - title, - block, - isLocked, - isComingSoon - } = challengeMap[dashedName] || {}; - const isCompleted = userChallengeMap ? !!userChallengeMap[id] : false; - const selected = dashedName === params.dashedName; - return { - dashedName, - isCompleted, - title, - block, - isLocked, - isComingSoon, - isDev: debug.enabled('fcc:*'), - selected - }; - } - ); -} - -export class Challenge extends PureComponent { - renderCompleted(isCompleted, isLocked) { - if (isLocked || !isCompleted) { - return null; - } - return completed; - } - - renderComingSoon(isComingSoon) { - if (!isComingSoon) { - return null; - } - return ( - -     - - Coming Soon - - - ); - } - - renderLocked(title, isComingSoon, className) { - return ( -

- { title } - { this.renderComingSoon(isComingSoon) } -

- ); - } - - - render() { - const { - block, - clickOnChallenge, - dashedName, - isComingSoon, - isCompleted, - isDev, - isLocked, - title, - selected - } = this.props; - if (!title) { - return null; - } - const challengeClassName = classnames({ - 'text-primary': true, - 'padded-ionic-icon': true, - 'map-challenge-title': true, - 'ion-checkmark-circled faded': !(isLocked || isComingSoon) && isCompleted, - 'ion-ios-circle-outline': !(isLocked || isComingSoon) && !isCompleted, - 'ion-locked': isLocked || isComingSoon, - disabled: isLocked || (!isDev && isComingSoon), - selectedChallenge: selected - }); - if (isLocked || (!isDev && isComingSoon)) { - return this.renderLocked( - title, - isComingSoon, - challengeClassName - ); - } - return ( -
- - - { title } - { this.renderCompleted(isCompleted, isLocked) } - - -
- ); - } -} - -Challenge.propTypes = propTypes; -Challenge.dispalyName = 'Challenge'; - -export default connect( - makeMapStateToProps, - mapDispatchToProps -)(Challenge); diff --git a/common/app/Map/Challenges.jsx b/common/app/Map/Challenges.jsx deleted file mode 100644 index 975553a48a..0000000000 --- a/common/app/Map/Challenges.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -import Challenge from './Challenge.jsx'; - -const propTypes = { - challenges: PropTypes.array.isRequired -}; - -export default class Challenges extends Component { - shouldComponentUpdate(nextProps) { - return this.props.challenges !== nextProps.challenges; - } - render() { - const { challenges } = this.props; - if (!challenges.length) { - return
No Challenges Found
; - } - return ( -
- { - challenges.map(dashedName => ( - - )) - } -
- ); - } -} - -Challenges.displayName = 'Challenges'; -Challenges.propTypes = propTypes; diff --git a/common/app/Map/Map.jsx b/common/app/Map/Map.jsx deleted file mode 100644 index 6d9ec9e3c8..0000000000 --- a/common/app/Map/Map.jsx +++ /dev/null @@ -1,127 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { Col, Row } from 'react-bootstrap'; - -import ns from './ns.json'; -import { Loader } from '../helperComponents'; -import SuperBlock from './Super-Block.jsx'; -import { currentChallengeSelector, superBlocksSelector } from '../redux'; -import { fetchMapUi } from './redux'; - -const mapStateToProps = state => ({ - currentChallenge: currentChallengeSelector(state), - superBlocks: superBlocksSelector(state) -}); - -const mapDispatchToProps = { fetchMapUi }; -const propTypes = { - currentChallenge: PropTypes.string, - fetchMapUi: PropTypes.func.isRequired, - params: PropTypes.object, - superBlocks: PropTypes.array -}; - -export class ShowMap extends PureComponent { - componentDidMount() { - this.setupMapScroll(); - } - - componentDidUpdate() { - this.setupMapScroll(); - } - - setupMapScroll() { - this.updateMapScrollAttempts = 0; - this.updateMapScroll(); - } - - updateMapScroll() { - const { currentChallenge } = this.props; - const rowNode = this._row; - const challengeNode = rowNode.querySelector( - `[data-challenge="${currentChallenge}"]` - ); - - if ( !challengeNode ) { - this.retryUpdateMapScroll(); - return; - } - - const containerScrollHeight = rowNode.scrollHeight; - const containerHeight = rowNode.clientHeight; - - const offset = 100; - const itemTop = challengeNode.offsetTop; - const itemBottom = itemTop + challengeNode.clientHeight; - - const currentViewBottom = rowNode.scrollTop + containerHeight; - - if ( itemBottom + offset < currentViewBottom ) { - // item is visible with enough offset from bottom => no need to scroll - return; - } - - if ( containerHeight === containerScrollHeight ) { - /* - * During a first run containerNode scrollHeight may be not updated yet. - * In this case containerNode ignores changes of scrollTop property. - * So we have to wait some time before scrollTop can be updated - * */ - this.retryUpdateMapScroll(); - return; - } - - const scrollTop = itemBottom + offset - containerHeight; - rowNode.scrollTop = scrollTop; - } - - retryUpdateMapScroll() { - const maxAttempts = 5; - this.updateMapScrollAttempts++; - - if (this.updateMapScrollAttempts < maxAttempts) { - setTimeout(() => this.updateMapScroll(), 300); - } - } - - renderSuperBlocks() { - const { superBlocks } = this.props; - if (!Array.isArray(superBlocks) || !superBlocks.length) { - return ( -
- -
- ); - } - return superBlocks.map(dashedName => ( - - )); - } - - render() { - return ( -
{ this._row = ref; }}> - - -
- { this.renderSuperBlocks() } -
-
- - -
- ); - } -} - -ShowMap.displayName = 'Map'; -ShowMap.propTypes = propTypes; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(ShowMap); diff --git a/common/app/Map/Super-Block.jsx b/common/app/Map/Super-Block.jsx deleted file mode 100644 index c5ce006263..0000000000 --- a/common/app/Map/Super-Block.jsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import FA from 'react-fontawesome'; -import { Panel } from 'react-bootstrap'; - -import ns from './ns.json'; -import Blocks from './Blocks.jsx'; -import { - toggleThisPanel, - - makePanelOpenSelector -} from './redux'; -import { makeSuperBlockSelector } from '../entities'; - -const mapDispatchToProps = { toggleThisPanel }; -// make selectors unique to each component -// see -// reactjs/reselect -// sharing-selectors-with-props-across-multiple-components -function mapStateToProps(_, { dashedName }) { - return createSelector( - makeSuperBlockSelector(dashedName), - makePanelOpenSelector(dashedName), - (superBlock, isOpen) => ({ - isOpen, - dashedName, - title: superBlock.title || dashedName, - blocks: superBlock.blocks || [] - }) - ); -} - -const propTypes = { - blocks: PropTypes.array, - dashedName: PropTypes.string, - isOpen: PropTypes.bool, - title: PropTypes.string, - toggleThisPanel: PropTypes.func -}; - -export class SuperBlock extends PureComponent { - constructor(...props) { - super(...props); - this.handleSelect = this.handleSelect.bind(this); - } - handleSelect(eventKey, e) { - e.preventDefault(); - this.props.toggleThisPanel(eventKey); - } - - renderHeader(isOpen, title, isCompleted) { - return ( -
- - { title } -
- ); - } - - render() { - const { - title, - dashedName, - blocks, - isOpen - } = this.props; - return ( - - - - ); - } -} - -SuperBlock.displayName = 'SuperBlock'; -SuperBlock.propTypes = propTypes; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(SuperBlock); diff --git a/common/app/Map/index.js b/common/app/Map/index.js deleted file mode 100644 index a046dd5d2d..0000000000 --- a/common/app/Map/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './Map.jsx'; diff --git a/common/app/Map/map.less b/common/app/Map/map.less deleted file mode 100644 index c95767bf79..0000000000 --- a/common/app/Map/map.less +++ /dev/null @@ -1,144 +0,0 @@ -// should be the same as the filename and ./ns.json -@ns: map; - -.@{ns}-container { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - overflow-x: hidden; - overflow-y: auto; -} - -.@{ns}-accordion { - max-width: 700px; - overflow-y: auto; - width: 100%; - - a:focus { - text-decoration: none; - color:darkgreen; - } - - a:focus:hover { - text-decoration: underline; - color:#001800; - } - - @media (max-width: 720px) { - left: 0; - right: 0; - width: 100%; - top: 195px; - bottom: 0; - // position:absolute; - overflow-x: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - h2 { - margin:15px 0; - padding:0; - &:first-of-type { - margin-top:0; - } - > a { - padding: 10px 0; - padding-left: 50px; - padding-right: 20px; - font-size: 20px; - } - } - a { - margin: 10px 0; - padding: 0; - > h3 { - clear:both; - font-size:20px; - } - } - } - - .@{ns}-caret { - color: @gray-dark; - text-decoration: none; - // make sure all carats are fixed width - width: 10px; - } -} - - -.@{ns}-accordion-block .@{ns}-accordion-panel-heading { - padding-left: 26px; -} - -.@{ns}-challenge-title, .@{ns}-accordion-panel-heading { - background: @body-bg; - line-height: 26px; - padding: 2px 12px 2px 10px; - width:100%; -} - -.@{ns}-challenge-title { - &::before { - padding-right: 6px; - } - padding-left: 40px; - a { - cursor: pointer; - } - @media (min-width: 721px) { - clear: both; - } -} - -.@{ns}-accordion-panel-heading a { - cursor: pointer; - display: block; -} - -.@{ns}-accordion-panel-collapse { - transition: height 0.001s; -} - -.@{ns}-accordion-panel-title { - @media (min-width: 721px) { - clear: both; - } -} - -.@{ns}-block-time { - color: #555555; - @media (min-width: 721px) { - float: right; - } -} - -.@{ns}-block-description { - margin:0; - margin-top:-10px; - padding:0 15px 23px 30px; -} - - -.night { - .@{ns}-accordion .no-link-underline { - color: @brand-primary; - } - .@{ns}-accordion h2 > a { - background: #666; - } - .@{ns}-accordion a:focus, #noneFound { - color: #ABABAB; - } - .@{ns}-challenge-title, .@{ns}-accordion-panel-heading { - background: @night-body-bg; - color: @night-text-color; - a { - color: @night-text-color; - } - } - .@{ns}-caret { - color: @night-text-color; - } -} diff --git a/common/app/Map/ns.json b/common/app/Map/ns.json deleted file mode 100644 index 6bc66fc09e..0000000000 --- a/common/app/Map/ns.json +++ /dev/null @@ -1 +0,0 @@ -"map" diff --git a/common/app/Map/redux/fetch-map-ui-epic.js b/common/app/Map/redux/fetch-map-ui-epic.js index 37e7cfa09b..9933f1121e 100644 --- a/common/app/Map/redux/fetch-map-ui-epic.js +++ b/common/app/Map/redux/fetch-map-ui-epic.js @@ -3,38 +3,34 @@ import debug from 'debug'; import { types as appTypes, - createErrorObservable, - currentChallengeSelector + createErrorObservable } from '../../redux'; import { types, fetchMapUiComplete } from './'; -import { langSelector } from '../../Router/redux'; import { shapeChallenges } from '../../redux/utils'; const isDev = debug.enabled('fcc:*'); export default function fetchMapUiEpic( actions, - { getState }, + _, { services } ) { - return actions::ofType( + return actions.do(console.log)::ofType( appTypes.appMounted, types.fetchMapUi.start ) .flatMapLatest(() => { - const lang = langSelector(getState()); const options = { - params: { lang }, service: 'map-ui' }; return services.readService$(options) .retry(3) + .do(console.info) .map(({ entities, ...res }) => ({ entities: shapeChallenges( entities, isDev ), - initialNode: currentChallengeSelector(getState()), ...res })) .map(fetchMapUiComplete) diff --git a/common/app/Map/redux/index.js b/common/app/Map/redux/index.js index f68e67c32e..8fdc57c0a7 100644 --- a/common/app/Map/redux/index.js +++ b/common/app/Map/redux/index.js @@ -8,13 +8,14 @@ import { createSelector } from 'reselect'; import { capitalize, noop } from 'lodash'; import * as utils from './utils.js'; -import ns from '../ns.json'; import { createEventMetaCreator } from '../../redux'; import fetchMapUiEpic from './fetch-map-ui-epic'; +const ns = 'map'; + export const epics = [ fetchMapUiEpic ]; export const types = createTypes([ diff --git a/common/app/Nav/LargeNav.jsx b/common/app/Nav/LargeNav.jsx index d0925bc37d..a1127e3bf4 100644 --- a/common/app/Nav/LargeNav.jsx +++ b/common/app/Nav/LargeNav.jsx @@ -2,11 +2,11 @@ import React from 'react'; import Media from 'react-media'; import { Col, Navbar, Row } from 'react-bootstrap'; import FCCSearchBar from 'react-freecodecamp-search'; -import { NavLogo, BinButtons, NavLinks } from './components'; +import { NavLogo, NavLinks } from './components'; import propTypes from './navPropTypes'; -function LargeNav({ clickOnLogo, clickOnMap, shouldShowMapButton, panes }) { +function LargeNav({ clickOnLogo }) { return ( - - - + - +
diff --git a/common/app/Nav/MediumNav.jsx b/common/app/Nav/MediumNav.jsx index 559bf623f1..76c84095b5 100644 --- a/common/app/Nav/MediumNav.jsx +++ b/common/app/Nav/MediumNav.jsx @@ -2,11 +2,11 @@ import React from 'react'; import Media from 'react-media'; import { Navbar, Row } from 'react-bootstrap'; import FCCSearchBar from 'react-freecodecamp-search'; -import { NavLogo, BinButtons, NavLinks } from './components'; +import { NavLogo, NavLinks } from './components'; import propTypes from './navPropTypes'; -function MediumNav({ clickOnLogo, clickOnMap, shouldShowMapButton, panes }) { +function MediumNav({ clickOnLogo }) { return ( matches && typeof window !== 'undefined' && (
- -
- - - -
-
- -
-
+ +
+ + + +
+
+ - +
diff --git a/common/app/Nav/Nav.jsx b/common/app/Nav/Nav.jsx index aaddb015fa..23714fcb0f 100644 --- a/common/app/Nav/Nav.jsx +++ b/common/app/Nav/Nav.jsx @@ -1,67 +1,25 @@ import React from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; import { Navbar } from 'react-bootstrap'; import LargeNav from './LargeNav.jsx'; import MediumNav from './MediumNav.jsx'; import SmallNav from './SmallNav.jsx'; import { - clickOnLogo, - clickOnMap + clickOnLogo } from './redux'; -import { panesSelector, panesByNameSelector } from '../Panes/redux'; import propTypes from './navPropTypes'; -const mapStateToProps = createSelector( - panesSelector, - panesByNameSelector, - (panes, panesByName) => { - return { - panes: panes.map(({ name, type }) => { - return { - content: name, - action: type, - isHidden: panesByName[name].isHidden - }; - }, {}), - shouldShowMapButton: panes.length === 0 - }; - } -); +const mapStateToProps = () => ({}); function mapDispatchToProps(dispatch) { - const dispatchers = bindActionCreators( + return bindActionCreators( { - clickOnMap: e => { - e.preventDefault(); - return clickOnMap(); - }, - clickOnLogo: e => { - e.preventDefault(); - return clickOnLogo(); - } + clickOnLogo }, dispatch ); - dispatchers.dispatch = dispatch; - return () => dispatchers; -} - -function mergeProps(stateProps, dispatchProps, ownProps) { - const panes = stateProps.panes.map(pane => { - return { - ...pane, - actionCreator: () => dispatchProps.dispatch({ type: pane.action }) - }; - }); - return { - ...ownProps, - ...stateProps, - ...dispatchProps, - panes - }; } const allNavs = [ @@ -72,18 +30,12 @@ const allNavs = [ function FCCNav(props) { const { - panes, - clickOnLogo, - clickOnMap, - shouldShowMapButton + clickOnLogo } = props; const withNavProps = Component => ( ); return ( @@ -104,6 +56,5 @@ FCCNav.propTypes = propTypes; export default connect( mapStateToProps, - mapDispatchToProps, - mergeProps + mapDispatchToProps )(FCCNav); diff --git a/common/app/Nav/SmallNav.jsx b/common/app/Nav/SmallNav.jsx index 50b456e1c3..dfa251437e 100644 --- a/common/app/Nav/SmallNav.jsx +++ b/common/app/Nav/SmallNav.jsx @@ -2,11 +2,11 @@ import React from 'react'; import Media from 'react-media'; import { Navbar, Row } from 'react-bootstrap'; import FCCSearchBar from 'react-freecodecamp-search'; -import { NavLogo, BinButtons, NavLinks } from './components'; +import { NavLogo, NavLinks } from './components'; import propTypes from './navPropTypes'; -function SmallNav({ clickOnLogo, clickOnMap, shouldShowMapButton, panes }) { +function SmallNav({ clickOnLogo }) { return ( matches && typeof window !== 'undefined' && (
- -
- - -
-
- -
-
+ +
+ + +
+
+ - + diff --git a/common/app/Nav/components/NavLinks.jsx b/common/app/Nav/components/NavLinks.jsx index 950d7ed8e8..ac12ba778b 100644 --- a/common/app/Nav/components/NavLinks.jsx +++ b/common/app/Nav/components/NavLinks.jsx @@ -8,10 +8,8 @@ import { MenuItem, NavDropdown, NavItem, Nav } from 'react-bootstrap'; import navLinks from '../links.json'; import SignUp from './Sign-Up.jsx'; -import NoPropsPassThrough from '../../utils/No-Props-Passthrough.jsx'; import { Link } from '../../Router'; -import { onRouteCurrentChallenge } from '../../routes/Challenges/redux'; import { openDropdown, closeDropdown, @@ -58,14 +56,12 @@ const navLinkPropType = PropTypes.shape({ const propTypes = { children: PropTypes.any, - clickOnMap: PropTypes.func.isRequired, closeDropdown: PropTypes.func.isRequired, isDropdownOpen: PropTypes.bool, isInNav: PropTypes.bool, isSignedIn: PropTypes.bool, navLinks: PropTypes.arrayOf(navLinkPropType), openDropdown: PropTypes.func.isRequired, - shouldShowMapButton: PropTypes.bool, showLoading: PropTypes.bool }; @@ -125,8 +121,6 @@ class NavLinks extends PureComponent { render() { const { - shouldShowMapButton, - clickOnMap, showLoading, isSignedIn, navLinks, @@ -136,20 +130,6 @@ class NavLinks extends PureComponent { return (