Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/components/menu-bar/menu-bar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,6 @@ MenuBar.propTypes = {
onUpdateProjectTitle: PropTypes.func,
renderLogin: PropTypes.func,
sessionExists: PropTypes.bool,
startSaving: PropTypes.func,
username: PropTypes.string
};

Expand Down
87 changes: 59 additions & 28 deletions src/containers/gui.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import {compose} from 'redux';
import {connect} from 'react-redux';
import ReactModal from 'react-modal';
import VM from 'scratch-vm';
import {injectIntl, intlShape} from 'react-intl';

import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
import {openExtensionLibrary} from '../reducers/modals';
import {
getIsShowingProject
} from '../reducers/project-state';
import {setProjectTitle} from '../reducers/project-title';
import {
activateTab,
Expand All @@ -25,18 +29,29 @@ import ProjectFetcherHOC from '../lib/project-fetcher-hoc.jsx';
import ProjectSaverHOC from '../lib/project-saver-hoc.jsx';
import vmListenerHOC from '../lib/vm-listener-hoc.jsx';
import vmManagerHOC from '../lib/vm-manager-hoc.jsx';
import {defaultProjectTitleMessages} from '../reducers/project-title';

import GUIComponent from '../components/gui/gui.jsx';

class GUI extends React.Component {
componentDidMount () {
if (this.props.projectTitle) {
this.props.onUpdateReduxProjectTitle(this.props.projectTitle);
this.setReduxTitle(this.props.projectTitle);
}
componentDidUpdate (prevProps) {
if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) {
this.props.onUpdateProjectId(this.props.projectId);

This comment was marked as abuse.

}
if (this.props.projectTitle !== prevProps.projectTitle) {
this.setReduxTitle(this.props.projectTitle);
}
}
componentWillReceiveProps (nextProps) {
if (this.props.projectTitle !== nextProps.projectTitle) {
this.props.onUpdateReduxProjectTitle(nextProps.projectTitle);
setReduxTitle (newTitle) {

This comment was marked as abuse.

if (newTitle === null || typeof newTitle === 'undefined') {
this.props.onUpdateReduxProjectTitle(
this.props.intl.formatMessage(defaultProjectTitleMessages.defaultProjectTitle)
);
} else {
this.props.onUpdateReduxProjectTitle(newTitle);
}
}
render () {
Expand All @@ -50,8 +65,11 @@ class GUI extends React.Component {
errorMessage,
hideIntro,
loadingError,
isShowingProject,
onUpdateProjectId,
onUpdateReduxProjectTitle,
projectHost,
projectId,
projectTitle,
/* eslint-enable no-unused-vars */
children,
Expand All @@ -78,40 +96,53 @@ GUI.propTypes = {
fetchingProject: PropTypes.bool,
hideIntro: PropTypes.bool,
importInfoVisible: PropTypes.bool,
intl: intlShape,
isLoading: PropTypes.bool,
isShowingProject: PropTypes.bool,
loadingError: PropTypes.bool,
loadingStateVisible: PropTypes.bool,
onChangeProjectInfo: PropTypes.func,
onSeeCommunity: PropTypes.func,
onUpdateProjectId: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
onUpdateReduxProjectTitle: PropTypes.func,
previewInfoVisible: PropTypes.bool,
projectHost: PropTypes.string,
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
projectTitle: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired
};

const mapStateToProps = (state, ownProps) => ({
activeTabIndex: state.scratchGui.editorTab.activeTabIndex,
alertsVisible: state.scratchGui.alerts.visible,
backdropLibraryVisible: state.scratchGui.modals.backdropLibrary,
blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX,
cardsVisible: state.scratchGui.cards.visible,
costumeLibraryVisible: state.scratchGui.modals.costumeLibrary,
costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX,
importInfoVisible: state.scratchGui.modals.importInfo,
isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
isRtl: state.locales.isRtl,
loadingStateVisible: state.scratchGui.modals.loadingProject,
previewInfoVisible: state.scratchGui.modals.previewInfo && !ownProps.hideIntro,
targetIsStage: (
state.scratchGui.targets.stage &&
state.scratchGui.targets.stage.id === state.scratchGui.targets.editingTarget
),
soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX,
tipsLibraryVisible: state.scratchGui.modals.tipsLibrary,
vm: state.scratchGui.vm
});
GUI.defaultProps = {
onUpdateProjectId: () => {}
};

const mapStateToProps = (state, ownProps) => {
const loadingState = state.scratchGui.projectState.loadingState;
return {
activeTabIndex: state.scratchGui.editorTab.activeTabIndex,
alertsVisible: state.scratchGui.alerts.visible,
backdropLibraryVisible: state.scratchGui.modals.backdropLibrary,
blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX,
cardsVisible: state.scratchGui.cards.visible,
costumeLibraryVisible: state.scratchGui.modals.costumeLibrary,
costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX,
importInfoVisible: state.scratchGui.modals.importInfo,
isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
isRtl: state.locales.isRtl,
isShowingProject: getIsShowingProject(loadingState),
loadingStateVisible: state.scratchGui.modals.loadingProject,
previewInfoVisible: state.scratchGui.modals.previewInfo && !ownProps.hideIntro,
projectId: state.scratchGui.projectState.projectId,
targetIsStage: (
state.scratchGui.targets.stage &&
state.scratchGui.targets.stage.id === state.scratchGui.targets.editingTarget
),
soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX,
tipsLibraryVisible: state.scratchGui.modals.tipsLibrary,
vm: state.scratchGui.vm
};
};

const mapDispatchToProps = dispatch => ({
onExtensionButtonClick: () => dispatch(openExtensionLibrary()),
Expand All @@ -123,10 +154,10 @@ const mapDispatchToProps = dispatch => ({
onUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title))
});

const ConnectedGUI = connect(
const ConnectedGUI = injectIntl(connect(
mapStateToProps,
mapDispatchToProps,
)(GUI);
)(GUI));

// note that redux's 'compose' function is just being used as a general utility to make
// the hierarchy of HOC constructor calls clearer here; it has nothing to do with redux's
Expand Down
5 changes: 3 additions & 2 deletions src/containers/sb-file-uploader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class SBFileUploader extends React.Component {
}

SBFileUploader.propTypes = {
canSave: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types

This comment was marked as abuse.

children: PropTypes.func,
intl: intlShape.isRequired,
loadingState: PropTypes.oneOf(LoadingStates),
Expand All @@ -127,9 +128,9 @@ const mapStateToProps = state => ({
vm: state.scratchGui.vm
});

const mapDispatchToProps = dispatch => ({
const mapDispatchToProps = (dispatch, ownProps) => ({
onLoadingFinished: loadingState => {
dispatch(onLoadedProject(loadingState));
dispatch(onLoadedProject(loadingState, ownProps.canSave));
dispatch(closeLoadingProject());
},
onLoadingStarted: () => {
Expand Down
3 changes: 2 additions & 1 deletion src/lib/project-fetcher-hoc.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {connect} from 'react-redux';
import {
LoadingStates,
defaultProjectId,
onFetchedProjectData,
getIsFetchingWithId,
onFetchedProjectData,
setProjectId
} from '../reducers/project-state';

Expand Down Expand Up @@ -102,6 +102,7 @@ const ProjectFetcherHOC = function (WrappedComponent) {
}
ProjectFetcherComponent.propTypes = {
assetHost: PropTypes.string,
canSave: PropTypes.bool,
intl: intlShape.isRequired,
isFetchingWithId: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
Expand Down
43 changes: 39 additions & 4 deletions src/lib/project-saver-hoc.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import VM from 'scratch-vm';
import storage from '../lib/storage';
import {
LoadingStates,
createProject,
getIsCreating,
getIsShowingProject,
getIsShowingWithoutId,
getIsUpdating,
onCreated,
onUpdated,
onError
onError,
saveProject
} from '../reducers/project-state';

/**
Expand All @@ -27,7 +31,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
componentDidUpdate (prevProps) {
if (this.props.isUpdating && !prevProps.isUpdating) {
this.storeProject(this.props.reduxProjectId)
.then(() => { // eslint-disable-line no-unused-vars
.then(() => {
// there is nothing we expect to find in response that we need to check here
this.props.onUpdated(this.props.loadingState);
})
Expand All @@ -46,6 +50,19 @@ const ProjectSaverHOC = function (WrappedComponent) {
this.props.onError(`Creating a new project failed with error: ${err}`);
});
}
// if this is the first time we're able to create this project on the server, create it!
const showingCreateable = this.props.canSave && this.props.isShowingWithoutId;
const prevShowingCreateable = prevProps.canSave && prevProps.isShowingWithoutId;
if (showingCreateable && !prevShowingCreateable) {
this.props.createProject();
} else {
// if we're newly *able* to save this project, save it!
const showingSaveable = this.props.canSave && this.props.isShowingWithId;
const becameAbleToSave = this.props.canSave && !prevProps.canSave;
if (showingSaveable && becameAbleToSave) {
this.props.saveProject();
}
}
}
/**
* storeProject:
Expand All @@ -72,13 +89,17 @@ const ProjectSaverHOC = function (WrappedComponent) {
render () {
const {
/* eslint-disable no-unused-vars */
createProject: createProjectProp,
onCreated: onCreatedProp,
onUpdated: onUpdatedProp,
onError: onErrorProp,
isCreating: isCreatingProp,
isShowingWithId: isShowingWithIdProp,
isShowingWithoutId: isShowingWithoutIdProp,
isUpdating: isUpdatingProp,
loadingState,
reduxProjectId,
saveProject: saveProjectProp,
/* eslint-enable no-unused-vars */
...componentProps
} = this.props;
Expand All @@ -90,33 +111,47 @@ const ProjectSaverHOC = function (WrappedComponent) {
}
}
ProjectSaverComponent.propTypes = {
canSave: PropTypes.bool,
createProject: PropTypes.func,
isCreating: PropTypes.bool,
isShowingWithId: PropTypes.bool,
isShowingWithoutId: PropTypes.bool,
isUpdating: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
onCreated: PropTypes.func,
onError: PropTypes.func,
onUpdated: PropTypes.func,
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
saveProject: PropTypes.func,
vm: PropTypes.instanceOf(VM).isRequired
};
const mapStateToProps = state => {
const loadingState = state.scratchGui.projectState.loadingState;
return {
isCreating: getIsCreating(loadingState),
isShowingWithId: getIsShowingProject(loadingState),
isShowingWithoutId: getIsShowingWithoutId(loadingState),
isUpdating: getIsUpdating(loadingState),
loadingState: loadingState,
reduxProjectId: state.scratchGui.projectState.projectId,
vm: state.scratchGui.vm
};
};
const mapDispatchToProps = dispatch => ({
createProject: () => dispatch(createProject()),
onCreated: projectId => dispatch(onCreated(projectId)),
onUpdated: (projectId, loadingState) => dispatch(onUpdated(projectId, loadingState)),
onError: errStr => dispatch(onError(errStr))
onError: errStr => dispatch(onError(errStr)),
saveProject: () => dispatch(saveProject())
});
// Allow incoming props to override redux-provided props. Used to mock in tests.
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
{}, stateProps, dispatchProps, ownProps
);
return connect(
mapStateToProps,
mapDispatchToProps
mapDispatchToProps,
mergeProps
)(ProjectSaverComponent);
};

Expand Down
12 changes: 7 additions & 5 deletions src/lib/vm-manager-hoc.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ const vmManagerHOC = function (WrappedComponent) {
// and they weren't both that way until now... load project!
if (this.props.isLoadingWithId && this.props.fontsLoaded &&
(!prevProps.isLoadingWithId || !prevProps.fontsLoaded)) {
this.loadProject(this.props.projectData, this.props.loadingState);
this.loadProject();
}
}
loadProject (projectData, loadingState) {
return this.props.vm.loadProject(projectData)
loadProject () {
return this.props.vm.loadProject(this.props.projectData)
.then(() => {
this.props.onLoadedProject(loadingState);
this.props.onLoadedProject(this.props.loadingState, this.props.canSave);
})
.catch(e => {
// Need to catch this error and update component state so that
Expand Down Expand Up @@ -82,6 +82,7 @@ const vmManagerHOC = function (WrappedComponent) {
}

VMManager.propTypes = {
canSave: PropTypes.bool,
fontsLoaded: PropTypes.bool,
isLoadingWithId: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates),
Expand All @@ -102,7 +103,8 @@ const vmManagerHOC = function (WrappedComponent) {
};

const mapDispatchToProps = dispatch => ({
onLoadedProject: loadingState => dispatch(onLoadedProject(loadingState))
onLoadedProject: (loadingState, canSave) =>
dispatch(onLoadedProject(loadingState, canSave))
});

// Allow incoming props to override redux-provided props. Used to mock in tests.
Expand Down
Loading