diff --git a/videoag_new_frontend/package-lock.json b/videoag_new_frontend/package-lock.json index 1a09c9a8f3c19b07bb0afb7679d4fc96f7aa5fdc..c374a353595b9eecdd74e6220fcaf1d5755fbaf1 100644 --- a/videoag_new_frontend/package-lock.json +++ b/videoag_new_frontend/package-lock.json @@ -17,7 +17,6 @@ "react-bootstrap": "^2.10.1", "react-dom": "^18.2.0", "react-markdown": "^9.0.1", - "react-router-dom": "^6.21.3", "react-toastify": "^10.0.5", "remark-gfm": "^4.0.0", "sass": "^1.68.0", @@ -452,14 +451,6 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, - "node_modules/@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@restart/hooks": { "version": "0.4.16", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", @@ -5325,36 +5316,6 @@ "react": ">=18" } }, - "node_modules/react-router": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", - "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", - "dependencies": { - "@remix-run/router": "1.15.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", - "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", - "dependencies": { - "@remix-run/router": "1.15.3", - "react-router": "6.22.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, "node_modules/react-toastify": { "version": "10.0.5", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz", diff --git a/videoag_new_frontend/package.json b/videoag_new_frontend/package.json index 522a4632efed7e303089a1d1e7cac5594198c6c8..90f38200c0749dbdb93b702fc650f24097480fce 100644 --- a/videoag_new_frontend/package.json +++ b/videoag_new_frontend/package.json @@ -24,7 +24,6 @@ "react-bootstrap": "^2.10.1", "react-dom": "^18.2.0", "react-markdown": "^9.0.1", - "react-router-dom": "^6.21.3", "react-toastify": "^10.0.5", "remark-gfm": "^4.0.0", "sass": "^1.68.0", diff --git a/videoag_new_frontend/src/components/CourseListing.tsx b/videoag_new_frontend/src/components/CourseListing.tsx index 893b3806ef24c0832acc1065a0c7d71cb640ddbd..f832e42c1185fef23fa50fa198023e0ab0796d46 100644 --- a/videoag_new_frontend/src/components/CourseListing.tsx +++ b/videoag_new_frontend/src/components/CourseListing.tsx @@ -1,7 +1,5 @@ -import { useLoaderData } from "react-router-dom"; import { useBackendContext } from "./BackendProvider"; import Link from "next/link"; -import { Backend } from "@/api/Backend"; import { OMCreate, OMDelete, @@ -15,13 +13,7 @@ import Title from "./TitleComponent"; import DownloadAllModal from "./DownloadManager"; import type { GetCourseResponse, lecture } from "@/api/api_v1_types"; - -export function getCourseDataLoader(api: Backend) { - const actualLoader = async ({ params }: { params: { course_id_string?: string } }) => { - return api.getCourse(params.course_id_string!, true); - }; - return actualLoader; -} +import { ResourceType } from "@/misc/PromiseHelpers"; function ListingHeader({ course }: { course: GetCourseResponse }) { const { editMode } = useEditMode(); @@ -330,8 +322,12 @@ function ListingBody({ course }: { course: GetCourseResponse }) { ); } -export default function CourseListing() { - const course = useLoaderData() as GetCourseResponse | undefined; +export default function CourseListing({ + courseData, +}: { + courseData: ResourceType<GetCourseResponse>; +}) { + const course = courseData.read()!; if (!course) return <>invalid course listing</>; diff --git a/videoag_new_frontend/src/pages/dynamic_course_listing.tsx b/videoag_new_frontend/src/pages/dynamic_course_listing.tsx index c6f0d4c32e906f1e81b7c70bae884962dcaa976d..fd95f5fff666240b97284d9e1bbe9d9f9fc6a58b 100644 --- a/videoag_new_frontend/src/pages/dynamic_course_listing.tsx +++ b/videoag_new_frontend/src/pages/dynamic_course_listing.tsx @@ -1,57 +1,54 @@ -import { useEffect, useState } from "react"; -import { RouterProvider, createBrowserRouter, useRouteError } from "react-router-dom"; -import nextConfig from "@/../basepath"; +import { Suspense, useEffect, useState } from "react"; import Four04 from "./404"; -import CourseListing, { getCourseDataLoader } from "@/components/CourseListing"; +import CourseListing from "@/components/CourseListing"; import { useBackendContext } from "@/components/BackendProvider"; import { ReloadBoundary } from "@/components/ReloadBoundary"; import { ErrorPage } from "@/misc/ErrorHandlers"; - -function Custom404() { - const error = useRouteError(); - return ( - <ErrorPage - error={error} - objectName="Kurs" - expectedErrorCodes={[ - "unauthorized", - "access_forbidden", - "unknown_object", - "rate_limited", - ]} - /> - ); -} +import { ResourceType, fetchDataWrapper } from "@/misc/PromiseHelpers"; +import { usePathname } from "next/navigation"; +import { useUserContext } from "@/components/UserDataProvider"; +import { GetCourseResponse } from "@/api/api_v1_types"; +import { FallbackErrorBoundary } from "@/components/FallbackErrorBoundary"; function ActualRedirector() { const api = useBackendContext(); + const userContext = useUserContext(); + const hasUserInfo = userContext.hasUserInfo(); + const pathname = usePathname(); + const matchedParams = pathname.match(/^\/([a-z0-9_-]+)\/?$/); + const [courseData, setCourseData] = useState<ResourceType<GetCourseResponse>>(); - const router = createBrowserRouter( - [ - { - path: ":course_id_string", - loader: getCourseDataLoader(api), - errorElement: <Custom404 />, - element: <CourseListing />, - }, - { - path: "404", - element: <Four04 />, - }, - { - path: "*", - element: <>* matched dynamic_course_listing</>, - }, - ], - { basename: nextConfig.basePath }, - ); const reload = () => { - router.navigate(".", { replace: true }); + if (!matchedParams) return; + setCourseData(fetchDataWrapper(api.getCourse(matchedParams[1], true))); }; + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(reload, [api, pathname, hasUserInfo]); + + if (!matchedParams) return <Four04 title="Unbekannter Pfad" text="dynamic_course_listing" />; + if (!courseData) return <></>; + return ( <ReloadBoundary reloadFunc={reload}> - <RouterProvider router={router} /> + <FallbackErrorBoundary + fallback={(e: any) => ( + <ErrorPage + error={e} + objectName="Kurs" + expectedErrorCodes={[ + "unauthorized", + "access_forbidden", + "unknown_object", + "rate_limited", + ]} + /> + )} + > + <Suspense fallback={<div className="spinner-border" />}> + <CourseListing courseData={courseData} /> + </Suspense> + </FallbackErrorBoundary> </ReloadBoundary> ); }