From 91e2fc92f30afb55bc82de465911efc32ed1a53c Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 12 Sep 2019 17:13:52 -0400 Subject: [PATCH 1/8] on showing project without id, tell owner of GUI to set title to default --- src/components/gui/gui.jsx | 1 - src/containers/gui.jsx | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 3c68eec90..ab7ec87b0 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -433,7 +433,6 @@ GUIComponent.defaultProps = { isCreating: false, isShared: false, loading: false, - onUpdateProjectTitle: () => {}, showComingSoon: false, stageSizeMode: STAGE_SIZE_MODES.large }; diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index 2012c61e5..d2a47b412 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -9,7 +9,8 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl'; import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import { getIsError, - getIsShowingProject + getIsShowingProject, + getIsShowingWithoutId } from '../reducers/project-state'; import {setProjectTitle} from '../reducers/project-title'; import { @@ -66,6 +67,9 @@ class GUI extends React.Component { // At this time the project view in www doesn't need to know when a project is unloaded this.props.onProjectLoaded(); } + if (this.props.isShowingWithoutId && !prevProps.isShowingWithoutId) { + this.props.onUpdateProjectTitle(this.props.intl.formatMessage(messages.defaultProjectTitle)); + } } setReduxTitle (newTitle) { if (newTitle === null || typeof newTitle === 'undefined') { @@ -126,6 +130,7 @@ GUI.propTypes = { isLoading: PropTypes.bool, isScratchDesktop: PropTypes.bool, isShowingProject: PropTypes.bool, + isShowingWithoutId: PropTypes.bool, loadingStateVisible: PropTypes.bool, onProjectLoaded: PropTypes.func, onSeeCommunity: PropTypes.func, @@ -146,6 +151,7 @@ GUI.defaultProps = { onStorageInit: storageInstance => storageInstance.addOfficialScratchWebStores(), onProjectLoaded: () => {}, onUpdateProjectId: () => {}, + onUpdateProjectTitle: () => {}, onVmInit: (/* vm */) => {} }; @@ -166,6 +172,7 @@ const mapStateToProps = state => { isPlayerOnly: state.scratchGui.mode.isPlayerOnly, isRtl: state.locales.isRtl, isShowingProject: getIsShowingProject(loadingState), + isShowingWithoutId: getIsShowingWithoutId(loadingState), loadingStateVisible: state.scratchGui.modals.loadingProject, projectId: state.scratchGui.projectState.projectId, soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX, From b63ac51fb1e022e17303062d84408c2e62a739a2 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Sat, 14 Sep 2019 20:16:04 -0400 Subject: [PATCH 2/8] move project title management into titled hoc --- src/containers/gui.jsx | 41 +++---------------- src/lib/titled-hoc.jsx | 93 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 90 insertions(+), 44 deletions(-) diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index d2a47b412..d7b8f78c2 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -4,15 +4,13 @@ import {compose} from 'redux'; import {connect} from 'react-redux'; import ReactModal from 'react-modal'; import VM from 'scratch-vm'; -import {defineMessages, injectIntl, intlShape} from 'react-intl'; +import {injectIntl, intlShape} from 'react-intl'; import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import { getIsError, - getIsShowingProject, - getIsShowingWithoutId + getIsShowingProject } from '../reducers/project-state'; -import {setProjectTitle} from '../reducers/project-title'; import { activateTab, BLOCKS_TAB_INDEX, @@ -30,6 +28,7 @@ import { import FontLoaderHOC from '../lib/font-loader-hoc.jsx'; import LocalizationHOC from '../lib/localization-hoc.jsx'; import ProjectFetcherHOC from '../lib/project-fetcher-hoc.jsx'; +import TitledHOC from '../lib/titled-hoc.jsx'; import ProjectSaverHOC from '../lib/project-saver-hoc.jsx'; import QueryParserHOC from '../lib/query-parser-hoc.jsx'; import storage from '../lib/storage'; @@ -40,18 +39,9 @@ import cloudManagerHOC from '../lib/cloud-manager-hoc.jsx'; import GUIComponent from '../components/gui/gui.jsx'; import {setIsScratchDesktop} from '../lib/isScratchDesktop.js'; -const messages = defineMessages({ - defaultProjectTitle: { - id: 'gui.gui.defaultProjectTitle', - description: 'Default title for project', - defaultMessage: 'Scratch Project' - } -}); - class GUI extends React.Component { componentDidMount () { setIsScratchDesktop(this.props.isScratchDesktop); - this.setReduxTitle(this.props.projectTitle); this.props.onStorageInit(storage); this.props.onVmInit(this.props.vm); } @@ -59,26 +49,11 @@ class GUI extends React.Component { if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) { this.props.onUpdateProjectId(this.props.projectId); } - if (this.props.projectTitle !== prevProps.projectTitle) { - this.setReduxTitle(this.props.projectTitle); - } if (this.props.isShowingProject && !prevProps.isShowingProject) { // this only notifies container when a project changes from not yet loaded to loaded // At this time the project view in www doesn't need to know when a project is unloaded this.props.onProjectLoaded(); } - if (this.props.isShowingWithoutId && !prevProps.isShowingWithoutId) { - this.props.onUpdateProjectTitle(this.props.intl.formatMessage(messages.defaultProjectTitle)); - } - } - setReduxTitle (newTitle) { - if (newTitle === null || typeof newTitle === 'undefined') { - this.props.onUpdateReduxProjectTitle( - this.props.intl.formatMessage(messages.defaultProjectTitle) - ); - } else { - this.props.onUpdateReduxProjectTitle(newTitle); - } } render () { if (this.props.isError) { @@ -96,11 +71,9 @@ class GUI extends React.Component { onProjectLoaded, onStorageInit, onUpdateProjectId, - onUpdateReduxProjectTitle, onVmInit, projectHost, projectId, - projectTitle, /* eslint-enable no-unused-vars */ children, fetchingProject, @@ -137,11 +110,9 @@ GUI.propTypes = { onStorageInit: PropTypes.func, onUpdateProjectId: PropTypes.func, onUpdateProjectTitle: PropTypes.func, - onUpdateReduxProjectTitle: PropTypes.func, onVmInit: PropTypes.func, projectHost: PropTypes.string, projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - projectTitle: PropTypes.string, telemetryModalVisible: PropTypes.bool, vm: PropTypes.instanceOf(VM).isRequired }; @@ -151,7 +122,6 @@ GUI.defaultProps = { onStorageInit: storageInstance => storageInstance.addOfficialScratchWebStores(), onProjectLoaded: () => {}, onUpdateProjectId: () => {}, - onUpdateProjectTitle: () => {}, onVmInit: (/* vm */) => {} }; @@ -172,7 +142,6 @@ const mapStateToProps = state => { isPlayerOnly: state.scratchGui.mode.isPlayerOnly, isRtl: state.locales.isRtl, isShowingProject: getIsShowingProject(loadingState), - isShowingWithoutId: getIsShowingWithoutId(loadingState), loadingStateVisible: state.scratchGui.modals.loadingProject, projectId: state.scratchGui.projectState.projectId, soundsTabVisible: state.scratchGui.editorTab.activeTabIndex === SOUNDS_TAB_INDEX, @@ -193,8 +162,7 @@ const mapDispatchToProps = dispatch => ({ onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)), onRequestCloseBackdropLibrary: () => dispatch(closeBackdropLibrary()), onRequestCloseCostumeLibrary: () => dispatch(closeCostumeLibrary()), - onRequestCloseTelemetryModal: () => dispatch(closeTelemetryModal()), - onUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title)) + onRequestCloseTelemetryModal: () => dispatch(closeTelemetryModal()) }); const ConnectedGUI = injectIntl(connect( @@ -211,6 +179,7 @@ const WrappedGui = compose( FontLoaderHOC, QueryParserHOC, ProjectFetcherHOC, + TitledHOC, ProjectSaverHOC, vmListenerHOC, vmManagerHOC, diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx index 377d6150d..fc8c33b8e 100644 --- a/src/lib/titled-hoc.jsx +++ b/src/lib/titled-hoc.jsx @@ -1,5 +1,19 @@ +import PropTypes from 'prop-types'; import React from 'react'; import bindAll from 'lodash.bindall'; +import {connect} from 'react-redux'; +import {defineMessages, injectIntl, intlShape} from 'react-intl'; + +import {getIsShowingWithoutId} from '../reducers/project-state'; +import {setProjectTitle} from '../reducers/project-title'; + +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 @@ -12,26 +26,89 @@ const TitledHOC = function (WrappedComponent) { bindAll(this, [ 'handleUpdateProjectTitle' ]); - this.state = { - projectTitle: null - }; + } + componentDidMount () { + this.setReduxTitle(this.props.projectTitle); + } + componentDidUpdate (prevProps) { + if (this.props.projectTitle !== prevProps.projectTitle) { + this.setReduxTitle(this.props.projectTitle); + } + if (this.props.isShowingWithoutId && !prevProps.isShowingWithoutId) { + const defaultProjectTitle = this.titleWithDefault(); + this.setReduxTitle(defaultProjectTitle); + this.props.onUpdateProjectTitle(defaultProjectTitle); + } + } + titleWithDefault (title) { + if (title === null || typeof title === 'undefined') { + return this.props.intl.formatMessage(messages.defaultProjectTitle); + } + return title; + } + setReduxTitle (newTitle) { + if (newTitle === null || typeof newTitle === 'undefined') { + this.props.onUpdateReduxProjectTitle( + this.props.intl.formatMessage(messages.defaultProjectTitle) + ); + } else { + this.props.onUpdateReduxProjectTitle(newTitle); + } } handleUpdateProjectTitle (newTitle) { - this.setState({projectTitle: newTitle}); + this.setReduxTitle(newTitle); + this.props.onUpdateProjectTitle(newTitle); } render () { + const { + /* eslint-disable no-unused-vars */ + intl, + isShowingWithoutId, + // for children, we replace onUpdateProjectTitle with our own + onUpdateProjectTitle, + onUpdateReduxProjectTitle, + // we don't pass projectTitle prop to children -- they must use + // redux value + projectTitle, + /* eslint-enable no-unused-vars */ + ...componentProps + } = this.props; return ( ); } } - return TitledComponent; + TitledComponent.propTypes = { + intl: intlShape, + isShowingWithoutId: PropTypes.bool, + onUpdateProjectTitle: PropTypes.func, + onUpdateReduxProjectTitle: PropTypes.func, + projectTitle: PropTypes.string + }; + + TitledComponent.defaultProps = { + onUpdateProjectTitle: () => {} + }; + + const mapStateToProps = state => { + const loadingState = state.scratchGui.projectState.loadingState; + return { + isShowingWithoutId: getIsShowingWithoutId(loadingState) + }; + }; + + const mapDispatchToProps = dispatch => ({ + onUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title)) + }); + + return injectIntl(connect( + mapStateToProps, + mapDispatchToProps, + )(TitledComponent)); }; export { From 9c33bbb2ee6d34ca0d995c6b8c642296af2baa7d Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Sat, 14 Sep 2019 20:17:11 -0400 Subject: [PATCH 3/8] remove TitledHOC from playground, and explicitly pass canEditTitle --- src/playground/player.jsx | 5 ++--- src/playground/render-gui.jsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/playground/player.jsx b/src/playground/player.jsx index 8224ad648..ae4cba1c9 100644 --- a/src/playground/player.jsx +++ b/src/playground/player.jsx @@ -9,7 +9,6 @@ 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'; @@ -24,6 +23,7 @@ const Player = ({isPlayerOnly, onSeeInside, projectId}) => ( {isPlayerOnly && } { @@ -37,8 +36,7 @@ export default appTarget => { // ability to compose reducers. const WrappedGui = compose( AppStateHOC, - HashParserHOC, - TitledHOC + HashParserHOC )(GUI); // TODO a hack for testing the backpack, allow backpack host to be set by url param @@ -67,6 +65,7 @@ export default appTarget => { // important: this is checking whether `simulateScratchDesktop` is truthy, not just defined! simulateScratchDesktop ? Date: Fri, 11 Oct 2019 16:17:02 -0400 Subject: [PATCH 4/8] =?UTF-8?q?don=E2=80=99t=20pass=20onUpdateProjectTitle?= =?UTF-8?q?=20below=20titledHIC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/gui/gui.jsx | 3 -- src/components/menu-bar/menu-bar.jsx | 3 -- .../menu-bar/project-title-input.jsx | 52 ++++++++----------- src/containers/gui.jsx | 2 - src/containers/sb-file-uploader.jsx | 8 +-- src/lib/titled-hoc.jsx | 47 ++++++----------- .../unit/containers/sb-file-uploader.test.jsx | 3 -- 7 files changed, 41 insertions(+), 77 deletions(-) diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index ab7ec87b0..9846eed75 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -94,7 +94,6 @@ const GUIComponent = props => { onLogOut, onOpenRegistration, onToggleLoginOpen, - onUpdateProjectTitle, onActivateCostumesTab, onActivateSoundsTab, onActivateTab, @@ -228,7 +227,6 @@ const GUIComponent = props => { onSeeCommunity={onSeeCommunity} onShare={onShare} onToggleLoginOpen={onToggleLoginOpen} - onUpdateProjectTitle={onUpdateProjectTitle} /> @@ -406,7 +404,6 @@ GUIComponent.propTypes = { onTelemetryModalOptIn: PropTypes.func, onTelemetryModalOptOut: PropTypes.func, onToggleLoginOpen: PropTypes.func, - onUpdateProjectTitle: PropTypes.func, renderLogin: PropTypes.func, showComingSoon: PropTypes.bool, soundsTabVisible: PropTypes.bool, diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx index 1fa7a01cc..98bc128a9 100644 --- a/src/components/menu-bar/menu-bar.jsx +++ b/src/components/menu-bar/menu-bar.jsx @@ -395,7 +395,6 @@ class MenuBar extends React.Component { {(className, renderFileInput, handleLoadProject) => ( @@ -746,7 +744,6 @@ MenuBar.propTypes = { onSeeCommunity: PropTypes.func, onShare: PropTypes.func, onToggleLoginOpen: PropTypes.func, - onUpdateProjectTitle: PropTypes.func, projectTitle: PropTypes.string, renderLogin: PropTypes.func, sessionExists: PropTypes.bool, diff --git a/src/components/menu-bar/project-title-input.jsx b/src/components/menu-bar/project-title-input.jsx index b9b5aa6d8..4f90914a3 100644 --- a/src/components/menu-bar/project-title-input.jsx +++ b/src/components/menu-bar/project-title-input.jsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; 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 {setProjectTitle} from '../../reducers/project-title'; import BufferedInputHOC from '../forms/buffered-input-hoc.jsx'; import Input from '../forms/input.jsx'; @@ -19,39 +19,27 @@ const messages = defineMessages({ } }); -class ProjectTitleInput extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'handleUpdateProjectTitle' - ]); - } - // call onUpdateProjectTitle if it is defined (only defined when gui - // is used within scratch-www) - handleUpdateProjectTitle (newTitle) { - if (this.props.onUpdateProjectTitle) { - this.props.onUpdateProjectTitle(newTitle); - } - } - render () { - return ( - - ); - } -} +const ProjectTitleInput = ({ + className, + handleUpdateReduxProjectTitle, + intl, + projectTitle +}) => ( + +); ProjectTitleInput.propTypes = { className: PropTypes.string, + handleUpdateReduxProjectTitle: PropTypes.func, intl: intlShape.isRequired, - onUpdateProjectTitle: PropTypes.func, projectTitle: PropTypes.string }; @@ -59,7 +47,9 @@ const mapStateToProps = state => ({ projectTitle: state.scratchGui.projectTitle }); -const mapDispatchToProps = () => ({}); +const mapDispatchToProps = dispatch => ({ + handleUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title)) +}); export default injectIntl(connect( mapStateToProps, diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index d7b8f78c2..314c38893 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -103,13 +103,11 @@ GUI.propTypes = { isLoading: PropTypes.bool, isScratchDesktop: PropTypes.bool, isShowingProject: PropTypes.bool, - isShowingWithoutId: PropTypes.bool, loadingStateVisible: PropTypes.bool, onProjectLoaded: PropTypes.func, onSeeCommunity: PropTypes.func, onStorageInit: PropTypes.func, onUpdateProjectId: PropTypes.func, - onUpdateProjectTitle: PropTypes.func, onVmInit: PropTypes.func, projectHost: PropTypes.string, projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), diff --git a/src/containers/sb-file-uploader.jsx b/src/containers/sb-file-uploader.jsx index ae5f42915..c4f484c22 100644 --- a/src/containers/sb-file-uploader.jsx +++ b/src/containers/sb-file-uploader.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {connect} from 'react-redux'; import {defineMessages, injectIntl, intlShape} from 'react-intl'; +import {setProjectTitle} from '../reducers/project-title'; import log from '../lib/log'; import sharedMessages from '../lib/shared-messages'; @@ -131,7 +132,7 @@ class SBFileUploader extends React.Component { // This is necessary in case the user wants to reload a project if (filename) { const uploadedProjectTitle = this.getProjectTitleFromFilename(filename); - this.props.onUpdateProjectTitle(uploadedProjectTitle); + this.props.updateReduxProjectTitle(uploadedProjectTitle); } this.resetFileInput(); }) @@ -179,9 +180,9 @@ SBFileUploader.propTypes = { loadingState: PropTypes.oneOf(LoadingStates), onLoadingFinished: PropTypes.func, onLoadingStarted: PropTypes.func, - onUpdateProjectTitle: PropTypes.func, projectChanged: PropTypes.bool, requestProjectUpload: PropTypes.func, + updateReduxProjectTitle: PropTypes.func, userOwnsProject: PropTypes.bool, vm: PropTypes.shape({ loadProject: PropTypes.func @@ -209,7 +210,8 @@ const mapDispatchToProps = (dispatch, ownProps) => ({ dispatch(closeFileMenu()); }, requestProjectUpload: loadingState => dispatch(requestProjectUpload(loadingState)), - onLoadingStarted: () => dispatch(openLoadingProject()) + onLoadingStarted: () => dispatch(openLoadingProject()), + updateReduxProjectTitle: title => dispatch(setProjectTitle(title)) }); // Allow incoming props to override redux-provided props. Used to mock in tests. diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx index fc8c33b8e..30ed66217 100644 --- a/src/lib/titled-hoc.jsx +++ b/src/lib/titled-hoc.jsx @@ -1,6 +1,5 @@ import PropTypes from 'prop-types'; import React from 'react'; -import bindAll from 'lodash.bindall'; import {connect} from 'react-redux'; import {defineMessages, injectIntl, intlShape} from 'react-intl'; @@ -21,23 +20,18 @@ const messages = defineMessages({ */ const TitledHOC = function (WrappedComponent) { class TitledComponent extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'handleUpdateProjectTitle' - ]); - } componentDidMount () { - this.setReduxTitle(this.props.projectTitle); + this.props.updateReduxProjectTitle(this.titleWithDefault(this.props.projectTitle)); } componentDidUpdate (prevProps) { if (this.props.projectTitle !== prevProps.projectTitle) { - this.setReduxTitle(this.props.projectTitle); + this.props.updateReduxProjectTitle(this.titleWithDefault(this.props.projectTitle)); } - if (this.props.isShowingWithoutId && !prevProps.isShowingWithoutId) { - const defaultProjectTitle = this.titleWithDefault(); - this.setReduxTitle(defaultProjectTitle); - this.props.onUpdateProjectTitle(defaultProjectTitle); + // if the projectTitle hasn't changed, but the reduxProjectTitle + // HAS changed, we need to report that change to the projectTitle's owner + if (this.props.reduxProjectTitle !== prevProps.reduxProjectTitle && + this.props.reduxProjectTitle !== this.props.projectTitle) { + this.props.onUpdateProjectTitle(this.props.reduxProjectTitle); } } titleWithDefault (title) { @@ -46,19 +40,6 @@ const TitledHOC = function (WrappedComponent) { } return title; } - setReduxTitle (newTitle) { - if (newTitle === null || typeof newTitle === 'undefined') { - this.props.onUpdateReduxProjectTitle( - this.props.intl.formatMessage(messages.defaultProjectTitle) - ); - } else { - this.props.onUpdateReduxProjectTitle(newTitle); - } - } - handleUpdateProjectTitle (newTitle) { - this.setReduxTitle(newTitle); - this.props.onUpdateProjectTitle(newTitle); - } render () { const { /* eslint-disable no-unused-vars */ @@ -66,16 +47,16 @@ const TitledHOC = function (WrappedComponent) { isShowingWithoutId, // for children, we replace onUpdateProjectTitle with our own onUpdateProjectTitle, - onUpdateReduxProjectTitle, // we don't pass projectTitle prop to children -- they must use // redux value projectTitle, + reduxProjectTitle, + updateReduxProjectTitle, /* eslint-enable no-unused-vars */ ...componentProps } = this.props; return ( ); @@ -86,8 +67,9 @@ const TitledHOC = function (WrappedComponent) { intl: intlShape, isShowingWithoutId: PropTypes.bool, onUpdateProjectTitle: PropTypes.func, - onUpdateReduxProjectTitle: PropTypes.func, - projectTitle: PropTypes.string + projectTitle: PropTypes.string, + reduxProjectTitle: PropTypes.string, + updateReduxProjectTitle: PropTypes.func }; TitledComponent.defaultProps = { @@ -97,12 +79,13 @@ const TitledHOC = function (WrappedComponent) { const mapStateToProps = state => { const loadingState = state.scratchGui.projectState.loadingState; return { - isShowingWithoutId: getIsShowingWithoutId(loadingState) + isShowingWithoutId: getIsShowingWithoutId(loadingState), + reduxProjectTitle: state.scratchGui.projectTitle }; }; const mapDispatchToProps = dispatch => ({ - onUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title)) + updateReduxProjectTitle: title => dispatch(setProjectTitle(title)) }); return injectIntl(connect( diff --git a/test/unit/containers/sb-file-uploader.test.jsx b/test/unit/containers/sb-file-uploader.test.jsx index 083dbfa2d..c757114b4 100644 --- a/test/unit/containers/sb-file-uploader.test.jsx +++ b/test/unit/containers/sb-file-uploader.test.jsx @@ -11,7 +11,6 @@ describe('SBFileUploader Container', () => { const mockStore = configureStore(); let onLoadingFinished; let onLoadingStarted; - let onUpdateProjectTitle; let store; // Wrap this in a function so it gets test specific states and can be reused. @@ -20,7 +19,6 @@ describe('SBFileUploader Container', () => { {(renderFileInput, loadProject) => (
{ vm: {} } }); - onUpdateProjectTitle = jest.fn(); onLoadingFinished = jest.fn(); onLoadingStarted = jest.fn(); }); From 68780ae6cf04476eb38ca7bcf2692e00d2c6431d Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 17 Oct 2019 13:26:21 -0400 Subject: [PATCH 5/8] added back missing canEditTitle --- src/playground/render-gui.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx index 2f5ccc8a7..0f15fbfa7 100644 --- a/src/playground/render-gui.jsx +++ b/src/playground/render-gui.jsx @@ -74,6 +74,7 @@ export default appTarget => { onTelemetryModalOptOut={handleTelemetryModalOptOut} /> : Date: Fri, 18 Oct 2019 13:40:58 -0400 Subject: [PATCH 6/8] better names for prop functions --- src/components/menu-bar/project-title-input.jsx | 8 ++++---- src/containers/sb-file-uploader.jsx | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/menu-bar/project-title-input.jsx b/src/components/menu-bar/project-title-input.jsx index 4f90914a3..fe7647cb0 100644 --- a/src/components/menu-bar/project-title-input.jsx +++ b/src/components/menu-bar/project-title-input.jsx @@ -21,8 +21,8 @@ const messages = defineMessages({ const ProjectTitleInput = ({ className, - handleUpdateReduxProjectTitle, intl, + onSubmit, projectTitle }) => ( ); ProjectTitleInput.propTypes = { className: PropTypes.string, - handleUpdateReduxProjectTitle: PropTypes.func, intl: intlShape.isRequired, + onSubmit: PropTypes.func, projectTitle: PropTypes.string }; @@ -48,7 +48,7 @@ const mapStateToProps = state => ({ }); const mapDispatchToProps = dispatch => ({ - handleUpdateReduxProjectTitle: title => dispatch(setProjectTitle(title)) + onSubmit: title => dispatch(setProjectTitle(title)) }); export default injectIntl(connect( diff --git a/src/containers/sb-file-uploader.jsx b/src/containers/sb-file-uploader.jsx index c4f484c22..2019f719e 100644 --- a/src/containers/sb-file-uploader.jsx +++ b/src/containers/sb-file-uploader.jsx @@ -132,7 +132,7 @@ class SBFileUploader extends React.Component { // This is necessary in case the user wants to reload a project if (filename) { const uploadedProjectTitle = this.getProjectTitleFromFilename(filename); - this.props.updateReduxProjectTitle(uploadedProjectTitle); + this.props.onReceivedProjectTitle(uploadedProjectTitle); } this.resetFileInput(); }) @@ -182,7 +182,7 @@ SBFileUploader.propTypes = { onLoadingStarted: PropTypes.func, projectChanged: PropTypes.bool, requestProjectUpload: PropTypes.func, - updateReduxProjectTitle: PropTypes.func, + onReceivedProjectTitle: PropTypes.func, userOwnsProject: PropTypes.bool, vm: PropTypes.shape({ loadProject: PropTypes.func @@ -211,7 +211,7 @@ const mapDispatchToProps = (dispatch, ownProps) => ({ }, requestProjectUpload: loadingState => dispatch(requestProjectUpload(loadingState)), onLoadingStarted: () => dispatch(openLoadingProject()), - updateReduxProjectTitle: title => dispatch(setProjectTitle(title)) + onReceivedProjectTitle: title => dispatch(setProjectTitle(title)) }); // Allow incoming props to override redux-provided props. Used to mock in tests. From f4a9bfac9f1c5c95879ec9c9b2f8a904cfe29728 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 18 Oct 2019 13:41:20 -0400 Subject: [PATCH 7/8] in titledhoc, consolidate function calls --- src/lib/titled-hoc.jsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx index 30ed66217..b1d431aa9 100644 --- a/src/lib/titled-hoc.jsx +++ b/src/lib/titled-hoc.jsx @@ -21,11 +21,11 @@ const messages = defineMessages({ const TitledHOC = function (WrappedComponent) { class TitledComponent extends React.Component { componentDidMount () { - this.props.updateReduxProjectTitle(this.titleWithDefault(this.props.projectTitle)); + this.updateReduxProjectTitleWithDefault(this.props.projectTitle); } componentDidUpdate (prevProps) { if (this.props.projectTitle !== prevProps.projectTitle) { - this.props.updateReduxProjectTitle(this.titleWithDefault(this.props.projectTitle)); + this.updateReduxProjectTitleWithDefault(this.props.projectTitle); } // if the projectTitle hasn't changed, but the reduxProjectTitle // HAS changed, we need to report that change to the projectTitle's owner @@ -34,11 +34,12 @@ const TitledHOC = function (WrappedComponent) { this.props.onUpdateProjectTitle(this.props.reduxProjectTitle); } } - titleWithDefault (title) { - if (title === null || typeof title === 'undefined') { - return this.props.intl.formatMessage(messages.defaultProjectTitle); + updateReduxProjectTitleWithDefault (requestedTitle) { + let newTitle = requestedTitle; + if (newTitle === null || typeof newTitle === 'undefined') { + newTitle = this.props.intl.formatMessage(messages.defaultProjectTitle); } - return title; + this.props.updateReduxProjectTitle(newTitle); } render () { const { From e3fa196adec4cddaa7537fc621050272d43eb624 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 18 Oct 2019 17:53:28 -0400 Subject: [PATCH 8/8] revised name of updateReduxProjectTitle, updateReduxProjectTitleWithDefault --- src/lib/titled-hoc.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/titled-hoc.jsx b/src/lib/titled-hoc.jsx index b1d431aa9..611599008 100644 --- a/src/lib/titled-hoc.jsx +++ b/src/lib/titled-hoc.jsx @@ -21,11 +21,11 @@ const messages = defineMessages({ const TitledHOC = function (WrappedComponent) { class TitledComponent extends React.Component { componentDidMount () { - this.updateReduxProjectTitleWithDefault(this.props.projectTitle); + this.handleReceivedProjectTitle(this.props.projectTitle); } componentDidUpdate (prevProps) { if (this.props.projectTitle !== prevProps.projectTitle) { - this.updateReduxProjectTitleWithDefault(this.props.projectTitle); + this.handleReceivedProjectTitle(this.props.projectTitle); } // if the projectTitle hasn't changed, but the reduxProjectTitle // HAS changed, we need to report that change to the projectTitle's owner @@ -34,25 +34,25 @@ const TitledHOC = function (WrappedComponent) { this.props.onUpdateProjectTitle(this.props.reduxProjectTitle); } } - updateReduxProjectTitleWithDefault (requestedTitle) { + handleReceivedProjectTitle (requestedTitle) { let newTitle = requestedTitle; if (newTitle === null || typeof newTitle === 'undefined') { newTitle = this.props.intl.formatMessage(messages.defaultProjectTitle); } - this.props.updateReduxProjectTitle(newTitle); + this.props.onChangedProjectTitle(newTitle); } render () { const { /* eslint-disable no-unused-vars */ intl, isShowingWithoutId, + onChangedProjectTitle, // for children, we replace onUpdateProjectTitle with our own onUpdateProjectTitle, // we don't pass projectTitle prop to children -- they must use // redux value projectTitle, reduxProjectTitle, - updateReduxProjectTitle, /* eslint-enable no-unused-vars */ ...componentProps } = this.props; @@ -67,10 +67,10 @@ const TitledHOC = function (WrappedComponent) { TitledComponent.propTypes = { intl: intlShape, isShowingWithoutId: PropTypes.bool, + onChangedProjectTitle: PropTypes.func, onUpdateProjectTitle: PropTypes.func, projectTitle: PropTypes.string, - reduxProjectTitle: PropTypes.string, - updateReduxProjectTitle: PropTypes.func + reduxProjectTitle: PropTypes.string }; TitledComponent.defaultProps = { @@ -86,7 +86,7 @@ const TitledHOC = function (WrappedComponent) { }; const mapDispatchToProps = dispatch => ({ - updateReduxProjectTitle: title => dispatch(setProjectTitle(title)) + onChangedProjectTitle: title => dispatch(setProjectTitle(title)) }); return injectIntl(connect(