From b63ac51fb1e022e17303062d84408c2e62a739a2 Mon Sep 17 00:00:00 2001 From: Ben Wheeler <wheeler.benjamin@gmail.com> Date: Sat, 14 Sep 2019 20:16:04 -0400 Subject: [PATCH] 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 ( <WrappedComponent - canEditTitle - projectTitle={this.state.projectTitle} onUpdateProjectTitle={this.handleUpdateProjectTitle} - {...this.props} + {...componentProps} /> ); } } - 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 { -- GitLab