feat(welcome): Gatsby the Welcome page
This commit is contained in:
committed by
mrugesh mohapatra
parent
9f6a2e35f7
commit
3ea38ee31a
@ -8,6 +8,6 @@ const spawnOpts = {
|
||||
};
|
||||
|
||||
// spawns loopback
|
||||
spawn('babel-node', ['./server/server.js'], spawnOpts);
|
||||
spawn('DEBUG=fcc* babel-node', ['./server/server.js'], spawnOpts);
|
||||
// spawns gatsby in development mode
|
||||
spawn('npm', ['run', 'develop'], spawnOpts);
|
||||
|
@ -1,74 +0,0 @@
|
||||
import { defaultProfileImage } from '../../common/utils/constantStrings.json';
|
||||
import { randomQuote } from '../../common/app/utils/get-words';
|
||||
import { cachedMap } from '../utils/map';
|
||||
// import NewsFeed from '../rss';
|
||||
|
||||
// const news = new NewsFeed();
|
||||
|
||||
module.exports = function(app, done) {
|
||||
const { About } = app.models;
|
||||
const router = app.loopback.Router();
|
||||
let challengeCount = 0;
|
||||
|
||||
if (!process.env.SEEDING) {
|
||||
cachedMap(app.models)
|
||||
.do(({ entities: { challenge } }) => {
|
||||
challengeCount = Object.keys(challenge).length;
|
||||
})
|
||||
.subscribe(
|
||||
() => {},
|
||||
err => {throw new Error(err);},
|
||||
() => {
|
||||
router.get('/', addDefaultImage, index);
|
||||
app.use(router);
|
||||
done();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function addDefaultImage(req, res, next) {
|
||||
if (!req.user || req.user.picture) {
|
||||
return next();
|
||||
}
|
||||
return req.user.update$({ picture: defaultProfileImage })
|
||||
.subscribe(
|
||||
() => next(),
|
||||
next
|
||||
);
|
||||
}
|
||||
|
||||
function index(req, res) {
|
||||
const { user } = req;
|
||||
const homePage = user ? 'userHome' : 'noUserHome';
|
||||
const { quote, author} = randomQuote();
|
||||
const title = user ?
|
||||
`Welcome, ${user.name ? user.name : 'Camper'}!` :
|
||||
'Learn to Code and Help Nonprofits';
|
||||
const completedChallengeCount = user && user.completedChallengeCount || 0;
|
||||
const completedProjectCount = user && user.completedProjectCount || 0;
|
||||
const completedCertCount = user && user.completedCertCount || 0;
|
||||
const completedLegacyCertCount = user && user.completedLegacyCertCount || 0;
|
||||
Promise.all([
|
||||
// news.getFeed(),
|
||||
About.getActiveUsersForRendering()
|
||||
])
|
||||
.then(([
|
||||
// feed,
|
||||
activeUsers
|
||||
]) => {
|
||||
return res.render(
|
||||
homePage, {
|
||||
activeUsers,
|
||||
author,
|
||||
challengeCount,
|
||||
completedChallengeCount,
|
||||
completedProjectCount,
|
||||
completedCertCount,
|
||||
completedLegacyCertCount,
|
||||
// feed,
|
||||
quote,
|
||||
title
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
@ -15,7 +15,7 @@ import {
|
||||
ifNotVerifiedRedirectToUpdateEmail
|
||||
} from '../utils/middleware';
|
||||
|
||||
const debug = debugFactory('fcc:boot:user');
|
||||
const log = debugFactory('fcc:boot:user');
|
||||
const sendNonUserToHome = ifNoUserRedirectTo('/');
|
||||
const sendNonUserToHomeWithMessage = curry(ifNoUserRedirectTo, 2)('/');
|
||||
|
||||
@ -49,6 +49,7 @@ module.exports = function bootUser(app) {
|
||||
|
||||
function readSessionUser(req, res, next) {
|
||||
const queryUser = req.user;
|
||||
|
||||
const source =
|
||||
queryUser &&
|
||||
Observable.forkJoin(
|
||||
@ -146,7 +147,7 @@ function getUnlinkSocial(req, res, next) {
|
||||
const updateData = { [social]: null };
|
||||
|
||||
return user.update$(updateData).subscribe(() => {
|
||||
debug(`${social} has been unlinked successfully`);
|
||||
log(`${social} has been unlinked successfully`);
|
||||
|
||||
req.flash('info', `You've successfully unlinked your ${social}.`);
|
||||
return res.redirect('/' + username);
|
||||
|
@ -25,6 +25,7 @@ export default function userServices() {
|
||||
config,
|
||||
cb) {
|
||||
const queryUser = req.user;
|
||||
console.log(queryUser.completedChallengeCount)
|
||||
const source = queryUser && Observable.forkJoin(
|
||||
queryUser.getCompletedChallenges$(),
|
||||
queryUser.getPoints$(),
|
||||
|
@ -46,7 +46,11 @@ export const userPropsForSession = [
|
||||
'emailVerified',
|
||||
'id',
|
||||
'sendQuincyEmail',
|
||||
'theme'
|
||||
'theme',
|
||||
'completedChallengeCount',
|
||||
'completedProjectCount',
|
||||
'completedCertCount',
|
||||
'completedLegacyCertCount'
|
||||
];
|
||||
|
||||
export function normaliseUserFields(user) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
.row.quote-partial
|
||||
.col-xs-12.col-sm-10.col-sm-offset-1
|
||||
blockquote.blockquote
|
||||
span
|
||||
span
|
||||
q=quote
|
||||
.spacer
|
||||
footer.quote-author.blockquote-footer=author
|
||||
footer.quote-author=author
|
||||
|
@ -1,13 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import Spinner from 'react-spinkit';
|
||||
|
||||
import {
|
||||
isSignedInSelector,
|
||||
userFetchStateSelector
|
||||
} from '../../../redux';
|
||||
import { isSignedInSelector, userFetchStateSelector } from '../../../redux';
|
||||
import Login from './Login';
|
||||
import SignedIn from './SignedIn';
|
||||
|
||||
@ -28,26 +25,23 @@ const propTypes = {
|
||||
showLoading: PropTypes.bool
|
||||
};
|
||||
|
||||
class UserState extends Component {
|
||||
render() {
|
||||
console.log(this.props);
|
||||
const { isSignedIn, showLoading, disableSettings } = this.props;
|
||||
if (disableSettings) {
|
||||
return <Login />;
|
||||
}
|
||||
if (showLoading) {
|
||||
return (
|
||||
<Spinner
|
||||
className='user-state-spinner'
|
||||
color='white'
|
||||
fadeIn='none'
|
||||
height='40px'
|
||||
name='line-scale'
|
||||
/>
|
||||
);
|
||||
}
|
||||
return isSignedIn ? <SignedIn /> : <Login />;
|
||||
function UserState(props) {
|
||||
const { isSignedIn, showLoading, disableSettings } = props;
|
||||
if (disableSettings) {
|
||||
return <Login />;
|
||||
}
|
||||
if (showLoading) {
|
||||
return (
|
||||
<Spinner
|
||||
className='user-state-spinner'
|
||||
color='white'
|
||||
fadeIn='none'
|
||||
height='40px'
|
||||
name='line-scale'
|
||||
/>
|
||||
);
|
||||
}
|
||||
return isSignedIn ? <SignedIn /> : <Login />;
|
||||
}
|
||||
|
||||
UserState.displayName = 'UserState';
|
||||
|
@ -8,24 +8,19 @@
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
.btn-cta-big {
|
||||
max-height: 100%;
|
||||
height: 70px;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.signup-btn.btn {
|
||||
background-color: #ffac33;
|
||||
background-image: linear-gradient(#ffcc4d, #ffac33);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffcc4d, endColorstr=#ffac33, GradientType=0)";
|
||||
-ms-filter: 'progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffcc4d, endColorstr=#ffac33, GradientType=0)';
|
||||
border-color: #f1a02a;
|
||||
color: #292f33 !important;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.signup-btn:hover, .signup-btn:focus {
|
||||
.signup-btn:hover,
|
||||
.signup-btn:focus {
|
||||
background-color: #e99110;
|
||||
background-image: linear-gradient(#ffcc4d, #e99110);
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffcc4d, endColorstr=#e99110, GradientType=0)";
|
||||
-ms-filter: 'progid:DXImageTransform.Microsoft.gradient(startColorstr=#ffcc4d, endColorstr=#e99110, GradientType=0)';
|
||||
border-color: #ec8b11;
|
||||
color: #292f33 !important;
|
||||
}
|
||||
@ -33,4 +28,4 @@
|
||||
background-color: #f2a330;
|
||||
background-image: none;
|
||||
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
17
src/components/global.css
Normal file
17
src/components/global.css
Normal file
@ -0,0 +1,17 @@
|
||||
.btn-cta-big {
|
||||
max-height: 100%;
|
||||
height: 70px;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.big-heading {
|
||||
font-size: 50px !important;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 400;
|
||||
}
|
@ -622,12 +622,3 @@ pre tt:after {
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
@ -2,14 +2,19 @@ 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 } from '../redux';
|
||||
import { fetchUser, isSignedInSelector } from '../redux';
|
||||
import Header from './Header';
|
||||
import './layout.css';
|
||||
|
||||
const mapStateToProps = () => ({});
|
||||
import './layout.css';
|
||||
import './global.css';
|
||||
|
||||
const mapStateToProps = createSelector(isSignedInSelector, isSignedIn => ({
|
||||
isSignedIn
|
||||
}));
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ fetchUser }, dispatch);
|
||||
|
||||
@ -19,7 +24,9 @@ class Layout extends Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchUser();
|
||||
if (!this.props.isSignedIn) {
|
||||
this.props.fetchUser();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -56,7 +63,8 @@ class Layout extends Component {
|
||||
Layout.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
disableSettings: PropTypes.bool,
|
||||
fetchUser: PropTypes.func.isRequired
|
||||
fetchUser: PropTypes.func.isRequired,
|
||||
isSignedIn: PropTypes.bool
|
||||
};
|
||||
|
||||
export default connect(
|
||||
|
@ -3,10 +3,6 @@
|
||||
font-weight: 400;
|
||||
font-size: 40px;
|
||||
}
|
||||
.landing-heading {
|
||||
font-size: 50px !important;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.large-p {
|
||||
font-size: 24px;
|
||||
|
@ -29,7 +29,7 @@ const IndexPage = () => (
|
||||
<Layout disableSettings={true}>
|
||||
<Grid className='text-center'>
|
||||
<Row>
|
||||
<h1 className='landing-heading'>Learn to code for free.</h1>
|
||||
<h1 className='big-heading'>Learn to code for free.</h1>
|
||||
<Spacer />
|
||||
<Col md={4} sm={12}>
|
||||
<Image
|
||||
|
22
src/pages/welcome.css
Normal file
22
src/pages/welcome.css
Normal file
@ -0,0 +1,22 @@
|
||||
.quote-partial .blockquote {
|
||||
font-size: 2.5rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.quote-author {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.update-link {
|
||||
text-decoration: underline;
|
||||
font-size: 1.4em;
|
||||
color: #006400;
|
||||
}
|
||||
|
||||
p.stats {
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
p.stats span {
|
||||
color: #006400;
|
||||
}
|
140
src/pages/welcome.js
Normal file
140
src/pages/welcome.js
Normal file
@ -0,0 +1,140 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { Grid, Row, Col, Button } from 'react-bootstrap';
|
||||
|
||||
import { Loader, Spacer } from '../components/helpers';
|
||||
import Layout from '../components/layout';
|
||||
import { userSelector, userFetchStateSelector } from '../redux';
|
||||
import { randomQuote } from '../utils/get-words';
|
||||
|
||||
import './welcome.css';
|
||||
|
||||
const propTypes = {
|
||||
fetchState: PropTypes.shape({
|
||||
pending: PropTypes.bool,
|
||||
complete: PropTypes.bool,
|
||||
errored: PropTypes.bool
|
||||
}),
|
||||
user: PropTypes.shape({
|
||||
username: PropTypes.string,
|
||||
completedChallengeCount: PropTypes.number,
|
||||
completedProjectCount: PropTypes.number,
|
||||
completedCertCount: PropTypes.number,
|
||||
completedLegacyCertCount: PropTypes.number
|
||||
})
|
||||
};
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
userFetchStateSelector,
|
||||
userSelector,
|
||||
(fetchState, user) => ({ fetchState, user })
|
||||
);
|
||||
const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);
|
||||
|
||||
function Welcome({
|
||||
fetchState: { pending, complete },
|
||||
user: {
|
||||
name,
|
||||
completedChallengeCount,
|
||||
completedProjectCount,
|
||||
completedCertCount,
|
||||
completedLegacyCertCount
|
||||
}
|
||||
}) {
|
||||
if (pending && !complete) {
|
||||
return (
|
||||
<Layout>
|
||||
<Loader />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
const { quote, author } = randomQuote();
|
||||
return (
|
||||
<Layout>
|
||||
<Grid className='text-center'>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<Spacer />
|
||||
<h1 className='big-heading'>Welcome {name ? name : 'Camper'}!</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<a
|
||||
className='update-link'
|
||||
href='/n/7gR5pBM-K?refsource=userhome'
|
||||
target='_blank'
|
||||
>
|
||||
We're building a massive open dataset about new coders. Take the
|
||||
2018 New Coder Survey. It only takes 5 minutes.
|
||||
</a>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Row className='quote-partial'>
|
||||
<Col sm={10} smOffset={1} xs={12}>
|
||||
<blockquote className='blockquote'>
|
||||
<span>
|
||||
<q>{quote}</q>
|
||||
<footer className='quote-author blockquote-footer'>
|
||||
<cite>{author}</cite>
|
||||
</footer>
|
||||
</span>
|
||||
</blockquote>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<p className='stats'>
|
||||
You have completed <span>{completedChallengeCount}</span> of{' '}
|
||||
<span>1408</span> coding challenges.
|
||||
</p>
|
||||
<p className='stats'>
|
||||
You have built <span>{completedProjectCount}</span> of{' '}
|
||||
<span>30</span> projects.
|
||||
</p>
|
||||
{completedLegacyCertCount ? (
|
||||
<p className='stats'>
|
||||
You have earned <span>{completedLegacyCertCount}</span> of{' '}
|
||||
<span>4</span> legacy certifications.
|
||||
</p>
|
||||
) : null}
|
||||
<p className='stats'>
|
||||
You have earned <span>{completedCertCount}</span> of{' '}
|
||||
<span>6</span> certifications.
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<Button block={true} bsStyle='primary' className='btn-cta-big'>
|
||||
Go to my next challenge
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Spacer />
|
||||
<Spacer />
|
||||
<Spacer />
|
||||
<Spacer />
|
||||
<Spacer />
|
||||
</Grid>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
Welcome.displayName = 'Welcome';
|
||||
Welcome.propTypes = propTypes;
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Welcome);
|
Reference in New Issue
Block a user