diff --git a/package.json b/package.json index d3198cbb..75f49cd9 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "@hookform/resolvers": "^3.1.1", "@neodrag/react": "^2.0.3", "@tanstack/react-query": "^4.29.14", + "@tanstack/router": "0.0.1-beta.86", + "@tanstack/router-devtools": "0.0.1-beta.86", "@types/deep-equal": "^1.0.1", "@zxch3n/tidy": "github:hackworthltd/tidy#e07fdef2ae7bf593701817113dd47b4cd56c7a97", "axios": "^1.4.0", @@ -36,7 +38,6 @@ "react-cookie": "^4.1.1", "react-dom": "^18.2.0", "react-hook-form": "^7.44.3", - "react-router-dom": "^6.13.0", "reactflow": "^11.7.2", "universal-cookie": "^4.0.4", "uuid": "^9.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86e596fc..c12c5d36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,6 +27,12 @@ dependencies: '@tanstack/react-query': specifier: ^4.29.14 version: 4.29.14(react-dom@18.2.0)(react@18.2.0) + '@tanstack/router': + specifier: 0.0.1-beta.86 + version: 0.0.1-beta.86(react-dom@18.2.0)(react@18.2.0) + '@tanstack/router-devtools': + specifier: 0.0.1-beta.86 + version: 0.0.1-beta.86(react-dom@18.2.0)(react@18.2.0) '@types/deep-equal': specifier: ^1.0.1 version: 1.0.1 @@ -57,9 +63,6 @@ dependencies: react-hook-form: specifier: ^7.44.3 version: 7.44.3(react@18.2.0) - react-router-dom: - specifier: ^6.13.0 - version: 6.13.0(react-dom@18.2.0)(react@18.2.0) reactflow: specifier: ^11.7.2 version: 11.7.2(react-dom@18.2.0)(react@18.2.0) @@ -3988,7 +3991,7 @@ packages: open: 8.4.2 picocolors: 1.0.0 tiny-glob: 0.2.9 - tslib: 2.5.2 + tslib: 2.5.3 dev: true /@rc-component/portal@1.1.1(react-dom@18.2.0)(react@18.2.0): @@ -4107,11 +4110,6 @@ packages: - immer dev: false - /@remix-run/router@1.6.3: - resolution: {integrity: sha512-EXJysQ7J3veRECd0kZFQwYYd5sJMcq2O/m60zu1W2l3oVQ9xtub8jTOtYRE0+M2iomyG/W3Ps7+vp2kna0C27Q==} - engines: {node: '>=14'} - dev: false - /@rollup/plugin-commonjs@22.0.2(rollup@2.79.1): resolution: {integrity: sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==} engines: {node: '>= 12.0.0'} @@ -5520,6 +5518,53 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false + /@tanstack/react-store@0.0.1-beta.86(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-jDz11No4r7mHyLMSqwremt8QaiSS3mMpcMZyWKQL3AH9hc9ygvnF+hTxgp1cZyfpE4vwNZDYf7eRX85TYo3Tyg==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@tanstack/store': 0.0.1-beta.86 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + + /@tanstack/router-devtools@0.0.1-beta.86(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-DGprT4VtugGNMOfPVKpyu3ez6QlCx2NQLr4zZ7CH4XcujOkFR44biPTq93sGoaJAOLw9gdjdt+j3w+oNuPUK8g==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@babel/runtime': 7.22.5 + '@tanstack/router': 0.0.1-beta.86(react-dom@18.2.0)(react@18.2.0) + date-fns: 2.30.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@tanstack/router@0.0.1-beta.86(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-OgPl/eY34cqTRbRbpn4/3A+b+c+QhYR2JlTv9G9HNmgY2fADs3ce1MWrq4ejX8O6uypavSgr+TP0kximGlQFpA==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@babel/runtime': 7.22.5 + '@tanstack/react-store': 0.0.1-beta.86(react-dom@18.2.0)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + tiny-invariant: 1.3.1 + tiny-warning: 1.0.3 + dev: false + + /@tanstack/store@0.0.1-beta.86: + resolution: {integrity: sha512-e49P3Lp44QyvB/1eNwDLmpl9LnfzbnDa6/8csylUgn8Yk0VJu5P7kyRwCwyX8/AoU74H1h0YaAqKl/gbbAE6nA==} + engines: {node: '>=12'} + dev: false + /@tootallnate/once@1.1.2: resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} @@ -12429,29 +12474,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /react-router-dom@6.13.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-6Nqoqd7fgwxxVGdbiMHTpDHCYPq62d7Wk1Of7B82vH7ZPwwsRaIa22zRZKPPg413R5REVNiyuQPKDG1bubcOFA==} - engines: {node: '>=14'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' - dependencies: - '@remix-run/router': 1.6.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-router: 6.13.0(react@18.2.0) - dev: false - - /react-router@6.13.0(react@18.2.0): - resolution: {integrity: sha512-Si6KnfEnJw7gUQkNa70dlpI1bul46FuSxX5t5WwlUBxE25DAz2BjVkwaK8Y2s242bQrZPXCpmwLPtIO5pv4tXg==} - engines: {node: '>=14'} - peerDependencies: - react: '>=16.8' - dependencies: - '@remix-run/router': 1.6.3 - react: 18.2.0 - dev: false - /react-universal-interface@0.6.2(react@18.2.0)(tslib@2.5.3): resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} peerDependencies: @@ -13509,7 +13531,7 @@ packages: engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@pkgr/utils': 2.3.1 - tslib: 2.5.0 + tslib: 2.5.3 dev: true /tailwindcss@3.3.2(ts-node@10.9.1): @@ -13693,7 +13715,10 @@ packages: /tiny-invariant@1.3.1: resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} - dev: true + + /tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + dev: false /tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} @@ -13829,14 +13854,6 @@ packages: resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} dev: false - /tslib@2.5.0: - resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} - dev: true - - /tslib@2.5.2: - resolution: {integrity: sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==} - dev: true - /tslib@2.5.3: resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} diff --git a/src/App.tsx b/src/App.tsx index ac8e5e8e..75c627b9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; +import { RouterProvider } from "@tanstack/router"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { CookiesProvider, useCookies } from "react-cookie"; import { CookieSetOptions } from "universal-cookie"; @@ -7,7 +7,7 @@ import { v4 as uuidv4 } from "uuid"; import "@/index.css"; -import { ChooseSession, Edit, NoMatch } from "@/components"; +import { router } from "@/router"; const queryClient = new QueryClient(); @@ -37,18 +37,9 @@ const App = (): JSX.Element => { return ( - - - - } /> - - } /> - } /> - - } /> - - - + + + ); }; diff --git a/src/components/ChooseSession/index.tsx b/src/components/ChooseSession/index.tsx index 3d126ae9..a70b338c 100644 --- a/src/components/ChooseSession/index.tsx +++ b/src/components/ChooseSession/index.tsx @@ -15,10 +15,14 @@ import { getGetSessionListQueryKey, useDeleteSession, } from "@/primer-api"; -import { useNavigate } from "react-router-dom"; +import { AnyRoute, useNavigate } from "@tanstack/router"; import { useQueryClient } from "@tanstack/react-query"; -const ChooseSession = (): JSX.Element => { +export interface ChooseSessionProps { + route: AnyRoute; +} + +const ChooseSession = (p: ChooseSessionProps): JSX.Element => { const [cookies] = useCookies(["id"]); // NOTE: pagination in our API is 1-indexed. @@ -67,10 +71,14 @@ const ChooseSession = (): JSX.Element => { setPage(meta.lastPage); } - const navigate = useNavigate(); + const navigate = useNavigate({ from: p.route.id }); const newSession = useCreateSession({ mutation: { - onSuccess: (newSessionID: Uuid) => navigate(`/sessions/${newSessionID}`), + onSuccess: (newSessionId: Uuid) => + navigate({ + to: "/sessions/$sessionId", + params: { sessionId: newSessionId }, + }), }, }); diff --git a/src/components/Edit/index.tsx b/src/components/Edit/index.tsx index 175f36f6..7491588f 100644 --- a/src/components/Edit/index.tsx +++ b/src/components/Edit/index.tsx @@ -21,7 +21,7 @@ import { useRef, useState, } from "react"; -import { useParams } from "react-router-dom"; +import { AnyRoute, useParams } from "@tanstack/router"; import { ReactFlowProvider } from "reactflow"; import { useGetAvailableActions, @@ -54,13 +54,17 @@ import { // hardcoded values (for now) const initialLevel: Level = "Expert"; -const Edit = (): JSX.Element => { - const params = useParams(); - const sessionId = params["sessionId"]; +export interface EditProps { + route: AnyRoute; +} +const Edit = (p: EditProps): JSX.Element => { + const { sessionId } = useParams({ from: p.route.id }); if (!sessionId) { return ( - + ); } // This hook is *technically* conditional. diff --git a/src/components/SessionPreview/index.tsx b/src/components/SessionPreview/index.tsx index 9ac9c5d1..fa994f82 100644 --- a/src/components/SessionPreview/index.tsx +++ b/src/components/SessionPreview/index.tsx @@ -1,8 +1,7 @@ import "@/index.css"; import { StarIcon, TrashIcon, UserPlusIcon } from "@heroicons/react/24/outline"; - -import { Link } from "react-router-dom"; +import { Link } from "@tanstack/router"; import { SessionMeta } from "@/Types"; import { BinaryTreePlaceholder } from "@/components"; @@ -21,8 +20,8 @@ export const SessionPreview = ({
diff --git a/src/router.tsx b/src/router.tsx new file mode 100644 index 00000000..f177cfe7 --- /dev/null +++ b/src/router.tsx @@ -0,0 +1,30 @@ +import { Outlet, RootRoute, Router } from "@tanstack/router"; +import { TanStackRouterDevtools } from "@tanstack/router-devtools"; + +import { indexRoute } from "@/routes"; +import { sessionsRoute } from "@/routes/sessions"; +import { sessionIdRoute } from "@/routes/sessions/sessionId"; +import { catchAllRoute } from "@/routes/catchAll"; + +export const rootRoute = new RootRoute({ + component: () => ( + <> + + + + ), +}); + +const routeTree = rootRoute.addChildren([ + indexRoute, + sessionsRoute.addChildren([sessionIdRoute]), + catchAllRoute, +]); + +export const router = new Router({ routeTree }); + +declare module "@tanstack/router" { + interface Register { + router: typeof router; + } +} diff --git a/src/routes/catchAll/index.ts b/src/routes/catchAll/index.ts new file mode 100644 index 00000000..ea3adb72 --- /dev/null +++ b/src/routes/catchAll/index.ts @@ -0,0 +1,9 @@ +import { Route } from "@tanstack/router"; +import { rootRoute } from "@/router"; +import { NoMatch } from "@/components"; + +export const catchAllRoute = new Route({ + getParentRoute: () => rootRoute, + path: "*", + component: NoMatch, +}); diff --git a/src/routes/index.tsx b/src/routes/index.tsx new file mode 100644 index 00000000..e2c98d1f --- /dev/null +++ b/src/routes/index.tsx @@ -0,0 +1,11 @@ +import { Navigate, Route } from "@tanstack/router"; + +import { rootRoute } from "@/router"; + +const Root = (): JSX.Element => ; + +export const indexRoute = new Route({ + getParentRoute: () => rootRoute, + path: "/", + component: Root, +}); diff --git a/src/routes/sessions/index.tsx b/src/routes/sessions/index.tsx new file mode 100644 index 00000000..1899bfe5 --- /dev/null +++ b/src/routes/sessions/index.tsx @@ -0,0 +1,12 @@ +import { Route } from "@tanstack/router"; + +import { rootRoute } from "@/router"; +import { ChooseSession } from "@/components"; + +const SessionsRoute = (): JSX.Element => ; + +export const sessionsRoute = new Route({ + getParentRoute: () => rootRoute, + path: "sessions", + component: SessionsRoute, +}); diff --git a/src/routes/sessions/sessionId/index.tsx b/src/routes/sessions/sessionId/index.tsx new file mode 100644 index 00000000..71653547 --- /dev/null +++ b/src/routes/sessions/sessionId/index.tsx @@ -0,0 +1,12 @@ +import { Route } from "@tanstack/router"; + +import { sessionsRoute } from "@/routes/sessions"; +import { Edit } from "@/components"; + +const SessionIdRoute = (): JSX.Element => ; + +export const sessionIdRoute = new Route({ + getParentRoute: () => sessionsRoute, + path: "$sessionId", + component: SessionIdRoute, +});