mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-17 00:21:20 -05:00
move login/registration functions and view state to session reducer, pass to gui (#2078)
* move login/registration functions and view state to session reducer, pass to gui * navigation reducer handles login; gui passed renderLogin function * put back in join class to make smoke tests keep working
This commit is contained in:
parent
c047612d68
commit
935eb0b15f
18 changed files with 549 additions and 349 deletions
|
@ -33,8 +33,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
// 100% minus border and padding
|
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
// 100% minus border and padding
|
||||||
width: calc(100% - 30px);
|
width: calc(100% - 30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ const connect = require('react-redux').connect;
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
const sessionActions = require('../../redux/session.js');
|
const navigationActions = require('../../redux/navigation.js');
|
||||||
|
|
||||||
const IframeModal = require('../modal/iframe/modal.jsx');
|
const IframeModal = require('../modal/iframe/modal.jsx');
|
||||||
const Registration = require('../registration/registration.jsx');
|
const Registration = require('../registration/registration.jsx');
|
||||||
|
@ -15,10 +15,7 @@ class Intro extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
bindAll(this, [
|
bindAll(this, [
|
||||||
'handleShowVideo',
|
'handleShowVideo',
|
||||||
'handleCloseVideo',
|
'handleCloseVideo'
|
||||||
'handleJoinClick',
|
|
||||||
'handleCloseRegistration',
|
|
||||||
'handleCompleteRegistration'
|
|
||||||
]);
|
]);
|
||||||
this.state = {
|
this.state = {
|
||||||
videoOpen: false
|
videoOpen: false
|
||||||
|
@ -30,17 +27,6 @@ class Intro extends React.Component {
|
||||||
handleCloseVideo () {
|
handleCloseVideo () {
|
||||||
this.setState({videoOpen: false});
|
this.setState({videoOpen: false});
|
||||||
}
|
}
|
||||||
handleJoinClick (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.setState({registrationOpen: true});
|
|
||||||
}
|
|
||||||
handleCloseRegistration () {
|
|
||||||
this.setState({registrationOpen: false});
|
|
||||||
}
|
|
||||||
handleCompleteRegistration () {
|
|
||||||
this.props.dispatch(sessionActions.refreshSession());
|
|
||||||
this.closeRegistration();
|
|
||||||
}
|
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<div className="intro">
|
<div className="intro">
|
||||||
|
@ -92,7 +78,7 @@ class Intro extends React.Component {
|
||||||
<a
|
<a
|
||||||
className="sprite sprite-3"
|
className="sprite sprite-3"
|
||||||
href="#"
|
href="#"
|
||||||
onClick={this.handleJoinClick}
|
onClick={this.props.handleOpenRegistration}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="Gobo"
|
alt="Gobo"
|
||||||
|
@ -111,10 +97,7 @@ class Intro extends React.Component {
|
||||||
<div className="text subtext">{this.props.messages['intro.itsFree']}</div>
|
<div className="text subtext">{this.props.messages['intro.itsFree']}</div>
|
||||||
</a>
|
</a>
|
||||||
<Registration
|
<Registration
|
||||||
isOpen={this.state.registrationOpen}
|
|
||||||
key="registration"
|
key="registration"
|
||||||
onRegistrationDone={this.handleCompleteRegistration}
|
|
||||||
onRequestClose={this.handleCloseRegistration}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -160,7 +143,7 @@ class Intro extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
Intro.propTypes = {
|
Intro.propTypes = {
|
||||||
dispatch: PropTypes.func.isRequired,
|
handleOpenRegistration: PropTypes.func,
|
||||||
messages: PropTypes.shape({
|
messages: PropTypes.shape({
|
||||||
'intro.aboutScratch': PropTypes.string,
|
'intro.aboutScratch': PropTypes.string,
|
||||||
'intro.forEducators': PropTypes.string,
|
'intro.forEducators': PropTypes.string,
|
||||||
|
@ -194,6 +177,17 @@ const mapStateToProps = state => ({
|
||||||
session: state.session
|
session: state.session
|
||||||
});
|
});
|
||||||
|
|
||||||
const ConnectedIntro = connect(mapStateToProps)(Intro);
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
handleOpenRegistration: event => {
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.handleOpenRegistration());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const ConnectedIntro = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Intro);
|
||||||
|
|
||||||
module.exports = ConnectedIntro;
|
module.exports = ConnectedIntro;
|
||||||
|
|
60
src/components/login/canceled-deletion-modal.jsx
Normal file
60
src/components/login/canceled-deletion-modal.jsx
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
const React = require('react');
|
||||||
|
const connect = require('react-redux').connect;
|
||||||
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
|
const intlShape = require('react-intl').intlShape;
|
||||||
|
|
||||||
|
const navigationActions = require('../../redux/navigation.js');
|
||||||
|
const Modal = require('../modal/base/modal.jsx');
|
||||||
|
|
||||||
|
const CanceledDeletionModal = ({
|
||||||
|
canceledDeletionOpen,
|
||||||
|
handleCloseCanceledDeletion,
|
||||||
|
intl
|
||||||
|
}) => (
|
||||||
|
<Modal
|
||||||
|
isOpen={canceledDeletionOpen}
|
||||||
|
style={{
|
||||||
|
content: {
|
||||||
|
padding: 15
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onRequestClose={handleCloseCanceledDeletion}
|
||||||
|
>
|
||||||
|
<h4><FormattedMessage id="general.noDeletionTitle" /></h4>
|
||||||
|
<p>
|
||||||
|
<FormattedMessage
|
||||||
|
id="general.noDeletionDescription"
|
||||||
|
values={{
|
||||||
|
resetLink: <a href="/accounts/password_reset/">
|
||||||
|
{intl.formatMessage({id: 'general.noDeletionLink'})}
|
||||||
|
</a>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
|
||||||
|
CanceledDeletionModal.propTypes = {
|
||||||
|
canceledDeletionOpen: PropTypes.bool,
|
||||||
|
handleCloseCanceledDeletion: PropTypes.func,
|
||||||
|
intl: intlShape
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
canceledDeletionOpen: state.navigation && state.navigation.canceledDeletionOpen
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
handleCloseCanceledDeletion: () => {
|
||||||
|
dispatch(navigationActions.setCanceledDeletionOpen(false));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ConnectedCanceledDeletionModal = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(CanceledDeletionModal);
|
||||||
|
|
||||||
|
module.exports = injectIntl(ConnectedCanceledDeletionModal);
|
34
src/components/login/connected-login.jsx
Normal file
34
src/components/login/connected-login.jsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
const connect = require('react-redux').connect;
|
||||||
|
|
||||||
|
const Login = require('./login.jsx');
|
||||||
|
|
||||||
|
require('./login-dropdown.scss');
|
||||||
|
|
||||||
|
const ConnectedLogin = ({
|
||||||
|
error,
|
||||||
|
onLogIn
|
||||||
|
}) => (
|
||||||
|
<Login
|
||||||
|
error={error}
|
||||||
|
key="login-dropdown-presentation"
|
||||||
|
onLogIn={onLogIn}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
ConnectedLogin.propTypes = {
|
||||||
|
error: PropTypes.string,
|
||||||
|
onLogIn: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
error: state.navigation && state.navigation.loginError
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = () => ({});
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ConnectedLogin);
|
50
src/components/login/login-dropdown.jsx
Normal file
50
src/components/login/login-dropdown.jsx
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
const connect = require('react-redux').connect;
|
||||||
|
|
||||||
|
const navigationActions = require('../../redux/navigation.js');
|
||||||
|
const Dropdown = require('../dropdown/dropdown.jsx');
|
||||||
|
const ConnectedLogin = require('./connected-login.jsx');
|
||||||
|
|
||||||
|
require('./login-dropdown.scss');
|
||||||
|
|
||||||
|
const LoginDropdown = ({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onLogIn
|
||||||
|
}) => (
|
||||||
|
<Dropdown
|
||||||
|
className={'with-arrow'}
|
||||||
|
isOpen={isOpen}
|
||||||
|
key="login-dropdown"
|
||||||
|
onRequestClose={onClose}
|
||||||
|
>
|
||||||
|
<ConnectedLogin
|
||||||
|
onLogIn={onLogIn}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
|
||||||
|
LoginDropdown.propTypes = {
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
onLogIn: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
isOpen: state.navigation && state.navigation.loginOpen
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
onClose: () => {
|
||||||
|
dispatch(navigationActions.setLoginOpen(false));
|
||||||
|
},
|
||||||
|
onLogIn: (formData, callback) => {
|
||||||
|
dispatch(navigationActions.handleLogIn(formData, callback));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(LoginDropdown);
|
0
src/components/login/login-dropdown.scss
Normal file
0
src/components/login/login-dropdown.scss
Normal file
|
@ -3,8 +3,6 @@ const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
const log = require('../../lib/log.js');
|
|
||||||
|
|
||||||
const Form = require('../forms/form.jsx');
|
const Form = require('../forms/form.jsx');
|
||||||
const Input = require('../forms/input.jsx');
|
const Input = require('../forms/input.jsx');
|
||||||
const Button = require('../forms/button.jsx');
|
const Button = require('../forms/button.jsx');
|
||||||
|
@ -24,8 +22,7 @@ class Login extends React.Component {
|
||||||
}
|
}
|
||||||
handleSubmit (formData) {
|
handleSubmit (formData) {
|
||||||
this.setState({waiting: true});
|
this.setState({waiting: true});
|
||||||
this.props.onLogIn(formData, err => {
|
this.props.onLogIn(formData, () => {
|
||||||
if (err) log.error(err);
|
|
||||||
this.setState({waiting: false});
|
this.setState({waiting: false});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -48,9 +45,6 @@ class Login extends React.Component {
|
||||||
key="usernameInput"
|
key="usernameInput"
|
||||||
maxLength="30"
|
maxLength="30"
|
||||||
name="username"
|
name="username"
|
||||||
ref={input => {
|
|
||||||
this.username = input;
|
|
||||||
}}
|
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -63,9 +57,6 @@ class Login extends React.Component {
|
||||||
required
|
required
|
||||||
key="passwordInput"
|
key="passwordInput"
|
||||||
name="password"
|
name="password"
|
||||||
ref={input => {
|
|
||||||
this.password = input;
|
|
||||||
}}
|
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
{this.state.waiting ? [
|
{this.state.waiting ? [
|
||||||
|
|
|
@ -2,6 +2,26 @@
|
||||||
|
|
||||||
.login {
|
.login {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
width: 200px;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
white-space: normal; // override any parent, such as in gui, who sets nowrap
|
||||||
|
color: $type-white;
|
||||||
|
font-size: .8125rem;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: .75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
// 100% minus border and padding
|
||||||
|
width: calc(100% - 30px);
|
||||||
|
height: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
|
@ -24,13 +44,19 @@
|
||||||
|
|
||||||
a {
|
a {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
|
color: $ui-white;
|
||||||
|
|
||||||
|
&:link,
|
||||||
|
&:visited,
|
||||||
|
&:active {
|
||||||
|
color: $ui-white;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
border: 1px solid $active-dark-gray;
|
border: 1px solid $active-dark-gray;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
|
@ -7,7 +7,7 @@ const ReactModal = require('react-modal');
|
||||||
|
|
||||||
require('./modal.scss');
|
require('./modal.scss');
|
||||||
|
|
||||||
ReactModal.setAppElement(document.getElementById('view'));
|
ReactModal.setAppElement(document.getElementById('app'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for pop up windows (See: registration window)
|
* Container for pop up windows (See: registration window)
|
||||||
|
@ -25,7 +25,7 @@ class Modal extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<ReactModal
|
<ReactModal
|
||||||
appElement={document.getElementById('view')}
|
appElement={document.getElementById('app')}
|
||||||
className={{
|
className={{
|
||||||
base: classNames('modal-content', this.props.className),
|
base: classNames('modal-content', this.props.className),
|
||||||
afterOpen: classNames('modal-content', this.props.className),
|
afterOpen: classNames('modal-content', this.props.className),
|
||||||
|
|
|
@ -23,10 +23,11 @@ const AccountNav = ({
|
||||||
}) => (
|
}) => (
|
||||||
<div className="account-nav">
|
<div className="account-nav">
|
||||||
<a
|
<a
|
||||||
className={classNames({
|
className={classNames([
|
||||||
'user-info': true,
|
'ignore-react-onclickoutside',
|
||||||
'open': isOpen
|
'user-info',
|
||||||
})}
|
{open: isOpen}
|
||||||
|
])}
|
||||||
href="#"
|
href="#"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
|
|
|
@ -8,16 +8,14 @@ const PropTypes = require('prop-types');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
const messageCountActions = require('../../../redux/message-count.js');
|
const messageCountActions = require('../../../redux/message-count.js');
|
||||||
|
const navigationActions = require('../../../redux/navigation.js');
|
||||||
const sessionActions = require('../../../redux/session.js');
|
const sessionActions = require('../../../redux/session.js');
|
||||||
|
|
||||||
const api = require('../../../lib/api');
|
|
||||||
const Button = require('../../forms/button.jsx');
|
const Button = require('../../forms/button.jsx');
|
||||||
const Dropdown = require('../../dropdown/dropdown.jsx');
|
|
||||||
const Form = require('../../forms/form.jsx');
|
const Form = require('../../forms/form.jsx');
|
||||||
const Input = require('../../forms/input.jsx');
|
const Input = require('../../forms/input.jsx');
|
||||||
const log = require('../../../lib/log.js');
|
const LoginDropdown = require('../../login/login-dropdown.jsx');
|
||||||
const Login = require('../../login/login.jsx');
|
const CanceledDeletionModal = require('../../login/canceled-deletion-modal.jsx');
|
||||||
const Modal = require('../../modal/base/modal.jsx');
|
|
||||||
const NavigationBox = require('../base/navigation.jsx');
|
const NavigationBox = require('../base/navigation.jsx');
|
||||||
const Registration = require('../../registration/registration.jsx');
|
const Registration = require('../../registration/registration.jsx');
|
||||||
const AccountNav = require('./accountnav.jsx');
|
const AccountNav = require('./accountnav.jsx');
|
||||||
|
@ -29,32 +27,16 @@ class Navigation extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
bindAll(this, [
|
bindAll(this, [
|
||||||
'getProfileUrl',
|
'getProfileUrl',
|
||||||
'handleJoinClick',
|
|
||||||
'handleLoginClick',
|
|
||||||
'handleCloseLogin',
|
|
||||||
'handleLogIn',
|
|
||||||
'handleLogOut',
|
|
||||||
'handleAccountNavClick',
|
|
||||||
'handleCloseAccountNav',
|
|
||||||
'showCanceledDeletion',
|
|
||||||
'handleCloseCanceledDeletion',
|
|
||||||
'handleCloseRegistration',
|
|
||||||
'handleCompleteRegistration',
|
|
||||||
'handleSearchSubmit'
|
'handleSearchSubmit'
|
||||||
]);
|
]);
|
||||||
this.state = {
|
this.state = {
|
||||||
accountNavOpen: false,
|
|
||||||
canceledDeletionOpen: false,
|
|
||||||
loginOpen: false,
|
|
||||||
loginError: null,
|
|
||||||
registrationOpen: false,
|
|
||||||
messageCountIntervalId: -1 // javascript method interval id for getting messsage count.
|
messageCountIntervalId: -1 // javascript method interval id for getting messsage count.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
if (this.props.session.session.user) {
|
if (this.props.user) {
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
this.props.getMessageCount(this.props.session.session.user.username);
|
this.props.getMessageCount(this.props.user.username);
|
||||||
}, 120000); // check for new messages every 2 mins.
|
}, 120000); // check for new messages every 2 mins.
|
||||||
this.setState({ // eslint-disable-line react/no-did-mount-set-state
|
this.setState({ // eslint-disable-line react/no-did-mount-set-state
|
||||||
messageCountIntervalId: intervalId
|
messageCountIntervalId: intervalId
|
||||||
|
@ -62,14 +44,11 @@ class Navigation extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
if (prevProps.session.session.user !== this.props.session.session.user) {
|
if (prevProps.user !== this.props.user) {
|
||||||
this.setState({ // eslint-disable-line react/no-did-update-set-state
|
this.props.closeAccountMenus();
|
||||||
loginOpen: false,
|
if (this.props.user) {
|
||||||
accountNavOpen: false
|
|
||||||
});
|
|
||||||
if (this.props.session.session.user) {
|
|
||||||
const intervalId = setInterval(() => {
|
const intervalId = setInterval(() => {
|
||||||
this.props.getMessageCount(this.props.session.session.user.username);
|
this.props.getMessageCount(this.props.user.username);
|
||||||
}, 120000); // check for new messages every 2 mins.
|
}, 120000); // check for new messages every 2 mins.
|
||||||
this.setState({ // eslint-disable-line react/no-did-update-set-state
|
this.setState({ // eslint-disable-line react/no-did-update-set-state
|
||||||
messageCountIntervalId: intervalId
|
messageCountIntervalId: intervalId
|
||||||
|
@ -88,104 +67,25 @@ class Navigation extends React.Component {
|
||||||
// clear message interval if it exists
|
// clear message interval if it exists
|
||||||
if (this.state.messageCountIntervalId !== -1) {
|
if (this.state.messageCountIntervalId !== -1) {
|
||||||
clearInterval(this.state.messageCountIntervalId);
|
clearInterval(this.state.messageCountIntervalId);
|
||||||
this.props.dispatch(messageCountActions.setCount(0));
|
this.props.setMessageCount(0);
|
||||||
this.setState({
|
this.setState({
|
||||||
messageCountIntervalId: -1
|
messageCountIntervalId: -1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getProfileUrl () {
|
getProfileUrl () {
|
||||||
if (!this.props.session.session.user) return;
|
if (!this.props.user) return;
|
||||||
return `/users/${this.props.session.session.user.username}/`;
|
return `/users/${this.props.user.username}/`;
|
||||||
}
|
|
||||||
handleJoinClick (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.setState({registrationOpen: true});
|
|
||||||
}
|
|
||||||
handleLoginClick (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.setState({loginOpen: !this.state.loginOpen});
|
|
||||||
}
|
|
||||||
handleCloseLogin () {
|
|
||||||
this.setState({loginOpen: false});
|
|
||||||
}
|
|
||||||
// NOTE: TODO: continue here. Should move these two functions up to a redux level,
|
|
||||||
// maybe into session...
|
|
||||||
handleLogIn (formData, callback) {
|
|
||||||
this.setState({loginError: null});
|
|
||||||
formData.useMessages = true;
|
|
||||||
api({
|
|
||||||
method: 'post',
|
|
||||||
host: '',
|
|
||||||
uri: '/accounts/login/',
|
|
||||||
json: formData,
|
|
||||||
useCsrf: true
|
|
||||||
}, (err, body) => {
|
|
||||||
if (err) this.setState({loginError: err.message});
|
|
||||||
if (body) {
|
|
||||||
body = body[0];
|
|
||||||
if (body.success) {
|
|
||||||
this.handleCloseLogin();
|
|
||||||
body.messages.map(message => { // eslint-disable-line array-callback-return
|
|
||||||
if (message.message === 'canceled-deletion') {
|
|
||||||
this.showCanceledDeletion();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.props.refreshSession();
|
|
||||||
} else {
|
|
||||||
if (body.redirect) {
|
|
||||||
window.location = body.redirect;
|
|
||||||
}
|
|
||||||
// Update login error message to a friendlier one if it exists
|
|
||||||
this.setState({loginError: body.msg});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// JS error already logged by api mixin
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
handleLogOut (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
api({
|
|
||||||
host: '',
|
|
||||||
method: 'post',
|
|
||||||
uri: '/accounts/logout/',
|
|
||||||
useCsrf: true
|
|
||||||
}, err => {
|
|
||||||
if (err) log.error(err);
|
|
||||||
this.handleCloseLogin();
|
|
||||||
window.location = '/';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
handleAccountNavClick (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.setState({accountNavOpen: true});
|
|
||||||
}
|
|
||||||
handleCloseAccountNav () {
|
|
||||||
this.setState({accountNavOpen: false});
|
|
||||||
}
|
|
||||||
showCanceledDeletion () {
|
|
||||||
this.setState({canceledDeletionOpen: true});
|
|
||||||
}
|
|
||||||
handleCloseCanceledDeletion () {
|
|
||||||
this.setState({canceledDeletionOpen: false});
|
|
||||||
}
|
|
||||||
handleCloseRegistration () {
|
|
||||||
this.setState({registrationOpen: false});
|
|
||||||
}
|
|
||||||
handleCompleteRegistration () {
|
|
||||||
this.props.dispatch(sessionActions.refreshSession());
|
|
||||||
this.handleCloseRegistration();
|
|
||||||
}
|
}
|
||||||
handleSearchSubmit (formData) {
|
handleSearchSubmit (formData) {
|
||||||
window.location.href = `/search/projects?q=${encodeURIComponent(formData.q)}`;
|
window.location.href = `/search/projects?q=${encodeURIComponent(formData.q)}`;
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
const createLink = this.props.session.session.user ? '/projects/editor/' : '/projects/editor/?tip_bar=home';
|
const createLink = this.props.user ? '/projects/editor/' : '/projects/editor/?tip_bar=home';
|
||||||
return (
|
return (
|
||||||
<NavigationBox
|
<NavigationBox
|
||||||
className={classNames({
|
className={classNames({
|
||||||
'logged-in': this.props.session.session.user
|
'logged-in': this.props.user
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -233,7 +133,7 @@ class Navigation extends React.Component {
|
||||||
</Form>
|
</Form>
|
||||||
</li>
|
</li>
|
||||||
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
{this.props.session.status === sessionActions.Status.FETCHED ? (
|
||||||
this.props.session.session.user ? [
|
this.props.user ? [
|
||||||
<li
|
<li
|
||||||
className="link right messages"
|
className="link right messages"
|
||||||
key="messages"
|
key="messages"
|
||||||
|
@ -267,16 +167,16 @@ class Navigation extends React.Component {
|
||||||
key="account-nav"
|
key="account-nav"
|
||||||
>
|
>
|
||||||
<AccountNav
|
<AccountNav
|
||||||
classroomId={this.props.session.session.user.classroomId}
|
classroomId={this.props.user.classroomId}
|
||||||
isEducator={this.props.permissions.educator}
|
isEducator={this.props.permissions.educator}
|
||||||
isOpen={this.state.accountNavOpen}
|
isOpen={this.props.accountNavOpen}
|
||||||
isStudent={this.props.permissions.student}
|
isStudent={this.props.permissions.student}
|
||||||
profileUrl={this.getProfileUrl()}
|
profileUrl={this.getProfileUrl()}
|
||||||
thumbnailUrl={this.props.session.session.user.thumbnailUrl}
|
thumbnailUrl={this.props.user.thumbnailUrl}
|
||||||
username={this.props.session.session.user.username}
|
username={this.props.user.username}
|
||||||
onClick={this.handleAccountNavClick}
|
onClick={this.props.handleToggleAccountNav}
|
||||||
onClickLogout={this.handleLogOut}
|
onClickLogout={this.props.handleLogOut}
|
||||||
onClose={this.handleCloseAccountNav}
|
onClose={this.props.handleCloseAccountNav}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
] : [
|
] : [
|
||||||
|
@ -286,16 +186,13 @@ class Navigation extends React.Component {
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
onClick={this.handleJoinClick}
|
onClick={this.props.handleOpenRegistration}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="general.joinScratch" />
|
<FormattedMessage id="general.joinScratch" />
|
||||||
</a>
|
</a>
|
||||||
</li>,
|
</li>,
|
||||||
<Registration
|
<Registration
|
||||||
isOpen={this.state.registrationOpen}
|
|
||||||
key="registration"
|
key="registration"
|
||||||
onRegistrationDone={this.handleCompleteRegistration}
|
|
||||||
onRequestClose={this.handleCloseRegistration}
|
|
||||||
/>,
|
/>,
|
||||||
<li
|
<li
|
||||||
className="link right login-item"
|
className="link right login-item"
|
||||||
|
@ -305,54 +202,31 @@ class Navigation extends React.Component {
|
||||||
className="ignore-react-onclickoutside"
|
className="ignore-react-onclickoutside"
|
||||||
href="#"
|
href="#"
|
||||||
key="login-link"
|
key="login-link"
|
||||||
onClick={this.handleLoginClick}
|
onClick={this.props.handleToggleLoginOpen}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="general.signIn" />
|
<FormattedMessage id="general.signIn" />
|
||||||
</a>
|
</a>
|
||||||
<Dropdown
|
<LoginDropdown
|
||||||
className="login-dropdown with-arrow"
|
|
||||||
isOpen={this.state.loginOpen}
|
|
||||||
key="login-dropdown"
|
key="login-dropdown"
|
||||||
onRequestClose={this.handleCloseLogin}
|
/>
|
||||||
>
|
|
||||||
<Login
|
|
||||||
error={this.state.loginError}
|
|
||||||
onLogIn={this.handleLogIn}
|
|
||||||
/>
|
|
||||||
</Dropdown>
|
|
||||||
</li>
|
</li>
|
||||||
]) : []}
|
]) : []}
|
||||||
</ul>
|
</ul>
|
||||||
<Modal
|
<CanceledDeletionModal />
|
||||||
isOpen={this.state.canceledDeletionOpen}
|
|
||||||
style={{
|
|
||||||
content: {
|
|
||||||
padding: 15
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onRequestClose={this.handleCloseCanceledDeletion}
|
|
||||||
>
|
|
||||||
<h4>Your Account Will Not Be Deleted</h4>
|
|
||||||
<h4><FormattedMessage id="general.noDeletionTitle" /></h4>
|
|
||||||
<p>
|
|
||||||
<FormattedMessage
|
|
||||||
id="general.noDeletionDescription"
|
|
||||||
values={{
|
|
||||||
resetLink: <a href="/accounts/password_reset/">
|
|
||||||
{this.props.intl.formatMessage({id: 'general.noDeletionLink'})}
|
|
||||||
</a>
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</Modal>
|
|
||||||
</NavigationBox>
|
</NavigationBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Navigation.propTypes = {
|
Navigation.propTypes = {
|
||||||
dispatch: PropTypes.func,
|
accountNavOpen: PropTypes.bool,
|
||||||
|
closeAccountMenus: PropTypes.func,
|
||||||
getMessageCount: PropTypes.func,
|
getMessageCount: PropTypes.func,
|
||||||
|
handleCloseAccountNav: PropTypes.func,
|
||||||
|
handleLogOut: PropTypes.func,
|
||||||
|
handleOpenRegistration: PropTypes.func,
|
||||||
|
handleToggleAccountNav: PropTypes.func,
|
||||||
|
handleToggleLoginOpen: PropTypes.func,
|
||||||
intl: intlShape,
|
intl: intlShape,
|
||||||
permissions: PropTypes.shape({
|
permissions: PropTypes.shape({
|
||||||
admin: PropTypes.bool,
|
admin: PropTypes.bool,
|
||||||
|
@ -361,20 +235,17 @@ Navigation.propTypes = {
|
||||||
educator_invitee: PropTypes.bool,
|
educator_invitee: PropTypes.bool,
|
||||||
student: PropTypes.bool
|
student: PropTypes.bool
|
||||||
}),
|
}),
|
||||||
refreshSession: PropTypes.func,
|
|
||||||
searchTerm: PropTypes.string,
|
searchTerm: PropTypes.string,
|
||||||
session: PropTypes.shape({
|
session: PropTypes.shape({
|
||||||
session: PropTypes.shape({
|
|
||||||
user: PropTypes.shape({
|
|
||||||
classroomId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
||||||
thumbnailUrl: PropTypes.string,
|
|
||||||
username: PropTypes.string
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
status: PropTypes.string
|
status: PropTypes.string
|
||||||
}),
|
}),
|
||||||
setMessageCount: PropTypes.func,
|
setMessageCount: PropTypes.func,
|
||||||
unreadMessageCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
|
unreadMessageCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
|
user: PropTypes.shape({
|
||||||
|
classroomId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||||
|
thumbnailUrl: PropTypes.string,
|
||||||
|
username: PropTypes.string
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
Navigation.defaultProps = {
|
Navigation.defaultProps = {
|
||||||
|
@ -384,18 +255,39 @@ Navigation.defaultProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
accountNavOpen: state.navigation && state.navigation.accountNavOpen,
|
||||||
session: state.session,
|
session: state.session,
|
||||||
permissions: state.permissions,
|
permissions: state.permissions,
|
||||||
|
searchTerm: state.navigation.searchTerm,
|
||||||
unreadMessageCount: state.messageCount.messageCount,
|
unreadMessageCount: state.messageCount.messageCount,
|
||||||
searchTerm: state.navigation
|
user: state.session && state.session.session && state.session.session.user
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
closeAccountMenus: () => {
|
||||||
|
dispatch(navigationActions.closeAccountMenus());
|
||||||
|
},
|
||||||
getMessageCount: username => {
|
getMessageCount: username => {
|
||||||
dispatch(messageCountActions.getCount(username));
|
dispatch(messageCountActions.getCount(username));
|
||||||
},
|
},
|
||||||
refreshSession: () => {
|
handleToggleAccountNav: event => {
|
||||||
dispatch(sessionActions.refreshSession());
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.handleToggleAccountNav());
|
||||||
|
},
|
||||||
|
handleCloseAccountNav: () => {
|
||||||
|
dispatch(navigationActions.setAccountNavOpen(false));
|
||||||
|
},
|
||||||
|
handleOpenRegistration: event => {
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.setRegistrationOpen(true));
|
||||||
|
},
|
||||||
|
handleLogOut: event => {
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.handleLogOut());
|
||||||
|
},
|
||||||
|
handleToggleLoginOpen: event => {
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.toggleLoginOpen());
|
||||||
},
|
},
|
||||||
setMessageCount: newCount => {
|
setMessageCount: newCount => {
|
||||||
dispatch(messageCountActions.setCount(newCount));
|
dispatch(messageCountActions.setCount(newCount));
|
||||||
|
|
|
@ -163,25 +163,6 @@
|
||||||
background-image: url("/images/mystuff.png");
|
background-image: url("/images/mystuff.png");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-dropdown {
|
|
||||||
width: 200px;
|
|
||||||
|
|
||||||
.button {
|
|
||||||
padding: .75em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown {
|
|
||||||
.row {
|
|
||||||
margin-bottom: 1.25rem;
|
|
||||||
|
|
||||||
input {
|
|
||||||
margin: 0;
|
|
||||||
height: 2.25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//4 columns
|
//4 columns
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
const bindAll = require('lodash.bindall');
|
const bindAll = require('lodash.bindall');
|
||||||
const PropTypes = require('prop-types');
|
const PropTypes = require('prop-types');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
const connect = require('react-redux').connect;
|
||||||
|
|
||||||
const IframeModal = require('../modal/iframe/modal.jsx');
|
const IframeModal = require('../modal/iframe/modal.jsx');
|
||||||
|
const navigationActions = require('../../redux/navigation.js');
|
||||||
|
|
||||||
require('./registration.scss');
|
require('./registration.scss');
|
||||||
|
|
||||||
|
@ -26,7 +28,7 @@ class Registration extends React.Component {
|
||||||
handleMessage (e) {
|
handleMessage (e) {
|
||||||
if (e.origin !== window.location.origin) return;
|
if (e.origin !== window.location.origin) return;
|
||||||
if (e.source !== this.registrationIframe.contentWindow) return;
|
if (e.source !== this.registrationIframe.contentWindow) return;
|
||||||
if (e.data === 'registration-done') this.props.onRegistrationDone();
|
if (e.data === 'registration-done') this.props.handleCompleteRegistration();
|
||||||
if (e.data === 'registration-relaunch') {
|
if (e.data === 'registration-relaunch') {
|
||||||
this.registrationIframe.contentWindow.location.reload();
|
this.registrationIframe.contentWindow.location.reload();
|
||||||
}
|
}
|
||||||
|
@ -47,16 +49,32 @@ class Registration extends React.Component {
|
||||||
}}
|
}}
|
||||||
isOpen={this.props.isOpen}
|
isOpen={this.props.isOpen}
|
||||||
src="/accounts/standalone-registration/"
|
src="/accounts/standalone-registration/"
|
||||||
onRequestClose={this.props.onRequestClose}
|
onRequestClose={this.props.handleCloseRegistration}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Registration.propTypes = {
|
Registration.propTypes = {
|
||||||
isOpen: PropTypes.bool,
|
handleCloseRegistration: PropTypes.func,
|
||||||
onRegistrationDone: PropTypes.func,
|
handleCompleteRegistration: PropTypes.func,
|
||||||
onRequestClose: PropTypes.func
|
isOpen: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Registration;
|
const mapStateToProps = state => ({
|
||||||
|
isOpen: state.navigation.registrationOpen
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
handleCloseRegistration: () => {
|
||||||
|
dispatch(navigationActions.setRegistrationOpen(false));
|
||||||
|
},
|
||||||
|
handleCompleteRegistration: () => {
|
||||||
|
dispatch(navigationActions.handleCompleteRegistration());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Registration);
|
||||||
|
|
|
@ -1,22 +1,153 @@
|
||||||
const keyMirror = require('keymirror');
|
const keyMirror = require('keymirror');
|
||||||
|
const defaults = require('lodash.defaults');
|
||||||
|
|
||||||
|
const api = require('../lib/api');
|
||||||
|
const log = require('../lib/log.js');
|
||||||
|
const sessionActions = require('./session.js');
|
||||||
|
|
||||||
const Types = keyMirror({
|
const Types = keyMirror({
|
||||||
SET_SEARCH_TERM: null
|
SET_SEARCH_TERM: null,
|
||||||
|
SET_ACCOUNT_NAV_OPEN: null,
|
||||||
|
TOGGLE_ACCOUNT_NAV_OPEN: null,
|
||||||
|
SET_LOGIN_ERROR: null,
|
||||||
|
SET_LOGIN_OPEN: null,
|
||||||
|
TOGGLE_LOGIN_OPEN: null,
|
||||||
|
SET_CANCELED_DELETION_OPEN: null,
|
||||||
|
SET_REGISTRATION_OPEN: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports.getInitialState = () => ({
|
||||||
|
accountNavOpen: false,
|
||||||
|
canceledDeletionOpen: false,
|
||||||
|
loginError: null,
|
||||||
|
loginOpen: false,
|
||||||
|
registrationOpen: false,
|
||||||
|
searchTerm: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports.navigationReducer = (state, action) => {
|
module.exports.navigationReducer = (state, action) => {
|
||||||
if (typeof state === 'undefined') {
|
if (typeof state === 'undefined') {
|
||||||
state = '';
|
state = module.exports.getInitialState();
|
||||||
}
|
}
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case Types.SET_SEARCH_TERM:
|
case Types.SET_SEARCH_TERM:
|
||||||
return action.searchTerm;
|
return defaults({searchTerm: action.searchTerm}, state);
|
||||||
|
case Types.SET_ACCOUNT_NAV_OPEN:
|
||||||
|
return defaults({accountNavOpen: action.isOpen}, state);
|
||||||
|
case Types.TOGGLE_ACCOUNT_NAV_OPEN:
|
||||||
|
return defaults({accountNavOpen: !state.accountNavOpen}, state);
|
||||||
|
case Types.SET_LOGIN_ERROR:
|
||||||
|
return defaults({loginError: action.loginError}, state);
|
||||||
|
case Types.SET_LOGIN_OPEN:
|
||||||
|
return defaults({loginOpen: action.isOpen}, state);
|
||||||
|
case Types.TOGGLE_LOGIN_OPEN:
|
||||||
|
return defaults({loginOpen: !state.loginOpen}, state);
|
||||||
|
case Types.SET_CANCELED_DELETION_OPEN:
|
||||||
|
return defaults({canceledDeletionOpen: action.isOpen}, state);
|
||||||
|
case Types.SET_REGISTRATION_OPEN:
|
||||||
|
return defaults({registrationOpen: action.isOpen}, state);
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.setAccountNavOpen = isOpen => ({
|
||||||
|
type: Types.SET_ACCOUNT_NAV_OPEN,
|
||||||
|
isOpen: isOpen
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.handleToggleAccountNav = () => ({
|
||||||
|
type: Types.TOGGLE_ACCOUNT_NAV_OPEN
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.setCanceledDeletionOpen = isOpen => ({
|
||||||
|
type: Types.SET_CANCELED_DELETION_OPEN,
|
||||||
|
isOpen: isOpen
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.setLoginError = loginError => ({
|
||||||
|
type: Types.SET_LOGIN_ERROR,
|
||||||
|
loginError: loginError
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.setLoginOpen = isOpen => ({
|
||||||
|
type: Types.SET_LOGIN_OPEN,
|
||||||
|
isOpen: isOpen
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.toggleLoginOpen = () => ({
|
||||||
|
type: Types.TOGGLE_LOGIN_OPEN
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.setRegistrationOpen = isOpen => ({
|
||||||
|
type: Types.SET_REGISTRATION_OPEN,
|
||||||
|
isOpen: isOpen
|
||||||
|
});
|
||||||
|
|
||||||
module.exports.setSearchTerm = searchTerm => ({
|
module.exports.setSearchTerm = searchTerm => ({
|
||||||
type: Types.SET_SEARCH_TERM,
|
type: Types.SET_SEARCH_TERM,
|
||||||
searchTerm: searchTerm
|
searchTerm: searchTerm
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports.handleCompleteRegistration = () => (dispatch => {
|
||||||
|
dispatch(sessionActions.refreshSession());
|
||||||
|
dispatch(module.exports.setRegistrationOpen(false));
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.closeAccountMenus = () => (dispatch => {
|
||||||
|
dispatch(module.exports.setAccountNavOpen(false));
|
||||||
|
dispatch(module.exports.setRegistrationOpen(false));
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.handleLogIn = (formData, callback) => (dispatch => {
|
||||||
|
dispatch(module.exports.setLoginError(null));
|
||||||
|
formData.useMessages = true; // NOTE: this may or may not be being used anywhere else
|
||||||
|
api({
|
||||||
|
method: 'post',
|
||||||
|
host: '',
|
||||||
|
uri: '/accounts/login/',
|
||||||
|
json: formData,
|
||||||
|
useCsrf: true
|
||||||
|
}, (err, body) => {
|
||||||
|
if (err) dispatch(module.exports.setLoginError(err.message));
|
||||||
|
if (body) {
|
||||||
|
body = body[0];
|
||||||
|
if (body.success) {
|
||||||
|
dispatch(module.exports.setLoginOpen(false));
|
||||||
|
body.messages.forEach(message => {
|
||||||
|
if (message.message === 'canceled-deletion') {
|
||||||
|
dispatch(module.exports.setCanceledDeletionOpen(true));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dispatch(sessionActions.refreshSession());
|
||||||
|
callback({success: true});
|
||||||
|
} else {
|
||||||
|
if (body.redirect) {
|
||||||
|
window.location = body.redirect;
|
||||||
|
}
|
||||||
|
// Update login error message to a friendlier one if it exists
|
||||||
|
dispatch(module.exports.setLoginError(body.msg));
|
||||||
|
// JS error already logged by api mixin
|
||||||
|
callback({success: false});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// JS error already logged by api mixin
|
||||||
|
callback({success: false});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.handleLogOut = () => (dispatch => {
|
||||||
|
api({
|
||||||
|
host: '',
|
||||||
|
method: 'post',
|
||||||
|
uri: '/accounts/logout/',
|
||||||
|
useCsrf: true
|
||||||
|
}, err => {
|
||||||
|
if (err) log.error(err);
|
||||||
|
dispatch(module.exports.setLoginOpen(false));
|
||||||
|
dispatch(module.exports.setAccountNavOpen(false));
|
||||||
|
window.location = '/';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ const defaults = require('lodash.defaults');
|
||||||
const messageCountReducer = require('./message-count.js').messageCountReducer;
|
const messageCountReducer = require('./message-count.js').messageCountReducer;
|
||||||
const permissionsReducer = require('./permissions.js').permissionsReducer;
|
const permissionsReducer = require('./permissions.js').permissionsReducer;
|
||||||
const sessionReducer = require('./session.js').sessionReducer;
|
const sessionReducer = require('./session.js').sessionReducer;
|
||||||
|
const navigationReducer = require('./navigation.js').navigationReducer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a combined reducer to be used for a page in `render.jsx`.
|
* Returns a combined reducer to be used for a page in `render.jsx`.
|
||||||
|
@ -18,8 +19,9 @@ const sessionReducer = require('./session.js').sessionReducer;
|
||||||
module.exports = opts => {
|
module.exports = opts => {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
return combineReducers(defaults(opts, {
|
return combineReducers(defaults(opts, {
|
||||||
session: sessionReducer,
|
messageCount: messageCountReducer,
|
||||||
|
navigation: navigationReducer,
|
||||||
permissions: permissionsReducer,
|
permissions: permissionsReducer,
|
||||||
messageCount: messageCountReducer
|
session: sessionReducer
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,6 @@ const connect = require('react-redux').connect;
|
||||||
const injectIntl = require('react-intl').injectIntl;
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
const parser = require('scratch-parser');
|
const parser = require('scratch-parser');
|
||||||
const Page = require('../../components/page/www/page.jsx');
|
const Page = require('../../components/page/www/page.jsx');
|
||||||
const api = require('../../lib/api');
|
|
||||||
const render = require('../../lib/render.jsx');
|
const render = require('../../lib/render.jsx');
|
||||||
const storage = require('../../lib/storage.js').default;
|
const storage = require('../../lib/storage.js').default;
|
||||||
const log = require('../../lib/log');
|
const log = require('../../lib/log');
|
||||||
|
@ -16,8 +15,12 @@ const EXTENSION_INFO = require('../../lib/extensions.js').default;
|
||||||
|
|
||||||
const PreviewPresentation = require('./presentation.jsx');
|
const PreviewPresentation = require('./presentation.jsx');
|
||||||
const projectShape = require('./projectshape.jsx').projectShape;
|
const projectShape = require('./projectshape.jsx').projectShape;
|
||||||
|
const Registration = require('../../components/registration/registration.jsx');
|
||||||
|
const ConnectedLogin = require('../../components/login/connected-login.jsx');
|
||||||
|
const CanceledDeletionModal = require('../../components/login/canceled-deletion-modal.jsx');
|
||||||
|
|
||||||
const sessionActions = require('../../redux/session.js');
|
const sessionActions = require('../../redux/session.js');
|
||||||
|
const navigationActions = require('../../redux/navigation.js');
|
||||||
const previewActions = require('../../redux/preview.js');
|
const previewActions = require('../../redux/preview.js');
|
||||||
|
|
||||||
const GUI = require('scratch-gui');
|
const GUI = require('scratch-gui');
|
||||||
|
@ -31,10 +34,7 @@ class Preview extends React.Component {
|
||||||
'handleToggleStudio',
|
'handleToggleStudio',
|
||||||
'handleFavoriteToggle',
|
'handleFavoriteToggle',
|
||||||
'handleLoadMore',
|
'handleLoadMore',
|
||||||
// temporary, to pass to GUI. Remove when nav bar components are shared between www and gui.
|
|
||||||
'handleLogout',
|
|
||||||
'handleLoveToggle',
|
'handleLoveToggle',
|
||||||
'handlePermissions',
|
|
||||||
'handlePopState',
|
'handlePopState',
|
||||||
'handleReportClick',
|
'handleReportClick',
|
||||||
'handleReportClose',
|
'handleReportClose',
|
||||||
|
@ -45,9 +45,8 @@ class Preview extends React.Component {
|
||||||
'handleUpdateProjectTitle',
|
'handleUpdateProjectTitle',
|
||||||
'handleUpdate',
|
'handleUpdate',
|
||||||
'initCounts',
|
'initCounts',
|
||||||
'isShared',
|
|
||||||
'pushHistory',
|
'pushHistory',
|
||||||
'userOwnsProject'
|
'renderLogin'
|
||||||
]);
|
]);
|
||||||
const pathname = window.location.pathname.toLowerCase();
|
const pathname = window.location.pathname.toLowerCase();
|
||||||
const parts = pathname.split('/').filter(Boolean);
|
const parts = pathname.split('/').filter(Boolean);
|
||||||
|
@ -55,7 +54,6 @@ class Preview extends React.Component {
|
||||||
// parts[1]: either :id or 'editor'
|
// parts[1]: either :id or 'editor'
|
||||||
// parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen'
|
// parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen'
|
||||||
this.state = {
|
this.state = {
|
||||||
editable: false,
|
|
||||||
extensions: [],
|
extensions: [],
|
||||||
favoriteCount: 0,
|
favoriteCount: 0,
|
||||||
loveCount: 0,
|
loveCount: 0,
|
||||||
|
@ -90,7 +88,6 @@ class Preview extends React.Component {
|
||||||
if (this.props.projectInfo.id !== prevProps.projectInfo.id) {
|
if (this.props.projectInfo.id !== prevProps.projectInfo.id) {
|
||||||
this.getExtensions(this.state.projectId);
|
this.getExtensions(this.state.projectId);
|
||||||
this.initCounts(this.props.projectInfo.stats.favorites, this.props.projectInfo.stats.loves);
|
this.initCounts(this.props.projectInfo.stats.favorites, this.props.projectInfo.stats.loves);
|
||||||
this.handlePermissions();
|
|
||||||
if (this.props.projectInfo.remix.parent !== null) {
|
if (this.props.projectInfo.remix.parent !== null) {
|
||||||
this.props.getParentInfo(this.props.projectInfo.remix.parent);
|
this.props.getParentInfo(this.props.projectInfo.remix.parent);
|
||||||
}
|
}
|
||||||
|
@ -145,21 +142,6 @@ class Preview extends React.Component {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Temporarily duplicated this function from navigation.jsx here.
|
|
||||||
// Should move handling of login/logout into session.js, and handle them
|
|
||||||
// from here as well as navigation.jsx.
|
|
||||||
handleLogout (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
api({
|
|
||||||
host: '',
|
|
||||||
method: 'post',
|
|
||||||
uri: '/accounts/logout/',
|
|
||||||
useCsrf: true
|
|
||||||
}, err => {
|
|
||||||
if (err) log.error(err);
|
|
||||||
window.location = '/';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
handleReportClick () {
|
handleReportClick () {
|
||||||
this.setState({reportOpen: true});
|
this.setState({reportOpen: true});
|
||||||
}
|
}
|
||||||
|
@ -261,12 +243,6 @@ class Preview extends React.Component {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handlePermissions () {
|
|
||||||
// TODO: handle admins and mods
|
|
||||||
if (this.props.projectInfo.author.username === this.props.user.username) {
|
|
||||||
this.setState({editable: true});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handleSeeInside () {
|
handleSeeInside () {
|
||||||
this.props.setPlayer(false);
|
this.props.setPlayer(false);
|
||||||
}
|
}
|
||||||
|
@ -289,26 +265,21 @@ class Preview extends React.Component {
|
||||||
loveCount: loves
|
loveCount: loves
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
isShared () {
|
renderLogin ({onClose}) {
|
||||||
return (
|
return (
|
||||||
// if we don't have projectInfo assume shared until we know otherwise
|
<ConnectedLogin
|
||||||
Object.keys(this.props.projectInfo).length === 0 || (
|
key="login-dropdown-presentation"
|
||||||
this.props.projectInfo.history &&
|
/* eslint-disable react/jsx-no-bind */
|
||||||
this.props.projectInfo.history.shared.length > 0
|
onLogIn={(formData, callback) => {
|
||||||
)
|
this.props.handleLogIn(formData, result => {
|
||||||
);
|
if (result.success === true) {
|
||||||
}
|
onClose();
|
||||||
isLoggedIn () {
|
}
|
||||||
return (
|
callback(result);
|
||||||
this.props.sessionStatus === sessionActions.Status.FETCHED &&
|
});
|
||||||
Object.keys(this.props.user).length > 0
|
}}
|
||||||
);
|
/* eslint-ensable react/jsx-no-bind */
|
||||||
}
|
/>
|
||||||
userOwnsProject () {
|
|
||||||
return (
|
|
||||||
this.isLoggedIn() &&
|
|
||||||
Object.keys(this.props.projectInfo).length > 0 &&
|
|
||||||
this.props.user.id === this.props.projectInfo.author.id
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
|
@ -320,13 +291,13 @@ class Preview extends React.Component {
|
||||||
assetHost={this.props.assetHost}
|
assetHost={this.props.assetHost}
|
||||||
backpackOptions={this.props.backpackOptions}
|
backpackOptions={this.props.backpackOptions}
|
||||||
comments={this.props.comments}
|
comments={this.props.comments}
|
||||||
editable={this.state.editable}
|
editable={this.props.isEditable}
|
||||||
extensions={this.state.extensions}
|
extensions={this.state.extensions}
|
||||||
faved={this.props.faved}
|
faved={this.props.faved}
|
||||||
favoriteCount={this.state.favoriteCount}
|
favoriteCount={this.state.favoriteCount}
|
||||||
isFullScreen={this.state.isFullScreen}
|
isFullScreen={this.state.isFullScreen}
|
||||||
isLoggedIn={this.isLoggedIn()}
|
isLoggedIn={this.props.isLoggedIn}
|
||||||
isShared={this.isShared()}
|
isShared={this.props.isShared}
|
||||||
loveCount={this.state.loveCount}
|
loveCount={this.state.loveCount}
|
||||||
loved={this.props.loved}
|
loved={this.props.loved}
|
||||||
originalInfo={this.props.original}
|
originalInfo={this.props.original}
|
||||||
|
@ -339,7 +310,7 @@ class Preview extends React.Component {
|
||||||
replies={this.props.replies}
|
replies={this.props.replies}
|
||||||
reportOpen={this.state.reportOpen}
|
reportOpen={this.state.reportOpen}
|
||||||
studios={this.props.studios}
|
studios={this.props.studios}
|
||||||
userOwnsProject={this.userOwnsProject()}
|
userOwnsProject={this.props.userOwnsProject}
|
||||||
onAddToStudioClicked={this.handleAddToStudioClick}
|
onAddToStudioClicked={this.handleAddToStudioClick}
|
||||||
onAddToStudioClosed={this.handleAddToStudioClose}
|
onAddToStudioClosed={this.handleAddToStudioClose}
|
||||||
onFavoriteClicked={this.handleFavoriteToggle}
|
onFavoriteClicked={this.handleFavoriteToggle}
|
||||||
|
@ -353,19 +324,27 @@ class Preview extends React.Component {
|
||||||
onUpdate={this.handleUpdate}
|
onUpdate={this.handleUpdate}
|
||||||
/>
|
/>
|
||||||
</Page> :
|
</Page> :
|
||||||
<IntlGUI
|
<React.Fragment>
|
||||||
enableCommunity
|
<IntlGUI
|
||||||
hideIntro
|
enableCommunity
|
||||||
assetHost={this.props.assetHost}
|
hideIntro
|
||||||
backpackOptions={this.props.backpackOptions}
|
assetHost={this.props.assetHost}
|
||||||
basePath="/"
|
backpackOptions={this.props.backpackOptions}
|
||||||
className="gui"
|
basePath="/"
|
||||||
projectHost={this.props.projectHost}
|
className="gui"
|
||||||
projectId={this.state.projectId}
|
projectHost={this.props.projectHost}
|
||||||
projectTitle={this.props.projectInfo.title}
|
projectId={this.state.projectId}
|
||||||
onClickLogout={this.handleLogout}
|
projectTitle={this.props.projectInfo.title}
|
||||||
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
renderLogin={this.renderLogin}
|
||||||
/>
|
onLogOut={this.props.handleLogOut}
|
||||||
|
onOpenRegistration={this.props.handleOpenRegistration}
|
||||||
|
onToggleLoginOpen={this.props.handleToggleLoginOpen}
|
||||||
|
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
||||||
|
/>
|
||||||
|
<Registration />
|
||||||
|
<CanceledDeletionModal />
|
||||||
|
</React.Fragment>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,6 +367,13 @@ Preview.propTypes = {
|
||||||
getProjectStudios: PropTypes.func.isRequired,
|
getProjectStudios: PropTypes.func.isRequired,
|
||||||
getRemixes: PropTypes.func.isRequired,
|
getRemixes: PropTypes.func.isRequired,
|
||||||
getTopLevelComments: PropTypes.func.isRequired,
|
getTopLevelComments: PropTypes.func.isRequired,
|
||||||
|
handleLogIn: PropTypes.func,
|
||||||
|
handleLogOut: PropTypes.func,
|
||||||
|
handleOpenRegistration: PropTypes.func,
|
||||||
|
handleToggleLoginOpen: PropTypes.func,
|
||||||
|
isEditable: PropTypes.bool,
|
||||||
|
isLoggedIn: PropTypes.bool,
|
||||||
|
isShared: PropTypes.bool,
|
||||||
loved: PropTypes.bool,
|
loved: PropTypes.bool,
|
||||||
original: projectShape,
|
original: projectShape,
|
||||||
parent: projectShape,
|
parent: projectShape,
|
||||||
|
@ -415,7 +401,8 @@ Preview.propTypes = {
|
||||||
dateJoined: PropTypes.string,
|
dateJoined: PropTypes.string,
|
||||||
email: PropTypes.string,
|
email: PropTypes.string,
|
||||||
classroomId: PropTypes.string
|
classroomId: PropTypes.string
|
||||||
})
|
}),
|
||||||
|
userOwnsProject: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
Preview.defaultProps = {
|
Preview.defaultProps = {
|
||||||
|
@ -463,26 +450,64 @@ const consolidateStudiosInfo = (curatedStudios, projectStudios, currentStudioIds
|
||||||
return consolidatedStudios;
|
return consolidatedStudios;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => {
|
||||||
comments: state.preview.comments,
|
const projectInfoPresent = Object.keys(state.preview.projectInfo).length > 0;
|
||||||
faved: state.preview.faved,
|
const userPresent = state.session.session.user &&
|
||||||
loved: state.preview.loved,
|
Object.keys(state.session.session.user).length > 0;
|
||||||
original: state.preview.original,
|
const isLoggedIn = state.session.status === sessionActions.Status.FETCHED &&
|
||||||
parent: state.preview.parent,
|
userPresent;
|
||||||
remixes: state.preview.remixes,
|
const authorPresent = projectInfoPresent && state.preview.projectInfo.author &&
|
||||||
replies: state.preview.replies,
|
Object.keys(state.preview.projectInfo.author).length > 0;
|
||||||
sessionStatus: state.session.status,
|
|
||||||
projectInfo: state.preview.projectInfo,
|
return {
|
||||||
projectStudios: state.preview.projectStudios,
|
comments: state.preview.comments,
|
||||||
studios: consolidateStudiosInfo(state.preview.curatedStudios,
|
faved: state.preview.faved,
|
||||||
state.preview.projectStudios, state.preview.currentStudioIds,
|
fullScreen: state.scratchGui.mode.isFullScreen,
|
||||||
state.preview.status.studioRequests),
|
// project is editable iff logged in user is the author of the project, or
|
||||||
user: state.session.session.user,
|
// logged in user is an admin.
|
||||||
playerMode: state.scratchGui.mode.isPlayerOnly,
|
isEditable: isLoggedIn &&
|
||||||
fullScreen: state.scratchGui.mode.isFullScreen
|
((authorPresent && state.preview.projectInfo.author.username === state.session.session.user.username) ||
|
||||||
});
|
state.permissions.admin === true),
|
||||||
|
isLoggedIn: isLoggedIn,
|
||||||
|
// if we don't have projectInfo, assume it's shared until we know otherwise
|
||||||
|
isShared: !projectInfoPresent || (
|
||||||
|
state.preview.projectInfo.history &&
|
||||||
|
state.preview.projectInfo.history.shared &&
|
||||||
|
state.preview.projectInfo.history.shared.length > 0),
|
||||||
|
loved: state.preview.loved,
|
||||||
|
original: state.preview.original,
|
||||||
|
parent: state.preview.parent,
|
||||||
|
playerMode: state.scratchGui.mode.isPlayerOnly,
|
||||||
|
projectInfo: state.preview.projectInfo,
|
||||||
|
projectStudios: state.preview.projectStudios,
|
||||||
|
remixes: state.preview.remixes,
|
||||||
|
replies: state.preview.replies,
|
||||||
|
sessionStatus: state.session.status, // check if used
|
||||||
|
studios: consolidateStudiosInfo(state.preview.curatedStudios,
|
||||||
|
state.preview.projectStudios, state.preview.currentStudioIds,
|
||||||
|
state.preview.status.studioRequests),
|
||||||
|
user: state.session.session.user,
|
||||||
|
userOwnsProject: isLoggedIn && authorPresent &&
|
||||||
|
state.session.session.user.id === state.preview.projectInfo.author.id
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
handleOpenRegistration: event => {
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.setRegistrationOpen(true));
|
||||||
|
},
|
||||||
|
handleLogIn: (formData, callback) => {
|
||||||
|
dispatch(navigationActions.handleLogIn(formData, callback));
|
||||||
|
},
|
||||||
|
handleLogOut: event => {
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.handleLogOut());
|
||||||
|
},
|
||||||
|
handleToggleLoginOpen: event => {
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.toggleLoginOpen());
|
||||||
|
},
|
||||||
getOriginalInfo: id => {
|
getOriginalInfo: id => {
|
||||||
dispatch(previewActions.getOriginalInfo(id));
|
dispatch(previewActions.getOriginalInfo(id));
|
||||||
},
|
},
|
||||||
|
@ -523,9 +548,6 @@ const mapDispatchToProps = dispatch => ({
|
||||||
setLovedStatus: (loved, id, username, token) => {
|
setLovedStatus: (loved, id, username, token) => {
|
||||||
dispatch(previewActions.setLovedStatus(loved, id, username, token));
|
dispatch(previewActions.setLovedStatus(loved, id, username, token));
|
||||||
},
|
},
|
||||||
refreshSession: () => {
|
|
||||||
dispatch(sessionActions.refreshSession());
|
|
||||||
},
|
|
||||||
reportProject: (id, formData) => {
|
reportProject: (id, formData) => {
|
||||||
dispatch(previewActions.reportProject(id, formData));
|
dispatch(previewActions.reportProject(id, formData));
|
||||||
},
|
},
|
||||||
|
|
|
@ -232,7 +232,7 @@ Search.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
searchTerm: state.navigation
|
searchTerm: state.navigation.searchTerm
|
||||||
});
|
});
|
||||||
|
|
||||||
const WrappedSearch = injectIntl(Search);
|
const WrappedSearch = injectIntl(Search);
|
||||||
|
|
|
@ -7,8 +7,8 @@ const React = require('react');
|
||||||
const api = require('../../lib/api');
|
const api = require('../../lib/api');
|
||||||
const injectIntl = require('../../lib/intl.jsx').injectIntl;
|
const injectIntl = require('../../lib/intl.jsx').injectIntl;
|
||||||
const intlShape = require('../../lib/intl.jsx').intlShape;
|
const intlShape = require('../../lib/intl.jsx').intlShape;
|
||||||
const log = require('../../lib/log.js');
|
|
||||||
const sessionStatus = require('../../redux/session').Status;
|
const sessionStatus = require('../../redux/session').Status;
|
||||||
|
const navigationActions = require('../../redux/navigation.js');
|
||||||
|
|
||||||
const Deck = require('../../components/deck/deck.jsx');
|
const Deck = require('../../components/deck/deck.jsx');
|
||||||
const Progression = require('../../components/progression/progression.jsx');
|
const Progression = require('../../components/progression/progression.jsx');
|
||||||
|
@ -24,7 +24,6 @@ class StudentCompleteRegistration extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
bindAll(this, [
|
bindAll(this, [
|
||||||
'handleAdvanceStep',
|
'handleAdvanceStep',
|
||||||
'handleLogOut',
|
|
||||||
'handleRegister',
|
'handleRegister',
|
||||||
'handleGoToClass'
|
'handleGoToClass'
|
||||||
]);
|
]);
|
||||||
|
@ -61,18 +60,6 @@ class StudentCompleteRegistration extends React.Component {
|
||||||
formData: defaults({}, formData, this.state.formData)
|
formData: defaults({}, formData, this.state.formData)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
handleLogOut (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
api({
|
|
||||||
host: '',
|
|
||||||
method: 'post',
|
|
||||||
uri: '/accounts/logout/',
|
|
||||||
useCsrf: true
|
|
||||||
}, err => {
|
|
||||||
if (err) return log.error(err);
|
|
||||||
window.location = '/';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
handleRegister (formData) {
|
handleRegister (formData) {
|
||||||
this.setState({waiting: true});
|
this.setState({waiting: true});
|
||||||
|
|
||||||
|
@ -147,7 +134,7 @@ class StudentCompleteRegistration extends React.Component {
|
||||||
classroom={this.state.classroom}
|
classroom={this.state.classroom}
|
||||||
studentUsername={this.props.studentUsername}
|
studentUsername={this.props.studentUsername}
|
||||||
waiting={this.state.waiting}
|
waiting={this.state.waiting}
|
||||||
onHandleLogOut={this.handleLogOut}
|
onHandleLogOut={this.props.handleLogOut}
|
||||||
onNextStep={this.handleAdvanceStep}
|
onNextStep={this.handleAdvanceStep}
|
||||||
/>
|
/>
|
||||||
{this.props.must_reset_password ?
|
{this.props.must_reset_password ?
|
||||||
|
@ -178,6 +165,7 @@ class StudentCompleteRegistration extends React.Component {
|
||||||
|
|
||||||
StudentCompleteRegistration.propTypes = {
|
StudentCompleteRegistration.propTypes = {
|
||||||
classroomId: PropTypes.number.isRequired,
|
classroomId: PropTypes.number.isRequired,
|
||||||
|
handleLogOut: PropTypes.func,
|
||||||
intl: intlShape,
|
intl: intlShape,
|
||||||
must_reset_password: PropTypes.bool.isRequired,
|
must_reset_password: PropTypes.bool.isRequired,
|
||||||
newStudent: PropTypes.bool.isRequired,
|
newStudent: PropTypes.bool.isRequired,
|
||||||
|
@ -199,6 +187,16 @@ const mapStateToProps = state => ({
|
||||||
studentUsername: state.session.session.user && state.session.session.user.username
|
studentUsername: state.session.session.user && state.session.session.user.username
|
||||||
});
|
});
|
||||||
|
|
||||||
const ConnectedStudentCompleteRegistration = connect(mapStateToProps)(IntlStudentCompleteRegistration);
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
handleLogOut: event => {
|
||||||
|
event.preventDefault();
|
||||||
|
dispatch(navigationActions.handleLogOut());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ConnectedStudentCompleteRegistration = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(IntlStudentCompleteRegistration);
|
||||||
|
|
||||||
render(<ConnectedStudentCompleteRegistration />, document.getElementById('app'));
|
render(<ConnectedStudentCompleteRegistration />, document.getElementById('app'));
|
||||||
|
|
Loading…
Reference in a new issue