Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27,631 changes: 50 additions & 27,581 deletions frontend/package-lock.json

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"@stripe/stripe-js": "^1.42.0",
"@turf/turf": "^7.2.0",
"@types/grecaptcha": "^3.0.4",
"@types/react-router": "^5.1.20",
"axios": "^1.7.9",
"classnames": "^2.3.1",
"core-js": "^3.25.5",
Expand All @@ -41,7 +40,8 @@
"react-dom": "^18.2.0",
"react-modal": "^3.16.1",
"react-number-format": "^5.3.1",
"react-router-dom": "^5.3.4",
"history": "^5.3.0",
"react-router-dom": "^6.28.0",
"react-transition-group": "^4.4.1",
"react-virtualized-auto-sizer": "^1.0.7",
"react-window": "^1.8.7",
Expand Down Expand Up @@ -79,7 +79,6 @@
"@types/react-dom": "^18",
"@types/react-modal": "^3.13.1",
"@types/react-refresh": "~0.14.5",
"@types/react-router-dom": "^5.3.3",
"@types/react-transition-group": "^4.4.7",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
Expand Down
132 changes: 78 additions & 54 deletions frontend/src/screens/App/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import React from 'react';

import { Redirect, Route, Router, Switch } from 'react-router-dom';
import {
Navigate,
Outlet,
Route,
Routes,
matchPath,
unstable_HistoryRouter as HistoryRouter,
useLocation,
} from 'react-router-dom';
import history from 'utils/history';
import AnnouncementBanner from './screens/AnnouncementBanner';
import MapPane from './screens/MapPane';
Expand Down Expand Up @@ -55,17 +63,22 @@ function Modals(): JSX.Element {
);

const openTipJar = useTipJarStore((state) => state.open);
const location = useLocation();

React.useEffect(() => {
if (openTipJarOnLoad) {
openTipJar();
}
}, [openTipJar]);

const isMapOrStories =
location.pathname.startsWith('/map') ||
location.pathname.startsWith('/stories');

return (
<>
<Route path={['/map', '/stories']}>
{IS_SHUTDOWN ? (
{isMapOrStories ? (
IS_SHUTDOWN ? (
<Shutdown isOpen={true} />
) : (
<Welcome
Expand All @@ -74,8 +87,8 @@ function Modals(): JSX.Element {
setWelcomeOpen(false);
}}
/>
)}
</Route>
)
) : null}

<ThankYou
isOpen={isThankYouOpen}
Expand All @@ -98,17 +111,14 @@ function Modals(): JSX.Element {
}

function MainContentLayout({
children,
}: {
children: React.ReactNode;
children?: React.ReactNode;
}): JSX.Element {
return (
<div className={stylesheet.outermostContainer}>
<AnnouncementBanner />
<Modals />
<div className={stylesheet.mainContentWrapper}>
<div className={stylesheet.mainContentContainer}>{children}</div>
</div>
<div className={stylesheet.mainContentWrapper}>{children}</div>
</div>
);
}
Expand All @@ -127,54 +137,68 @@ function ContextWrappers({
return <OptimizeExperimentsProvider>{children}</OptimizeExperimentsProvider>;
}

function MainContentRoutes(): JSX.Element {
const location = useLocation();

const viewerMatch =
matchPath('/:section/photo/:identifier', location.pathname) ||
null;

return (
<MainContentLayout>
<div className={stylesheet.mainContentContainer}>
{viewerMatch ? <ViewerPane className={stylesheet.viewer} /> : null}
<Outlet />
</div>
</MainContentLayout>
);
}

export default function App(): JSX.Element {
return (
<ContextWrappers>
<Router history={history}>
<Switch>
{/* Routes with main layout (image viewer, announcements, modals) */}
<Route path={['/map', '/outtakes', '/stories']}>
<MainContentLayout>
<Route path="/*/photo/:identifier">
<ViewerPane className={stylesheet.viewer} />
</Route>
<Switch>
<Route
path={['/map/photo/:identifier', '/map']}
render={() => <MapPane className={stylesheet.mapContainer} />}
/>
<Route path={['/outtakes/photo/:identifier', '/outtakes']}>
<Outtakes className={stylesheet.outtakesContainer} />
</Route>
<Route path="/stories/edit">
<EditStory />
</Route>
<Route path={['/stories/photo/:identifier', '/stories']}>
<AllStories className={stylesheet.outtakesContainer} />
</Route>
</Switch>
</MainContentLayout>
</Route>
<Route>
{/* Routes without main layout */}
<Switch>
<Route path="/orders">
<Orders />
</Route>
<Route path="/labs">
<FeatureFlags />
</Route>
<Route path="/admin">
<AdminRoutes />
</Route>
<Route path="/render-merch/tote-bag">
<ToteBag />
</Route>
<Redirect to="/map" />
</Switch>
<HistoryRouter history={history}>
<Routes>
<Route element={<MainContentRoutes />}>
<Route path="map">
<Route
index
element={<MapPane className={stylesheet.mapContainer} />}
/>
<Route
path="photo/:identifier"
element={<MapPane className={stylesheet.mapContainer} />}
/>
</Route>
<Route path="outtakes">
<Route
index
element={<Outtakes className={stylesheet.outtakesContainer} />}
/>
<Route
path="photo/:identifier"
element={<Outtakes className={stylesheet.outtakesContainer} />}
/>
</Route>
<Route path="stories">
<Route
index
element={<AllStories className={stylesheet.outtakesContainer} />}
/>
<Route
path="photo/:identifier"
element={<AllStories className={stylesheet.outtakesContainer} />}
/>
<Route path="edit" element={<EditStory />} />
</Route>
</Route>
</Switch>
</Router>
<Route path="orders" element={<Orders />} />
<Route path="labs" element={<FeatureFlags />} />
<Route path="admin/*" element={<AdminRoutes />} />
<Route path="render-merch/tote-bag" element={<ToteBag />} />
<Route path="*" element={<Navigate to="/map" replace />} />
</Routes>
</HistoryRouter>
</ContextWrappers>
);
}
16 changes: 8 additions & 8 deletions frontend/src/screens/App/screens/Admin/AdminRoutes.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import React from 'react';

import { Route, Switch, useRouteMatch } from 'react-router-dom';
import { Route, Routes } from 'react-router-dom';
import LoginPage from './screens/LoginPage';
import ReviewMerch from './screens/ReviewMerch';
import ReviewStories from './screens/ReviewStories';
import PrivateRoute from './shared/components/PrivateRoute';

export default function AdminRoutes(): JSX.Element {
const { path } = useRouteMatch();

return (
<Switch>
<Route path={`${path}/login`} component={LoginPage} />
<PrivateRoute path={`${path}/review-stories`} component={ReviewStories} />
<PrivateRoute path={`${path}/review-merch`} component={ReviewMerch} />
</Switch>
<Routes>
<Route path="login" element={<LoginPage />} />
<Route element={<PrivateRoute />}>
<Route path="review-stories" element={<ReviewStories />} />
<Route path="review-merch" element={<ReviewMerch />} />
</Route>
</Routes>
);
}
14 changes: 7 additions & 7 deletions frontend/src/screens/App/screens/Admin/screens/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import React from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';
import type { To } from 'react-router-dom';
import Button from 'shared/components/Button';
import useAuthStore from 'shared/stores/AuthStore';
import { LocationDescriptor } from 'history';

export default function LoginPage(): JSX.Element {
const login = useAuthStore((state) => state.login);
const close = useAuthStore((state) => state.close);
const isAutheticated = useAuthStore((state) => state.isAutheticated);
const returnToRoute = useAuthStore((state) => state.returnToRoute);

const location = useLocation<{ from?: string }>();
const history = useHistory();
const location = useLocation<{ from?: To }>();
const navigate = useNavigate();

const from: LocationDescriptor = location.state?.from || returnToRoute || '/';
const from: To = location.state?.from || returnToRoute || '/';

// If authenticated, redirect to the page they were trying to access
React.useEffect(() => {
if (isAutheticated) {
history.replace(from);
navigate(from, { replace: true });
} else {
//automatically trigger login modal
login(from);
Expand All @@ -27,7 +27,7 @@ export default function LoginPage(): JSX.Element {
return () => {
close();
};
}, [isAutheticated, history, from, login, close]);
}, [isAutheticated, navigate, from, login, close]);

return (
<div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
import React from 'react';

import { Redirect, Route, RouteProps, useLocation } from 'react-router-dom';
import { Navigate, Outlet, useLocation } from 'react-router-dom';

import useAuthStore from 'shared/stores/AuthStore';

interface PrivateRouteProps {
children?: React.ReactNode;
}

export default function PrivateRoute({
children,
component,
...rest
}: RouteProps): JSX.Element {
const ProtectedComponent = (): JSX.Element => {
const isAuthenticated = useAuthStore((state) => state.isAutheticated);
const location = useLocation();
}: PrivateRouteProps): JSX.Element {
const isAuthenticated = useAuthStore((state) => state.isAutheticated);
const location = useLocation();

if (isAuthenticated) {
return component ? React.createElement(component) : <>{children}</>;
} else {
return (
<Redirect
to={{
pathname: '/admin/login',
state: { from: location },
}}
/>
);
}
};
if (!isAuthenticated) {
return (
<Navigate
to="/admin/login"
replace
state={{ from: location }}
/>
);
}

return <Route {...rest} component={ProtectedComponent} render={null}></Route>;
return <>{children ?? <Outlet />}</>;
}
6 changes: 3 additions & 3 deletions frontend/src/screens/App/screens/AllStories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import classnames from 'classnames';

import { Link, useHistory, useParams } from 'react-router-dom';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Story } from 'screens/App/shared/types/Story';
import Grid from 'shared/components/Grid';
import StoryView from 'shared/components/Story';
Expand Down Expand Up @@ -75,7 +75,7 @@ export default function Outtakes({
[stories, loadNextPage]
);

const history = useHistory();
const location = useLocation();
const { identifier: selectedIdentifier } = useParams<{
identifier?: string;
}>();
Expand All @@ -89,7 +89,7 @@ export default function Outtakes({
<i>Know This Place?</i> on any photo.
</p>
<Link
to={{ pathname: '/map', hash: history.location.hash }}
to={{ pathname: '/map', hash: location.hash }}
className={stylesheet.backToMap}
>
Back to map
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import Modal from 'components/Modal';
import React from 'react';
import { useHistory } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';
import recordEvent from 'shared/utils/recordEvent';

export default function ThankYou({
isOpen,
onRequestClose,
}: Pick<ReactModal.Props, 'isOpen' | 'onRequestClose'>): JSX.Element {
const history = useHistory();
const navigate = useNavigate();
const location = useLocation();

const queryParamsRef = React.useRef(
new URLSearchParams(window.location.search)
Expand All @@ -16,11 +17,14 @@ export default function ThankYou({

React.useEffect(() => {
// Remove query from url
history.replace({
pathname: history.location.pathname,
hash: history.location.hash,
});
}, [history]);
navigate(
{
pathname: location.pathname,
hash: location.hash,
},
{ replace: true }
);
}, [navigate, location.pathname, location.hash]);

React.useEffect(() => {
if (!isOpen) return;
Expand Down
Loading