diff --git a/src/components/browser-modal/browser-modal.jsx b/src/components/browser-modal/browser-modal.jsx
index 0a271ea7a78..9afac0e3891 100644
--- a/src/components/browser-modal/browser-modal.jsx
+++ b/src/components/browser-modal/browser-modal.jsx
@@ -84,4 +84,8 @@ BrowserModal.propTypes = {
onBack: PropTypes.func.isRequired
};
-export default injectIntl(BrowserModal);
+const WrappedBrowserModal = injectIntl(BrowserModal);
+
+WrappedBrowserModal.setAppElement = ReactModal.setAppElement;
+
+export default WrappedBrowserModal;
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 96fea86537f..71c62313509 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -62,7 +62,6 @@ const GUIComponent = props => {
costumeLibraryVisible,
costumesTabVisible,
enableCommunity,
- hideIntro,
importInfoVisible,
intl,
isPlayerOnly,
@@ -116,7 +115,7 @@ const GUIComponent = props => {
{...componentProps}
>
{previewInfoVisible ? (
-
+
) : null}
{loading ? (
@@ -282,7 +281,6 @@ GUIComponent.propTypes = {
costumeLibraryVisible: PropTypes.bool,
costumesTabVisible: PropTypes.bool,
enableCommunity: PropTypes.bool,
- hideIntro: PropTypes.bool,
importInfoVisible: PropTypes.bool,
intl: intlShape.isRequired,
isPlayerOnly: PropTypes.bool,
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index 6e5d96070b7..f08e6bab746 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -72,10 +72,15 @@ class GUI extends React.Component {
`Failed to load project from server [id=${window.location.hash}]: ${this.state.errorMessage}`);
}
const {
+ /* eslint-disable no-unused-vars */
+ assetHost,
+ hideIntro,
+ projectData,
+ projectHost,
+ /* eslint-enable no-unused-vars */
children,
fetchingProject,
loadingStateVisible,
- projectData, // eslint-disable-line no-unused-vars
vm,
...componentProps
} = this.props;
@@ -94,6 +99,7 @@ class GUI extends React.Component {
GUI.propTypes = {
children: PropTypes.node,
fetchingProject: PropTypes.bool,
+ hideIntro: PropTypes.bool,
importInfoVisible: PropTypes.bool,
loadingStateVisible: PropTypes.bool,
onSeeCommunity: PropTypes.func,
@@ -102,7 +108,7 @@ GUI.propTypes = {
vm: PropTypes.instanceOf(VM)
};
-const mapStateToProps = state => ({
+const mapStateToProps = (state, ownProps) => ({
activeTabIndex: state.scratchGui.editorTab.activeTabIndex,
backdropLibraryVisible: state.scratchGui.modals.backdropLibrary,
blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX,
@@ -113,7 +119,7 @@ const mapStateToProps = state => ({
isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
isRtl: state.locales.isRtl,
loadingStateVisible: state.scratchGui.modals.loadingProject,
- previewInfoVisible: state.scratchGui.modals.previewInfo,
+ previewInfoVisible: state.scratchGui.modals.previewInfo && !ownProps.hideIntro,
targetIsStage: (
state.scratchGui.targets.stage &&
state.scratchGui.targets.stage.id === state.scratchGui.targets.editingTarget
diff --git a/src/containers/preview-modal.jsx b/src/containers/preview-modal.jsx
index 7145cfa6acb..47b3cc3115e 100644
--- a/src/containers/preview-modal.jsx
+++ b/src/containers/preview-modal.jsx
@@ -6,8 +6,6 @@ import {connect} from 'react-redux';
import tabletFullScreen from '../lib/tablet-full-screen';
import PreviewModalComponent from '../components/preview-modal/preview-modal.jsx';
-import BrowserModalComponent from '../components/browser-modal/browser-modal.jsx';
-import supportedBrowser from '../lib/supported-browser';
import {
closePreviewInfo,
@@ -27,25 +25,6 @@ class PreviewModal extends React.Component {
previewing: false
};
}
-
- /**
- * Conditionally returns an intro modal depending on the hideIntro prop
- * @returns { React.Component | null } null if hideIntro is true, the intro modal component otherwise
- */
- introIfShown () {
- if (this.props.hideIntro) {
- return null; // If hideIntro is true, the intro modal should not appear
- }
-
- // otherwise, show the intro modal
- return ();
- }
handleTryIt () {
this.setState({previewing: true});
// try to run in fullscreen mode on tablets.
@@ -59,18 +38,19 @@ class PreviewModal extends React.Component {
this.props.onViewProject();
}
render () {
- return (supportedBrowser() ?
- this.introIfShown() :
-
);
}
}
PreviewModal.propTypes = {
- hideIntro: PropTypes.bool,
isRtl: PropTypes.bool,
onTryIt: PropTypes.func,
onViewProject: PropTypes.func
diff --git a/src/lib/app-state-hoc.jsx b/src/lib/app-state-hoc.jsx
index 2f70b44d251..fe7ac023eab 100644
--- a/src/lib/app-state-hoc.jsx
+++ b/src/lib/app-state-hoc.jsx
@@ -4,7 +4,6 @@ import {Provider} from 'react-redux';
import {createStore, combineReducers, compose} from 'redux';
import ConnectedIntlProvider from './connected-intl-provider.jsx';
-import guiReducer, {guiInitialState, guiMiddleware, initFullScreen, initPlayer} from '../reducers/gui';
import localesReducer, {initLocale, localesInitialState} from '../reducers/locales';
import {setPlayer, setFullScreen} from '../reducers/mode.js';
@@ -12,49 +11,76 @@ import {setPlayer, setFullScreen} from '../reducers/mode.js';
import locales from 'scratch-l10n';
import {detectLocale} from './detect-locale';
-import {ScratchPaintReducer} from 'scratch-paint';
-
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
-const enhancer = composeEnhancers(guiMiddleware);
/*
* Higher Order Component to provide redux state. If an `intl` prop is provided
* it will override the internal `intl` redux state
* @param {React.Component} WrappedComponent - component to provide state for
+ * @param {boolean} localesOnly - only provide the locale state, not everything
+ * required by the GUI. Used to exclude excess state when
+ only rendering modals, not the GUI.
* @returns {React.Component} component with redux and intl state provided
*/
-const AppStateHOC = function (WrappedComponent) {
+const AppStateHOC = function (WrappedComponent, localesOnly) {
class AppStateWrapper extends React.Component {
constructor (props) {
super(props);
- let initializedGui = guiInitialState;
- if (props.isFullScreen) {
- initializedGui = initFullScreen(initializedGui);
- }
- if (props.isPlayerOnly) {
- initializedGui = initPlayer(initializedGui);
- }
+ let initialState = {};
+ let reducers = {};
+ let enhancer;
let initializedLocales = localesInitialState;
const locale = detectLocale(Object.keys(locales));
if (locale !== 'en') {
initializedLocales = initLocale(initializedLocales, locale);
}
+ if (localesOnly) {
+ // Used for instantiating minimal state for the unsupported
+ // browser modal
+ reducers = {locales: localesReducer};
+ initialState = {locales: initializedLocales};
+ enhancer = composeEnhancers();
+ } else {
+ // You are right, this is gross. But it's necessary to avoid
+ // importing unneeded code that will crash unsupported browsers.
+ const guiRedux = require('../reducers/gui');
+ const guiReducer = guiRedux.default;
+ const {
+ guiInitialState,
+ guiMiddleware,
+ initFullScreen,
+ initPlayer
+ } = guiRedux;
+ const {ScratchPaintReducer} = require('scratch-paint');
- const reducer = combineReducers({
- locales: localesReducer,
- scratchGui: guiReducer,
- scratchPaint: ScratchPaintReducer
- });
- this.store = createStore(
- reducer,
- {
+ let initializedGui = guiInitialState;
+ if (props.isFullScreen) {
+ initializedGui = initFullScreen(initializedGui);
+ }
+ if (props.isPlayerOnly) {
+ initializedGui = initPlayer(initializedGui);
+ }
+ reducers = {
+ locales: localesReducer,
+ scratchGui: guiReducer,
+ scratchPaint: ScratchPaintReducer
+ };
+ initialState = {
locales: initializedLocales,
scratchGui: initializedGui
- },
- enhancer);
+ };
+ enhancer = composeEnhancers(guiMiddleware);
+ }
+ const reducer = combineReducers(reducers);
+ this.store = createStore(
+ reducer,
+ initialState,
+ enhancer
+ );
}
componentDidUpdate (prevProps) {
+ if (localesOnly) return;
if (prevProps.isPlayerOnly !== this.props.isPlayerOnly) {
this.store.dispatch(setPlayer(this.props.isPlayerOnly));
}
diff --git a/src/playground/index.jsx b/src/playground/index.jsx
index da2be7a2783..ba0ada4da78 100644
--- a/src/playground/index.jsx
+++ b/src/playground/index.jsx
@@ -7,17 +7,12 @@ import React from 'react';
import ReactDOM from 'react-dom';
import analytics from '../lib/analytics';
-import GUI from '../containers/gui.jsx';
-import HashParserHOC from '../lib/hash-parser-hoc.jsx';
import AppStateHOC from '../lib/app-state-hoc.jsx';
+import BrowserModalComponent from '../components/browser-modal/browser-modal.jsx';
+import supportedBrowser from '../lib/supported-browser';
import styles from './index.css';
-if (process.env.NODE_ENV === 'production' && typeof window === 'object') {
- // Warn before navigating away
- window.onbeforeunload = () => true;
-}
-
// Register "base" page view
analytics.pageview('/');
@@ -25,16 +20,15 @@ const appTarget = document.createElement('div');
appTarget.className = styles.app;
document.body.appendChild(appTarget);
-GUI.setAppElement(appTarget);
-const WrappedGui = HashParserHOC(AppStateHOC(GUI));
-
-// TODO a hack for testing the backpack, allow backpack host to be set by url param
-const backpackHostMatches = window.location.href.match(/[?&]backpack_host=([^&]*)&?/);
-const backpackHost = backpackHostMatches ? backpackHostMatches[1] : null;
-
-const backpackOptions = {
- visible: true,
- host: backpackHost
-};
-
-ReactDOM.render(, appTarget);
+if (supportedBrowser()) {
+ // require needed here to avoid importing unsupported browser-crashing code
+ // at the top level
+ require('./render-gui.jsx').default(appTarget);
+
+} else {
+ BrowserModalComponent.setAppElement(appTarget);
+ const WrappedBrowserModalComponent = AppStateHOC(BrowserModalComponent, true /* localesOnly */);
+ const handleBack = () => {};
+ // eslint-disable-next-line react/jsx-no-bind
+ ReactDOM.render(, appTarget);
+}
diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx
new file mode 100644
index 00000000000..adec922b8c2
--- /dev/null
+++ b/src/playground/render-gui.jsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import AppStateHOC from '../lib/app-state-hoc.jsx';
+import GUI from '../containers/gui.jsx';
+import HashParserHOC from '../lib/hash-parser-hoc.jsx';
+
+/*
+ * Render the GUI playground. This is a separate function because importing anything
+ * that instantiates the VM causes unsupported browsers to crash
+ * {object} appTarget - the DOM element to render to
+ */
+export default appTarget => {
+ GUI.setAppElement(appTarget);
+ const WrappedGui = HashParserHOC(AppStateHOC(GUI));
+
+ // TODO a hack for testing the backpack, allow backpack host to be set by url param
+ const backpackHostMatches = window.location.href.match(/[?&]backpack_host=([^&]*)&?/);
+ const backpackHost = backpackHostMatches ? backpackHostMatches[1] : null;
+
+ const backpackOptions = {
+ visible: true,
+ host: backpackHost
+ };
+ if (process.env.NODE_ENV === 'production' && typeof window === 'object') {
+ // Warn before navigating away
+ window.onbeforeunload = () => true;
+ }
+
+ ReactDOM.render(, appTarget);
+};