soiz1's picture
Upload 1525 files
f2bee8a verified
import classNames from 'classnames';
import omit from 'lodash.omit';
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import Draggable from "react-draggable";
import {ContextMenuTrigger} from 'react-contextmenu';
import {BorderedMenuItem, ContextMenu, DangerousMenuItem, MenuItem} from '../context-menu/context-menu.jsx';
import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl';
import {connect} from 'react-redux';
import MediaQuery from 'react-responsive';
import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
import tabStyles from 'react-tabs/style/react-tabs.css';
import VM from 'scratch-vm';
import Renderer from 'scratch-render';
import Blocks from '../../containers/blocks.jsx';
import CostumeTab from '../../containers/costume-tab.jsx';
import TargetPane from '../../containers/target-pane.jsx';
import SoundTab from '../../containers/sound-tab.jsx';
import VariablesTab from '../../containers/variables-tab.jsx';
import FilesTab from '../../containers/files-tab.jsx';
import StageWrapper from '../../containers/stage-wrapper.jsx';
import Loader from '../loader/loader.jsx';
import Box from '../box/box.jsx';
import MenuBar from '../menu-bar/menu-bar.jsx';
import CostumeLibrary from '../../containers/costume-library.jsx';
import BackdropLibrary from '../../containers/backdrop-library.jsx';
import Watermark from '../../containers/watermark.jsx';
import Backpack from '../../containers/backpack.jsx';
import BrowserModal from '../browser-modal/browser-modal.jsx';
import TipsLibrary from '../../containers/tips-library.jsx';
import Cards from '../../containers/cards.jsx';
import Alerts from '../../containers/alerts.jsx';
import DragLayer from '../../containers/drag-layer.jsx';
import ConnectionModal from '../../containers/connection-modal.jsx';
import TelemetryModal from '../telemetry-modal/telemetry-modal.jsx';
import TWUsernameModal from '../../containers/tw-username-modal.jsx';
import TWSettingsModal from '../../containers/tw-settings-modal.jsx';
import TWSecurityManager from '../../containers/tw-security-manager.jsx';
import TWCustomExtensionModal from '../../containers/tw-custom-extension-modal.jsx';
import TWRestorePointManager from '../../containers/tw-restore-point-manager.jsx';
import TWFontsModal from '../../containers/tw-fonts-modal.jsx';
import PMExtensionModals from '../../containers/pm-extension-modals.jsx';
import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants';
import {resolveStageSize} from '../../lib/screen-utils';
import {isRendererSupported, isBrowserSupported} from '../../lib/tw-environment-support-prober';
import styles from './gui.css';
import plusIcon from './add-tab.svg';
import addExtensionIcon from './icon--extensions.svg';
import codeIcon from './icon--code.svg';
import costumesIcon from './icon--costumes.svg';
import soundsIcon from './icon--sounds.svg';
import variablesIcon from './icon--variables.svg';
import filesIcon from './icon--files.svg';
const urlParams = new URLSearchParams(location.search);
const IsLocal = String(window.location.href).startsWith(`http://localhost:`);
const IsLiveTests = urlParams.has('livetests');
const messages = defineMessages({
addExtension: {
id: 'gui.gui.addExtension',
description: 'Button to add an extension in the target pane',
defaultMessage: 'Add Extension'
}
});
const getFullscreenBackgroundColor = () => {
const params = new URLSearchParams(location.search);
if (params.has('fullscreen-background')) {
return params.get('fullscreen-background');
}
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
return '#111';
}
return 'white';
};
const safeJSONParse = (json, defaul, mustBeArray) => {
try {
const parsed = JSON.parse(json);
if (mustBeArray && !Array.isArray(parsed)) throw 'Not array';
return parsed;
} catch {
return defaul;
}
};
const fullscreenBackgroundColor = getFullscreenBackgroundColor();
const GUIComponent = props => {
const {
accountNavOpen,
activeTabIndex,
alertsVisible,
authorId,
authorThumbnailUrl,
authorUsername,
basePath,
backdropLibraryVisible,
backpackHost,
backpackVisible,
blocksTabVisible,
cardsVisible,
canChangeLanguage,
canCreateNew,
canEditTitle,
canManageFiles,
canRemix,
canSave,
canCreateCopy,
canShare,
canUseCloud,
children,
connectionModalVisible,
costumeLibraryVisible,
costumesTabVisible,
customStageSize,
enableCommunity,
intl,
isCreating,
isDark,
isEmbedded,
isFullScreen,
isPlayerOnly,
isRtl,
isShared,
isWindowFullScreen,
isTelemetryEnabled,
loading,
logo,
renderLogin,
onClickAbout,
onClickAccountNav,
onCloseAccountNav,
onClickAddonSettings,
onClickNewWindow,
onClickTheme,
onClickPackager,
onLogOut,
onOpenRegistration,
onToggleLoginOpen,
onActivateCostumesTab,
onActivateSoundsTab,
onActivateVariablesTab,
onActivateFilesTab,
onActivateTab,
onClickLogo,
onExtensionButtonClick,
onProjectTelemetryEvent,
onRequestCloseBackdropLibrary,
onRequestCloseCostumeLibrary,
onRequestCloseTelemetryModal,
onSeeCommunity,
onShare,
onShowPrivacyPolicy,
onStartSelectingFileUpload,
onTelemetryModalCancel,
onTelemetryModalOptIn,
onTelemetryModalOptOut,
showComingSoon,
soundsTabVisible,
variablesTabVisible,
filesTabVisible,
stageSizeMode,
targetIsStage,
telemetryModalVisible,
tipsLibraryVisible,
usernameModalVisible,
settingsModalVisible,
customExtensionModalVisible,
fontsModalVisible,
isPlayground,
vm,
...componentProps
} = omit(props, 'dispatch');
if (children) {
return <Box {...componentProps}>{children}</Box>;
}
const tabClassNames = {
tabs: styles.tabs,
tab: classNames(tabStyles.reactTabsTab, styles.tab),
tabList: classNames(tabStyles.reactTabsTabList, styles.tabList),
tabPanel: classNames(tabStyles.reactTabsTabPanel, styles.tabPanel),
tabPanelSelected: classNames(tabStyles.reactTabsTabPanelSelected, styles.isSelected),
tabSelected: classNames(tabStyles.reactTabsTabSelected, styles.isSelected)
};
// We can't move this into it's own component or it'll break the selected tab styles & disable switching to the code tab
// Moving the whole TabList element will also break the code panel from resizing properly
const getTabOrder = () => {
const tabOrderStr = localStorage.getItem('pm:taborder') || '["code", "costume", "sound"]';
const tabOrder = safeJSONParse(tabOrderStr, [], true);
return tabOrder;
};
const tabOrder = getTabOrder();
const ContextMenuWrapTab = ({ children, ...props }) => {
const {tabId} = props;
const disabled = tabId === 'code';
return (<><ContextMenuTrigger disable={disabled} id={`remove-editor-tab-${tabId}`}>
{children}
</ContextMenuTrigger>
{ReactDOM.createPortal(<ContextMenu id={`remove-editor-tab-${tabId}`}>
<DangerousMenuItem onClick={() => removeTabFromEditor(tabId)}>
<FormattedMessage
defaultMessage="delete"
description="Menu item to delete in the right click menu"
id="gui.spriteSelectorItem.contextMenuDelete"
/>
</DangerousMenuItem>
</ContextMenu>, document.body)}
</>);
};
// currently each tab can decide whether or not its hidden, remove this once rearranging tabs is supported
const codeTab = (<Tab className={classNames(tabClassNames.tab, tabOrder.includes('code') ? null : styles.tabDisabled)}>
<ContextMenuWrapTab tabId="code">
<img
draggable={false}
src={codeIcon}
/>
<FormattedMessage
defaultMessage="Code"
description="Button to get to the code panel"
id="gui.gui.codeTab"
/>
</ContextMenuWrapTab>
</Tab>);
const costumesTab = (<Tab className={classNames(tabClassNames.tab, tabOrder.includes('costume') ? null : styles.tabDisabled)} onClick={onActivateCostumesTab}>
<ContextMenuWrapTab tabId="costume">
<img
draggable={false}
src={costumesIcon}
/>
{targetIsStage ? (
<FormattedMessage
defaultMessage="Backdrops"
description="Button to get to the backdrops panel"
id="gui.gui.backdropsTab"
/>
) : (
<FormattedMessage
defaultMessage="Costumes"
description="Button to get to the costumes panel"
id="gui.gui.costumesTab"
/>
)}
</ContextMenuWrapTab>
</Tab>);
const soundsTab = (<Tab className={classNames(tabClassNames.tab, tabOrder.includes('sound') ? null : styles.tabDisabled)} onClick={onActivateSoundsTab}>
<ContextMenuWrapTab tabId="sound">
<img
draggable={false}
src={soundsIcon}
/>
<FormattedMessage
defaultMessage="Sounds"
description="Button to get to the sounds panel"
id="gui.gui.soundsTab"
/>
</ContextMenuWrapTab>
</Tab>);
const variablesTab = (<Tab className={classNames(tabClassNames.tab, tabOrder.includes('variable') ? null : styles.tabDisabled)} onClick={onActivateVariablesTab}>
<ContextMenuWrapTab tabId="variable">
<img
draggable={false}
src={variablesIcon}
/>
<FormattedMessage
defaultMessage="Variables"
description="Button to get to the variables panel"
id="gui.gui.variablesTab"
/>
</ContextMenuWrapTab>
</Tab>);
const filesTab = (<Tab className={classNames(tabClassNames.tab, tabOrder.includes('file') ? null : styles.tabDisabled)} onClick={onActivateFilesTab}>
<ContextMenuWrapTab tabId="file">
<img
draggable={false}
src={filesIcon}
/>
<FormattedMessage
defaultMessage="Files"
description="Button to get to the files panel"
id="gui.gui.filesTab"
/>
</ContextMenuWrapTab>
</Tab>);
const tabPairs = {
code: codeTab,
costume: costumesTab,
sound: soundsTab,
variable: variablesTab,
// file: filesTab,
};
// For now, rearranging tabs is not supported
const organizedTabs = Object.values(tabPairs);
// const organizedTabs = (() => {
// const enabledTabs = [];
// // Either add in rearranged order
// // for (const tabId of tabOrder) {
// // enabledTabs.push(tabPairs[tabId] || codeTab)
// // }
// // or we can add tabs in order of table inclusion
// // for (const key in tabPairs) {
// // const tab = tabPairs[key];
// // if (tabOrder.includes(key)) {
// // enabledTabs.push(tab);
// // }
// // }
// return enabledTabs;
// })();
const addTabButtonDisabled = tabOrder.length >= Object.keys(tabPairs).length;
const addTabToEditor = (tabId) => {
const tabOrder = getTabOrder();
tabOrder.push(tabId);
localStorage.setItem('pm:taborder', JSON.stringify(tabOrder));
const tabKeys = Object.keys(tabPairs);
const tabIndex = tabKeys.indexOf(tabId);
if (tabIndex === -1) {
return onActivateTab(0);
}
onActivateTab(tabIndex);
};
const removeTabFromEditor = (tabId) => {
setTimeout(() => { // sometimes clicking delete will switch to the deleted tab
const tabOrder = getTabOrder();
const idx = tabOrder.indexOf(tabId);
if (idx === -1) return;
tabOrder.splice(idx, 1);
localStorage.setItem('pm:taborder', JSON.stringify(tabOrder));
if (tabId !== 'code') {
return onActivateTab(0);
}
const tabKeys = Object.keys(tabPairs);
const firstTab = tabOrder[0];
const firstTabIdx = tabKeys.indexOf(firstTab);
if (firstTabIdx !== -1) {
onActivateTab(firstTabIdx);
}
});
};
const minWidth = layout.fullSizeMinWidth + Math.max(0, customStageSize.width - layout.referenceWidth);
return (<MediaQuery minWidth={minWidth}>{isFullSize => {
const stageSize = resolveStageSize(stageSizeMode, isFullSize);
const alwaysEnabledModals = (
<React.Fragment>
<TWSecurityManager />
<TWRestorePointManager />
{usernameModalVisible && <TWUsernameModal />}
{settingsModalVisible && <TWSettingsModal />}
{customExtensionModalVisible && <TWCustomExtensionModal />}
{fontsModalVisible && <TWFontsModal />}
<PMExtensionModals vm={vm} />
</React.Fragment>
);
return isPlayerOnly ? (
<React.Fragment>
{/* TW: When the window is fullscreen, use an element to display the background color */}
{/* The default color for transparency is inconsistent between browsers and there isn't an existing */}
{/* element for us to style that fills the entire screen. */}
{isWindowFullScreen ? (
<div
className={styles.fullscreenBackground}
style={{
backgroundColor: fullscreenBackgroundColor
}}
/>
) : null}
<StageWrapper
isFullScreen={isFullScreen}
isEmbedded={isEmbedded}
isRendererSupported={isRendererSupported()}
isRtl={isRtl}
loading={loading}
stageSize={STAGE_SIZE_MODES.large}
vm={vm}
>
{alertsVisible ? (
<Alerts className={styles.alertsContainer} />
) : null}
</StageWrapper>
{alwaysEnabledModals}
</React.Fragment>
) : (
<Box
className={styles.pageWrapper}
dir={isRtl ? 'rtl' : 'ltr'}
{...componentProps}
>
{alwaysEnabledModals}
{telemetryModalVisible ? (
<TelemetryModal
isRtl={isRtl}
isTelemetryEnabled={isTelemetryEnabled}
onCancel={onTelemetryModalCancel}
onOptIn={onTelemetryModalOptIn}
onOptOut={onTelemetryModalOptOut}
onRequestClose={onRequestCloseTelemetryModal}
onShowPrivacyPolicy={onShowPrivacyPolicy}
/>
) : null}
{loading ? (
<Loader isFullScreen />
) : null}
{isCreating ? (
<Loader
isFullScreen
messageId={isPlayground ? "gui.loader.playground" : "gui.loader.creating"}
/>
) : null}
{isBrowserSupported() ? null : (
<BrowserModal isRtl={isRtl} />
)}
{tipsLibraryVisible ? (
<TipsLibrary />
) : null}
{cardsVisible ? (
<Cards />
) : null}
{alertsVisible ? (
<Alerts className={styles.alertsContainer} />
) : null}
{connectionModalVisible ? (
<ConnectionModal
vm={vm}
/>
) : null}
{costumeLibraryVisible ? (
<CostumeLibrary
vm={vm}
onRequestClose={onRequestCloseCostumeLibrary}
/>
) : null}
{backdropLibraryVisible ? (
<BackdropLibrary
vm={vm}
onRequestClose={onRequestCloseBackdropLibrary}
/>
) : null}
{(!isPlayground) ? (
<MenuBar
accountNavOpen={accountNavOpen}
authorId={authorId}
authorThumbnailUrl={authorThumbnailUrl}
authorUsername={authorUsername}
canChangeLanguage={canChangeLanguage}
canCreateCopy={canCreateCopy}
canCreateNew={canCreateNew}
canEditTitle={canEditTitle}
canManageFiles={canManageFiles}
canRemix={canRemix}
canSave={canSave}
canShare={canShare}
className={styles.menuBarPosition}
enableCommunity={enableCommunity}
isShared={isShared}
logo={logo}
renderLogin={renderLogin}
showComingSoon={showComingSoon}
onClickAbout={onClickAbout}
onClickAccountNav={onClickAccountNav}
onClickAddonSettings={onClickAddonSettings}
onClickNewWindow={onClickNewWindow}
onClickTheme={onClickTheme}
onClickPackager={onClickPackager}
onClickLogo={onClickLogo}
onCloseAccountNav={onCloseAccountNav}
onLogOut={onLogOut}
onOpenRegistration={onOpenRegistration}
onProjectTelemetryEvent={onProjectTelemetryEvent}
onSeeCommunity={onSeeCommunity}
onShare={onShare}
onStartSelectingFileUpload={onStartSelectingFileUpload}
onToggleLoginOpen={onToggleLoginOpen}
/>
) : null}
<Box className={classNames(styles.bodyWrapper, isPlayground ? styles.bodyWrapperPlayground : null)}>
<Box className={styles.flexWrapper}>
<Box className={styles.editorWrapper}>
<Tabs
forceRenderTabPanel
className={tabClassNames.tabs}
selectedIndex={activeTabIndex}
selectedTabClassName={tabClassNames.tabSelected}
selectedTabPanelClassName={tabClassNames.tabPanelSelected}
onSelect={onActivateTab}
>
<TabList className={tabClassNames.tabList}>
{organizedTabs}
<ContextMenuTrigger
disable={addTabButtonDisabled}
holdToDisplay={0}
id={`add-editor-tab-button`}
>
<button className={classNames(styles.addTabButton, addTabButtonDisabled ? styles.addTabButtonDisabled : null)}>
<img
draggable={false}
src={plusIcon}
/>
</button>
</ContextMenuTrigger>
<ContextMenu id={`add-editor-tab-button`}>
{!tabOrder.includes('code') && <MenuItem onClick={() => addTabToEditor('code')}>
<div className={styles.tabAdditionItem}>
<img
draggable={false}
src={codeIcon}
/>
<FormattedMessage
defaultMessage="Code"
description="Button to get to the code panel"
id="gui.gui.codeTab"
/>
</div>
</MenuItem>}
{!tabOrder.includes('costume') && <MenuItem onClick={() => addTabToEditor('costume')}>
<div className={styles.tabAdditionItem}>
<img
draggable={false}
src={costumesIcon}
/>
<FormattedMessage
defaultMessage="Costumes"
description="Button to get to the costumes panel"
id="gui.gui.costumesTab"
/>
</div>
</MenuItem>}
{!tabOrder.includes('sound') && <MenuItem onClick={() => addTabToEditor('sound')}>
<div className={styles.tabAdditionItem}>
<img
draggable={false}
src={soundsIcon}
/>
<FormattedMessage
defaultMessage="Sounds"
description="Button to get to the sounds panel"
id="gui.gui.soundsTab"
/>
</div>
</MenuItem>}
{!tabOrder.includes('variable') && <MenuItem onClick={() => addTabToEditor('variable')}>
<div className={styles.tabAdditionItem}>
<img
draggable={false}
src={variablesIcon}
/>
<FormattedMessage
defaultMessage="Variables"
description="Button to get to the variables panel"
id="gui.gui.variablesTab"
/>
</div>
</MenuItem>}
{/* {!tabOrder.includes('file') && <MenuItem onClick={() => addTabToEditor('file')}>
<div className={styles.tabAdditionItem}>
<img
draggable={false}
src={filesIcon}
/>
<FormattedMessage
defaultMessage="Files"
description="Button to get to the files panel"
id="gui.gui.filesTab"
/>
</div>
</MenuItem>} */}
</ContextMenu>
<div id="sa_addons_after_add_tab_anchor" />
</TabList>
<TabPanel className={tabClassNames.tabPanel}>
<Box className={styles.blocksWrapper}>
<Blocks
canUseCloud={canUseCloud}
grow={1}
isVisible={blocksTabVisible}
options={{
media: `${basePath}static/blocks-media/`
}}
stageSize={stageSize}
vm={vm}
/>
</Box>
<Box className={styles.extensionButtonContainer}>
<button
className={styles.extensionButton}
title={intl.formatMessage(messages.addExtension)}
onClick={onExtensionButtonClick}
>
<img
className={styles.extensionButtonIcon}
draggable={false}
src={addExtensionIcon}
/>
</button>
</Box>
<Box className={styles.watermark}>
<Watermark />
</Box>
</TabPanel>
<TabPanel className={tabClassNames.tabPanel}>
{costumesTabVisible ? <CostumeTab
vm={vm}
isDark={isDark}
/> : null}
</TabPanel>
<TabPanel className={tabClassNames.tabPanel}>
{soundsTabVisible ? <SoundTab vm={vm} /> : null}
</TabPanel>
<TabPanel className={tabClassNames.tabPanel}>
{variablesTabVisible ? <VariablesTab vm={vm} /> : null}
</TabPanel>
</Tabs>
{backpackVisible ? (
<Backpack host={backpackHost} />
) : null}
</Box>
<Box className={classNames(styles.stageAndTargetWrapper, styles[stageSize])}>
<StageWrapper
isFullScreen={isFullScreen}
isRendererSupported={isRendererSupported()}
isRtl={isRtl}
stageSize={stageSize}
vm={vm}
/>
<Box className={styles.targetWrapper}>
<TargetPane
stageSize={stageSize}
vm={vm}
/>
</Box>
</Box>
</Box>
</Box>
<DragLayer />
</Box>
);
}}</MediaQuery>);
};
GUIComponent.propTypes = {
accountNavOpen: PropTypes.bool,
activeTabIndex: PropTypes.number,
authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false
authorThumbnailUrl: PropTypes.string,
authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false
backdropLibraryVisible: PropTypes.bool,
backpackHost: PropTypes.string,
backpackVisible: PropTypes.bool,
basePath: PropTypes.string,
blocksTabVisible: PropTypes.bool,
canChangeLanguage: PropTypes.bool,
canCreateCopy: PropTypes.bool,
canCreateNew: PropTypes.bool,
canEditTitle: PropTypes.bool,
canManageFiles: PropTypes.bool,
canRemix: PropTypes.bool,
canSave: PropTypes.bool,
canShare: PropTypes.bool,
canUseCloud: PropTypes.bool,
cardsVisible: PropTypes.bool,
children: PropTypes.node,
costumeLibraryVisible: PropTypes.bool,
costumesTabVisible: PropTypes.bool,
customStageSize: PropTypes.shape({
width: PropTypes.number,
height: PropTypes.number
}),
enableCommunity: PropTypes.bool,
intl: intlShape.isRequired,
isCreating: PropTypes.bool,
isDark: PropTypes.bool,
isEmbedded: PropTypes.bool,
isFullScreen: PropTypes.bool,
isPlayerOnly: PropTypes.bool,
isRtl: PropTypes.bool,
isShared: PropTypes.bool,
isWindowFullScreen: PropTypes.bool,
loading: PropTypes.bool,
logo: PropTypes.string,
onActivateCostumesTab: PropTypes.func,
onActivateSoundsTab: PropTypes.func,
onActivateVariablesTab: PropTypes.func,
onActivateFilesTab: PropTypes.func,
onActivateTab: PropTypes.func,
onClickAccountNav: PropTypes.func,
onClickAddonSettings: PropTypes.func,
onClickNewWindow: PropTypes.func,
onClickTheme: PropTypes.func,
onClickPackager: PropTypes.func,
onClickLogo: PropTypes.func,
onCloseAccountNav: PropTypes.func,
onExtensionButtonClick: PropTypes.func,
onLogOut: PropTypes.func,
onOpenRegistration: PropTypes.func,
onRequestCloseBackdropLibrary: PropTypes.func,
onRequestCloseCostumeLibrary: PropTypes.func,
onRequestCloseTelemetryModal: PropTypes.func,
onSeeCommunity: PropTypes.func,
onShare: PropTypes.func,
onShowPrivacyPolicy: PropTypes.func,
onStartSelectingFileUpload: PropTypes.func,
onTabSelect: PropTypes.func,
onTelemetryModalCancel: PropTypes.func,
onTelemetryModalOptIn: PropTypes.func,
onTelemetryModalOptOut: PropTypes.func,
onToggleLoginOpen: PropTypes.func,
renderLogin: PropTypes.func,
showComingSoon: PropTypes.bool,
soundsTabVisible: PropTypes.bool,
variablesTabVisible: PropTypes.bool,
filesTabVisible: PropTypes.bool,
stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
targetIsStage: PropTypes.bool,
telemetryModalVisible: PropTypes.bool,
tipsLibraryVisible: PropTypes.bool,
usernameModalVisible: PropTypes.bool,
settingsModalVisible: PropTypes.bool,
customExtensionModalVisible: PropTypes.bool,
fontsModalVisible: PropTypes.bool,
vm: PropTypes.instanceOf(VM).isRequired
};
GUIComponent.defaultProps = {
backpackHost: null,
backpackVisible: false,
basePath: './',
canChangeLanguage: true,
canCreateNew: false,
canEditTitle: false,
canManageFiles: true,
canRemix: false,
canSave: false,
canCreateCopy: false,
canShare: false,
canUseCloud: false,
enableCommunity: false,
isCreating: false,
isShared: false,
loading: false,
showComingSoon: false,
stageSizeMode: STAGE_SIZE_MODES.large
};
const mapStateToProps = state => ({
customStageSize: state.scratchGui.customStageSize,
isWindowFullScreen: state.scratchGui.tw.isWindowFullScreen,
// This is the button's mode, as opposed to the actual current state
stageSizeMode: state.scratchGui.stageSize.stageSize
});
export default injectIntl(connect(
mapStateToProps
)(GUIComponent));