Spaces:
Running
Running
import PropTypes from 'prop-types'; | |
import React from 'react'; | |
import {compose} from 'redux'; | |
import {connect} from 'react-redux'; | |
import ReactModal from 'react-modal'; | |
import VM from 'scratch-vm'; | |
import {injectIntl, intlShape} from 'react-intl'; | |
import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; | |
import { | |
getIsError, | |
getIsShowingProject | |
} from '../reducers/project-state'; | |
import { | |
activateTab, | |
BLOCKS_TAB_INDEX, | |
COSTUMES_TAB_INDEX, | |
SOUNDS_TAB_INDEX, | |
VARIABLES_TAB_INDEX, | |
FILES_TAB_INDEX | |
} from '../reducers/editor-tab'; | |
import { | |
closeCostumeLibrary, | |
closeBackdropLibrary, | |
closeTelemetryModal, | |
openExtensionLibrary | |
} from '../reducers/modals'; | |
import FontLoaderHOC from '../lib/font-loader-hoc.jsx'; | |
import LocalizationHOC from '../lib/localization-hoc.jsx'; | |
import SBFileUploaderHOC from '../lib/sb-file-uploader-hoc.jsx'; | |
import ProjectFetcherHOC from '../lib/project-fetcher-hoc.jsx'; | |
import TitledHOC from '../lib/titled-hoc.jsx'; | |
import ProjectSaverHOC from '../lib/project-saver-hoc.jsx'; | |
import QueryParserHOC from '../lib/query-parser-hoc.jsx'; | |
import storage from '../lib/storage'; | |
import vmListenerHOC from '../lib/vm-listener-hoc.jsx'; | |
import vmManagerHOC from '../lib/vm-manager-hoc.jsx'; | |
import cloudManagerHOC from '../lib/cloud-manager-hoc.jsx'; | |
import TWFullScreenResizerHOC from '../lib/tw-fullscreen-resizer-hoc.jsx'; | |
import GUIComponent from '../components/gui/gui.jsx'; | |
import HomeCommunication from './home-communication.jsx'; | |
import {setIsScratchDesktop} from '../lib/isScratchDesktop.js'; | |
class GUI extends React.Component { | |
componentDidMount () { | |
setIsScratchDesktop(this.props.isScratchDesktop); | |
this.props.onStorageInit(storage); | |
this.props.onVmInit(this.props.vm); | |
} | |
componentDidUpdate (prevProps) { | |
if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) { | |
this.props.onUpdateProjectId(this.props.projectId); | |
} | |
if (this.props.isShowingProject && !prevProps.isShowingProject) { | |
// this only notifies container when a project changes from not yet loaded to loaded | |
// At this time the project view in www doesn't need to know when a project is unloaded | |
this.props.onProjectLoaded(); | |
} | |
} | |
render () { | |
if (this.props.isError) { | |
console.log('the below error was caught by the gui'); | |
throw this.props.error; | |
} | |
const { | |
/* eslint-disable no-unused-vars */ | |
assetHost, | |
cloudHost, | |
error, | |
isError, | |
isScratchDesktop, | |
isShowingProject, | |
onProjectLoaded, | |
onStorageInit, | |
onUpdateProjectId, | |
onVmInit, | |
projectHost, | |
projectId, | |
/* eslint-enable no-unused-vars */ | |
children, | |
fetchingProject, | |
isLoading, | |
loadingStateVisible, | |
isPlayground, | |
...componentProps | |
} = this.props; | |
return (<> | |
<GUIComponent | |
loading={fetchingProject || isLoading || loadingStateVisible} | |
isPlayground={isPlayground} | |
{...componentProps} | |
> | |
{children} | |
</GUIComponent> | |
<HomeCommunication | |
projectId={projectId} | |
isPlayground={isPlayground} | |
/> | |
</>); | |
} | |
} | |
GUI.propTypes = { | |
assetHost: PropTypes.string, | |
children: PropTypes.node, | |
cloudHost: PropTypes.string, | |
error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), | |
fetchingProject: PropTypes.bool, | |
intl: intlShape, | |
isError: PropTypes.bool, | |
isEmbedded: PropTypes.bool, | |
isFullScreen: PropTypes.bool, | |
isLoading: PropTypes.bool, | |
isScratchDesktop: PropTypes.bool, | |
isShowingProject: PropTypes.bool, | |
isPlayground: PropTypes.bool, | |
loadingStateVisible: PropTypes.bool, | |
onProjectLoaded: PropTypes.func, | |
onSeeCommunity: PropTypes.func, | |
onStorageInit: PropTypes.func, | |
onUpdateProjectId: PropTypes.func, | |
onVmInit: PropTypes.func, | |
projectHost: PropTypes.string, | |
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | |
telemetryModalVisible: PropTypes.bool, | |
vm: PropTypes.instanceOf(VM).isRequired | |
}; | |
GUI.defaultProps = { | |
isScratchDesktop: false, | |
isPlayground: false, | |
onStorageInit: storageInstance => storageInstance.addOfficialScratchWebStores(), | |
onProjectLoaded: () => {}, | |
onUpdateProjectId: () => {}, | |
onVmInit: (/* vm */) => {} | |
}; | |
const mapStateToProps = state => { | |
const loadingState = state.scratchGui.projectState.loadingState; | |
return { | |
activeTabIndex: state.scratchGui.editorTab.activeTabIndex, | |
alertsVisible: state.scratchGui.alerts.visible, | |
backdropLibraryVisible: state.scratchGui.modals.backdropLibrary, | |
blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX, | |
cardsVisible: state.scratchGui.cards.visible, | |
connectionModalVisible: state.scratchGui.modals.connectionModal, | |
costumeLibraryVisible: state.scratchGui.modals.costumeLibrary, | |
costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX, | |
error: state.scratchGui.projectState.error, | |
isError: getIsError(loadingState), | |
isEmbedded: state.scratchGui.mode.isEmbedded, | |
isFullScreen: state.scratchGui.mode.isFullScreen || state.scratchGui.mode.isEmbedded, | |
isPlayerOnly: state.scratchGui.mode.isPlayerOnly, | |
isRtl: state.locales.isRtl, | |
isShowingProject: getIsShowingProject(loadingState), | |
loadingStateVisible: state.scratchGui.modals.loadingProject, | |
projectId: state.scratchGui.projectState.projectId, | |
soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX, | |
variablesTabVisible: state.scratchGui.editorTab.activeTabIndex === VARIABLES_TAB_INDEX, | |
filesTabVisible: state.scratchGui.editorTab.activeTabIndex === FILES_TAB_INDEX, | |
targetIsStage: ( | |
state.scratchGui.targets.stage && | |
state.scratchGui.targets.stage.id === state.scratchGui.targets.editingTarget | |
), | |
telemetryModalVisible: state.scratchGui.modals.telemetryModal, | |
tipsLibraryVisible: state.scratchGui.modals.tipsLibrary, | |
usernameModalVisible: state.scratchGui.modals.usernameModal, | |
settingsModalVisible: state.scratchGui.modals.settingsModal, | |
customExtensionModalVisible: state.scratchGui.modals.customExtensionModal, | |
fontsModalVisible: state.scratchGui.modals.fontsModal, | |
vm: state.scratchGui.vm | |
}; | |
}; | |
const mapDispatchToProps = dispatch => ({ | |
onExtensionButtonClick: () => dispatch(openExtensionLibrary()), | |
onActivateTab: tab => dispatch(activateTab(tab)), | |
onActivateCostumesTab: () => dispatch(activateTab(COSTUMES_TAB_INDEX)), | |
onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)), | |
onActivateVariablesTab: () => dispatch(activateTab(VARIABLES_TAB_INDEX)), | |
onActivateFilesTab: () => dispatch(activateTab(FILES_TAB_INDEX)), | |
onRequestCloseBackdropLibrary: () => dispatch(closeBackdropLibrary()), | |
onRequestCloseCostumeLibrary: () => dispatch(closeCostumeLibrary()), | |
onRequestCloseTelemetryModal: () => dispatch(closeTelemetryModal()) | |
}); | |
const ConnectedGUI = injectIntl(connect( | |
mapStateToProps, | |
mapDispatchToProps, | |
)(GUI)); | |
// note that redux's 'compose' function is just being used as a general utility to make | |
// the hierarchy of HOC constructor calls clearer here; it has nothing to do with redux's | |
// ability to compose reducers. | |
const WrappedGui = compose( | |
LocalizationHOC, | |
ErrorBoundaryHOC('Top Level App'), | |
FontLoaderHOC, | |
// QueryParserHOC, // tw: HOC is unused | |
ProjectFetcherHOC, | |
TitledHOC, | |
ProjectSaverHOC, | |
vmListenerHOC, | |
vmManagerHOC, | |
SBFileUploaderHOC, | |
cloudManagerHOC, | |
TWFullScreenResizerHOC | |
)(ConnectedGUI); | |
WrappedGui.setAppElement = ReactModal.setAppElement; | |
export default WrappedGui; | |