From 22e2c17ff4a11a78ba5a7a48d3bcd868b10f6043 Mon Sep 17 00:00:00 2001
From: Ben Wheeler <wheeler.benjamin@gmail.com>
Date: Fri, 11 Oct 2019 16:17:02 -0400
Subject: [PATCH] =?UTF-8?q?don=E2=80=99t=20pass=20onUpdateProjectTitle=20b?=
 =?UTF-8?q?elow=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}
                 />
                 <Box className={styles.bodyWrapper}>
                     <Box className={styles.flexWrapper}>
@@ -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 {
                                         <SBFileUploader
                                             canSave={this.props.canSave}
                                             userOwnsProject={this.props.userOwnsProject}
-                                            onUpdateProjectTitle={this.props.onUpdateProjectTitle}
                                         >
                                             {(className, renderFileInput, handleLoadProject) => (
                                                 <MenuItem
@@ -495,7 +494,6 @@ class MenuBar extends React.Component {
                             >
                                 <ProjectTitleInput
                                     className={classNames(styles.titleFieldGrowable)}
-                                    onUpdateProjectTitle={this.props.onUpdateProjectTitle}
                                 />
                             </MenuBarItemTooltip>
                         </div>
@@ -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 (
-            <BufferedInput
-                className={classNames(styles.titleField, this.props.className)}
-                maxLength="100"
-                placeholder={this.props.intl.formatMessage(messages.projectTitlePlaceholder)}
-                tabIndex="0"
-                type="text"
-                value={this.props.projectTitle}
-                onSubmit={this.handleUpdateProjectTitle}
-            />
-        );
-    }
-}
+const ProjectTitleInput = ({
+    className,
+    handleUpdateReduxProjectTitle,
+    intl,
+    projectTitle
+}) => (
+    <BufferedInput
+        className={classNames(styles.titleField, className)}
+        maxLength="100"
+        placeholder={intl.formatMessage(messages.projectTitlePlaceholder)}
+        tabIndex="0"
+        type="text"
+        value={projectTitle}
+        onSubmit={handleUpdateReduxProjectTitle}
+    />
+);
 
 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 (
                 <WrappedComponent
-                    onUpdateProjectTitle={this.handleUpdateProjectTitle}
                     {...componentProps}
                 />
             );
@@ -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', () => {
             <SBFileUploader
                 onLoadingFinished={onLoadingFinished}
                 onLoadingStarted={onLoadingStarted}
-                onUpdateProjectTitle={onUpdateProjectTitle}
             >
                 {(renderFileInput, loadProject) => (
                     <div
@@ -40,7 +38,6 @@ describe('SBFileUploader Container', () => {
                 vm: {}
             }
         });
-        onUpdateProjectTitle = jest.fn();
         onLoadingFinished = jest.fn();
         onLoadingStarted = jest.fn();
     });
-- 
GitLab