From 80de164d0201de74334c40d78df00ae4e0f3fbeb Mon Sep 17 00:00:00 2001 From: chrisgarrity Date: Thu, 24 May 2018 16:23:07 -0400 Subject: [PATCH] Handle See inside button (#1890) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Handle See inside button Import GUI reducers, initial state and middleware (throttle). Update render to handle state and middleware. Remove preview mode state and use scratchGui.mode from redux instead. URL is getting updated, and mode will be updated based on URL. However, the history needs work - it doesn’t work with the modal back. * Update to latest GUI This is the minimum version of GUI that is needed for see-inside * Really remove cruft. --- package.json | 2 +- src/lib/render.jsx | 23 ++++-- src/views/preview/presentation.jsx | 2 +- src/views/preview/preview.jsx | 116 ++++++++++++++++++++++++----- 4 files changed, 118 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 61b2d4a12..143b05f6e 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "redux-thunk": "2.0.1", "sass-lint": "1.5.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20180427201459", + "scratch-gui": "0.1.0-prerelease.20180522203439", "scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master", "slick-carousel": "1.6.0", "source-map-support": "0.3.2", diff --git a/src/lib/render.jsx b/src/lib/render.jsx index 394eee801..3bbc66743 100644 --- a/src/lib/render.jsx +++ b/src/lib/render.jsx @@ -14,11 +14,13 @@ require('../main.scss'); /** * Function to render views into a full page - * @param {object} jsx jsx component of the view - * @param {object} element html element to render to on the template - * @param {array} reducers list of view-specific reducers + * @param {object} jsx jsx component of the view + * @param {object} element html element to render to on the template + * @param {array} reducers list of view-specific reducers + * @param {object} initialState optional initialState for store + * @param {bool} enhancer whether or not to apply redux-throttle middleware */ -const render = (jsx, element, reducers) => { +const render = (jsx, element, reducers, initialState, enhancer) => { // Get locale and messages from global namespace (see "init.js") let locale = window._locale || 'en'; let messages = {}; @@ -35,9 +37,20 @@ const render = (jsx, element, reducers) => { } const allReducers = reducer(reducers); + + const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || redux.compose; + const enhancers = enhancer ? + composeEnhancers( + redux.applyMiddleware(thunk), + enhancer + ) : + composeEnhancers( + redux.applyMiddleware(thunk) + ); const store = redux.createStore( allReducers, - redux.applyMiddleware(thunk) + initialState || {}, + enhancers ); // Render view component diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx index 79df3f93b..d72170592 100644 --- a/src/views/preview/presentation.jsx +++ b/src/views/preview/presentation.jsx @@ -306,7 +306,7 @@ PreviewPresentation.propTypes = { onLoveClicked: PropTypes.func, onSeeInside: PropTypes.func, onUpdate: PropTypes.func, - projectId: PropTypes.number, + projectId: PropTypes.string, projectInfo: PropTypes.shape({ id: PropTypes.number, title: PropTypes.string, diff --git a/src/views/preview/preview.jsx b/src/views/preview/preview.jsx index fb40c2cff..3c040377d 100644 --- a/src/views/preview/preview.jsx +++ b/src/views/preview/preview.jsx @@ -10,21 +10,26 @@ const PreviewPresentation = require('./presentation.jsx'); const sessionActions = require('../../redux/session.js'); const previewActions = require('../../redux/preview.js'); -const GUI = require('scratch-gui').default; -const IntlGUI = injectIntl(GUI); + +const GUI = require('scratch-gui'); +const IntlGUI = injectIntl(GUI.default); class Preview extends React.Component { constructor (props) { super(props); bindAll(this, [ + 'addEventListeners', 'handleFavoriteToggle', 'handleLoveToggle', 'handlePermissions', + 'handlePopState', 'handleSeeInside', 'handleUpdate', - 'initCounts' + 'initCounts', + 'pushHistory' ]); this.state = this.initState(); + this.addEventListeners(); } componentDidUpdate (prevProps) { if (this.props.sessionStatus !== prevProps.sessionStatus && @@ -52,6 +57,51 @@ class Preview extends React.Component { this.props.getCreditInfo(this.props.projectInfo.remix.root); } } + if (this.props.playerMode !== prevProps.playerMode || this.props.fullScreen !== prevProps.fullScreen) { + this.pushHistory(history.state === null); + } + } + componentWillUnmount () { + this.removeEventListeners(); + } + addEventListeners () { + window.addEventListener('popstate', this.handlePopState); + } + removeEventListeners () { + window.removeEventListener('popstate', this.handlePopState); + } + handlePopState () { + const path = window.location.pathname.toLowerCase(); + const playerMode = path.indexOf('editor') === -1; + const fullScreen = path.indexOf('fullscreen') !== -1; + if (this.props.playerMode !== playerMode) { + this.props.setPlayer(playerMode); + } + if (this.props.fullScreen !== fullScreen) { + this.props.setFullScreen(fullScreen); + } + } + pushHistory (push) { + // update URI to match mode + const idPath = this.state.projectId ? `${this.state.projectId}/` : ''; + let modePath = ''; + if (!this.props.playerMode) modePath = 'editor/'; + // fullscreen overrides editor + if (this.props.fullScreen) modePath = 'fullscreen/'; + const newPath = `/preview/${idPath}${modePath}`; + if (push) { + history.pushState( + {}, + document.title, + newPath + ); + } else { + history.replaceState( + {}, + document.title, + newPath + ); + } } initState () { const pathname = window.location.pathname.toLowerCase(); @@ -62,10 +112,8 @@ class Preview extends React.Component { return { editable: false, favoriteCount: 0, - inEditor: parts.indexOf('editor') !== -1, - isFullScreen: parts.indexOf('fullscreen') !== -1, loveCount: 0, - projectId: parts[1] === 'editor' ? null : parts[1] + projectId: parts[1] === 'editor' ? 0 : parts[1] }; } handleFavoriteToggle () { @@ -109,8 +157,7 @@ class Preview extends React.Component { } } handleSeeInside () { - this.setState({inEditor: true}); - history.pushState({}, document.title, `/preview/${this.state.projectId}/editor`); + this.props.setPlayer(false); } handleUpdate (jsonData) { this.props.updateProject( @@ -128,13 +175,7 @@ class Preview extends React.Component { } render () { return ( - this.state.inEditor ? - : + this.props.playerMode ? - + : + ); } } @@ -186,6 +233,7 @@ Preview.propTypes = { }) }), faved: PropTypes.bool, + fullScreen: PropTypes.bool, getCreditInfo: PropTypes.func.isRequired, getFavedStatus: PropTypes.func.isRequired, getLovedStatus: PropTypes.func.isRequired, @@ -193,6 +241,7 @@ Preview.propTypes = { getRemixes: PropTypes.func.isRequired, getStudios: PropTypes.func.isRequired, loved: PropTypes.bool, + playerMode: PropTypes.bool, projectInfo: PropTypes.shape({ author: PropTypes.shape({ id: PropTypes.number, @@ -219,7 +268,9 @@ Preview.propTypes = { remixes: PropTypes.arrayOf(PropTypes.object), sessionStatus: PropTypes.string, setFavedStatus: PropTypes.func.isRequired, + setFullScreen: PropTypes.func.isRequired, setLovedStatus: PropTypes.func.isRequired, + setPlayer: PropTypes.func.isRequired, studios: PropTypes.arrayOf(PropTypes.object), updateProject: PropTypes.func.isRequired, user: PropTypes.shape({ @@ -248,7 +299,9 @@ const mapStateToProps = state => ({ remixes: state.preview.remixes, sessionStatus: state.session.status, studios: state.preview.studios, - user: state.session.session.user + user: state.session.session.user, + playerMode: state.scratchGui.mode.isPlayerOnly, + fullScreen: state.scratchGui.mode.isFullScreen }); @@ -285,6 +338,12 @@ const mapDispatchToProps = dispatch => ({ }, updateProject: (id, formData, username, token) => { dispatch(previewActions.updateProject(id, formData, username, token)); + }, + setPlayer: player => { + dispatch(GUI.setPlayer(player)); + }, + setFullScreen: fullscreen => { + dispatch(GUI.setFullScreen(fullscreen)); } }); @@ -293,8 +352,29 @@ const ConnectedPreview = connect( mapDispatchToProps )(Preview); +GUI.setAppElement(document.getElementById('app')); +const initGuiState = guiInitialState => { + const pathname = window.location.pathname.toLowerCase(); + const parts = pathname.split('/').filter(Boolean); + // parts[0]: 'preview' + // parts[1]: either :id or 'editor' + // parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen' + if (parts.indexOf('editor') === -1) { + guiInitialState = GUI.initPlayer(guiInitialState); + } + if (parts.indexOf('fullscreen') !== -1) { + guiInitialState = GUI.initFullScreen(guiInitialState); + } + return guiInitialState; +}; + render( , document.getElementById('app'), - {preview: previewActions.previewReducer} + { + preview: previewActions.previewReducer, + ...GUI.guiReducers + }, + {scratchGui: initGuiState(GUI.guiInitialState)}, + GUI.guiMiddleware );