From 00ac42b9440bdb95879e37593b6d1745e0e4e1b7 Mon Sep 17 00:00:00 2001 From: Linda Paiste Date: Wed, 12 Jul 2023 20:46:06 -0500 Subject: [PATCH 1/2] Upgrade react-router to v4 --- client/browserHistory.js | 5 + client/common/Button.jsx | 2 +- client/common/ButtonOrLink.jsx | 2 +- client/components/Nav.jsx | 22 +- client/components/PreviewNav.jsx | 2 +- .../__snapshots__/Nav.unit.test.jsx.snap | 13 +- .../components/createRedirectWithUsername.jsx | 2 +- client/components/mobile/Tab.jsx | 2 +- client/index.integration.test.jsx | 7 +- client/index.jsx | 10 +- client/modules/App/App.jsx | 3 +- client/modules/App/components/Overlay.jsx | 2 +- client/modules/IDE/actions/collections.js | 2 +- client/modules/IDE/actions/project.js | 2 +- client/modules/IDE/components/About.jsx | 2 +- client/modules/IDE/components/AssetList.jsx | 2 +- .../CollectionList/CollectionListRow.jsx | 2 +- client/modules/IDE/components/ErrorModal.jsx | 2 +- .../components/QuickAddList/QuickAddList.jsx | 2 +- client/modules/IDE/components/SketchList.jsx | 2 +- client/modules/IDE/components/Toolbar.jsx | 2 +- .../IDE/components/UploadFileModal.jsx | 2 +- .../SketchList.unit.test.jsx.snap | 8 +- client/modules/IDE/pages/IDEView.jsx | 83 +++-- client/modules/IDE/pages/Legal.jsx | 2 +- client/modules/IDE/pages/MobileIDEView.jsx | 2 +- client/modules/Mobile/MobileDashboardView.jsx | 2 +- client/modules/Mobile/MobilePreferences.jsx | 2 +- client/modules/User/actions.js | 2 +- client/modules/User/components/Collection.jsx | 2 +- .../modules/User/components/CookieConsent.jsx | 2 +- .../User/components/DashboardTabSwitcher.jsx | 2 +- client/modules/User/pages/AccountView.jsx | 3 +- client/modules/User/pages/DashboardView.jsx | 2 +- .../User/pages/EmailVerificationView.jsx | 2 +- client/modules/User/pages/LoginView.jsx | 2 +- .../modules/User/pages/ResetPasswordView.jsx | 2 +- client/modules/User/pages/SignupView.jsx | 2 +- client/routes.jsx | 90 ++--- client/test-utils.js | 11 +- client/utils/auth.js | 4 +- package-lock.json | 339 +++++++++++------- package.json | 4 +- 43 files changed, 391 insertions(+), 269 deletions(-) create mode 100644 client/browserHistory.js diff --git a/client/browserHistory.js b/client/browserHistory.js new file mode 100644 index 0000000000..0bf3a5fdd6 --- /dev/null +++ b/client/browserHistory.js @@ -0,0 +1,5 @@ +import { createBrowserHistory } from 'history'; + +const browserHistory = createBrowserHistory(); + +export default browserHistory; diff --git a/client/common/Button.jsx b/client/common/Button.jsx index b7f3c3bddd..d6dd18491e 100644 --- a/client/common/Button.jsx +++ b/client/common/Button.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { remSize, prop } from '../theme'; diff --git a/client/common/ButtonOrLink.jsx b/client/common/ButtonOrLink.jsx index f2c31e1c6e..924f108024 100644 --- a/client/common/ButtonOrLink.jsx +++ b/client/common/ButtonOrLink.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; /** diff --git a/client/components/Nav.jsx b/client/components/Nav.jsx index 6e6a350d5e..51e125314f 100644 --- a/client/components/Nav.jsx +++ b/client/components/Nav.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { withTranslation } from 'react-i18next'; import { connect } from 'react-redux'; -import { Link, withRouter } from 'react-router'; +import { Link } from 'react-router-dom'; import { availableLanguages, languageKeyToLabel } from '../i18n'; import * as IDEActions from '../modules/IDE/actions/ide'; import * as toastActions from '../modules/IDE/actions/toast'; @@ -37,12 +37,12 @@ class Nav extends React.PureComponent { } handleNew() { - const { unsavedChanges, warnIfUnsavedChanges } = this.props; + const { unsavedChanges } = this.props; if (!unsavedChanges) { this.props.showToast(1500); this.props.setToastText('Toast.OpenedNewSketch'); this.props.newProject(); - } else if (warnIfUnsavedChanges && warnIfUnsavedChanges()) { + } else if (window.confirm(this.props.t('Nav.WarningUnsavedChanges'))) { this.props.showToast(1500); this.props.setToastText('Toast.OpenedNewSketch'); this.props.newProject(); @@ -73,11 +73,10 @@ class Nav extends React.PureComponent { } handleShare() { - const { username } = this.props.params; this.props.showShareModal( this.props.project.id, this.props.project.name, - username + this.props.project.owner.username ); } @@ -351,14 +350,14 @@ Nav.propTypes = { id: PropTypes.string, name: PropTypes.string, owner: PropTypes.shape({ - id: PropTypes.string + id: PropTypes.string, + username: PropTypes.string }) }), logoutUser: PropTypes.func.isRequired, showShareModal: PropTypes.func.isRequired, showErrorModal: PropTypes.func.isRequired, unsavedChanges: PropTypes.bool.isRequired, - warnIfUnsavedChanges: PropTypes.func, showKeyboardShortcutModal: PropTypes.func.isRequired, cmController: PropTypes.shape({ tidyCode: PropTypes.func, @@ -374,9 +373,6 @@ Nav.propTypes = { rootFile: PropTypes.shape({ id: PropTypes.string.isRequired }).isRequired, - params: PropTypes.shape({ - username: PropTypes.string - }), t: PropTypes.func.isRequired, setLanguage: PropTypes.func.isRequired, language: PropTypes.string.isRequired, @@ -391,10 +387,6 @@ Nav.defaultProps = { }, cmController: {}, layout: 'project', - warnIfUnsavedChanges: undefined, - params: { - username: undefined - }, editorLink: '/' }; @@ -420,6 +412,6 @@ const mapDispatchToProps = { }; export default withTranslation()( - withRouter(connect(mapStateToProps, mapDispatchToProps)(Nav)) + connect(mapStateToProps, mapDispatchToProps)(Nav) ); export { Nav as NavComponent }; diff --git a/client/components/PreviewNav.jsx b/client/components/PreviewNav.jsx index 7bb14b0550..fa42a89427 100644 --- a/client/components/PreviewNav.jsx +++ b/client/components/PreviewNav.jsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import LogoIcon from '../images/p5js-logo-small.svg'; diff --git a/client/components/__snapshots__/Nav.unit.test.jsx.snap b/client/components/__snapshots__/Nav.unit.test.jsx.snap index 08f6a665a1..bbd34fb994 100644 --- a/client/components/__snapshots__/Nav.unit.test.jsx.snap +++ b/client/components/__snapshots__/Nav.unit.test.jsx.snap @@ -57,7 +57,9 @@ exports[`Nav renders correctly 1`] = ` @@ -195,7 +197,9 @@ exports[`Nav renders correctly 1`] = ` @@ -229,6 +233,7 @@ exports[`Nav renders dashboard version 1`] = ` > + About diff --git a/client/components/createRedirectWithUsername.jsx b/client/components/createRedirectWithUsername.jsx index 760cd4fcd3..a6b69233c4 100644 --- a/client/components/createRedirectWithUsername.jsx +++ b/client/components/createRedirectWithUsername.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; -import { browserHistory } from 'react-router'; +import browserHistory from '../browserHistory'; const RedirectToUser = ({ username, url = '/:username/sketches' }) => { React.useEffect(() => { diff --git a/client/components/mobile/Tab.jsx b/client/components/mobile/Tab.jsx index 23741f82ec..bd064b3f36 100644 --- a/client/components/mobile/Tab.jsx +++ b/client/components/mobile/Tab.jsx @@ -1,5 +1,5 @@ import styled from 'styled-components'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { prop, remSize } from '../../theme'; export default styled(Link)` diff --git a/client/index.integration.test.jsx b/client/index.integration.test.jsx index c7a803089d..d76345293d 100644 --- a/client/index.integration.test.jsx +++ b/client/index.integration.test.jsx @@ -1,16 +1,14 @@ import { setupServer } from 'msw/node'; import { rest } from 'msw'; import React from 'react'; -import { Router, browserHistory } from 'react-router'; +import Routing from './routes'; import { reduxRender, act, waitFor, screen, within } from './test-utils'; import configureStore from './store'; -import routes from './routes'; import * as Actions from './modules/User/actions'; import { userResponse } from './testData/testServerResponses'; // setup for the app -const history = browserHistory; const initialState = window.__INITIAL_STATE__; const store = configureStore(initialState); @@ -56,8 +54,7 @@ document.createRange = () => { // start testing describe('index.jsx integration', () => { // the subject under test - const subject = () => - reduxRender(, { store }); + const subject = () => reduxRender(, { store }); // spy on this function and wait for it to be called before making assertions const spy = jest.spyOn(Actions, 'getUser'); diff --git a/client/index.jsx b/client/index.jsx index be714fc0d5..a38e9307c6 100644 --- a/client/index.jsx +++ b/client/index.jsx @@ -2,10 +2,11 @@ import React, { Suspense } from 'react'; import { render } from 'react-dom'; import { hot } from 'react-hot-loader/root'; import { Provider } from 'react-redux'; -import { Router, browserHistory } from 'react-router'; +import { Router } from 'react-router-dom'; +import browserHistory from './browserHistory'; import configureStore from './store'; -import routes from './routes'; +import Routing from './routes'; import ThemeProvider from './modules/App/components/ThemeProvider'; import Loader from './modules/App/components/loader'; import './i18n'; @@ -15,7 +16,6 @@ require('./styles/main.scss'); // Load the p5 png logo, so that webpack will use it require('./images/p5js-square-logo.png'); -const history = browserHistory; const initialState = window.__INITIAL_STATE__; const store = configureStore(initialState); @@ -23,7 +23,9 @@ const store = configureStore(initialState); const App = () => ( - + + + ); diff --git a/client/modules/App/App.jsx b/client/modules/App/App.jsx index 1f41446a79..d4d335c9e7 100644 --- a/client/modules/App/App.jsx +++ b/client/modules/App/App.jsx @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; +import { withRouter } from 'react-router-dom'; import getConfig from '../../utils/getConfig'; import DevTools from './components/DevTools'; import { setPreviousPath } from '../IDE/actions/ide'; @@ -87,4 +88,4 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = { setPreviousPath, setLanguage }; -export default connect(mapStateToProps, mapDispatchToProps)(App); +export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App)); diff --git a/client/modules/App/components/Overlay.jsx b/client/modules/App/components/Overlay.jsx index 39f492fc6f..bddc2983e1 100644 --- a/client/modules/App/components/Overlay.jsx +++ b/client/modules/App/components/Overlay.jsx @@ -1,8 +1,8 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { browserHistory } from 'react-router'; import { withTranslation } from 'react-i18next'; +import browserHistory from '../../../browserHistory'; import ExitIcon from '../../../images/exit.svg'; class Overlay extends React.Component { diff --git a/client/modules/IDE/actions/collections.js b/client/modules/IDE/actions/collections.js index 69da0f23ba..5a9218520b 100644 --- a/client/modules/IDE/actions/collections.js +++ b/client/modules/IDE/actions/collections.js @@ -1,4 +1,4 @@ -import { browserHistory } from 'react-router'; +import browserHistory from '../../../browserHistory'; import apiClient from '../../../utils/apiClient'; import * as ActionTypes from '../../../constants'; import { startLoader, stopLoader } from './loader'; diff --git a/client/modules/IDE/actions/project.js b/client/modules/IDE/actions/project.js index 9a528a34f6..a3f354a93c 100644 --- a/client/modules/IDE/actions/project.js +++ b/client/modules/IDE/actions/project.js @@ -1,7 +1,7 @@ -import { browserHistory } from 'react-router'; import objectID from 'bson-objectid'; import each from 'async/each'; import isEqual from 'lodash/isEqual'; +import browserHistory from '../../../browserHistory'; import apiClient from '../../../utils/apiClient'; import getConfig from '../../../utils/getConfig'; import * as ActionTypes from '../../../constants'; diff --git a/client/modules/IDE/components/About.jsx b/client/modules/IDE/components/About.jsx index 6a68828a65..e9a17f141d 100644 --- a/client/modules/IDE/components/About.jsx +++ b/client/modules/IDE/components/About.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { useSelector } from 'react-redux'; import { Helmet } from 'react-helmet'; import { useTranslation } from 'react-i18next'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import SquareLogoIcon from '../../../images/p5js-square-logo.svg'; // import PlayIcon from '../../../images/play.svg'; import AsteriskIcon from '../../../images/p5-asterisk.svg'; diff --git a/client/modules/IDE/components/AssetList.jsx b/client/modules/IDE/components/AssetList.jsx index a77a0d6d66..559f60c580 100644 --- a/client/modules/IDE/components/AssetList.jsx +++ b/client/modules/IDE/components/AssetList.jsx @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { Helmet } from 'react-helmet'; import prettyBytes from 'pretty-bytes'; import { withTranslation } from 'react-i18next'; diff --git a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx index bb5282027c..ed109141d7 100644 --- a/client/modules/IDE/components/CollectionList/CollectionListRow.jsx +++ b/client/modules/IDE/components/CollectionList/CollectionListRow.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { bindActionCreators } from 'redux'; import { withTranslation } from 'react-i18next'; import * as ProjectActions from '../../actions/project'; diff --git a/client/modules/IDE/components/ErrorModal.jsx b/client/modules/IDE/components/ErrorModal.jsx index 95be4e096a..81065bd42a 100644 --- a/client/modules/IDE/components/ErrorModal.jsx +++ b/client/modules/IDE/components/ErrorModal.jsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; const ErrorModal = ({ type, service, closeModal }) => { diff --git a/client/modules/IDE/components/QuickAddList/QuickAddList.jsx b/client/modules/IDE/components/QuickAddList/QuickAddList.jsx index e3bbb6be5d..3011fd51ad 100644 --- a/client/modules/IDE/components/QuickAddList/QuickAddList.jsx +++ b/client/modules/IDE/components/QuickAddList/QuickAddList.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import Icons from './Icons'; diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index 757bacd904..e52fd40364 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -3,7 +3,7 @@ import React from 'react'; import { Helmet } from 'react-helmet'; import { withTranslation } from 'react-i18next'; import { connect } from 'react-redux'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { bindActionCreators } from 'redux'; import classNames from 'classnames'; import slugify from 'slugify'; diff --git a/client/modules/IDE/components/Toolbar.jsx b/client/modules/IDE/components/Toolbar.jsx index 92d6bcb5d0..08005c7b7c 100644 --- a/client/modules/IDE/components/Toolbar.jsx +++ b/client/modules/IDE/components/Toolbar.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import classNames from 'classnames'; import { withTranslation } from 'react-i18next'; import * as IDEActions from '../actions/ide'; diff --git a/client/modules/IDE/components/UploadFileModal.jsx b/client/modules/IDE/components/UploadFileModal.jsx index 0084912cbf..f1e0b90fef 100644 --- a/client/modules/IDE/components/UploadFileModal.jsx +++ b/client/modules/IDE/components/UploadFileModal.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { Link } from 'react-router'; +import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import prettyBytes from 'pretty-bytes'; import getConfig from '../../../utils/getConfig'; diff --git a/client/modules/IDE/components/__snapshots__/SketchList.unit.test.jsx.snap b/client/modules/IDE/components/__snapshots__/SketchList.unit.test.jsx.snap index c4e9752218..bd7475ebf9 100644 --- a/client/modules/IDE/components/__snapshots__/SketchList.unit.test.jsx.snap +++ b/client/modules/IDE/components/__snapshots__/SketchList.unit.test.jsx.snap @@ -70,7 +70,9 @@ exports[` snapshot testing 1`] = ` - + testsketch1 @@ -100,7 +102,9 @@ exports[` snapshot testing 1`] = ` - + testsketch2 diff --git a/client/modules/IDE/pages/IDEView.jsx b/client/modules/IDE/pages/IDEView.jsx index 5158e33c44..a420a326a5 100644 --- a/client/modules/IDE/pages/IDEView.jsx +++ b/client/modules/IDE/pages/IDEView.jsx @@ -1,9 +1,9 @@ import PropTypes from 'prop-types'; import React from 'react'; +import { Prompt } from 'react-router-dom'; import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router'; -import { withTranslation } from 'react-i18next'; +import { connect, useSelector } from 'react-redux'; +import { useTranslation, withTranslation } from 'react-i18next'; import { Helmet } from 'react-helmet'; import SplitPane from 'react-split-pane'; import Editor from '../components/Editor'; @@ -41,24 +41,43 @@ function getTitle(props) { return id ? `p5.js Web Editor | ${props.project.name}` : 'p5.js Web Editor'; } -function warnIfUnsavedChanges(props, nextLocation) { - const toAuth = - nextLocation && - nextLocation.action === 'PUSH' && - (nextLocation.pathname === '/login' || nextLocation.pathname === '/signup'); - const onAuth = - nextLocation && - (props.location.pathname === '/login' || - props.location.pathname === '/signup'); - if (props.ide.unsavedChanges && !toAuth && !onAuth) { - if (!window.confirm(props.t('Nav.WarningUnsavedChanges'))) { - return false; - } - return true; - } - return true; +function isAuth(pathname) { + return pathname === '/login' || pathname === '/signup'; +} + +function isOverlay(pathname) { + return pathname === '/about' || pathname === '/feedback'; +} + +function WarnIfUnsavedChanges({ currentLocation }) { + const hasUnsavedChanges = useSelector((state) => state.ide.unsavedChanges); + + const { t } = useTranslation(); + + return ( + { + if ( + isAuth(nextLocation.pathname) || + isAuth(currentLocation.pathname) || + isOverlay(nextLocation.pathname) || + isOverlay(currentLocation.pathname) + ) { + return true; // allow navigation + } + return t('Nav.WarningUnsavedChanges'); + }} + /> + ); } +WarnIfUnsavedChanges.propTypes = { + currentLocation: PropTypes.shape({ + pathname: PropTypes.string + }).isRequired +}; + class IDEView extends React.Component { constructor(props) { super(props); @@ -86,11 +105,6 @@ class IDEView extends React.Component { this.isMac = navigator.userAgent.toLowerCase().indexOf('mac') !== -1; document.addEventListener('keydown', this.handleGlobalKeydown, false); - this.props.router.setRouteLeaveHook( - this.props.route, - this.handleUnsavedChanges - ); - // window.onbeforeunload = this.handleUnsavedChanges; window.addEventListener('beforeunload', this.handleBeforeUnload); @@ -140,12 +154,6 @@ class IDEView extends React.Component { clearTimeout(this.autosaveInterval); this.autosaveInterval = null; } - - if (this.props.route.path !== prevProps.route.path) { - this.props.router.setRouteLeaveHook(this.props.route, () => - warnIfUnsavedChanges(this.props) - ); - } } componentWillUnmount() { document.removeEventListener('keydown', this.handleGlobalKeydown, false); @@ -231,9 +239,6 @@ class IDEView extends React.Component { } } - handleUnsavedChanges = (nextLocation) => - warnIfUnsavedChanges(this.props, nextLocation); - handleBeforeUnload = (e) => { const confirmationMessage = this.props.t('Nav.WarningUnsavedChanges'); if (this.props.ide.unsavedChanges) { @@ -254,11 +259,9 @@ class IDEView extends React.Component { {getTitle(this.props)} + -