Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Routing Improvements #797

Merged
merged 3 commits into from
May 10, 2024
Merged
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
29 changes: 21 additions & 8 deletions frontend2/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
type LoaderFunction,
} from "react-router-dom";
import { DEFAULT_EPISODE } from "./utils/constants";
import NotFound from "./views/NotFound";
import EpisodeNotFound from "./views/EpisodeNotFound";
import Rankings from "./views/Rankings";
import { CurrentUserProvider } from "./contexts/CurrentUserProvider";
import PrivateRoute from "./components/PrivateRoute";
Expand Down Expand Up @@ -48,6 +48,7 @@ import { tournamentLoader } from "./api/loaders/tournamentLoader";
import { homeLoader } from "./api/loaders/homeLoader";
import ErrorBoundary from "./views/ErrorBoundary";
import { searchTeamsFactory } from "api/team/teamFactories";
import PageNotFound from "views/PageNotFound";

const queryClient = new QueryClient({
queryCache: new QueryCache({
Expand All @@ -62,6 +63,9 @@ const queryClient = new QueryClient({
}),
});

queryClient.setQueryDefaults(["episode"], {
retry: 1,
});
queryClient.setQueryDefaults(["team"], { retry: false });
queryClient.setQueryDefaults(["user"], { retry: false });

Expand All @@ -82,21 +86,29 @@ const App: React.FC = () => {
);
};

const episodeLoader: LoaderFunction = ({ params }) => {
const episodeLoader: LoaderFunction = async ({ params }) => {
// check if the episodeId is a valid one.
// if the episode is not found, throw an error.
const id = params.episodeId ?? "";
if (id === "") {
throw new ResponseError(
new Response("Episode not found.", { status: 404 }),
);
}

// Prefetch the episode info.
void queryClient.ensureQueryData({
// Await the episode info so we can be sure that it exists.
const episodeInfo = await queryClient.ensureQueryData({
queryKey: buildKey(episodeInfoFactory.queryKey, { id }),
queryFn: async () => await episodeInfoFactory.queryFn({ id }),
staleTime: Infinity,
});

// Prefetch the top 10 ranked teams' rating histories.
void queryClient.ensureQueryData({
queryKey: buildKey(searchTeamsFactory.queryKey, { episodeId: id, page: 1 }),
queryKey: buildKey(searchTeamsFactory.queryKey, {
episodeId: id,
page: 1,
}),
queryFn: async () =>
await searchTeamsFactory.queryFn(
{ episodeId: id, page: 1 },
Expand All @@ -105,7 +117,7 @@ const episodeLoader: LoaderFunction = ({ params }) => {
),
});

return null;
return episodeInfo;
};

const router = createBrowserRouter([
Expand Down Expand Up @@ -137,13 +149,14 @@ const router = createBrowserRouter([
// Pages that will contain the episode sidebar and navbar (excl. account page)
{
element: <EpisodeLayout />,
errorElement: <ErrorBoundary />,
errorElement: <EpisodeNotFound />,
path: "/:episodeId",
loader: episodeLoader,
children: [
{
// Pages that should only be visible when logged in
element: <PrivateRoute />,
errorElement: <ErrorBoundary />,
children: [
{
path: "submissions",
Expand Down Expand Up @@ -197,7 +210,7 @@ const router = createBrowserRouter([
},
{
path: "*",
element: <NotFound />,
element: <PageNotFound />,
},
],
},
Expand Down
32 changes: 16 additions & 16 deletions frontend2/src/components/EpisodeSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@ import Icon from "./elements/Icon";
import { useEpisodeId } from "../contexts/EpisodeContext";
import { isPresent } from "../utils/utilTypes";
import { useNavigate } from "react-router-dom";
import { useEpisodeList } from "../api/episode/useEpisode";
import { useEpisodeInfo, useEpisodeList } from "../api/episode/useEpisode";
import { useQueryClient } from "@tanstack/react-query";
import toast from "react-hot-toast";
import { episodeInfoFactory } from "../api/episode/episodeFactories";
import { buildKey } from "../api/helpers";
import Spinner from "./Spinner";

const EpisodeSwitcher: React.FC = () => {
const queryClient = useQueryClient();
const { episodeId, setEpisodeId } = useEpisodeId();
const { data: episodeList } = useEpisodeList({ page: 1 }, queryClient);
const episodeList = useEpisodeList({ page: 1 }, queryClient);
const episodeInfo = useEpisodeInfo({ id: episodeId });
const navigate = useNavigate();
const idToName = useMemo(
() =>
new Map(
(episodeList?.results ?? []).map((ep) => [ep.name_short, ep.name_long]),
(episodeList?.data?.results ?? []).map((ep) => [
ep.name_short,
ep.name_long,
]),
),
[episodeList],
);

if (!isPresent(episodeList)) {
return null;
}
Expand All @@ -30,27 +33,24 @@ const EpisodeSwitcher: React.FC = () => {
<Listbox
value={episodeId}
onChange={(newEpisodeId) => {
(async () =>
await queryClient.fetchQuery({
queryKey: buildKey(episodeInfoFactory.queryKey, {
id: newEpisodeId,
}),
queryFn: async () =>
await episodeInfoFactory.queryFn({ id: newEpisodeId }),
}))().catch((e) => toast.error((e as Error).message));
setEpisodeId(newEpisodeId);
navigate(`/${newEpisodeId}/home`);
}}
>
<div className="relative">
<Listbox.Button
className={`relative h-9 w-full truncate rounded-full bg-gray-900/80 py-1.5
className={`relative flex h-9 w-full flex-row items-center justify-center gap-3 truncate rounded-full bg-gray-900/80 py-1.5
pl-3.5 pr-8 text-left text-gray-100 shadow-sm focus:outline-none
sm:text-sm sm:leading-6`}
>
<span className="text-sm font-semibold">
{idToName.get(episodeId)}
</span>
{(episodeList.isLoading || episodeInfo.isLoading) && (
<div className="my-1 flex w-full justify-center">
<Spinner size="xs" />
</div>
)}
<div
className="absolute inset-y-0 right-0 mr-2 flex transform items-center
transition duration-300 ui-open:rotate-180"
Expand All @@ -69,7 +69,7 @@ const EpisodeSwitcher: React.FC = () => {
bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none
sm:max-h-60 sm:text-sm"
>
{episodeList?.results?.map((ep) => (
{episodeList?.data?.results?.map((ep) => (
<Listbox.Option
className="flex cursor-default flex-row justify-between py-1.5 pl-4 pr-2 ui-active:bg-cyan-100"
key={ep.name_short}
Expand Down
18 changes: 18 additions & 0 deletions frontend2/src/views/EpisodeNotFound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import { Link } from "react-router-dom";
import { DEFAULT_EPISODE } from "utils/constants";

const EpisodeNotFound: React.FC = () => {
return (
<div className="flex h-full w-full flex-col overflow-auto p-6">
<p>
That episode was not found.{" "}
<Link to={`/${DEFAULT_EPISODE}/home`} className="hover:underline">
Go Home?
</Link>
</p>
</div>
);
};

export default EpisodeNotFound;
2 changes: 1 addition & 1 deletion frontend2/src/views/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ const Login: React.FC = () => {
<div>
<hr />
<div className="mt-3 flex flex-row justify-between text-sm text-cyan-600">
<Link to="/forgot_password">Forgot password?</Link>
<Link to="/password_forgot">Forgot password?</Link>
<Link to={episodeId !== undefined ? `/${episodeId}/home` : "/"}>
Back to home
</Link>
Expand Down
7 changes: 0 additions & 7 deletions frontend2/src/views/NotFound.tsx

This file was deleted.

18 changes: 18 additions & 0 deletions frontend2/src/views/PageNotFound.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import { NavLink } from "react-router-dom";
import { DEFAULT_EPISODE } from "utils/constants";

const PageNotFound: React.FC = () => {
return (
<div className="flex h-full w-full flex-col overflow-auto p-6">
<p>
That page was not found.{" "}
<NavLink to={`/${DEFAULT_EPISODE}/home`} className="hover:underline">
Go Home?
</NavLink>
</p>
</div>
);
};

export default PageNotFound;
Loading