state machine working well

This commit is contained in:
Ben Wheeler committed 2018-10-08 18:02:56 -04:00
1 parent 676b3fc278
commit e70d047258
15 files changed
+227 -123

No files matched your search

+6 -3
View File
@@ -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,
+6 -2
View File
@@ -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,
+6 -6
View File
@@ -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
};
+2 -2
View File
@@ -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());
}
});
+4 -3
View File
@@ -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
View File
@@ -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
+25 -16
View File
@@ -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(
+10 -9
View File
@@ -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
};
+37 -6
View File
@@ -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(
+9 -1
View File
@@ -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}
/>
);
}
+2 -2
View File
@@ -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
View File
@@ -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,
+1 -1
View File
@@ -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
View File
@@ -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
};
-21
View File
@@ -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
};