Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 52 additions & 170 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions packages/scratch-gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
"balance-text": "3.3.1",
"base64-loader": "1.0.0",
"bowser": "1.9.4",
"cat-blocks": "npm:[email protected]",
"classnames": "2.5.1",
"computed-style-to-inline-style": "3.0.0",
"cookie": "0.6.0",
Expand Down Expand Up @@ -107,7 +106,7 @@
"react-intl": "6.8.9",
"react-modal": "3.16.3",
"react-popover": "0.5.10",
"react-redux": "8.1.3",
"react-redux": "^8.0.0",
"react-responsive": "9.0.2",
"react-style-proptype": "3.2.2",
"react-tabs": "5.2.0",
Expand All @@ -116,7 +115,7 @@
"react-visibility-sensor": "5.1.1",
"redux-throttle": "0.1.1",
"scratch-audio": "2.0.268",
"scratch-blocks": "1.2.5",
"scratch-blocks": "1.3.0",
"scratch-l10n": "6.1.25",
"scratch-paint": "4.1.19",
"scratch-render-fonts": "1.0.252",
Expand Down Expand Up @@ -146,6 +145,7 @@
"@types/react-modal": "3.16.3",
"babel-core": "7.0.0-bridge.0",
"babel-loader": "9.2.1",
"babel-plugin-react-intl": "3.5.1",
"buffer": "6.0.3",
"cheerio": "1.1.2",
"cross-env": "7.0.3",
Expand Down
42 changes: 36 additions & 6 deletions packages/scratch-gui/src/components/gui/gui.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ import TelemetryModal from '../telemetry-modal/telemetry-modal.jsx';

import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants';
import {resolveStageSize} from '../../lib/screen-utils';
import {themeMap} from '../../lib/themes';
import {colorModeMap} from '../../lib/settings/color-mode/index.js';
import {DEFAULT_THEME, themeMap} from '../../lib/settings/theme/index.js';
import {AccountMenuOptionsPropTypes} from '../../lib/account-menu-options';

import styles from './gui.css';
Expand All @@ -43,6 +44,7 @@ import costumesIcon from './icon--costumes.svg';
import soundsIcon from './icon--sounds.svg';
import DebugModal from '../debug-modal/debug-modal.jsx';
import {setPlatform} from '../../reducers/platform.js';
import {setTheme} from '../../reducers/settings.js';
import {PLATFORM} from '../../lib/platform.js';

// Cache this value to only retrieve it once the first time.
Expand All @@ -67,6 +69,7 @@ const GUIComponent = props => {
blocksTabVisible,
cardsVisible,
canChangeLanguage,
canChangeColorMode,
canChangeTheme,
canCreateNew,
canEditTitle,
Expand All @@ -84,7 +87,9 @@ const GUIComponent = props => {
onDebugModalClose,
onTutorialSelect,
enableCommunity,
hasActiveMembership,
isCreating,
isFetchingUserData,
isFullScreen,
isPlayerOnly,
isRtl,
Expand Down Expand Up @@ -129,6 +134,7 @@ const GUIComponent = props => {
stageSizeMode,
targetIsStage,
telemetryModalVisible,
colorMode,
theme,
tipsLibraryVisible,
useExternalPeripheralList,
Expand All @@ -144,10 +150,23 @@ const GUIComponent = props => {

useEffect(() => {
if (props.platform) {
// TODO: This uses the imported `setPlatform` directly,
// but it should probably use the dispatched version from props.
setPlatform(props.platform);
}
}, [props.platform]);

useEffect(() => {
if (
!isFetchingUserData &&
!themeMap[theme]?.isAvailable?.({hasActiveMembership})
) {
// If the preferred theme is not available, fall back to default.
// TODO: It would be cleaner to do this on redux init.
props.setTheme(DEFAULT_THEME);
}
}, [theme, hasActiveMembership, props.setTheme]);

const tabClassNames = {
tabs: styles.tabs,
tab: classNames(tabStyles.reactTabsTab, styles.tab),
Expand Down Expand Up @@ -258,6 +277,7 @@ const GUIComponent = props => {
authorThumbnailUrl={authorThumbnailUrl}
authorUsername={authorUsername}
canChangeLanguage={canChangeLanguage}
canChangeColorMode={canChangeColorMode}
canChangeTheme={canChangeTheme}
canCreateCopy={canCreateCopy}
canCreateNew={canCreateNew}
Expand All @@ -268,6 +288,7 @@ const GUIComponent = props => {
canShare={canShare}
className={styles.menuBarPosition}
enableCommunity={enableCommunity}
hasActiveMembership={hasActiveMembership}
isShared={isShared}
isTotallyNormal={isTotallyNormal}
logo={logo}
Expand Down Expand Up @@ -362,15 +383,15 @@ const GUIComponent = props => {
<TabPanel className={tabClassNames.tabPanel}>
<Box className={styles.blocksWrapper}>
<Blocks
key={`${blocksId}/${theme}`}
key={`${blocksId}/${colorMode}/${theme}`}
canUseCloud={canUseCloud}
grow={1}
isVisible={blocksTabVisible}
options={{
media: `${basePath}static/${themeMap[theme].blocksMediaFolder}/`
media: `${basePath}static/${colorModeMap[colorMode].blocksMediaFolder}/`
}}
stageSize={stageSize}
theme={theme}
colorMode={colorMode}
vm={vm}
showNewFeatureCallouts={showNewFeatureCallouts}
username={username}
Expand Down Expand Up @@ -442,6 +463,7 @@ GUIComponent.propTypes = {
blocksTabVisible: PropTypes.bool,
blocksId: PropTypes.string,
canChangeLanguage: PropTypes.bool,
canChangeColorMode: PropTypes.bool,
canChangeTheme: PropTypes.bool,
canCreateCopy: PropTypes.bool,
canCreateNew: PropTypes.bool,
Expand All @@ -456,10 +478,12 @@ GUIComponent.propTypes = {
costumeLibraryVisible: PropTypes.bool,
costumesTabVisible: PropTypes.bool,
debugModalVisible: PropTypes.bool,
hasActiveMembership: PropTypes.bool,
onDebugModalClose: PropTypes.func,
onTutorialSelect: PropTypes.func,
enableCommunity: PropTypes.bool,
isCreating: PropTypes.bool,
isFetchingUserData: PropTypes.bool,
isFullScreen: PropTypes.bool,
isPlayerOnly: PropTypes.bool,
isRtl: PropTypes.bool,
Expand Down Expand Up @@ -496,13 +520,15 @@ GUIComponent.propTypes = {
onUpdateProjectThumbnail: PropTypes.func,
platform: PropTypes.oneOf(Object.keys(PLATFORM)),
renderLogin: PropTypes.func,
setTheme: PropTypes.func.isRequired,
showComingSoon: PropTypes.bool,
showNewFeatureCallouts: PropTypes.bool,
soundsTabVisible: PropTypes.bool,
stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
setPlatform: PropTypes.func,
targetIsStage: PropTypes.bool,
telemetryModalVisible: PropTypes.bool,
colorMode: PropTypes.string,
theme: PropTypes.string,
tipsLibraryVisible: PropTypes.bool,
useExternalPeripheralList: PropTypes.bool, // true for CDM, false for normal Scratch Link
Expand All @@ -517,7 +543,9 @@ GUIComponent.defaultProps = {
backpackVisible: false,
basePath: './',
blocksId: 'original',
// TODO: Currently all of those are always true. Do we actually need them?
canChangeLanguage: true,
canChangeColorMode: true,
canChangeTheme: true,
canCreateNew: false,
canEditTitle: false,
Expand All @@ -543,11 +571,13 @@ const mapStateToProps = state => ({
// This is the button's mode, as opposed to the actual current state
blocksId: state.scratchGui.timeTravel.year.toString(),
stageSizeMode: state.scratchGui.stageSize.stageSize,
theme: state.scratchGui.theme.theme
colorMode: state.scratchGui.settings.colorMode,
theme: state.scratchGui.settings.theme
});

const mapDispatchToProps = dispatch => ({
setPlatform: platform => dispatch(setPlatform(platform))
setPlatform: platform => dispatch(setPlatform(platform)),
setTheme: theme => dispatch(setTheme(theme))
});

export default connect(mapStateToProps,
Expand Down
7 changes: 6 additions & 1 deletion packages/scratch-gui/src/components/menu-bar/menu-bar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -452,9 +452,12 @@ class MenuBar extends React.Component {
onClick={this.props.onClickLogo}
/>
</div>
{(this.props.canChangeTheme || this.props.canChangeLanguage) && (<SettingsMenu
{(this.props.canChangeColorMode || this.props.canChangeLanguage || this.props.canChangeTheme) &&
(<SettingsMenu
canChangeLanguage={this.props.canChangeLanguage}
canChangeColorMode={this.props.canChangeColorMode}
canChangeTheme={this.props.canChangeTheme}
hasActiveMembership={this.props.hasActiveMembership}
isRtl={this.props.isRtl}
onRequestClose={this.props.onRequestCloseSettings}
onRequestOpen={this.props.onClickSettings}
Expand Down Expand Up @@ -901,6 +904,7 @@ MenuBar.propTypes = {
authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
autoUpdateProject: PropTypes.func,
canChangeLanguage: PropTypes.bool,
canChangeColorMode: PropTypes.bool,
canChangeTheme: PropTypes.bool,
canCreateCopy: PropTypes.bool,
canCreateNew: PropTypes.bool,
Expand All @@ -915,6 +919,7 @@ MenuBar.propTypes = {
editMenuOpen: PropTypes.bool,
enableCommunity: PropTypes.bool,
fileMenuOpen: PropTypes.bool,
hasActiveMembership: PropTypes.bool,
intl: intlShape,
isRtl: PropTypes.bool,
isShared: PropTypes.bool,
Expand Down
114 changes: 114 additions & 0 deletions packages/scratch-gui/src/components/menu-bar/preference-menu.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, {useMemo} from 'react';
import {FormattedMessage} from 'react-intl';
import {connect} from 'react-redux';

import check from './check.svg';
import {MenuItem, Submenu} from '../menu/menu.jsx';

import styles from './settings-menu.css';

import dropdownCaret from './dropdown-caret.svg';

const intlMessageShape = PropTypes.shape({
defaultMessage: PropTypes.string,
description: PropTypes.string,
id: PropTypes.string
});

const PreferenceItem = props => {
const item = props.item;

return (
<MenuItem onClick={props.onClick}>
<div className={styles.option}>
<img
className={classNames(styles.check, {[styles.selected]: props.isSelected})}
src={check}
/>
{item.icon && <img
className={styles.icon}
src={item.icon}
/>}
<FormattedMessage {...item.label} />
</div>
</MenuItem>);
};

PreferenceItem.propTypes = {
isSelected: PropTypes.bool,
onClick: PropTypes.func,
item: PropTypes.shape({
icon: PropTypes.string,
label: intlMessageShape.isRequired
})
};

const PreferenceMenu = ({
itemsMap,
open,
onChange,
onRequestOpen,
defaultMenuIconSrc,
submenuLabel,
selectedItemKey,
isRtl
}) => {
const itemKeys = useMemo(() => Object.keys(itemsMap), [itemsMap]);
const selectedItem = useMemo(() => itemsMap[selectedItemKey], [itemsMap, selectedItemKey]);
return (
<MenuItem expanded={open}>
<div
className={styles.option}
onClick={onRequestOpen}
>
<img
src={selectedItem.icon || defaultMenuIconSrc}
style={{width: 24}}
/>
<span className={styles.submenuLabel}>
<FormattedMessage {...submenuLabel} />
</span>
<img
className={styles.expandCaret}
src={dropdownCaret}
/>
</div>
<Submenu place={isRtl ? 'left' : 'right'}>
{itemKeys.map(itemKey => (
<PreferenceItem
key={itemKey}
isSelected={itemKey === selectedItemKey}
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onChange(itemKey)}
item={itemsMap[itemKey]}
/>)
)}
</Submenu>
</MenuItem>
);
};

PreferenceMenu.propTypes = {
itemsMap: PropTypes.objectOf(PropTypes.shape({
icon: PropTypes.string,
label: intlMessageShape.isRequired
})).isRequired,
open: PropTypes.bool,
onChange: PropTypes.func,
onRequestCloseSettings: PropTypes.func,
onRequestOpen: PropTypes.func,
defaultMenuIconSrc: PropTypes.string,
submenuLabel: intlMessageShape.isRequired,
selectedItemKey: PropTypes.string,
isRtl: PropTypes.bool
};

const mapStateToProps = state => ({
isRtl: state.locales.isRtl
});

export default connect(
mapStateToProps
)(PreferenceMenu);
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
width: 1.5rem;
}

.theme-label {
/* Unused? */
.color-mode-label {
flex: 1;
}

Expand Down
Loading
Loading