From f0a10fdfa0e022dc2d05c30d73381e4c2c633598 Mon Sep 17 00:00:00 2001 From: Ben Wheeler <wheeler.benjamin@gmail.com> Date: Thu, 6 Sep 2018 19:11:34 -0400 Subject: [PATCH] localized initial project name, fixed misuse of TitledHOC, truncate local filenames --- .../menu-bar/project-title-input.css | 12 +++- .../menu-bar/project-title-input.jsx | 18 ++++-- src/containers/gui.jsx | 7 ++- src/containers/project-loader.jsx | 2 +- src/css/colors.css | 1 + src/lib/titled-hoc.jsx | 54 ++++++++++++++++++ src/playground/index.jsx | 52 ------------------ src/playground/player.jsx | 55 ++++++------------- src/playground/render-gui.jsx | 3 +- src/reducers/project-title.js | 4 +- 10 files changed, 104 insertions(+), 104 deletions(-) create mode 100644 src/lib/titled-hoc.jsx diff --git a/src/components/menu-bar/project-title-input.css b/src/components/menu-bar/project-title-input.css index 04d5021d7..84dcd0afc 100644 --- a/src/components/menu-bar/project-title-input.css +++ b/src/components/menu-bar/project-title-input.css @@ -17,20 +17,26 @@ $title-width: 12rem; padding: .5rem; } -.title-field, -.title-field::placeholder { +.title-field { color: $ui-white; font-weight: bold; font-size: .8rem; } +.title-field::placeholder { + color: $ui-white; + font-weight: normal; + font-size: .8rem; + font-style: italic; +} + .title-field:hover { background-color: hsla(0, 100%, 100%, 0.5); } .title-field:focus { outline:none; - border: none; + border: 1px solid $ui-transparent; -webkit-box-shadow: 0 0 0 calc($space * .5) $ui-white-transparent; box-shadow: 0 0 0 calc($space * .5) $ui-white-transparent; background-color: $ui-white; diff --git a/src/components/menu-bar/project-title-input.jsx b/src/components/menu-bar/project-title-input.jsx index da49372f8..8b927417f 100644 --- a/src/components/menu-bar/project-title-input.jsx +++ b/src/components/menu-bar/project-title-input.jsx @@ -3,6 +3,7 @@ import {connect} from 'react-redux'; import PropTypes from 'prop-types'; import bindAll from 'lodash.bindall'; import React from 'react'; +import {defineMessages, intlShape, injectIntl} from 'react-intl'; import BufferedInputHOC from '../forms/buffered-input-hoc.jsx'; import Input from '../forms/input.jsx'; @@ -10,6 +11,14 @@ const BufferedInput = BufferedInputHOC(Input); import styles from './project-title-input.css'; +const messages = defineMessages({ + projectTitlePlaceholder: { + id: 'gui.gui.projectTitlePlaceholder', + description: 'Placeholder for project title when blank', + defaultMessage: 'Project title here' + } +}); + class ProjectTitleInput extends React.Component { constructor (props) { super(props); @@ -28,8 +37,8 @@ class ProjectTitleInput extends React.Component { return ( <BufferedInput className={classNames(styles.titleField)} - maxlength="100" - placeholder="" + maxLength="100" + placeholder={this.props.intl.formatMessage(messages.projectTitlePlaceholder)} tabIndex="0" type="text" value={this.props.projectTitle} @@ -40,6 +49,7 @@ class ProjectTitleInput extends React.Component { } ProjectTitleInput.propTypes = { + intl: intlShape.isRequired, onUpdateProjectTitle: PropTypes.func, projectTitle: PropTypes.string }; @@ -50,7 +60,7 @@ const mapStateToProps = state => ({ const mapDispatchToProps = () => ({}); -export default connect( +export default injectIntl(connect( mapStateToProps, mapDispatchToProps -)(ProjectTitleInput); +)(ProjectTitleInput)); diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index 8486f29e3..04c6142dc 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -35,6 +35,10 @@ class GUI extends React.Component { }; } componentDidMount () { + if (this.props.projectTitle) { + this.props.onUpdateReduxProjectTitle(this.props.projectTitle); + } + if (this.props.vm.initialized) return; this.audioEngine = new AudioEngine(); this.props.vm.attachAudioEngine(this.audioEngine); @@ -51,9 +55,6 @@ class GUI extends React.Component { this.setState({loadingError: true, errorMessage: e}); }); this.props.vm.initialized = true; - if (this.props.projectTitle) { - this.props.onUpdateReduxProjectTitle(this.props.projectTitle); - } } componentWillReceiveProps (nextProps) { if (this.props.projectData !== nextProps.projectData) { diff --git a/src/containers/project-loader.jsx b/src/containers/project-loader.jsx index dae8ed62c..66081b7a9 100644 --- a/src/containers/project-loader.jsx +++ b/src/containers/project-loader.jsx @@ -79,7 +79,7 @@ class ProjectLoader extends React.Component { if (thisFileInput.files[0].name) { const matches = thisFileInput.files[0].name.match(/^(.*)\.sb3$/); if (matches) { - this.props.onSetProjectTitle(matches[1]); + this.props.onSetProjectTitle(matches[1].substring(0, 100)); } } } diff --git a/src/css/colors.css b/src/css/colors.css index 37ffa6222..222260892 100644 --- a/src/css/colors.css +++ b/src/css/colors.css @@ -6,6 +6,7 @@ $ui-modal-overlay: hsla(215, 100%, 65%, 0.9); /* 90% transparent version of moti $ui-white: hsla(0, 100%, 100%, 1); /* #FFFFFF */ $ui-white-transparent: hsla(0, 100%, 100%, 0.25); /* 25% transparent version of ui-white */ +$ui-transparent: hsla(0, 100%, 100%, 0); /* 25% transparent version of ui-white */ $ui-black-transparent: hsla(0, 0%, 0%, 0.15); /* 15% transparent version of black */ diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx new file mode 100644 index 000000000..2534d96bd --- /dev/null +++ b/src/lib/titled-hoc.jsx @@ -0,0 +1,54 @@ +import React from 'react'; +import bindAll from 'lodash.bindall'; +import {defineMessages, intlShape, injectIntl} from 'react-intl'; + +const messages = defineMessages({ + defaultProjectTitle: { + id: 'gui.gui.defaultProjectTitle', + description: 'Default title for project', + defaultMessage: 'Scratch Project' + } +}); + +/* Higher Order Component to get and set the project title + * @param {React.Component} WrappedComponent component to receive project title related props + * @returns {React.Component} component with project loading behavior + */ +const TitledHOC = function (WrappedComponent) { + class TitledComponent extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleUpdateProjectTitle' + ]); + this.state = { + projectTitle: this.props.intl.formatMessage(messages.defaultProjectTitle) + }; + } + handleUpdateProjectTitle (newTitle) { + this.setState({projectTitle: newTitle}); + } + render () { + return ( + <WrappedComponent + projectTitle={this.state.projectTitle} + onUpdateProjectTitle={this.handleUpdateProjectTitle} + {...this.props} + /> + ); + } + } + + TitledComponent.propTypes = { + intl: intlShape.isRequired + }; + + // return TitledComponent; + const IntlTitledComponent = injectIntl(TitledComponent); + return IntlTitledComponent; + +}; + +export { + TitledHOC as default +}; diff --git a/src/playground/index.jsx b/src/playground/index.jsx index 8eac6bd22..ba0ada4da 100644 --- a/src/playground/index.jsx +++ b/src/playground/index.jsx @@ -5,7 +5,6 @@ import 'intl'; // For Safari 9 import React from 'react'; import ReactDOM from 'react-dom'; -import bindAll from 'lodash.bindall'; import analytics from '../lib/analytics'; import AppStateHOC from '../lib/app-state-hoc.jsx'; @@ -33,54 +32,3 @@ if (supportedBrowser()) { // eslint-disable-next-line react/jsx-no-bind ReactDOM.render(<WrappedBrowserModalComponent onBack={handleBack} />, appTarget); } - -// GUI.setAppElement(appTarget); -// -// // simple example of how you might manage project title externally. -// // Changing project title within GUI interface will update it here. -// class TitledGUI extends React.Component { -// constructor (props) { -// super(props); -// bindAll(this, [ -// 'handleUpdateProjectTitle' -// ]); -// this.state = { -// projectTitle: 'Untitled-1' -// }; -// } -// handleUpdateProjectTitle (newTitle) { -// this.setState({projectTitle: newTitle}); -// } -// render () { -// const { -// projectTitle, // eslint-disable-line no-unused-vars -// onUpdateProjectTitle, // eslint-disable-line no-unused-vars -// ...componentProps -// } = this.props; -// return ( -// <GUI -// {...componentProps} -// projectTitle={this.state.projectTitle} -// onUpdateProjectTitle={this.handleUpdateProjectTitle} -// /> -// ); -// } -// } -// -// const WrappedGui = HashParserHOC(AppStateHOC(TitledGUI)); -// -// // 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 -// ); diff --git a/src/playground/player.jsx b/src/playground/player.jsx index 9203104a2..e236de667 100644 --- a/src/playground/player.jsx +++ b/src/playground/player.jsx @@ -3,12 +3,12 @@ import PropTypes from 'prop-types'; import React from 'react'; import ReactDOM from 'react-dom'; import {connect} from 'react-redux'; -import bindAll from 'lodash.bindall'; import Box from '../components/box/box.jsx'; import GUI from '../containers/gui.jsx'; import HashParserHOC from '../lib/hash-parser-hoc.jsx'; import AppStateHOC from '../lib/app-state-hoc.jsx'; +import TitledHOC from '../lib/titled-hoc.jsx'; import {setPlayer} from '../reducers/mode'; @@ -19,43 +19,20 @@ if (process.env.NODE_ENV === 'production' && typeof window === 'object') { import styles from './player.css'; -class Player extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'handleUpdateProjectTitle' - ]); - this.state = { - projectTitle: 'Untitled-1' - }; - } - handleUpdateProjectTitle (newTitle) { - this.setState({projectTitle: newTitle}); - } - render () { - const { - isPlayerOnly, - onSeeInside, - projectId - } = this.props; - return ( - <Box - className={classNames({ - [styles.stageOnly]: isPlayerOnly - })} - > - {isPlayerOnly && <button onClick={onSeeInside}>{'See inside'}</button>} - <GUI - enableCommunity - isPlayerOnly={isPlayerOnly} - projectId={projectId} - projectTitle={this.state.projectTitle} - onUpdateProjectTitle={this.handleUpdateProjectTitle} - /> - </Box> - ); - } -} +const Player = ({isPlayerOnly, onSeeInside, projectId}) => ( + <Box + className={classNames({ + [styles.stageOnly]: isPlayerOnly + })} + > + {isPlayerOnly && <button onClick={onSeeInside}>{'See inside'}</button>} + <GUI + enableCommunity + isPlayerOnly={isPlayerOnly} + projectId={projectId} + /> + </Box> +); Player.propTypes = { isPlayerOnly: PropTypes.bool, @@ -72,7 +49,7 @@ const mapDispatchToProps = dispatch => ({ }); const ConnectedPlayer = connect(mapStateToProps, mapDispatchToProps)(Player); -const WrappedPlayer = HashParserHOC(AppStateHOC(ConnectedPlayer)); +const WrappedPlayer = HashParserHOC(AppStateHOC(TitledHOC(ConnectedPlayer))); const appTarget = document.createElement('div'); document.body.appendChild(appTarget); diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx index adec922b8..562e57eac 100644 --- a/src/playground/render-gui.jsx +++ b/src/playground/render-gui.jsx @@ -4,6 +4,7 @@ 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'; +import TitledHOC from '../lib/titled-hoc.jsx'; /* * Render the GUI playground. This is a separate function because importing anything @@ -12,7 +13,7 @@ import HashParserHOC from '../lib/hash-parser-hoc.jsx'; */ export default appTarget => { GUI.setAppElement(appTarget); - const WrappedGui = HashParserHOC(AppStateHOC(GUI)); + const WrappedGui = HashParserHOC(AppStateHOC(TitledHOC(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=([^&]*)&?/); diff --git a/src/reducers/project-title.js b/src/reducers/project-title.js index b8cd3ade5..09bf6c4ea 100644 --- a/src/reducers/project-title.js +++ b/src/reducers/project-title.js @@ -1,6 +1,8 @@ const SET_PROJECT_TITLE = 'projectTitle/SET_PROJECT_TITLE'; -const initialState = 'Untitled-1'; +// we are initializing to a blank string instead of an actual title, +// because it would be hard to localize here +const initialState = ''; const reducer = function (state, action) { if (typeof state === 'undefined') state = initialState; -- GitLab