Feat: anon navbar (#16189)

* chore(React): %s/react-pure-render/React.PureComponent/gc

* fix(Settings): Should redirect to signup when unauthen

* feat(Development): Use SES for mail if defined

* feat(Nav): Show anon navbar when logged in

* fix(server/datasources): Make sure mailhog works if no ses keys are found

LB will use both mail settings if using both local and dev

* fix(Nav): Use text instead of icons

* fix(Nav): Make donate page open in new tab
pull/16241/head
Berkeley Martinez 2017-12-15 13:53:32 -08:00 committed by Quincy Larson
parent 1720c61753
commit b1e9a172a2
21 changed files with 78 additions and 121 deletions

View File

@ -1,10 +1,14 @@
import { types } from '../../common/app/redux';
import _ from 'lodash';
import { ofType } from 'redux-epic';
export default function hardGoToSaga(actions, _, { history }) {
import { types } from '../../common/app/redux';
export default function hardGoToSaga(actions, store, { location }) {
return actions::ofType(types.hardGoTo)
.map(({ payload = '/settings' }) => {
history.pushState(history.state, null, payload);
return null;
});
.pluck('payload')
.filter(_.isString)
.do((payload = '/') => {
location.pathname = payload;
})
.ignoreElements();
}

View File

@ -1,9 +1,8 @@
import React from 'react';
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 PureComponent from 'react-pure-render/component';
import { Panel } from 'react-bootstrap';
import ns from './ns.json';

View File

@ -1,8 +1,7 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import PureComponent from 'react-pure-render/component';
import classnames from 'classnames';
import debug from 'debug';

View File

@ -1,7 +1,6 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import PureComponent from 'react-pure-render/component';
import { Col, Row } from 'react-bootstrap';
import ns from './ns.json';

View File

@ -1,8 +1,7 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import PureComponent from 'react-pure-render/component';
import FA from 'react-fontawesome';
import { Panel } from 'react-bootstrap';

View File

@ -27,24 +27,18 @@ import {
dropdownSelector
} from './redux';
import {
userSelector,
isSignedInSelector,
signInLoadingSelector
} from '../redux';
import { isSignedInSelector, signInLoadingSelector } from '../redux';
import { panesSelector } from '../Panes/redux';
const fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg';
const mapStateToProps = createSelector(
userSelector,
isSignedInSelector,
dropdownSelector,
signInLoadingSelector,
panesSelector,
(
{ username, picture, points },
isSignedIn,
isDropdownOpen,
showLoading,
@ -59,10 +53,7 @@ const mapStateToProps = createSelector(
}, {}),
isDropdownOpen,
isSignedIn,
picture,
points,
showLoading,
username
showLoading
};
}
);
@ -111,13 +102,11 @@ const propTypes = {
clickOnMap: PropTypes.func.isRequired,
closeDropdown: PropTypes.func.isRequired,
isDropdownOpen: PropTypes.bool,
isSignedIn: PropTypes.bool,
openDropdown: PropTypes.func.isRequired,
panes: PropTypes.array,
picture: PropTypes.string,
points: PropTypes.number,
showLoading: PropTypes.bool,
signedIn: PropTypes.bool,
username: PropTypes.string
signedIn: PropTypes.bool
};
export class FCCNav extends React.Component {
@ -178,11 +167,9 @@ export class FCCNav extends React.Component {
render() {
const {
panes,
isSignedIn,
clickOnLogo,
clickOnMap,
username,
points,
picture,
showLoading
} = this.props;
@ -236,10 +223,8 @@ export class FCCNav extends React.Component {
)
}
<SignUp
picture={ picture }
points={ points }
showLoading={ showLoading }
username={ username }
showSignUp={ !isSignedIn }
/>
</Nav>
</Navbar.Collapse>

View File

@ -5,26 +5,16 @@ import { NavItem } from 'react-bootstrap';
import { Link } from '../Router';
import { onRouteSettings } from '../routes/Settings/redux';
// this is separated out to prevent react bootstrap's
// NavBar from injecting unknown props to the li component
const propTypes = {
picture: PropTypes.string,
points: PropTypes.number,
showLoading: PropTypes.bool,
username: PropTypes.string
showSignUp: PropTypes.bool
};
export default function SignUpButton({
picture,
points,
showLoading,
username
}) {
export default function SignUpButton({ showLoading, showSignUp }) {
if (showLoading) {
return null;
}
if (!username) {
if (showSignUp) {
return (
<NavItem
href='/signup'
@ -40,14 +30,7 @@ export default function SignUpButton({
key='user'
>
<Link to={ onRouteSettings() }>
<span className='nav-username hidden-sm'> { username } </span>
<span className='nav-points'> [ { points || 1 } ] </span>
<span className='nav-picture-container'>
<img
className='nav-picture float-right'
src={ picture }
/>
</span>
My Profile
</Link>
</li>
);

View File

@ -37,6 +37,7 @@
},
{
"content": "Donate",
"link": "https://www.freecodecamp.org/donate"
"link": "https://www.freecodecamp.org/donate",
"target": "_blank"
}
]

View File

@ -77,31 +77,10 @@ li.nav-avatar {
> a {
margin: 0;
padding: 0 @navbar-padding-horizontal 0 @navbar-padding-horizontal;
padding: 7.5px @navbar-padding-horizontal 7.5px @navbar-padding-horizontal;
}
}
}
.nav-username {
padding-right: @navbar-padding-horizontal;
}
.nav-points {
@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {
padding: @navbar-padding-vertical 0 @navbar-padding-vertical 0;
}
@media (min-width: @screen-md-min) {
padding: @navbar-padding-vertical @navbar-padding-horizontal @navbar-padding-vertical 0;
}
}
.nav-picture {
margin-top: @navbar-logo-padding;
margin-bottom: @navbar-logo-padding;
height: @navbar-logo-height;
width: @navbar-logo-height;
}
.navbar-nav a {
color: @body-bg;
margin-top: -5px;

View File

@ -1,8 +1,7 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Button, Modal } from 'react-bootstrap';
import PureComponent from 'react-pure-render/component';
import ns from './ns.json';
import {

View File

@ -1,6 +1,5 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import PureComponent from 'react-pure-render/component';
import { Grid, Col, Row } from 'react-bootstrap';
import ns from './ns.json';

View File

@ -1,9 +1,8 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import ReactDom from 'react-dom';
import { createSelector } from 'reselect';
import { connect } from 'react-redux';
import PureComponent from 'react-pure-render/component';
import ns from './ns.json';

View File

@ -1,8 +1,7 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { createSelector } from 'reselect';
import { connect } from 'react-redux';
import PureComponent from 'react-pure-render/component';
import { Col, Image } from 'react-bootstrap';
import SidePanel from './Side-Panel.jsx';

View File

@ -1,6 +1,5 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import PureComponent from 'react-pure-render/component';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';

View File

@ -21,7 +21,8 @@ import {
signInLoadingSelector,
userSelector,
themeSelector
themeSelector,
hardGoTo
} from '../../redux';
import ChildContainer from '../../Child-Container.jsx';
@ -64,6 +65,7 @@ const mapStateToProps = createSelector(
);
const mapDispatchToProps = {
hardGoTo,
toggleIsAvailableForHire: () => toggleUserFlag('isAvailableForHire'),
toggleIsLocked: () => toggleUserFlag('isLocked'),
toggleMonthlyEmail: () => toggleUserFlag('sendMonthlyEmail'),
@ -77,6 +79,7 @@ const propTypes = {
children: PropTypes.element,
currentTheme: PropTypes.string,
email: PropTypes.string,
hardGoTo: PropTypes.func.isRequired,
initialLang: PropTypes.string,
isAvailableForHire: PropTypes.bool,
isGithubCool: PropTypes.bool,
@ -115,6 +118,11 @@ export class Settings extends React.Component {
componentWillMount() {
this.props.updateTitle('Settings');
}
componentWillReceiveProps({ username, showLoading, hardGoTo }) {
if (!username && !showLoading) {
hardGoTo('/signup');
}
}
render() {
const {
@ -138,7 +146,7 @@ export class Settings extends React.Component {
toggleQuincyEmail,
username
} = this.props;
if (!username && !showLoading) {
if (!username && showLoading) {
return <SettingsSkeleton />;
}
if (showUpdateEmailView) {

11
package-lock.json generated
View File

@ -5391,9 +5391,9 @@
}
},
"font-awesome": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.5.0.tgz",
"integrity": "sha1-Hp18z31jvb5XAA4Y1RiMslV+cPg="
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
},
"for-each": {
"version": "0.3.2",
@ -12781,11 +12781,6 @@
"warning": "3.0.0"
}
},
"react-pure-render": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/react-pure-render/-/react-pure-render-1.0.2.tgz",
"integrity": "sha1-nYqSjH8sN1E8LQZOV7Pjw1bp+rs="
},
"react-redux": {
"version": "4.4.8",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-4.4.8.tgz",

View File

@ -64,7 +64,7 @@
"express-state": "^1.2.0",
"express-validator": "^3.0.0",
"fetchr": "~0.5.12",
"font-awesome": "~4.5.0",
"font-awesome": "^4.7.0",
"frameguard": "^3.0.0",
"googleapis": "16.1.0",
"helmet": "^3.1.0",
@ -115,7 +115,6 @@
"react-motion": "~0.4.2",
"react-no-ssr": "^1.0.1",
"react-notification": "git+https://github.com/BerkeleyTrue/react-notification.git#freecodecamp",
"react-pure-render": "^1.0.2",
"react-redux": "^4.0.6",
"react-test-renderer": "^15.6.2",
"react-youtube": "^7.0.0",

View File

@ -1,18 +1,29 @@
module.exports = {
mail: {
connector: 'mail',
transport: {
type: 'smtp',
host: 'localhost',
secure: false,
port: 1025,
tls: {
rejectUnauthorized: false
}
},
auth: {
user: 'test',
pass: 'test'
}
}
const debug = require('debug')('fcc:server:datasources');
const dsLocal = require('./datasources.production.js');
const ds = {
...dsLocal
};
// use [MailHog](https://github.com/mailhog/MailHog) if no SES keys are found
if (!process.env.SES_ID) {
ds.mail = {
connector: 'mail',
transport: {
type: 'smtp',
host: 'localhost',
secure: false,
port: 1025,
tls: {
rejectUnauthorized: false
}
},
auth: {
user: 'test',
pass: 'test'
}
};
debug(`using MailHog server on port ${ds.mail.transport.port}`);
} else {
debug('using AWS SES to deliver emails');
}
module.exports = ds;

View File

@ -1,4 +1,5 @@
nav.navbar.navbar-default.navbar-static-top.nav-height
.navbar-header
button.hamburger.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse')
.col-xs-12
@ -25,10 +26,10 @@ nav.navbar.navbar-default.navbar-static-top.nav-height
li
a(href='/map') Map
li
a(href='https://www.freecodecamp.org/donate') Donate
a(href='https://www.freecodecamp.org/donate', target='_blank') Donate
if !user
li
a(href='/signin') Sign In
a(href='/signin') Sign Up
else
li.nav-avatar
a(href='/settings')

View File

@ -1,6 +1,6 @@
link(rel='stylesheet', type='text/css' href='/css/lato.css')
link(rel='stylesheet', type='text/css' href='/css/ubuntu.css')
link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css')
link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css')
link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/codemirror.min.css')
link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/addon/lint/lint.min.css')
link(rel='stylesheet', href=rev('/css', 'main.css'))