diff --git a/UPGRADE.md b/UPGRADE.md index 56ffefc..43207a6 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -316,6 +316,7 @@ MONGO_CONTAINER=tcef-mongo7 MONGO_SHELL=mongosh MONGO_PORT=27019 ./smoke/smoke.s | maximum:server-transform, meteorhacks:zones, less, markdown, test stack | — | — | ✅ removed (dead/unused) | | fourseven:scss | 4.5.4 | 4.14.1 | ✅ node-12 compatible | | node-gcm | 1.0.2 | — | ✅ removed (dead API, moved to microservice) | +| react-router-dom | 4.2.2 | 6.30 | ✅ done — with `history` 4→5 and `react-router-bootstrap` 0.24→0.26. Kills the `Router`/`Switch`/`Route`/`Link`/`LinkContainer` legacy-context warnings. Strategy: keep the shared `history` **singleton** (used outside React by NotificationsObserver and Utils/location) via `unstable_HistoryRouter`, and DON'T rewrite the ~20 class pages — a new `withRouterCompat` HOC bridges v6 hooks back to v4-shaped `history`/`match`/`location` props. ``→``, `component=`→`element=`, `Authenticated`/`Public` become guard components rendering `children` or ``, `LocationListener` is now a `useLocation`+`useEffect` function. The regex route `/fire/:type(active\|archive\|alert)/:id` → `/fire/:type/:id` (type validated in the component; the only behavior delta is an invalid two-segment type now hits Fires instead of 404 — negligible). `history.listen` callback is `({location})` in v5. Browser-verified: SPA nav, `/subscriptions`→`/login` auth redirect, deep-link `/fire/archive/:id` (params flow), browser back/forward; REST smoke byte-identical. Gotcha hit & fixed: the rewritten guard files need `import React` for their `` JSX. | | react-helmet | 5.2.0 | react-helmet-async 2.0.5 | ✅ done — react-helmet is unmaintained and misbehaves under React 18 StrictMode. Drop-in: same `` children API across the 15 pages; `` wraps `` in `imports/startup/client/index.js`. Verified in browser: per-page titles, meta description and hreflang alternates injected (`data-rh`). Note: head updates are rAF-deferred (like react-helmet), so hidden/background tabs don't flush — irrelevant for real users. | | react-bootstrap | 0.31.5 | 2.10 | ✅ done — kills the biggest batch of React-19-blocking warnings (Grid/FormGroup/ControlLabel/Navbar.Header/Navbar.Brand/Checkbox/SafeAnchor all used legacy context/defaultProps). 29 files: `Grid`→`Container`, `FormGroup/ControlLabel/FormControl/HelpBlock`→`Form.Group/Label/Control/Text`, `Checkbox`→`Form.Check` (label moves to a prop), `bsStyle`→`variant` (×23, `"default"`→`"secondary"`), `bsSize`→`size` (×2), `pull-right`→`float-end` (×7). Custom `Col.js` (used v0.31 `bootstrapUtils`/`StyleConfig` internals) now re-exports v2's `Col`; custom `NavItem.js` rewritten self-contained (dropped `SafeAnchor`+`createChainedFunction`); `Navigation.js` drops the react-bootstrap `Navbar` (was hand-rolled raw markup anyway). **CSS kept at Bootstrap 4** (see next row) — the only BS5-only class v2 emits that BS4 lacks is `.form-label`, shimmed in `forms.scss`. Browser-verified: home carousel+navbar, signup form + terms `Form.Check` (toggles submit), login form; REST smoke byte-identical. | | Bootstrap CSS/JS | 4.1 (alexwine:bootstrap-4) | 5.x | ⏸️ **deferred as debt.** The app's CSS+JS still comes from the Atmosphere `alexwine:bootstrap-4` (Bootstrap 4 + jQuery). The BS4→5 jump is a separate invasive sub-project: the home **carousel** uses the jQuery `bootstrap-carousel-swipe` plugin (BS4-only), the **navbar collapse** and ~17 `data-toggle`/`data-slide` attributes across 6 files are BS4 jQuery behaviors, and BS5 is vanilla-JS with `data-bs-*`. react-bootstrap v2 runs fine on BS4 CSS for every component we use (Button/Alert/Row/Col/Table/ButtonGroup/Modal render identical classes; only `.form-label` needed a shim). Do this jump on its own: npm `bootstrap@5` SCSS+JS, rewrite carousel (BS5 has native swipe → drop the plugin), `data-toggle`→`data-bs-toggle`, `ml-auto`→`ms-auto`, etc. | diff --git a/imports/ui/components/Authenticated/Authenticated.js b/imports/ui/components/Authenticated/Authenticated.js index cfe1a69..80b0efe 100644 --- a/imports/ui/components/Authenticated/Authenticated.js +++ b/imports/ui/components/Authenticated/Authenticated.js @@ -1,25 +1,16 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Route, Redirect } from 'react-router-dom'; +import { Navigate } from 'react-router-dom'; -const Authenticated = ({ loggingIn, authenticated, component, path, exact, ...rest }) => ( - ( - authenticated ? - (React.createElement(component, { ...props, ...rest, loggingIn, authenticated })) : - () - )} - /> +// v6 route guard: rendered as a route `element` wrapping the real page. +// Renders its children when authenticated, otherwise redirects to /login. +const Authenticated = ({ authenticated, children }) => ( + authenticated ? children : ); Authenticated.propTypes = { - loggingIn: PropTypes.bool.isRequired, authenticated: PropTypes.bool.isRequired, - // elementType (not func): route components may be HOC-wrapped (translate(), - // memo, forwardRef), which are objects, not plain functions. - component: PropTypes.elementType.isRequired, + children: PropTypes.node.isRequired }; export default Authenticated; diff --git a/imports/ui/components/AuthenticatedNavigation/AuthenticatedNavigation.js b/imports/ui/components/AuthenticatedNavigation/AuthenticatedNavigation.js index d1f0004..eb102b9 100644 --- a/imports/ui/components/AuthenticatedNavigation/AuthenticatedNavigation.js +++ b/imports/ui/components/AuthenticatedNavigation/AuthenticatedNavigation.js @@ -1,8 +1,8 @@ /* eslint-disable import/no-absolute-path */ import React from 'react'; import PropTypes from 'prop-types'; -import { withRouter } from 'react-router-dom'; import { LinkContainer } from 'react-router-bootstrap'; +import withRouterCompat from '../withRouterCompat/withRouterCompat'; /* import { Nav, NavDropdown } from 'react-bootstrap'; */ import { withTranslation, Trans } from 'react-i18next'; import { testId } from '/imports/ui/components/Utils/TestUtils'; @@ -39,4 +39,4 @@ AuthenticatedNavigation.propTypes = { name: PropTypes.string.isRequired }; -export default withTranslation()(withRouter(AuthenticatedNavigation)); +export default withTranslation()(withRouterCompat(AuthenticatedNavigation)); diff --git a/imports/ui/components/History/History.js b/imports/ui/components/History/History.js index a33464f..d5e32e5 100644 --- a/imports/ui/components/History/History.js +++ b/imports/ui/components/History/History.js @@ -1,5 +1,8 @@ -import createHistory from 'history/createBrowserHistory'; +import { createBrowserHistory } from 'history'; -const history = createHistory(); +// Shared history singleton. It backs react-router's (App.js) +// AND is used outside the React tree (NotificationsObserver push, Utils/location +// listen), so it must be the same instance the router navigates. +const history = createBrowserHistory(); export default history; diff --git a/imports/ui/components/Public/Public.js b/imports/ui/components/Public/Public.js index a708408..f53325e 100644 --- a/imports/ui/components/Public/Public.js +++ b/imports/ui/components/Public/Public.js @@ -1,27 +1,20 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Route, Redirect } from 'react-router-dom'; +import { Navigate, useLocation } from 'react-router-dom'; -const Public = ({ - loggingIn, authenticated, component, location, path, exact, ...rest -}) => ( - ( - !authenticated ? - (React.createElement(component, { - ...props, ...rest, loggingIn, authenticated -})) : - () - )} - /> -); +// v6 route guard for public-only pages (login/signup/auth): rendered as a route +// `element` wrapping the real page. Redirects authenticated users to their +// subscriptions, preserving any location state (e.g. a pending new-zone flow). +const Public = ({ authenticated, children }) => { + const location = useLocation(); + return authenticated + ? + : children; +}; Public.propTypes = { - loggingIn: PropTypes.bool.isRequired, authenticated: PropTypes.bool.isRequired, - component: PropTypes.elementType.isRequired + children: PropTypes.node.isRequired }; export default Public; diff --git a/imports/ui/components/Utils/location.js b/imports/ui/components/Utils/location.js index 3d4669a..d102804 100644 --- a/imports/ui/components/Utils/location.js +++ b/imports/ui/components/Utils/location.js @@ -7,9 +7,9 @@ import history from '/imports/ui/components/History/History'; export const location = new ReactiveVar(history.location.pathname); if (Meteor.isClient) { - history.listen((loc) => { // , action) => { - // console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`) - // console.log(`The last navigation action was ${action}`) + // history v5 hands the listener a { location, action } update object + // (v4 passed the location directly). + history.listen(({ location: loc }) => { location.set(loc.pathname); }); } diff --git a/imports/ui/components/withRouterCompat/withRouterCompat.js b/imports/ui/components/withRouterCompat/withRouterCompat.js new file mode 100644 index 0000000..6005000 --- /dev/null +++ b/imports/ui/components/withRouterCompat/withRouterCompat.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { useNavigate, useLocation, useParams } from 'react-router-dom'; + +// react-router v6 dropped the `history`/`match`/`location` props that our ~20 +// class page components still read. Rather than convert every class to hooks, +// this HOC bridges the v6 hooks back to the v4-shaped props those components +// expect: `history.push/replace(to, state)`, `match.params`, `match.url`, +// and `location`. New code should use the hooks directly instead. +export default function withRouterCompat(Component) { + function WithRouterCompat(props) { + const navigate = useNavigate(); + const location = useLocation(); + const params = useParams(); + + const history = { + push: (to, state) => navigate(to, state !== undefined ? { state } : undefined), + replace: (to, state) => navigate(to, { replace: true, ...(state !== undefined ? { state } : {}) }), + goBack: () => navigate(-1), + goForward: () => navigate(1), + location + }; + + // v4 `match.url` was the matched path; for our leaf/exact routes that is + // simply the current pathname. + const match = { params, url: location.pathname, path: location.pathname }; + + return ; + } + + const name = Component.displayName || Component.name || 'Component'; + WithRouterCompat.displayName = `withRouterCompat(${name})`; + return WithRouterCompat; +} diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js index 32a5235..f39847f 100644 --- a/imports/ui/layouts/App/App.js +++ b/imports/ui/layouts/App/App.js @@ -1,9 +1,9 @@ /* eslint-disable jsx-a11y/no-href */ /* eslint import/no-absolute-path: [2, { esmodule: false, commonjs: false, amd: false }] */ -import React, { Component } from 'react'; +import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; -import { Router, Switch, Route } from 'react-router-dom'; +import { unstable_HistoryRouter as HistoryRouter, Routes, Route, useLocation } from 'react-router-dom'; import { Container } from 'react-bootstrap'; import { I18nextProvider } from 'react-i18next'; import { Helmet } from 'react-helmet-async'; @@ -22,6 +22,7 @@ import Reconnect from '../../components/Reconnect/Reconnect'; import Navigation from '../../components/Navigation/Navigation'; import Authenticated from '../../components/Authenticated/Authenticated'; import Public from '../../components/Public/Public'; +import withRouterCompat from '../../components/withRouterCompat/withRouterCompat'; import Index from '../../pages/Index/Index'; import Subscriptions from '../../pages/Subscriptions/Subscriptions'; import NewSubscription from '../../pages/NewSubscription/NewSubscription'; @@ -56,35 +57,16 @@ import history from '../../components/History/History'; import '../../components/NotificationsObserver/NotificationsObserver'; import './App.scss'; -class LocationListener extends Component { - // https://stackoverflow.com/questions/43512450/react-router-v4-route-onchange-event - - componentDidMount() { +// Tracks route changes for Piwik/analytics. v6 replaces the old +// contextTypes.router.history.listen wiring with the useLocation hook. +const LocationListener = ({ children }) => { + const location = useLocation(); + useEffect(() => { // https://github.com/GoogleChrome/rendertron#rendering-budget-timeout window.renderComplete = true; - this.handleLocationChange(this.context.router.history.location); - this.unlisten = - this.context.router.history.listen(this.handleLocationChange); - } - - componentWillUnmount() { - this.unlisten(); - } - - handleLocationChange(location) { - // your staff here - console.log(`----- location: '${location.pathname}'`); Meteor.Piwik.trackPage(location.pathname); - } - - // https://stackoverflow.com/questions/39133797/react-only-return-props-children - render() { - return this.props.children; - } -} - -LocationListener.contextTypes = { - router: PropTypes.object + }, [location.pathname]); + return children; }; LocationListener.propTypes = { @@ -94,6 +76,27 @@ LocationListener.propTypes = { ]).isRequired }; +// v6 routes render an `element`, not a `component`, and no longer pass down +// history/match/location. Wrap each class page once (module scope, so the +// component identity stays stable across renders) with the v4-compat HOC. +const IndexR = withRouterCompat(Index); +const SubscriptionsR = withRouterCompat(Subscriptions); +const NewSubscriptionR = withRouterCompat(NewSubscription); +const ViewSubscriptionR = withRouterCompat(ViewSubscription); +const EditSubscriptionR = withRouterCompat(EditSubscription); +const ProfileR = withRouterCompat(Profile); +const StatusR = withRouterCompat(Status); +const FiresMapR = withRouterCompat(FiresMap); +const ZonesMapR = withRouterCompat(ZonesMap); +const FiresR = withRouterCompat(Fires); +const AuthR = withRouterCompat(Auth); +const SignupR = withRouterCompat(Signup); +const LoginR = withRouterCompat(Login); +const LogoutR = withRouterCompat(Logout); +const VerifyEmailR = withRouterCompat(VerifyEmail); +const RecoverPasswordR = withRouterCompat(RecoverPassword); +const ResetPasswordR = withRouterCompat(ResetPassword); + const App = props => ( /* https://react.i18next.com/components/i18nextprovider.html */
@@ -101,7 +104,7 @@ const App = props => ( - + { !props.loading &&
@@ -116,37 +119,38 @@ const App = props => ( - - - - - - - - - - + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - - - - - - - - + {/* v6 has no regex params; `:type` is validated inside Fires. */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - - - - - - - - + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> - - + } /> +
}
-
+
} diff --git a/package-lock.json b/package-lock.json index 2f144a0..d812abc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "google-maps": "^3.2.1", "google-maps-image-api-url": "^1.0.3", "handlebars": "^4.0.11", - "history": "^4.7.2", + "history": "^5.3.0", "html5-device-mockups": "^3.2.0", "i18next": "^23.16.8", "i18next-browser-languagedetector": "^8.2.1", @@ -73,8 +73,8 @@ "react-places-autocomplete": "^5.4.3", "react-progress-bar.js": "^0.2.3", "react-resize-detector": "^1.1.0", - "react-router-bootstrap": "^0.24.4", - "react-router-dom": "^4.2.2", + "react-router-bootstrap": "^0.26.3", + "react-router-dom": "^6.30.4", "react-share": "^2.0.0", "simpl-schema": "^1.5.0", "simple-line-icons": "^2.4.1", @@ -1442,6 +1442,15 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, + "node_modules/@remix-run/router": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz", + "integrity": "sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@restart/hooks": { "version": "0.4.16", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", @@ -8318,23 +8327,12 @@ } }, "node_modules/history": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", - "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "license": "MIT", "dependencies": { - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "resolve-pathname": "^2.2.0", - "value-equal": "^0.4.0", - "warning": "^3.0.0" - } - }, - "node_modules/history/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "dependencies": { - "loose-envify": "^1.0.0" + "@babel/runtime": "^7.7.6" } }, "node_modules/hoek": { @@ -19763,97 +19761,49 @@ "react": "^0.14.7 || ^15.0.0 || ^16.0.0" } }, - "node_modules/react-router-bootstrap": { - "version": "0.24.4", - "resolved": "https://registry.npmjs.org/react-router-bootstrap/-/react-router-bootstrap-0.24.4.tgz", - "integrity": "sha512-kEwk3ml4wvE3IbJvRVjx0zBBBxW4JLhD0wyy0hBdlWSdfjvgoHVvlxx9gBPxvEs5VwWlbFvNRyUghLZ2AMcmzg==", + "node_modules/react-router": { + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.4.tgz", + "integrity": "sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==", + "license": "MIT", "dependencies": { - "prop-types": "^15.5.10" + "@remix-run/router": "1.23.3" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=0.14.0", - "react-router-dom": ">=4.0.0" + "react": ">=16.8" + } + }, + "node_modules/react-router-bootstrap": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/react-router-bootstrap/-/react-router-bootstrap-0.26.3.tgz", + "integrity": "sha512-cBgcWekti6lFRl/vXP8ZfKuA/0Qe7L5xBjQ6OwbGI30+NSAAH/YZGbO6whSeBWFILn6uTVOX939HDGhs+5WzOw==", + "license": "Apache-2.0", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16.13.1", + "react-router-dom": ">=6.0.0" } }, "node_modules/react-router-dom": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.2.2.tgz", - "integrity": "sha512-cHMFC1ZoLDfEaMFoKTjN7fry/oczMgRt5BKfMAkTu5zEuJvUiPp1J8d0eXSVTnBh6pxlbdqDhozunOOLtmKfPA==", + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.4.tgz", + "integrity": "sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==", + "license": "MIT", "dependencies": { - "history": "^4.7.2", - "invariant": "^2.2.2", - "loose-envify": "^1.3.1", - "prop-types": "^15.5.4", - "react-router": "^4.2.0", - "warning": "^3.0.0" + "@remix-run/router": "1.23.3", + "react-router": "6.30.4" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-dom/node_modules/history": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", - "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", - "dependencies": { - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "resolve-pathname": "^2.2.0", - "value-equal": "^0.4.0", - "warning": "^3.0.0" - } - }, - "node_modules/react-router-dom/node_modules/hoist-non-react-statics": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz", - "integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA=" - }, - "node_modules/react-router-dom/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "node_modules/react-router-dom/node_modules/path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/react-router-dom/node_modules/react-router": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz", - "integrity": "sha512-DY6pjwRhdARE4TDw7XjxjZsbx9lKmIcyZoZ+SDO7SBJ1KUeWNxT22Kara2AC7u6/c2SYEHlEDLnzBCcNhLE8Vg==", - "dependencies": { - "history": "^4.7.2", - "hoist-non-react-statics": "^2.3.0", - "invariant": "^2.2.2", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.5.4", - "warning": "^3.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-dom/node_modules/resolve-pathname": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" - }, - "node_modules/react-router-dom/node_modules/value-equal": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", - "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" - }, - "node_modules/react-router-dom/node_modules/warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", - "dependencies": { - "loose-envify": "^1.0.0" + "react": ">=16.8", + "react-dom": ">=16.8" } }, "node_modules/react-share": { @@ -20453,11 +20403,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-pathname": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" - }, "node_modules/restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -21495,11 +21440,6 @@ "spdx-expression-parse": "~1.0.0" } }, - "node_modules/value-equal": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", - "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" - }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/package.json b/package.json index 188d49e..9231a53 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "google-maps": "^3.2.1", "google-maps-image-api-url": "^1.0.3", "handlebars": "^4.0.11", - "history": "^4.7.2", + "history": "^5.3.0", "html5-device-mockups": "^3.2.0", "i18next": "^23.16.8", "i18next-browser-languagedetector": "^8.2.1", @@ -73,8 +73,8 @@ "react-places-autocomplete": "^5.4.3", "react-progress-bar.js": "^0.2.3", "react-resize-detector": "^1.1.0", - "react-router-bootstrap": "^0.24.4", - "react-router-dom": "^4.2.2", + "react-router-bootstrap": "^0.26.3", + "react-router-dom": "^6.30.4", "react-share": "^2.0.0", "simpl-schema": "^1.5.0", "simple-line-icons": "^2.4.1",