Skip to content

Commit

Permalink
update react-router to 6.4 (#730)
Browse files Browse the repository at this point in the history
[#188449418]
  • Loading branch information
uraniumanchor authored Nov 1, 2024
1 parent 17c87b9 commit 01ae17e
Show file tree
Hide file tree
Showing 26 changed files with 264 additions and 566 deletions.
148 changes: 84 additions & 64 deletions bundles/admin/app.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Route, Switch, useRouteMatch } from 'react-router';
import { Link } from 'react-router-dom';
import loadable from '@loadable/component';
import { Outlet, Route, Routes } from 'react-router';
import { BrowserRouter, Link } from 'react-router-dom';

import { useConstants } from '@common/Constants';
import Loading from '@common/Loading';
import { actions } from '@public/api';
import { usePermission, usePermissions } from '@public/api/helpers/auth';
import { usePermissions } from '@public/api/helpers/auth';
import V2HTTPUtils from '@public/apiv2/HTTPUtils';
import Dropdown from '@public/dropdown';
import Spinner from '@public/spinner';
Expand All @@ -17,29 +16,22 @@ import { setAPIRoot } from '@tracker/Endpoints';
import NotFound from '../public/notFound';
import ScheduleEditor from './scheduleEditor';

const Interstitials = loadable(() => import('./interstitials' /* webpackChunkName: 'interstitials' */), {
loading: Loading,
});
const Interstitials = React.lazy(() => import('./interstitials' /* webpackChunkName: 'interstitials' */));

const ProcessPendingBids = loadable(
() => import('./donationProcessing/processPendingBids' /* webpackChunkName: 'donationProcessing' */),
{
loading: Loading,
},
const ProcessPendingBids = React.lazy(() =>
import('./donationProcessing/processPendingBids' /* webpackChunkName: 'donationProcessing' */),
);

const EventMenuComponents = {};

function EventMenu(name, path) {
function EventMenu(name) {
return (
EventMenuComponents[name] ||
(EventMenuComponents[name] = function EventMenuInner() {
const { events, status } = useSelector(state => ({
events: state.models.event,
status: state.status,
}));
const url = useRouteMatch().url;
path = path || url;
const sortedEvents = React.useMemo(
() => [...(events || [])].sort((a, b) => b.datetime.localeCompare(a.datetime)),
[events],
Expand All @@ -52,7 +44,7 @@ function EventMenu(name, path) {
{sortedEvents &&
sortedEvents.map(e => (
<li key={e.pk}>
<Link to={`${path}/${e.pk}`}>{e.short}</Link>
<Link to={`${e.pk}`}>{e.short}</Link>
{(!e.allow_donations || e.locked) && '🔒'}
</li>
))}
Expand All @@ -64,8 +56,6 @@ function EventMenu(name, path) {
}

function DropdownMenu({ name, path }) {
const match = useRouteMatch();

const events = useSelector(state => state.models.event);
const sortedEvents = React.useMemo(
() => [...(events || [])].sort((a, b) => b.datetime.localeCompare(a.datetime)),
Expand All @@ -87,7 +77,7 @@ function DropdownMenu({ name, path }) {
{sortedEvents &&
sortedEvents.map(e => (
<li key={e.pk}>
<Link to={`${match.url}/${path}/${e.pk}`}>{e.short}</Link>
<Link to={`${path}/${e.pk}`}>{e.short}</Link>
{(!e.allow_donations || e.locked) && '🔒'}
</li>
))}
Expand All @@ -97,8 +87,36 @@ function DropdownMenu({ name, path }) {
);
}

function App() {
const match = useRouteMatch();
function Menu() {
const { ADMIN_ROOT } = useConstants();
const canSeeHiddenBids = usePermissions(['tracker.change_bid', 'tracker.view_hidden_bid']);
const { status } = useSelector(state => ({
status: state.status,
}));
return (
<div style={{ height: 60, display: 'flex', alignItems: 'center' }}>
<Spinner spinning={status.event !== 'success'}>
{ADMIN_ROOT && (
<>
<a href={ADMIN_ROOT}>Admin Home</a>
&mdash;
</>
)}
<DropdownMenu name="Schedule Editor" path="schedule_editor" />
&mdash;
<DropdownMenu name="Interstitials" path="interstitials" />
{canSeeHiddenBids && (
<>
&mdash;
<DropdownMenu name="Process Pending Bids" path="process_pending_bids" />
</>
)}
</Spinner>
</div>
);
}

function App({ rootPath }) {
const dispatch = useDispatch();

const [ready, setReady] = React.useState(false);
Expand All @@ -107,11 +125,10 @@ function App() {
status: state.status,
}));

const { API_ROOT, APIV2_ROOT, ADMIN_ROOT } = useConstants();
const canChangeDonations = usePermission('tracker.change_donation');
const { API_ROOT, APIV2_ROOT } = useConstants();
const canSeeHiddenBids = usePermissions(['tracker.change_bid', 'tracker.view_hidden_bid']);

React.useEffect(() => {
React.useLayoutEffect(() => {
setAPIRoot(API_ROOT);
V2HTTPUtils.setAPIRoot(APIV2_ROOT);
setReady(true);
Expand All @@ -130,51 +147,54 @@ function App() {
}, [dispatch, status.event, ready]);

return (
<Switch>
<Route>
<div style={{ position: 'relative', display: 'flex', height: 'calc(100vh - 51px)', flexDirection: 'column' }}>
<div style={{ height: 60, display: 'flex', alignItems: 'center' }}>
<Spinner spinning={status.event !== 'success'}>
{ADMIN_ROOT && (
<Spinner spinning={!ready}>
<BrowserRouter>
<Routes>
<Route path={rootPath}>
<Route
element={
<>
<a href={ADMIN_ROOT}>Admin Home</a>
&mdash;
<Menu />
<Outlet />
</>
}>
<Route path="" element={<div />} />
<Route path="schedule_editor/" element={React.createElement(EventMenu('Schedule Editor'))} />
<Route
path="schedule_editor/:eventId"
element={
<React.Suspense fallback={<Loading />}>
<ScheduleEditor />
</React.Suspense>
}
/>
<Route
path="interstitials/:eventId"
element={
<React.Suspense fallback={<Loading />}>
<Interstitials />
</React.Suspense>
}
/>
{canSeeHiddenBids && (
<Route path="process_pending_bids/" element={React.createElement(EventMenu('Process Pending Bids'))} />
)}
<DropdownMenu name="Schedule Editor" path="schedule_editor" />
&mdash;
<DropdownMenu name="Interstitials" path="interstitials" />
{canSeeHiddenBids && (
<>
&mdash;
<DropdownMenu name="Process Pending Bids" path="process_pending_bids" />
</>
<Route
path="process_pending_bids/:eventId"
element={
<React.Suspense fallback={<Loading />}>
<ProcessPendingBids />
</React.Suspense>
}
/>
)}
</Spinner>
</div>
<Spinner spinning={!ready}>
<div style={{ flex: '1 0 1', overflow: 'auto' }}>
<Switch>
<Route path={`${match.url}/schedule_editor/`} exact component={EventMenu('Schedule Editor')} />
<Route path={`${match.url}/schedule_editor/:event`} component={ScheduleEditor} />
<Route path={`${match.url}/interstitials/:event`} component={Interstitials} />
{canSeeHiddenBids && (
<Route
path={`${match.url}/process_pending_bids/`}
exact
component={EventMenu('Process Pending Bids')}
/>
)}
{canSeeHiddenBids && (
<Route path={`${match.url}/process_pending_bids/:event`} component={ProcessPendingBids} />
)}
<Route component={NotFound} />
</Switch>
</div>
</Spinner>
</div>
</Route>
</Switch>
<Route path="*" element={<NotFound />} />
</Route>
</Route>
</Routes>
</BrowserRouter>
</Spinner>
);
}

Expand Down
2 changes: 1 addition & 1 deletion bundles/admin/donationProcessing/processPendingBids.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const stateMap = {

export default React.memo(function ProcessPendingBids() {
const { ADMIN_ROOT } = useConstants();
const eventId = +useParams<{ event: string }>().event;
const eventId = +useParams<{ eventId: string }>().eventId!;
const status = useSelector((state: any) => state.status);
const bids = useSelector((state: any) => state.models.bid) as Bid[];
const event = useSelector((state: any) => state.models.event?.find((e: any) => e.pk === eventId));
Expand Down
10 changes: 7 additions & 3 deletions bundles/admin/donationProcessing/processPendingBidsSpec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React from 'react';
import MockAdapter from 'axios-mock-adapter';
import { Provider } from 'react-redux';
import { Route, StaticRouter } from 'react-router';
import { Route } from 'react-router';
import { Routes } from 'react-router-dom';
import { StaticRouter } from 'react-router-dom/server';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { render, waitFor } from '@testing-library/react';
Expand Down Expand Up @@ -89,8 +91,10 @@ describe('ProcessPendingBids', () => {
});
return render(
<Provider store={store}>
<StaticRouter location={`${eventId}`}>
<Route path={':event'} component={ProcessPendingBids} />
<StaticRouter location={`/${eventId}`}>
<Routes>
<Route path="/:eventId" element={<ProcessPendingBids />} />
</Routes>
</StaticRouter>
</Provider>,
);
Expand Down
11 changes: 1 addition & 10 deletions bundles/admin/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import React from 'react';
import { ConnectedRouter } from 'connected-react-router';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from 'react-query';
import { Provider } from 'react-redux';
import { Redirect, Route, Switch } from 'react-router';

import Constants from '@common/Constants';
import { createTrackerStore } from '@public/api';
Expand All @@ -17,8 +15,6 @@ import App from './app';
import '@common/init';

function Routes(props) {
const redirect = React.useCallback(({ location }) => <Redirect to={location.pathname.replace(/\/\/+/g, '/')} />, []);

const store = createTrackerStore();
const queryClient = new QueryClient({
defaultOptions: {
Expand All @@ -34,12 +30,7 @@ function Routes(props) {
<QueryClientProvider client={queryClient}>
<Provider store={store}>
<Constants.Provider value={props.CONSTANTS}>
<ConnectedRouter history={store.history}>
<Switch>
<Route exact strict path="(.*//+.*)" render={redirect} />
<Route path={props.ROOT_PATH} component={App} />
</Switch>
</ConnectedRouter>
<App rootPath={props.ROOT_PATH} />
</Constants.Provider>
</Provider>
</QueryClientProvider>
Expand Down
14 changes: 7 additions & 7 deletions bundles/admin/interstitials/Editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ type Aggregate = [Interview[], Ad[] | ServerError, Run[], Person | ServerError];

export default function InterstitialEditor() {
const { API_ROOT, CSRF_TOKEN } = useConstants();
const { event } = useParams<{ event: string }>();
const { eventId } = useParams<{ eventId: string }>();
const [promise, setPromise] = useState<Promise<Aggregate>>(new Promise(() => {}));
const [saveError, setSaveError] = useState<ServerError | null>(null);
const fetchAll = useCallback(() => {
setPromise(
Promise.all([
fetch(`${API_ROOT}interviews/${event}`).then(JSONResponseWithForbidden),
fetch(`${API_ROOT}ads/${event}`).then(JSONResponseWithForbidden),
fetch(`${API_ROOT}search?type=run&event=${event}`).then(JSONResponse),
fetch(`${API_ROOT}interviews/${eventId}`).then(JSONResponseWithForbidden),
fetch(`${API_ROOT}ads/${eventId}`).then(JSONResponseWithForbidden),
fetch(`${API_ROOT}search?type=run&event=${eventId}`).then(JSONResponse),
fetch(`${API_ROOT}me`).then(JSONResponseWithForbidden),
]),
);
setSaveError(null);
}, [API_ROOT, event]);
}, [API_ROOT, eventId]);
useEffect(fetchAll, [fetchAll]);

const moveInterstitial = useCallback(
Expand Down Expand Up @@ -97,7 +97,7 @@ export default function InterstitialEditor() {
);

const [result, error, state] = usePromise(promise, [promise]);
if (state === 'pending') return 'Loading...';
if (state === 'pending') return <>Loading...</>;
if (error) {
return (
<div className="error">
Expand All @@ -109,7 +109,7 @@ export default function InterstitialEditor() {
);
}
if (!result) {
return 'shrug';
return <>shrug</>;
}
const interviews = result[0] as Interview[] | ServerError;
const ads = result[1] as Ad[] | ServerError;
Expand Down
16 changes: 11 additions & 5 deletions bundles/admin/scheduleEditor/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import { useParams } from 'react-router';

import { actions } from '@public/api';
import authHelper from '@public/api/helpers/auth';
Expand Down Expand Up @@ -40,13 +41,13 @@ class ScheduleEditor extends React.Component {
}

componentDidUpdate(newProps) {
if (this.props.match.params.event !== newProps.match.params.event) {
this.refreshSpeedruns_(newProps.match.params.event);
if (this.props.eventId !== newProps.eventId) {
this.refreshSpeedruns_(newProps.eventId);
}
}

componentDidMount() {
this.refreshSpeedruns_(this.props.match.params.event);
this.refreshSpeedruns_(this.props.eventId);
}

refreshSpeedruns_(event) {
Expand Down Expand Up @@ -87,7 +88,7 @@ class ScheduleEditor extends React.Component {
function select(state, props) {
const { models, drafts, status, singletons } = state;
const { speedrun: speedruns, event: events = [] } = models;
const event = events.find(e => e.pk === parseInt(props.match?.params?.event)) || null;
const event = events.find(e => e.pk === parseInt(props.eventId)) || null;
const { me } = singletons;
return {
event,
Expand Down Expand Up @@ -149,4 +150,9 @@ function dispatch(dispatch) {
};
}

export default connect(select, dispatch)(ScheduleEditor);
const Connected = connect(select, dispatch)(ScheduleEditor);

export default function Wrapped() {
const { eventId } = useParams();
return <Connected eventId={eventId} />;
}
Loading

0 comments on commit 01ae17e

Please sign in to comment.