8 files changed
+117
-78
No files matched your search
+4
-4
@@ -87,9 +87,9 @@
|
||||
"react-redux": "5.0.7",
|
||||
"react-responsive": "5.0.0",
|
||||
"react-style-proptype": "3.2.1",
|
||||
"react-tabs": "2.2.2",
|
||||
"react-tabs": "2.3.0",
|
||||
"react-test-renderer": "16.2.0",
|
||||
"react-tooltip": "3.6.1",
|
||||
"react-tooltip": "3.8.0",
|
||||
"react-virtualized": "9.20.1",
|
||||
"redux": "3.7.2",
|
||||
"redux-mock-store": "^1.2.3",
|
||||
@@ -98,11 +98,11 @@
|
||||
"scratch-audio": "0.1.0-prerelease.20180625202813",
|
||||
"scratch-blocks": "0.1.0-prerelease.1535662135",
|
||||
"scratch-l10n": "3.0.20180830210150",
|
||||
"scratch-paint": "0.2.0-prerelease.20180831175055",
|
||||
"scratch-paint": "0.2.0-prerelease.20180905220723",
|
||||
"scratch-render": "0.1.0-prerelease.20180824141819",
|
||||
"scratch-storage": "1.0.2",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20180817005452",
|
||||
"scratch-vm": "0.2.0-prerelease.20180830155110",
|
||||
"scratch-vm": "0.2.0-prerelease.20180906133035",
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"startaudiocontext": "1.2.1",
|
||||
"style-loader": "^0.23.0",
|
||||
|
||||
@@ -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;
|
||||
@@ -62,7 +62,6 @@ const GUIComponent = props => {
|
||||
costumeLibraryVisible,
|
||||
costumesTabVisible,
|
||||
enableCommunity,
|
||||
hideIntro,
|
||||
importInfoVisible,
|
||||
intl,
|
||||
isPlayerOnly,
|
||||
@@ -116,7 +115,7 @@ const GUIComponent = props => {
|
||||
{...componentProps}
|
||||
>
|
||||
{previewInfoVisible ? (
|
||||
<PreviewModal hideIntro={hideIntro} />
|
||||
<PreviewModal />
|
||||
) : null}
|
||||
{loading ? (
|
||||
<Loader />
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 (<PreviewModalComponent
|
||||
isRtl={this.props.isRtl}
|
||||
previewing={this.state.previewing}
|
||||
onCancel={this.handleCancel}
|
||||
onTryIt={this.handleTryIt}
|
||||
onViewProject={this.handleViewProject}
|
||||
/>);
|
||||
}
|
||||
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() :
|
||||
<BrowserModalComponent
|
||||
return (
|
||||
<PreviewModalComponent
|
||||
isRtl={this.props.isRtl}
|
||||
onBack={this.handleCancel}
|
||||
previewing={this.state.previewing}
|
||||
onCancel={this.handleCancel}
|
||||
onTryIt={this.handleTryIt}
|
||||
onViewProject={this.handleViewProject}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PreviewModal.propTypes = {
|
||||
hideIntro: PropTypes.bool,
|
||||
isRtl: PropTypes.bool,
|
||||
onTryIt: PropTypes.func,
|
||||
onViewProject: PropTypes.func
|
||||
|
||||
+48
-22
@@ -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));
|
||||
}
|
||||
|
||||
+13
-19
@@ -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));
|
||||
if (supportedBrowser()) {
|
||||
// require needed here to avoid importing unsupported browser-crashing code
|
||||
// at the top level
|
||||
require('./render-gui.jsx').default(appTarget);
|
||||
|
||||
// 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(<WrappedGui backpackOptions={backpackOptions} />, appTarget);
|
||||
} else {
|
||||
BrowserModalComponent.setAppElement(appTarget);
|
||||
const WrappedBrowserModalComponent = AppStateHOC(BrowserModalComponent, true /* localesOnly */);
|
||||
const handleBack = () => {};
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
ReactDOM.render(<WrappedBrowserModalComponent onBack={handleBack} />, appTarget);
|
||||
}
|
||||
@@ -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(<WrappedGui backpackOptions={backpackOptions} />, appTarget);
|
||||
};
|
||||
Reference in new issue
Block a user