diff --git a/src/components/navigation/www/navigation.jsx b/src/components/navigation/www/navigation.jsx
index dffd88fc4..03120ca46 100644
--- a/src/components/navigation/www/navigation.jsx
+++ b/src/components/navigation/www/navigation.jsx
@@ -8,16 +8,14 @@ const PropTypes = require('prop-types');
const React = require('react');
const messageCountActions = require('../../../redux/message-count.js');
+const navigationActions = require('../../../redux/navigation.js');
const sessionActions = require('../../../redux/session.js');
-const api = require('../../../lib/api');
const Button = require('../../forms/button.jsx');
-const Dropdown = require('../../dropdown/dropdown.jsx');
const Form = require('../../forms/form.jsx');
const Input = require('../../forms/input.jsx');
-const log = require('../../../lib/log.js');
-const Login = require('../../login/login.jsx');
-const Modal = require('../../modal/base/modal.jsx');
+const LoginDropdown = require('../../login/login-dropdown.jsx');
+const CanceledDeletionModal = require('../../login/canceled-deletion-modal.jsx');
const NavigationBox = require('../base/navigation.jsx');
const Registration = require('../../registration/registration.jsx');
const AccountNav = require('./accountnav.jsx');
@@ -29,32 +27,16 @@ class Navigation extends React.Component {
super(props);
bindAll(this, [
'getProfileUrl',
- 'handleJoinClick',
- 'handleLoginClick',
- 'handleCloseLogin',
- 'handleLogIn',
- 'handleLogOut',
- 'handleAccountNavClick',
- 'handleCloseAccountNav',
- 'showCanceledDeletion',
- 'handleCloseCanceledDeletion',
- 'handleCloseRegistration',
- 'handleCompleteRegistration',
'handleSearchSubmit'
]);
this.state = {
- accountNavOpen: false,
- canceledDeletionOpen: false,
- loginOpen: false,
- loginError: null,
- registrationOpen: false,
messageCountIntervalId: -1 // javascript method interval id for getting messsage count.
};
}
componentDidMount () {
- if (this.props.session.session.user) {
+ if (this.props.user) {
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.
this.setState({ // eslint-disable-line react/no-did-mount-set-state
messageCountIntervalId: intervalId
@@ -62,14 +44,11 @@ class Navigation extends React.Component {
}
}
componentDidUpdate (prevProps) {
- if (prevProps.session.session.user !== this.props.session.session.user) {
- this.setState({ // eslint-disable-line react/no-did-update-set-state
- loginOpen: false,
- accountNavOpen: false
- });
- if (this.props.session.session.user) {
+ if (prevProps.user !== this.props.user) {
+ this.props.closeAccountMenus();
+ if (this.props.user) {
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.
this.setState({ // eslint-disable-line react/no-did-update-set-state
messageCountIntervalId: intervalId
@@ -88,104 +67,25 @@ class Navigation extends React.Component {
// clear message interval if it exists
if (this.state.messageCountIntervalId !== -1) {
clearInterval(this.state.messageCountIntervalId);
- this.props.dispatch(messageCountActions.setCount(0));
+ this.props.setMessageCount(0);
this.setState({
messageCountIntervalId: -1
});
}
}
getProfileUrl () {
- if (!this.props.session.session.user) return;
- return `/users/${this.props.session.session.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();
+ if (!this.props.user) return;
+ return `/users/${this.props.user.username}/`;
}
handleSearchSubmit (formData) {
window.location.href = `/search/projects?q=${encodeURIComponent(formData.q)}`;
}
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 (
@@ -233,7 +133,7 @@ class Navigation extends React.Component {
{this.props.session.status === sessionActions.Status.FETCHED ? (
- this.props.session.session.user ? [
+ this.props.user ? [
-
] : [
@@ -286,16 +186,13 @@ class Navigation extends React.Component {
>
,
,
-
-
-
-
+ />
]) : []}
-
- Your Account Will Not Be Deleted
-
-
-
- {this.props.intl.formatMessage({id: 'general.noDeletionLink'})}
-
- }}
- />
-
-
+
);
}
}
Navigation.propTypes = {
- dispatch: PropTypes.func,
+ accountNavOpen: PropTypes.bool,
+ closeAccountMenus: PropTypes.func,
getMessageCount: PropTypes.func,
+ handleCloseAccountNav: PropTypes.func,
+ handleLogOut: PropTypes.func,
+ handleOpenRegistration: PropTypes.func,
+ handleToggleAccountNav: PropTypes.func,
+ handleToggleLoginOpen: PropTypes.func,
intl: intlShape,
permissions: PropTypes.shape({
admin: PropTypes.bool,
@@ -361,20 +235,17 @@ Navigation.propTypes = {
educator_invitee: PropTypes.bool,
student: PropTypes.bool
}),
- refreshSession: PropTypes.func,
searchTerm: PropTypes.string,
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
}),
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 = {
@@ -384,18 +255,39 @@ Navigation.defaultProps = {
};
const mapStateToProps = state => ({
+ accountNavOpen: state.navigation && state.navigation.accountNavOpen,
session: state.session,
permissions: state.permissions,
+ searchTerm: state.navigation.searchTerm,
unreadMessageCount: state.messageCount.messageCount,
- searchTerm: state.navigation
+ user: state.session && state.session.session && state.session.session.user
});
const mapDispatchToProps = dispatch => ({
+ closeAccountMenus: () => {
+ dispatch(navigationActions.closeAccountMenus());
+ },
getMessageCount: username => {
dispatch(messageCountActions.getCount(username));
},
- refreshSession: () => {
- dispatch(sessionActions.refreshSession());
+ handleToggleAccountNav: event => {
+ 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 => {
dispatch(messageCountActions.setCount(newCount));
diff --git a/src/components/navigation/www/navigation.scss b/src/components/navigation/www/navigation.scss
index c736fa1f4..62429789a 100644
--- a/src/components/navigation/www/navigation.scss
+++ b/src/components/navigation/www/navigation.scss
@@ -163,25 +163,6 @@
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
diff --git a/src/components/registration/registration.jsx b/src/components/registration/registration.jsx
index fed69ed49..a7a700659 100644
--- a/src/components/registration/registration.jsx
+++ b/src/components/registration/registration.jsx
@@ -1,8 +1,10 @@
const bindAll = require('lodash.bindall');
const PropTypes = require('prop-types');
const React = require('react');
+const connect = require('react-redux').connect;
const IframeModal = require('../modal/iframe/modal.jsx');
+const navigationActions = require('../../redux/navigation.js');
require('./registration.scss');
@@ -26,7 +28,7 @@ class Registration extends React.Component {
handleMessage (e) {
if (e.origin !== window.location.origin) 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') {
this.registrationIframe.contentWindow.location.reload();
}
@@ -47,16 +49,32 @@ class Registration extends React.Component {
}}
isOpen={this.props.isOpen}
src="/accounts/standalone-registration/"
- onRequestClose={this.props.onRequestClose}
+ onRequestClose={this.props.handleCloseRegistration}
/>
);
}
}
Registration.propTypes = {
- isOpen: PropTypes.bool,
- onRegistrationDone: PropTypes.func,
- onRequestClose: PropTypes.func
+ handleCloseRegistration: PropTypes.func,
+ handleCompleteRegistration: 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);
diff --git a/src/redux/navigation.js b/src/redux/navigation.js
index 088fc7fb6..40782c490 100644
--- a/src/redux/navigation.js
+++ b/src/redux/navigation.js
@@ -1,22 +1,153 @@
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({
- 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) => {
if (typeof state === 'undefined') {
- state = '';
+ state = module.exports.getInitialState();
}
switch (action.type) {
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:
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 => ({
type: Types.SET_SEARCH_TERM,
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 = '/';
+ });
+});
diff --git a/src/redux/reducer.js b/src/redux/reducer.js
index 3df093a98..1adad733c 100644
--- a/src/redux/reducer.js
+++ b/src/redux/reducer.js
@@ -4,6 +4,7 @@ const defaults = require('lodash.defaults');
const messageCountReducer = require('./message-count.js').messageCountReducer;
const permissionsReducer = require('./permissions.js').permissionsReducer;
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`.
@@ -18,8 +19,9 @@ const sessionReducer = require('./session.js').sessionReducer;
module.exports = opts => {
opts = opts || {};
return combineReducers(defaults(opts, {
- session: sessionReducer,
+ messageCount: messageCountReducer,
+ navigation: navigationReducer,
permissions: permissionsReducer,
- messageCount: messageCountReducer
+ session: sessionReducer
}));
};
diff --git a/src/views/preview/preview.jsx b/src/views/preview/preview.jsx
index 5ee45f1a1..bbd7a9490 100644
--- a/src/views/preview/preview.jsx
+++ b/src/views/preview/preview.jsx
@@ -8,7 +8,6 @@ const connect = require('react-redux').connect;
const injectIntl = require('react-intl').injectIntl;
const parser = require('scratch-parser');
const Page = require('../../components/page/www/page.jsx');
-const api = require('../../lib/api');
const render = require('../../lib/render.jsx');
const storage = require('../../lib/storage.js').default;
const log = require('../../lib/log');
@@ -16,8 +15,12 @@ const EXTENSION_INFO = require('../../lib/extensions.js').default;
const PreviewPresentation = require('./presentation.jsx');
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 navigationActions = require('../../redux/navigation.js');
const previewActions = require('../../redux/preview.js');
const GUI = require('scratch-gui');
@@ -31,10 +34,7 @@ class Preview extends React.Component {
'handleToggleStudio',
'handleFavoriteToggle',
'handleLoadMore',
- // temporary, to pass to GUI. Remove when nav bar components are shared between www and gui.
- 'handleLogout',
'handleLoveToggle',
- 'handlePermissions',
'handlePopState',
'handleReportClick',
'handleReportClose',
@@ -45,9 +45,8 @@ class Preview extends React.Component {
'handleUpdateProjectTitle',
'handleUpdate',
'initCounts',
- 'isShared',
'pushHistory',
- 'userOwnsProject'
+ 'renderLogin'
]);
const pathname = window.location.pathname.toLowerCase();
const parts = pathname.split('/').filter(Boolean);
@@ -55,7 +54,6 @@ class Preview extends React.Component {
// parts[1]: either :id or 'editor'
// parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen'
this.state = {
- editable: false,
extensions: [],
favoriteCount: 0,
loveCount: 0,
@@ -90,7 +88,6 @@ class Preview extends React.Component {
if (this.props.projectInfo.id !== prevProps.projectInfo.id) {
this.getExtensions(this.state.projectId);
this.initCounts(this.props.projectInfo.stats.favorites, this.props.projectInfo.stats.loves);
- this.handlePermissions();
if (this.props.projectInfo.remix.parent !== null) {
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 () {
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 () {
this.props.setPlayer(false);
}
@@ -289,26 +265,21 @@ class Preview extends React.Component {
loveCount: loves
});
}
- isShared () {
+ renderLogin ({onClose}) {
return (
- // if we don't have projectInfo assume shared until we know otherwise
- Object.keys(this.props.projectInfo).length === 0 || (
- this.props.projectInfo.history &&
- this.props.projectInfo.history.shared.length > 0
- )
- );
- }
- isLoggedIn () {
- return (
- this.props.sessionStatus === sessionActions.Status.FETCHED &&
- Object.keys(this.props.user).length > 0
- );
- }
- userOwnsProject () {
- return (
- this.isLoggedIn() &&
- Object.keys(this.props.projectInfo).length > 0 &&
- this.props.user.id === this.props.projectInfo.author.id
+
{
+ this.props.handleLogIn(formData, result => {
+ if (result.success === true) {
+ onClose();
+ }
+ callback(result);
+ });
+ }}
+ /* eslint-ensable react/jsx-no-bind */
+ />
);
}
render () {
@@ -320,13 +291,13 @@ class Preview extends React.Component {
assetHost={this.props.assetHost}
backpackOptions={this.props.backpackOptions}
comments={this.props.comments}
- editable={this.state.editable}
+ editable={this.props.isEditable}
extensions={this.state.extensions}
faved={this.props.faved}
favoriteCount={this.state.favoriteCount}
isFullScreen={this.state.isFullScreen}
- isLoggedIn={this.isLoggedIn()}
- isShared={this.isShared()}
+ isLoggedIn={this.props.isLoggedIn}
+ isShared={this.props.isShared}
loveCount={this.state.loveCount}
loved={this.props.loved}
originalInfo={this.props.original}
@@ -339,7 +310,7 @@ class Preview extends React.Component {
replies={this.props.replies}
reportOpen={this.state.reportOpen}
studios={this.props.studios}
- userOwnsProject={this.userOwnsProject()}
+ userOwnsProject={this.props.userOwnsProject}
onAddToStudioClicked={this.handleAddToStudioClick}
onAddToStudioClosed={this.handleAddToStudioClose}
onFavoriteClicked={this.handleFavoriteToggle}
@@ -353,19 +324,27 @@ class Preview extends React.Component {
onUpdate={this.handleUpdate}
/>
:
-
+
+
+
+
+
+
);
}
}
@@ -388,6 +367,13 @@ Preview.propTypes = {
getProjectStudios: PropTypes.func.isRequired,
getRemixes: 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,
original: projectShape,
parent: projectShape,
@@ -415,7 +401,8 @@ Preview.propTypes = {
dateJoined: PropTypes.string,
email: PropTypes.string,
classroomId: PropTypes.string
- })
+ }),
+ userOwnsProject: PropTypes.bool
};
Preview.defaultProps = {
@@ -463,26 +450,64 @@ const consolidateStudiosInfo = (curatedStudios, projectStudios, currentStudioIds
return consolidatedStudios;
};
-const mapStateToProps = state => ({
- comments: state.preview.comments,
- faved: state.preview.faved,
- loved: state.preview.loved,
- original: state.preview.original,
- parent: state.preview.parent,
- remixes: state.preview.remixes,
- replies: state.preview.replies,
- sessionStatus: state.session.status,
- projectInfo: state.preview.projectInfo,
- projectStudios: state.preview.projectStudios,
- studios: consolidateStudiosInfo(state.preview.curatedStudios,
- state.preview.projectStudios, state.preview.currentStudioIds,
- state.preview.status.studioRequests),
- user: state.session.session.user,
- playerMode: state.scratchGui.mode.isPlayerOnly,
- fullScreen: state.scratchGui.mode.isFullScreen
-});
+const mapStateToProps = state => {
+ const projectInfoPresent = Object.keys(state.preview.projectInfo).length > 0;
+ const userPresent = state.session.session.user &&
+ Object.keys(state.session.session.user).length > 0;
+ const isLoggedIn = state.session.status === sessionActions.Status.FETCHED &&
+ userPresent;
+ const authorPresent = projectInfoPresent && state.preview.projectInfo.author &&
+ Object.keys(state.preview.projectInfo.author).length > 0;
+
+ return {
+ comments: state.preview.comments,
+ faved: state.preview.faved,
+ fullScreen: state.scratchGui.mode.isFullScreen,
+ // project is editable iff logged in user is the author of the project, or
+ // logged in user is an admin.
+ isEditable: isLoggedIn &&
+ ((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 => ({
+ 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 => {
dispatch(previewActions.getOriginalInfo(id));
},
@@ -523,9 +548,6 @@ const mapDispatchToProps = dispatch => ({
setLovedStatus: (loved, id, username, token) => {
dispatch(previewActions.setLovedStatus(loved, id, username, token));
},
- refreshSession: () => {
- dispatch(sessionActions.refreshSession());
- },
reportProject: (id, formData) => {
dispatch(previewActions.reportProject(id, formData));
},
diff --git a/src/views/search/search.jsx b/src/views/search/search.jsx
index 169afa9fa..01d092f6c 100644
--- a/src/views/search/search.jsx
+++ b/src/views/search/search.jsx
@@ -37,7 +37,7 @@ class Search extends React.Component {
this.state.mode = 'popular';
this.state.offset = 0;
this.state.loadMore = false;
-
+
let mode = '';
const query = window.location.search;
const m = query.lastIndexOf('mode=');
@@ -54,7 +54,7 @@ class Search extends React.Component {
if (ACCEPTABLE_MODES.indexOf(mode) !== -1) {
this.state.mode = mode;
}
-
+
}
componentDidMount () {
const query = window.location.search;
@@ -232,7 +232,7 @@ Search.propTypes = {
};
const mapStateToProps = state => ({
- searchTerm: state.navigation
+ searchTerm: state.navigation.searchTerm
});
const WrappedSearch = injectIntl(Search);
diff --git a/src/views/studentcompleteregistration/studentcompleteregistration.jsx b/src/views/studentcompleteregistration/studentcompleteregistration.jsx
index c5f3fc104..d62b47c7e 100644
--- a/src/views/studentcompleteregistration/studentcompleteregistration.jsx
+++ b/src/views/studentcompleteregistration/studentcompleteregistration.jsx
@@ -7,8 +7,8 @@ const React = require('react');
const api = require('../../lib/api');
const injectIntl = require('../../lib/intl.jsx').injectIntl;
const intlShape = require('../../lib/intl.jsx').intlShape;
-const log = require('../../lib/log.js');
const sessionStatus = require('../../redux/session').Status;
+const navigationActions = require('../../redux/navigation.js');
const Deck = require('../../components/deck/deck.jsx');
const Progression = require('../../components/progression/progression.jsx');
@@ -24,7 +24,6 @@ class StudentCompleteRegistration extends React.Component {
super(props);
bindAll(this, [
'handleAdvanceStep',
- 'handleLogOut',
'handleRegister',
'handleGoToClass'
]);
@@ -61,18 +60,6 @@ class StudentCompleteRegistration extends React.Component {
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) {
this.setState({waiting: true});
@@ -147,7 +134,7 @@ class StudentCompleteRegistration extends React.Component {
classroom={this.state.classroom}
studentUsername={this.props.studentUsername}
waiting={this.state.waiting}
- onHandleLogOut={this.handleLogOut}
+ onHandleLogOut={this.props.handleLogOut}
onNextStep={this.handleAdvanceStep}
/>
{this.props.must_reset_password ?
@@ -178,6 +165,7 @@ class StudentCompleteRegistration extends React.Component {
StudentCompleteRegistration.propTypes = {
classroomId: PropTypes.number.isRequired,
+ handleLogOut: PropTypes.func,
intl: intlShape,
must_reset_password: PropTypes.bool.isRequired,
newStudent: PropTypes.bool.isRequired,
@@ -199,6 +187,16 @@ const mapStateToProps = state => ({
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(, document.getElementById('app'));