diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 06ac902e6..95996e139 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -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} { 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, diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index dda83cd37..87af589dd 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -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 { {/* )} */} - {(downloadProject, updateProject) => ( + {(downloadProject, updateProject) => ( this.props.canUpdateProject && this.props.userOwnsProject ? ( {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, diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index b13ae7d0b..8eefddd0d 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -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 }; diff --git a/src/containers/project-loader.jsx b/src/containers/project-loader.jsx index ff8377030..1e8efb6fc 100644 --- a/src/containers/project-loader.jsx +++ b/src/containers/project-loader.jsx @@ -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()); } }); diff --git a/src/containers/project-saver.jsx b/src/containers/project-saver.jsx index f49647383..f67137018 100644 --- a/src/containers/project-saver.jsx +++ b/src/containers/project-saver.jsx @@ -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, diff --git a/src/lib/hash-parser-hoc.jsx b/src/lib/hash-parser-hoc.jsx index 7768e2d81..ad02e8c03 100644 --- a/src/lib/hash-parser-hoc.jsx +++ b/src/lib/hash-parser-hoc.jsx @@ -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 ( ); } } 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 diff --git a/src/lib/project-loader-hoc.jsx b/src/lib/project-loader-hoc.jsx index f1350ad07..31d0ce208 100644 --- a/src/lib/project-loader-hoc.jsx +++ b/src/lib/project-loader-hoc.jsx @@ -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 ( ); @@ -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( diff --git a/src/lib/project-metadata-hoc.jsx b/src/lib/project-metadata-hoc.jsx index 4216eb53b..17b215eba 100644 --- a/src/lib/project-metadata-hoc.jsx +++ b/src/lib/project-metadata-hoc.jsx @@ -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 ( ); @@ -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 }; diff --git a/src/lib/project-saver-hoc.jsx b/src/lib/project-saver-hoc.jsx index 50f9707d9..9fbcca748 100644 --- a/src/lib/project-saver-hoc.jsx +++ b/src/lib/project-saver-hoc.jsx @@ -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 ( ); @@ -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( diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx index e3d78262e..53b545a72 100644 --- a/src/lib/titled-hoc.jsx +++ b/src/lib/titled-hoc.jsx @@ -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 ( ); } diff --git a/src/lib/vm-listener-hoc.jsx b/src/lib/vm-listener-hoc.jsx index 6b8d17777..6ca0bb018 100644 --- a/src/lib/vm-listener-hoc.jsx +++ b/src/lib/vm-listener-hoc.jsx @@ -101,9 +101,9 @@ const vmListenerHOC = function (WrappedComponent) { onTurboModeOn, onShowAlert, /* eslint-enable no-unused-vars */ - ...props + ...componentProps } = this.props; - return ; + return ; } } VMListener.propTypes = { diff --git a/src/lib/vm-manager-hoc.jsx b/src/lib/vm-manager-hoc.jsx index 197badcec..cbe9d97fe 100644 --- a/src/lib/vm-manager-hoc.jsx +++ b/src/lib/vm-manager-hoc.jsx @@ -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 ( ); @@ -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, diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx index 3dc3a52f7..1bb4ead50 100644 --- a/src/playground/render-gui.jsx +++ b/src/playground/render-gui.jsx @@ -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); diff --git a/src/reducers/project-id.js b/src/reducers/project-id.js index 7aaab7761..e62dcd74a 100644 --- a/src/reducers/project-id.js +++ b/src/reducers/project-id.js @@ -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 }; diff --git a/src/reducers/vm.js b/src/reducers/vm.js index 6132f0e0d..0ed085444 100644 --- a/src/reducers/vm.js +++ b/src/reducers/vm.js @@ -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 };