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 tabpull/16241/head
parent
1720c61753
commit
b1e9a172a2
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
},
|
||||
{
|
||||
"content": "Donate",
|
||||
"link": "https://www.freecodecamp.org/donate"
|
||||
"link": "https://www.freecodecamp.org/donate",
|
||||
"target": "_blank"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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'))
|
||||
|
|
Loading…
Reference in New Issue