diff --git a/ui/package.json b/ui/package.json index 45efbe5..58957dd 100644 --- a/ui/package.json +++ b/ui/package.json @@ -13,6 +13,7 @@ "@testing-library/user-event": "^14.5.2", "axios": "^1.7.7", "classnames": "^2.5.1", + "history": "^4.10.1", "eslint-config-prettier": "8.5.*", "eslint-plugin-prettier": "4.2.*", "eslint-watch": "^7.0.0", @@ -26,7 +27,7 @@ "react-dom": "^19.0.0", "react-i18next": "^15.1.1", "react-redux": "^9.1.2", - "react-router-dom": "^6.27.0", + "react-router-dom": "^5.3.4", "react-scripts": "5.0.1", "react-tooltip": "^5.28.0", "redux": "^5.0.1", @@ -71,6 +72,8 @@ }, "resolutions": { "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-router-dom": "^5.3.4", + "react-router": "^5.3.4" } } diff --git a/ui/src/App.jsx b/ui/src/App.jsx index d9627f8..491689c 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -1,7 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { withTranslation } from 'react-i18next'; -import { Outlet } from 'react-router-dom'; import { connect as connectWS, disconnect as disconnectWS, @@ -94,7 +93,7 @@ class App extends React.Component { } render() { - const { t, connected } = this.props; + const { t, connected, children } = this.props; const { username, dashboard, dispatchLogout } = this.props; const cloudOnline = this.getCurrentTimestamp() - dashboard.cloudOnline < 30; return ( @@ -211,7 +210,7 @@ class App extends React.Component { )} - + {children} @@ -247,6 +246,11 @@ App.propTypes = { connected: PropTypes.bool.isRequired, dashboard: PropTypes.object.isRequired, dispatchGetDashboardInformation: PropTypes.func.isRequired, + children: PropTypes.node, +}; + +App.defaultProps = { + children: null, }; export default withTranslation()( diff --git a/ui/src/containers/RequireAuth.jsx b/ui/src/containers/RequireAuth.jsx index 67f477d..2e682f4 100644 --- a/ui/src/containers/RequireAuth.jsx +++ b/ui/src/containers/RequireAuth.jsx @@ -1,12 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; -import { Navigate } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; export default function RequireAuth({ children }) { const isAuthenticated = useSelector((s) => s.authentication.loggedIn); if (!isAuthenticated) { - return ; + return ; } return children; } diff --git a/ui/src/containers/RequireGuest.jsx b/ui/src/containers/RequireGuest.jsx index 08b22b5..69eedf9 100644 --- a/ui/src/containers/RequireGuest.jsx +++ b/ui/src/containers/RequireGuest.jsx @@ -1,16 +1,16 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; -import { Navigate } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; export default function RequireGuest({ children }) { const isAuthenticated = useSelector((s) => s.authentication.loggedIn); const isInstalled = useSelector((s) => s.authentication.installed); if (!isInstalled) { - return ; + return ; } if (isAuthenticated) { - return ; + return ; } return children; } diff --git a/ui/src/containers/RequireInstall.jsx b/ui/src/containers/RequireInstall.jsx index 9f3c2d8..1bc39ca 100644 --- a/ui/src/containers/RequireInstall.jsx +++ b/ui/src/containers/RequireInstall.jsx @@ -1,13 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; -import { Navigate } from 'react-router-dom'; +import { Redirect } from 'react-router-dom'; export default function RequireInstall({ children }) { const isAuthenticated = useSelector((s) => s.authentication.loggedIn); const isInstalled = useSelector((s) => s.authentication.installed); if (isInstalled) { - return ; + return ; } return children; } diff --git a/ui/src/history.js b/ui/src/history.js new file mode 100644 index 0000000..997ede5 --- /dev/null +++ b/ui/src/history.js @@ -0,0 +1,4 @@ +import { createBrowserHistory } from 'history'; + +const history = createBrowserHistory(); +export default history; diff --git a/ui/src/index.jsx b/ui/src/index.jsx index 0ccf4c3..49e4a0c 100644 --- a/ui/src/index.jsx +++ b/ui/src/index.jsx @@ -1,12 +1,6 @@ -import React, { Suspense, useEffect } from 'react'; +import React, { Suspense } from 'react'; import { createRoot } from 'react-dom/client'; -import { - BrowserRouter, - Routes, - Route, - Navigate, - useNavigate, -} from 'react-router-dom'; +import { Router, Switch, Route, Redirect } from 'react-router-dom'; import { createStore, applyMiddleware, compose } from 'redux'; import { Provider } from 'react-redux'; import reduxWebsocket from '@giantmachines/redux-websocket'; @@ -20,7 +14,7 @@ import Media from './pages/Media/Media'; import Settings from './pages/Settings/Settings'; import RequireAuth from './containers/RequireAuth'; import RequireGuest from './containers/RequireGuest'; -import { setNavigator } from './navigation'; +import history from './history'; import './i18n'; // We get the token from the store to initialise the store. @@ -66,65 +60,45 @@ const store = createStore( const Loader = () =>
loading...
; -// Bridges React Router's navigate function into a module-scoped singleton -// so Redux thunks (e.g. login/logout) can navigate without a hook. -function NavigationSetup() { - const nav = useNavigate(); - useEffect(() => { - setNavigator(nav); - }, [nav]); - return null; -} - const container = document.getElementById('root'); const root = createRoot(container); root.render( - - + }> - - - - - } - /> - }> - } - /> - - - - } - /> - - - - } - /> - - - - } - /> + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + - + ); diff --git a/ui/src/navigation.js b/ui/src/navigation.js index 9d19d6c..7a48b14 100644 --- a/ui/src/navigation.js +++ b/ui/src/navigation.js @@ -1,16 +1,12 @@ // Lightweight navigation singleton so non-React code (Redux thunks) can -// trigger client-side navigation. The value is set from a component that -// has access to React Router's `useNavigate` hook (see NavigationSetup in -// index.jsx). +// trigger client-side navigation. Backed by the shared `history` instance +// passed to react-router's . +import history from './history'; -let navigatorFn = null; - -export const setNavigator = (fn) => { - navigatorFn = fn; +export const setNavigator = () => { + // Kept for API compatibility; no-op since history is module-scoped. }; -export const navigate = (path, options) => { - if (navigatorFn) { - navigatorFn(path, options); - } +export const navigate = (path) => { + history.push(path); };