state machine working well
This commit is contained in:
1 parent
676b3fc278
commit
e70d047258
15 files changed
+227
-123
No files matched your search
@@ -64,6 +64,7 @@ const GUIComponent = props => {
|
||||
children,
|
||||
costumeLibraryVisible,
|
||||
costumesTabVisible,
|
||||
doStoreProject,
|
||||
enableCommunity,
|
||||
importInfoVisible,
|
||||
intl,
|
||||
@@ -83,7 +84,7 @@ const GUIComponent = props => {
|
||||
onExtensionButtonClick,
|
||||
onRequestCloseBackdropLibrary,
|
||||
onRequestCloseCostumeLibrary,
|
||||
onRequestNewProject,
|
||||
// onRequestNewProject,
|
||||
onSeeCommunity,
|
||||
onShare,
|
||||
previewInfoVisible,
|
||||
@@ -166,6 +167,7 @@ const GUIComponent = props => {
|
||||
) : null}
|
||||
<MenuBar
|
||||
accountNavOpen={accountNavOpen}
|
||||
doStoreProject={doStoreProject}
|
||||
enableCommunity={enableCommunity}
|
||||
renderLogin={renderLogin}
|
||||
userOwnsProject={userOwnsProject}
|
||||
@@ -173,7 +175,7 @@ const GUIComponent = props => {
|
||||
onCloseAccountNav={onCloseAccountNav}
|
||||
onLogOut={onLogOut}
|
||||
onOpenRegistration={onOpenRegistration}
|
||||
onRequestNewProject={onRequestNewProject}
|
||||
// onRequestNewProject={onRequestNewProject}
|
||||
onSeeCommunity={onSeeCommunity}
|
||||
onShare={onShare}
|
||||
onToggleLoginOpen={onToggleLoginOpen}
|
||||
@@ -312,6 +314,7 @@ GUIComponent.propTypes = {
|
||||
children: PropTypes.node,
|
||||
costumeLibraryVisible: PropTypes.bool,
|
||||
costumesTabVisible: PropTypes.bool,
|
||||
doStoreProject: PropTypes.func,
|
||||
enableCommunity: PropTypes.bool,
|
||||
importInfoVisible: PropTypes.bool,
|
||||
intl: intlShape.isRequired,
|
||||
@@ -328,7 +331,7 @@ GUIComponent.propTypes = {
|
||||
onOpenRegistration: PropTypes.func,
|
||||
onRequestCloseBackdropLibrary: PropTypes.func,
|
||||
onRequestCloseCostumeLibrary: PropTypes.func,
|
||||
onRequestNewProject: PropTypes.func,
|
||||
// onRequestNewProject: PropTypes.func,
|
||||
onSeeCommunity: PropTypes.func,
|
||||
onShare: PropTypes.func,
|
||||
onTabSelect: PropTypes.func,
|
||||
|
||||
@@ -125,6 +125,7 @@ class MenuBar extends React.Component {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleLanguageMouseUp',
|
||||
'handleNewProject',
|
||||
'handleRestoreOption',
|
||||
'handleProjectLoadFinished',
|
||||
'handleCloseFileMenuAndThen',
|
||||
@@ -320,7 +321,9 @@ class MenuBar extends React.Component {
|
||||
{/* )}
|
||||
</ProjectSaver> */}
|
||||
<MenuSection>
|
||||
<ProjectSaver>{(downloadProject, updateProject) => (
|
||||
<ProjectSaver
|
||||
doStoreProject={this.props.doStoreProject}
|
||||
>{(downloadProject, updateProject) => (
|
||||
this.props.canUpdateProject && this.props.userOwnsProject ? (
|
||||
<MenuItem onClick={this.handleUpdateProject(updateProject)}>
|
||||
{saveNowMessage}
|
||||
@@ -638,6 +641,7 @@ class MenuBar extends React.Component {
|
||||
MenuBar.propTypes = {
|
||||
accountMenuOpen: PropTypes.bool,
|
||||
canUpdateProject: PropTypes.bool,
|
||||
doStoreProject: PropTypes.func,
|
||||
editMenuOpen: PropTypes.bool,
|
||||
enableCommunity: PropTypes.bool,
|
||||
fileMenuOpen: PropTypes.bool,
|
||||
@@ -660,7 +664,7 @@ MenuBar.propTypes = {
|
||||
onRequestCloseFile: PropTypes.func,
|
||||
onRequestCloseLanguage: PropTypes.func,
|
||||
onRequestCloseLogin: PropTypes.func,
|
||||
onRequestNewProject: PropTypes.func,
|
||||
// onRequestNewProject: PropTypes.func,
|
||||
onSeeCommunity: PropTypes.func,
|
||||
onToggleLoginOpen: PropTypes.func,
|
||||
onUpdateProjectTitle: PropTypes.func,
|
||||
|
||||
@@ -27,9 +27,9 @@ import vmManagerHOC from '../lib/vm-manager-hoc.jsx';
|
||||
import GUIComponent from '../components/gui/gui.jsx';
|
||||
|
||||
class GUI extends React.Component {
|
||||
// constructor (props) {
|
||||
// super(props);
|
||||
// }
|
||||
constructor (props) {
|
||||
super(props);
|
||||
}
|
||||
componentDidMount () {
|
||||
if (this.props.projectTitle) {
|
||||
this.props.onUpdateReduxProjectTitle(this.props.projectTitle);
|
||||
@@ -50,15 +50,15 @@ class GUI extends React.Component {
|
||||
assetHost,
|
||||
errorMessage,
|
||||
hideIntro,
|
||||
loadingError,
|
||||
onUpdateReduxProjectTitle,
|
||||
projectData,
|
||||
// projectData,
|
||||
projectHost,
|
||||
projectTitle,
|
||||
/* eslint-enable no-unused-vars */
|
||||
children,
|
||||
fetchingProject,
|
||||
isLoading,
|
||||
loadingError,
|
||||
loadingStateVisible,
|
||||
...componentProps
|
||||
} = this.props;
|
||||
@@ -87,7 +87,7 @@ GUI.propTypes = {
|
||||
onUpdateProjectTitle: PropTypes.func,
|
||||
onUpdateReduxProjectTitle: PropTypes.func,
|
||||
previewInfoVisible: PropTypes.bool,
|
||||
projectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
// projectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
projectHost: PropTypes.string,
|
||||
projectTitle: PropTypes.string
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
|
||||
import analytics from '../lib/analytics';
|
||||
import log from '../lib/log';
|
||||
import {setProjectTitle} from '../reducers/project-title';
|
||||
import {startLoadingFileUpload, doneLoadingFileUpload} from '../reducers/project-id';
|
||||
import {startFetchingFileUpload, doneLoadingFileUpload} from '../reducers/project-id';
|
||||
|
||||
import {
|
||||
openLoadingProject,
|
||||
@@ -144,7 +144,7 @@ const mapDispatchToProps = dispatch => ({
|
||||
onSetReduxProjectTitle: title => dispatch(setProjectTitle(title)),
|
||||
openLoadingState: () => {
|
||||
dispatch(openLoadingProject());
|
||||
dispatch(startLoadingFileUpload());
|
||||
dispatch(startFetchingFileUpload());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {connect} from 'react-redux';
|
||||
// import storage from '../lib/storage';
|
||||
import {projectTitleInitialState} from '../reducers/project-title';
|
||||
import {ProjectState} from '../reducers/project-id';
|
||||
import {doStoreProject} from '../reducers/vm';
|
||||
// import {doStoreProject} from '../reducers/vm';
|
||||
|
||||
|
||||
/**
|
||||
@@ -73,13 +73,13 @@ class ProjectSaver extends React.Component {
|
||||
// }
|
||||
createProject () {
|
||||
if (this.props.isShowingWithId) {
|
||||
return doStoreProject(null, this.props.onSaveFinished);
|
||||
return this.props.doStoreProject(null, this.props.onSaveFinished);
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
updateProject () {
|
||||
if (this.props.isShowingWithId) {
|
||||
return doStoreProject(this.props.projectId, this.props.onSaveFinished);
|
||||
return this.props.doStoreProject(this.props.projectId, this.props.onSaveFinished);
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
@@ -105,6 +105,7 @@ const getProjectFilename = (curTitle, defaultTitle) => {
|
||||
|
||||
ProjectSaver.propTypes = {
|
||||
children: PropTypes.func,
|
||||
doStoreProject: PropTypes.func,
|
||||
isShowingWithId: PropTypes.bool,
|
||||
onSaveFinished: PropTypes.func,
|
||||
projectFilename: PropTypes.string,
|
||||
|
||||
+18
-10
@@ -7,15 +7,14 @@ import {ProjectState, defaultProjectId, setHashProjectId} from '../reducers/proj
|
||||
|
||||
/* Higher Order Component to get the project id from location.hash
|
||||
* @param {React.Component} WrappedComponent component to receive projectData prop
|
||||
* @returns {React.Component} component with project loading behavior
|
||||
* @returns {React.Component} component with project fetching behavior
|
||||
*/
|
||||
const HashParserHOC = function (WrappedComponent) {
|
||||
class HashParserComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleHashChange',
|
||||
'handleRequestNewProject'
|
||||
'handleHashChange'
|
||||
]);
|
||||
}
|
||||
componentDidMount () {
|
||||
@@ -23,8 +22,8 @@ const HashParserHOC = function (WrappedComponent) {
|
||||
this.handleHashChange();
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
// if we are newly loading a non-hash project...
|
||||
if (nextProps.isLoadingNonHashProject && !this.props.isLoadingNonHashProject) {
|
||||
// if we are newly fetching a non-hash project...
|
||||
if (nextProps.isFetchingNonHashProject && !this.props.isFetchingNonHashProject) {
|
||||
// ...clear the hash from the url
|
||||
history.pushState('new-project', 'new-project',
|
||||
window.location.pathname + window.location.search);
|
||||
@@ -50,32 +49,41 @@ const HashParserHOC = function (WrappedComponent) {
|
||||
// });
|
||||
// }
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
isFetchingNonHashProject,
|
||||
reduxProjectId,
|
||||
setHashProjectId: setHashProjectIdProp,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return (
|
||||
<WrappedComponent
|
||||
hideIntro={this.state.projectId && this.state.projectId !== 0}
|
||||
// projectId={this.state.projectId}
|
||||
onRequestNewProject={this.handleRequestNewProject}
|
||||
{...this.props}
|
||||
// onRequestNewProject={this.handleRequestNewProject}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
HashParserComponent.propTypes = {
|
||||
isLoadingNonHashProject: PropTypes.bool,
|
||||
isFetchingNonHashProject: PropTypes.bool,
|
||||
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
setHashProjectId: PropTypes.func
|
||||
};
|
||||
const mapStateToProps = state => {
|
||||
const projectState = state.scratchGui.projectId.projectState;
|
||||
return {
|
||||
isLoadingNonHashProject: projectState === ProjectState.LOADING_FILE_UPLOAD ||
|
||||
projectState === ProjectState.LOADING_NEW_DEFAULT,
|
||||
isFetchingNonHashProject: projectState === ProjectState.FETCHING_FILE_UPLOAD ||
|
||||
projectState === ProjectState.FETCHING_NEW_DEFAULT,
|
||||
reduxProjectId: state.scratchGui.projectId.projectId
|
||||
};
|
||||
};
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setHashProjectId: projectId => dispatch(setHashProjectId(projectId))
|
||||
});
|
||||
// return HashParserComponent;
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
|
||||
@@ -4,7 +4,7 @@ import {intlShape, injectIntl} from 'react-intl';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {ProjectState, defaultProjectId, setInitialProjectId} from '../reducers/project-id';
|
||||
import {ProjectState, defaultProjectId, fetchedProjectData, setInitialProjectId} from '../reducers/project-id';
|
||||
|
||||
import analytics from './analytics';
|
||||
import log from './log';
|
||||
@@ -15,16 +15,16 @@ import storage from './storage';
|
||||
* @param {React.Component} WrappedComponent component to receive projectData prop
|
||||
* @returns {React.Component} component with project loading behavior
|
||||
*/
|
||||
// NOTE: rename to project fetcher ?
|
||||
const ProjectLoaderHOC = function (WrappedComponent) {
|
||||
class ProjectLoaderComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleRequestNewProject',
|
||||
'updateProject'
|
||||
'fetchProject'
|
||||
]);
|
||||
this.state = {
|
||||
projectData: null,
|
||||
// projectData: null,
|
||||
fetchingProject: false
|
||||
};
|
||||
storage.setProjectHost(props.projectHost);
|
||||
@@ -53,8 +53,8 @@ const ProjectLoaderHOC = function (WrappedComponent) {
|
||||
if (this.props.assetHost !== nextProps.assetHost) {
|
||||
storage.setAssetHost(nextProps.assetHost);
|
||||
}
|
||||
if (nextProps.isLoadingProjectWithId && !this.props.isLoadingProjectWithId) {
|
||||
this.updateProject(nextProps.reduxProjectId);
|
||||
if (nextProps.isFetchingProjectWithId && !this.props.isFetchingProjectWithId) {
|
||||
this.fetchProject(nextProps.reduxProjectId);
|
||||
}
|
||||
// if (this.props.projectId !== nextProps.projectId) {
|
||||
// this.setState({fetchingProject: true}, () => {
|
||||
@@ -75,14 +75,16 @@ const ProjectLoaderHOC = function (WrappedComponent) {
|
||||
// }
|
||||
// NOTE: should we instesad have updateProject return a promise, and resolve those
|
||||
// in the functions that call updatePromise to improve the execution sequence?
|
||||
updateProject (projectId) {
|
||||
fetchProject (projectId) {
|
||||
return storage
|
||||
.load(storage.AssetType.Project, projectId, storage.DataFormat.JSON)
|
||||
.then(projectAsset => {
|
||||
if (projectAsset) {
|
||||
this.setState({
|
||||
projectData: projectAsset.data,
|
||||
// projectData: projectAsset.data,
|
||||
fetchingProject: false
|
||||
}, () => {
|
||||
this.props.fetchedProjectData(projectAsset.data);
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -103,18 +105,22 @@ const ProjectLoaderHOC = function (WrappedComponent) {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
assetHost,
|
||||
onRequestNewProject,
|
||||
fetchedProjectData: fetchedProjectDataProp,
|
||||
intl,
|
||||
isFetchingProjectWithId,
|
||||
// onRequestNewProject,
|
||||
projectHost,
|
||||
projectId,
|
||||
reduxProjectId,
|
||||
setInitialProjectId: setInitialProjectIdProp,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...componentProps
|
||||
} = this.props;
|
||||
if (!this.state.projectData) return null;
|
||||
return (
|
||||
<WrappedComponent
|
||||
fetchingProject={this.state.fetchingProject}
|
||||
projectData={this.state.projectData}
|
||||
onRequestNewProject={this.handleRequestNewProject}
|
||||
// projectData={this.state.projectData}
|
||||
// onRequestNewProject={this.handleRequestNewProject}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
@@ -122,9 +128,10 @@ const ProjectLoaderHOC = function (WrappedComponent) {
|
||||
}
|
||||
ProjectLoaderComponent.propTypes = {
|
||||
assetHost: PropTypes.string,
|
||||
fetchedProjectData: PropTypes.func,
|
||||
intl: intlShape.isRequired,
|
||||
isLoadingProjectWithId: PropTypes.bool,
|
||||
onRequestNewProject: PropTypes.func,
|
||||
isFetchingProjectWithId: PropTypes.bool,
|
||||
// onRequestNewProject: PropTypes.func,
|
||||
projectHost: PropTypes.string,
|
||||
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
@@ -141,12 +148,14 @@ const ProjectLoaderHOC = function (WrappedComponent) {
|
||||
const mapStateToProps = state => {
|
||||
const projectState = state.scratchGui.projectId.projectState;
|
||||
return {
|
||||
isLoadingProjectWithId: projectState === ProjectState.LOADING_WITH_ID ||
|
||||
projectState === ProjectState.LOADING_NEW_DEFAULT,
|
||||
isFetchingProjectWithId: projectState === ProjectState.FETCHING_WITH_ID ||
|
||||
projectState === ProjectState.FETCHING_NEW_DEFAULT,
|
||||
reduxProjectId: state.scratchGui.projectId.projectId
|
||||
};
|
||||
};
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
// NOTE: should I rename props like this so there's no name conflict? Like 'handle...'
|
||||
fetchedProjectData: data => dispatch(fetchedProjectData(data)),
|
||||
setInitialProjectId: projectId => dispatch(setInitialProjectId(projectId))
|
||||
});
|
||||
return injectIntl(connect(
|
||||
|
||||
@@ -45,13 +45,12 @@ const ProjectMetaDataHOC = function (WrappedComponent) {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleRequestNewProject',
|
||||
'updateProjectMetaData'
|
||||
]);
|
||||
this.updateProjectMetaData(props.projectId);
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.isLoadingProjectWithId && !this.props.isLoadingProjectWithId) {
|
||||
if (nextProps.isFetchingProjectWithId && !this.props.isFetchingProjectWithId) {
|
||||
this.updateProjectMetaData(nextProps.projectId);
|
||||
}
|
||||
// // if projectId has changed, and not just from one default value to
|
||||
@@ -96,13 +95,16 @@ const ProjectMetaDataHOC = function (WrappedComponent) {
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
// don't pass down parent's onRequestNewProject
|
||||
onRequestNewProject, // eslint-disable-line no-unused-vars
|
||||
/* eslint-disable no-unused-vars */
|
||||
intl,
|
||||
isFetchingProjectWithId,
|
||||
projectId,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return (
|
||||
<WrappedComponent
|
||||
onRequestNewProject={this.handleRequestNewProject}
|
||||
// onRequestNewProject={this.handleRequestNewProject}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
@@ -111,8 +113,7 @@ const ProjectMetaDataHOC = function (WrappedComponent) {
|
||||
|
||||
ProjectMetaDataComponent.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
isLoadingProjectWithId: PropTypes.bool,
|
||||
onRequestNewProject: PropTypes.func,
|
||||
isFetchingProjectWithId: PropTypes.bool,
|
||||
onUpdateProjectTitle: PropTypes.func,
|
||||
projectId: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
|
||||
// token: PropTypes.string
|
||||
@@ -121,8 +122,8 @@ const ProjectMetaDataHOC = function (WrappedComponent) {
|
||||
const mapStateToProps = state => {
|
||||
const projectState = state.scratchGui.projectId.projectState;
|
||||
return {
|
||||
isLoadingProjectWithId: projectState === ProjectState.LOADING_WITH_ID ||
|
||||
projectState === ProjectState.LOADING_NEW_DEFAULT,
|
||||
isFetchingProjectWithId: projectState === ProjectState.FETCHING_WITH_ID ||
|
||||
projectState === ProjectState.FETCHING_NEW_DEFAULT,
|
||||
projectId: state.scratchGui.projectId.projectId
|
||||
// token: state.session.session && state.session.session.user && state.session.session.user.token
|
||||
};
|
||||
|
||||
@@ -4,9 +4,11 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import {connect} from 'react-redux';
|
||||
import VM from 'scratch-vm';
|
||||
|
||||
import storage from '../lib/storage';
|
||||
import {ProjectState, doneSavingWithId} from '../reducers/project-id';
|
||||
import {doStoreProject} from '../reducers/vm';
|
||||
// import {doStoreProject} from '../reducers/vm';
|
||||
// import storage from './storage';
|
||||
|
||||
/* Higher Order Component to provide behavior for saving projects.
|
||||
@@ -16,6 +18,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'doStoreProject'
|
||||
]);
|
||||
this.state = {
|
||||
};
|
||||
@@ -23,18 +26,45 @@ const ProjectSaverHOC = function (WrappedComponent) {
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.isSavingWithId && !this.props.isSavingWithId) {
|
||||
this.doStoreProject(nextProps.reduxProjectId).then(() => {
|
||||
this.doneSavingWithId();
|
||||
this.props.doneSavingWithId();
|
||||
});
|
||||
}
|
||||
}
|
||||
doStoreProject (id, onSaveFinished) {
|
||||
// NOTE: temporarily just pretend saving worked
|
||||
if (1===1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this.props.vm.saveProjectSb3()
|
||||
.then(content => {
|
||||
if (onSaveFinished) {
|
||||
onSaveFinished();
|
||||
}
|
||||
const assetType = storage.AssetType.Project;
|
||||
const dataFormat = storage.DataFormat.SB3;
|
||||
const body = new FormData();
|
||||
body.append('sb3_file', content, 'sb3_file');
|
||||
return storage.store(
|
||||
assetType,
|
||||
dataFormat,
|
||||
body,
|
||||
id
|
||||
);
|
||||
});
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
// doStoreProject: doStoreProjectProp,
|
||||
doneSavingWithId: doneSavingWithIdProp,
|
||||
isSavingWithId,
|
||||
reduxProjectId,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return (
|
||||
<WrappedComponent
|
||||
doStoreProject={this.doStoreProject}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
@@ -44,19 +74,20 @@ const ProjectSaverHOC = function (WrappedComponent) {
|
||||
doStoreProject: PropTypes.func,
|
||||
doneSavingWithId: PropTypes.func,
|
||||
isSavingWithId: PropTypes.bool,
|
||||
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
||||
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const projectState = state.scratchGui.projectId.projectState;
|
||||
return {
|
||||
isSavingWithId: projectState === ProjectState.SAVING_WITH_ID ||
|
||||
projectState === ProjectState.SAVING_WITH_ID_BEFORE_NEW,
|
||||
reduxProjectId: state.scratchGui.projectId.projectId
|
||||
reduxProjectId: state.scratchGui.projectId.projectId,
|
||||
vm: state.scratchGui.vm
|
||||
};
|
||||
};
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
doStoreProject: projectId => dispatch(doStoreProject(projectId)),
|
||||
// doStoreProject: projectId => doStoreProject(projectId),
|
||||
doneSavingWithId: projectId => dispatch(doneSavingWithId(projectId))
|
||||
});
|
||||
return connect(
|
||||
|
||||
@@ -19,14 +19,22 @@ const TitledHOC = function (WrappedComponent) {
|
||||
};
|
||||
}
|
||||
handleUpdateProjectTitle (newTitle) {
|
||||
// NOTE: this seems to be called twice each time you change the title, resulting from
|
||||
// two handleKeyPress calls in BufferedInput. Might want to investigate why.
|
||||
this.setState({projectTitle: newTitle});
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
intl,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return (
|
||||
<WrappedComponent
|
||||
projectTitle={this.state.projectTitle}
|
||||
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
||||
{...this.props}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,9 +101,9 @@ const vmListenerHOC = function (WrappedComponent) {
|
||||
onTurboModeOn,
|
||||
onShowAlert,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...props
|
||||
...componentProps
|
||||
} = this.props;
|
||||
return <WrappedComponent {...props} />;
|
||||
return <WrappedComponent {...componentProps} />;
|
||||
}
|
||||
}
|
||||
VMListener.propTypes = {
|
||||
|
||||
+27
-15
@@ -6,7 +6,7 @@ import {connect} from 'react-redux';
|
||||
import VM from 'scratch-vm';
|
||||
import AudioEngine from 'scratch-audio';
|
||||
|
||||
import {ProjectState} from '../reducers/project-id';
|
||||
import {ProjectState, doneLoading} from '../reducers/project-id';
|
||||
|
||||
/*
|
||||
* Higher Order Component to manage events emitted by the VM
|
||||
@@ -18,7 +18,6 @@ const vmManagerHOC = function (WrappedComponent) {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleRequestNewProject',
|
||||
'loadProject'
|
||||
]);
|
||||
this.state = {
|
||||
@@ -31,13 +30,10 @@ const vmManagerHOC = function (WrappedComponent) {
|
||||
if (this.props.vm.initialized) return;
|
||||
this.audioEngine = new AudioEngine();
|
||||
this.props.vm.attachAudioEngine(this.audioEngine);
|
||||
this.loadProject(true).then(() => {
|
||||
this.props.vm.initialized = true;
|
||||
});
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.isLoadingProjectWithId && !this.props.isLoadingProjectWithId) {
|
||||
this.loadProject(false);
|
||||
this.loadProject(nextProps.projectData);
|
||||
}
|
||||
// if (this.props.projectData !== nextProps.projectData) {
|
||||
// this.setState({isLoading: true}, () => {
|
||||
@@ -47,14 +43,22 @@ const vmManagerHOC = function (WrappedComponent) {
|
||||
}
|
||||
// NOTE: keep working here. problem is that we ONLY want to load the project
|
||||
// here when the old project id was also default... otherwise, we load twice!!!
|
||||
loadProject (isInitial) {
|
||||
return this.props.vm.loadProject(this.props.projectData)
|
||||
loadProject (projectData) {
|
||||
return this.props.vm.loadProject(projectData)
|
||||
.then(() => {
|
||||
this.setState({isLoading: false}, () => {
|
||||
if (isInitial) {
|
||||
if (!this.props.vm.initialized) {
|
||||
this.props.vm.setCompatibilityMode(true);
|
||||
this.props.vm.start();
|
||||
// NOTE: this logic is slightly different from previous
|
||||
// https://github.com/LLK/scratch-gui/blob/develop/src/containers/gui.jsx
|
||||
// Here, we wait until this point to set initialized to true;
|
||||
// previously, we set it to true immediately on initiating vm.loadProject,
|
||||
// and we relied on an explicit initial call in componentDidMount
|
||||
// to setCompatibilityMode and start().
|
||||
this.props.vm.initialized = true;
|
||||
}
|
||||
this.props.doneLoading();
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
@@ -77,19 +81,24 @@ const vmManagerHOC = function (WrappedComponent) {
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
doneLoading,
|
||||
isLoadingProjectWithId,
|
||||
projectData,
|
||||
onRequestNewProject, // must remove parent's onRequestNewProject to use our own
|
||||
projectId,
|
||||
// onRequestNewProject, // must remove parent's onRequestNewProject to use our own
|
||||
/* eslint-enable no-unused-vars */
|
||||
vm,
|
||||
...componentProps
|
||||
} = this.props;
|
||||
// don't display anything until we have data loaded
|
||||
if (!this.props.projectData) return null;
|
||||
return (
|
||||
<WrappedComponent
|
||||
errorMessage={this.state.errorMessage}
|
||||
isLoading={this.state.isLoading}
|
||||
loadingError={this.state.loadingError}
|
||||
vm={vm}
|
||||
onRequestNewProject={this.handleRequestNewProject}
|
||||
// onRequestNewProject={this.handleRequestNewProject}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
@@ -97,8 +106,8 @@ const vmManagerHOC = function (WrappedComponent) {
|
||||
}
|
||||
|
||||
VMManager.propTypes = {
|
||||
doneLoading: PropTypes.func,
|
||||
isLoadingProjectWithId: PropTypes.bool,
|
||||
onRequestNewProject: PropTypes.func,
|
||||
projectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
@@ -107,14 +116,17 @@ const vmManagerHOC = function (WrappedComponent) {
|
||||
const mapStateToProps = state => {
|
||||
const projectState = state.scratchGui.projectId.projectState;
|
||||
return {
|
||||
isLoadingProjectWithId: projectState === ProjectState.LOADING_WITH_ID ||
|
||||
projectState === ProjectState.LOADING_NEW_DEFAULT,
|
||||
isLoadingProjectWithId: projectState === ProjectState.LOADING_VM_WITH_ID ||
|
||||
projectState === ProjectState.LOADING_VM_NEW_DEFAULT,
|
||||
projectData: state.scratchGui.projectId.projectData,
|
||||
projectId: state.scratchGui.projectId.projectId,
|
||||
vm: state.scratchGui.vm
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = () => ({});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
doneLoading: () => dispatch(doneLoading())
|
||||
});
|
||||
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
|
||||
@@ -16,8 +16,8 @@ import ProjectMetaDataHOC from '../lib/project-metadata-hoc.jsx';
|
||||
export default appTarget => {
|
||||
GUI.setAppElement(appTarget);
|
||||
const WrappedGui = compose(
|
||||
HashParserHOC,
|
||||
AppStateHOC,
|
||||
HashParserHOC, // NOTE: IMPORTANT: ok that i've reordered this?
|
||||
TitledHOC,
|
||||
ProjectMetaDataHOC
|
||||
)(GUI);
|
||||
|
||||
+74
-26
@@ -8,9 +8,13 @@ const defaultProjectId = 0; // hardcoded id of default project
|
||||
const ProjectState = keyMirror({
|
||||
NOT_LOADED: null,
|
||||
ERROR: null,
|
||||
LOADING_WITH_ID: null,
|
||||
LOADING_FILE_UPLOAD: null,
|
||||
LOADING_NEW_DEFAULT: null,
|
||||
ANY: null,
|
||||
FETCHING_WITH_ID: null,
|
||||
FETCHING_FILE_UPLOAD: null,
|
||||
FETCHING_NEW_DEFAULT: null,
|
||||
LOADING_VM_WITH_ID: null,
|
||||
LOADING_VM_FILE_UPLOAD: null,
|
||||
LOADING_VM_NEW_DEFAULT: null,
|
||||
SHOWING_WITH_ID: null,
|
||||
SHOWING_FILE_UPLOAD: null,
|
||||
SHOWING_NEW_DEFAULT: null,
|
||||
@@ -19,6 +23,7 @@ const ProjectState = keyMirror({
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
projectData: null,
|
||||
projectId: null,
|
||||
projectState: ProjectState.NOT_LOADED
|
||||
};
|
||||
@@ -33,26 +38,45 @@ const reducer = function (state, action) {
|
||||
case TRANSITION_STATE:
|
||||
// "from" state must be either an array that includes current project state,
|
||||
// or null/undefined
|
||||
if (state.projectState in action.transitions) {
|
||||
const resultState = action.transitions[state.projectState];
|
||||
switch (resultState) {
|
||||
case ProjectState.LOADING_WITH_ID:
|
||||
if (state.projectState in action.transitions || ProjectState.ANY in action.transitions) {
|
||||
// const resultState = action.transitions[state.projectState];
|
||||
switch (action.transitions[state.projectState] ?
|
||||
action.transitions[state.projectState] : action.transitions[ProjectState.ANY]) {
|
||||
case ProjectState.FETCHING_WITH_ID:
|
||||
return Object.assign({}, state, {
|
||||
projectId: action.id,
|
||||
projectState: resultState
|
||||
projectState: ProjectState.FETCHING_WITH_ID
|
||||
});
|
||||
case ProjectState.LOADING_FILE_UPLOAD:
|
||||
case ProjectState.FETCHING_FILE_UPLOAD:
|
||||
// goes straight to LOADING_VM_FILE_UPLOAD
|
||||
return Object.assign({}, state, {
|
||||
projectId: null,
|
||||
projectState: resultState
|
||||
projectState: ProjectState.LOADING_VM_FILE_UPLOAD
|
||||
});
|
||||
case ProjectState.LOADING_NEW_DEFAULT:
|
||||
case ProjectState.FETCHING_NEW_DEFAULT:
|
||||
return Object.assign({}, state, {
|
||||
projectId: defaultProjectId,
|
||||
projectState: resultState
|
||||
projectState: ProjectState.FETCHING_NEW_DEFAULT
|
||||
});
|
||||
case ProjectState.LOADING_VM_WITH_ID:
|
||||
return Object.assign({}, state, {
|
||||
projectData: action.data,
|
||||
projectState: ProjectState.LOADING_VM_WITH_ID
|
||||
});
|
||||
case ProjectState.LOADING_VM_FILE_UPLOAD:
|
||||
// goes straight to LOADING_VM_FILE_UPLOAD
|
||||
return Object.assign({}, state, {
|
||||
projectData: action.data,
|
||||
projectState: ProjectState.LOADING_VM_FILE_UPLOAD
|
||||
});
|
||||
case ProjectState.LOADING_VM_NEW_DEFAULT:
|
||||
return Object.assign({}, state, {
|
||||
projectData: action.data,
|
||||
projectState: ProjectState.LOADING_VM_NEW_DEFAULT
|
||||
});
|
||||
default:
|
||||
return Object.assign({}, state, {projectState: resultState});
|
||||
return Object.assign({}, state, {projectState: (action.transitions[state.projectState] ?
|
||||
action.transitions[state.projectState] : action.transitions[ProjectState.ANY])});
|
||||
}
|
||||
}
|
||||
// default to requiring transitions to successfully match current state
|
||||
@@ -78,7 +102,7 @@ const setProjectId = function (id) {
|
||||
const setInitialProjectId = id => ({
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.NOT_LOADED]: ProjectState.LOADING_WITH_ID
|
||||
[ProjectState.NOT_LOADED]: ProjectState.FETCHING_WITH_ID
|
||||
},
|
||||
id: id
|
||||
});
|
||||
@@ -90,7 +114,9 @@ const setHashProjectId = id => {
|
||||
return {
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.NOT_LOADED]: ProjectState.LOADING_NEW_DEFAULT
|
||||
// NOTE: add other states like show -> loading_new, check that they work and
|
||||
// hash reaction to new or upload doesn't cause this to fire
|
||||
[ProjectState.NOT_LOADED]: ProjectState.FETCHING_NEW_DEFAULT
|
||||
},
|
||||
require: false
|
||||
};
|
||||
@@ -98,26 +124,46 @@ const setHashProjectId = id => {
|
||||
return {
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.NOT_LOADED]: ProjectState.LOADING_WITH_ID
|
||||
[ProjectState.ANY]: ProjectState.FETCHING_WITH_ID
|
||||
},
|
||||
id: id
|
||||
};
|
||||
};
|
||||
|
||||
const startLoadingFileUpload = () => ({
|
||||
const fetchedProjectData = data => ({
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.NOT_LOADED]: ProjectState.LOADING_FILE_UPLOAD,
|
||||
[ProjectState.SHOWING_WITH_ID]: ProjectState.LOADING_FILE_UPLOAD,
|
||||
[ProjectState.SHOWING_FILE_UPLOAD]: ProjectState.LOADING_FILE_UPLOAD,
|
||||
[ProjectState.SHOWING_NEW_DEFAULT]: ProjectState.LOADING_FILE_UPLOAD
|
||||
[ProjectState.FETCHING_WITH_ID]: ProjectState.LOADING_VM_WITH_ID,
|
||||
[ProjectState.FETCHING_FILE_UPLOAD]: ProjectState.LOADING_VM_FILE_UPLOAD,
|
||||
[ProjectState.FETCHING_NEW_DEFAULT]: ProjectState.LOADING_VM_NEW_DEFAULT
|
||||
},
|
||||
data: data
|
||||
});
|
||||
|
||||
|
||||
const startFetchingFileUpload = () => ({
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.NOT_LOADED]: ProjectState.FETCHING_FILE_UPLOAD,
|
||||
[ProjectState.SHOWING_WITH_ID]: ProjectState.FETCHING_FILE_UPLOAD,
|
||||
[ProjectState.SHOWING_FILE_UPLOAD]: ProjectState.FETCHING_FILE_UPLOAD,
|
||||
[ProjectState.SHOWING_NEW_DEFAULT]: ProjectState.FETCHING_FILE_UPLOAD
|
||||
}
|
||||
});
|
||||
|
||||
const doneLoadingFileUpload = () => ({
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.LOADING_FILE_UPLOAD]: ProjectState.SHOWING_FILE_UPLOAD
|
||||
[ProjectState.LOADING_VM_FILE_UPLOAD]: ProjectState.SHOWING_FILE_UPLOAD
|
||||
}
|
||||
});
|
||||
|
||||
const doneLoading = () => ({
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.LOADING_VM_WITH_ID]: ProjectState.SHOWING_WITH_ID,
|
||||
[ProjectState.LOADING_VM_FILE_UPLOAD]: ProjectState.SHOWING_FILE_UPLOAD,
|
||||
[ProjectState.LOADING_VM_NEW_DEFAULT]: ProjectState.SHOWING_NEW_DEFAULT
|
||||
}
|
||||
});
|
||||
|
||||
@@ -125,8 +171,8 @@ const stepTowardsNewProject = () => ({
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.SHOWING_WITH_ID]: ProjectState.SAVING_WITH_ID_BEFORE_NEW,
|
||||
[ProjectState.SHOWING_FILE_UPLOAD]: ProjectState.LOADING_NEW_DEFAULT,
|
||||
[ProjectState.SHOWING_NEW_DEFAULT]: ProjectState.LOADING_NEW_DEFAULT
|
||||
[ProjectState.SHOWING_FILE_UPLOAD]: ProjectState.FETCHING_NEW_DEFAULT,
|
||||
[ProjectState.SHOWING_NEW_DEFAULT]: ProjectState.FETCHING_NEW_DEFAULT
|
||||
}
|
||||
});
|
||||
|
||||
@@ -134,7 +180,7 @@ const doneSavingWithId = () => ({
|
||||
type: TRANSITION_STATE,
|
||||
transitions: {
|
||||
[ProjectState.SAVING_WITH_ID]: ProjectState.SHOWING_WITH_ID,
|
||||
[ProjectState.SAVING_WITH_ID_BEFORE_NEW]: ProjectState.LOADING_NEW_DEFAULT
|
||||
[ProjectState.SAVING_WITH_ID_BEFORE_NEW]: ProjectState.FETCHING_NEW_DEFAULT
|
||||
}
|
||||
});
|
||||
|
||||
@@ -143,11 +189,13 @@ export {
|
||||
initialState as projectIdInitialState,
|
||||
ProjectState,
|
||||
defaultProjectId,
|
||||
fetchedProjectData,
|
||||
doneLoading,
|
||||
doneLoadingFileUpload,
|
||||
doneSavingWithId,
|
||||
setHashProjectId,
|
||||
setInitialProjectId,
|
||||
setProjectId,
|
||||
startLoadingFileUpload,
|
||||
startFetchingFileUpload,
|
||||
stepTowardsNewProject
|
||||
};
|
||||
@@ -22,29 +22,8 @@ const setVM = function (vm) {
|
||||
};
|
||||
};
|
||||
|
||||
const doStoreProject = (id, onSaveFinished) => (
|
||||
reducer.saveProjectSb3()
|
||||
.then(content => {
|
||||
if (onSaveFinished) {
|
||||
onSaveFinished();
|
||||
}
|
||||
const assetType = storage.AssetType.Project;
|
||||
const dataFormat = storage.DataFormat.SB3;
|
||||
const body = new FormData();
|
||||
body.append('sb3_file', content, 'sb3_file');
|
||||
return storage.store(
|
||||
assetType,
|
||||
dataFormat,
|
||||
body,
|
||||
id
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
export {
|
||||
reducer as default,
|
||||
initialState as vmInitialState,
|
||||
doStoreProject,
|
||||
setVM
|
||||
};
|
||||
Reference in new issue
Block a user