From 859c9033f081d4159ded59090f069d71fda4ed47 Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Fri, 1 Nov 2024 14:01:19 -0600 Subject: [PATCH] starting lecture --- nextjs/src/hooks/hookHydration.ts | 3 + nextjs/src/hooks/localCourse/lectureHooks.ts | 15 +++++ nextjs/src/hooks/localCourse/lectureKeys.ts | 3 + nextjs/src/models/local/lecture.ts | 5 ++ .../fileStorage/lectureFileStorageService.ts | 62 +++++++++++++++++++ .../fileStorage/moduleFileStorageService.ts | 4 +- 6 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 nextjs/src/hooks/localCourse/lectureHooks.ts create mode 100644 nextjs/src/hooks/localCourse/lectureKeys.ts create mode 100644 nextjs/src/models/local/lecture.ts create mode 100644 nextjs/src/services/fileStorage/lectureFileStorageService.ts diff --git a/nextjs/src/hooks/hookHydration.ts b/nextjs/src/hooks/hookHydration.ts index d300acf..c1a9f11 100644 --- a/nextjs/src/hooks/hookHydration.ts +++ b/nextjs/src/hooks/hookHydration.ts @@ -11,6 +11,7 @@ import { canvasQuizService } from "@/services/canvas/canvasQuizService"; import { canvasPageService } from "@/services/canvas/canvasPageService"; import { canvasQuizKeys } from "./canvas/canvasQuizHooks"; import { canvasPageKeys } from "./canvas/canvasPageHooks"; +import { getLecturesQueryConfig } from "./localCourse/lectureHooks"; // https://tanstack.com/query/latest/docs/framework/react/guides/ssr export const hydrateCourses = async (queryClient: QueryClient) => { @@ -39,6 +40,8 @@ export const hydrateCourse = async ( moduleNames.map((moduleName) => loadAllModuleData(courseName, moduleName)) ); + await queryClient.prefetchQuery(getLecturesQueryConfig(courseName)); + await queryClient.prefetchQuery({ queryKey: localCourseKeys.settings(courseName), queryFn: () => courseSettings, diff --git a/nextjs/src/hooks/localCourse/lectureHooks.ts b/nextjs/src/hooks/localCourse/lectureHooks.ts new file mode 100644 index 0000000..4dc2d85 --- /dev/null +++ b/nextjs/src/hooks/localCourse/lectureHooks.ts @@ -0,0 +1,15 @@ +import { useSuspenseQuery } from "@tanstack/react-query"; +import { lectureKeys } from "./lectureKeys"; +import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; +import { getLectures } from "@/services/fileStorage/lectureFileStorageService"; + +export const getLecturesQueryConfig = (courseName: string) => + ({ + queryKey: lectureKeys.allLectures(courseName), + queryFn: async () => await getLectures(courseName), + } as const); + +export const useLecturesQuery = () => { + const { courseName } = useCourseContext(); + return useSuspenseQuery(getLecturesQueryConfig(courseName)); +}; diff --git a/nextjs/src/hooks/localCourse/lectureKeys.ts b/nextjs/src/hooks/localCourse/lectureKeys.ts new file mode 100644 index 0000000..e865898 --- /dev/null +++ b/nextjs/src/hooks/localCourse/lectureKeys.ts @@ -0,0 +1,3 @@ +export const lectureKeys = { + allLectures: (courseName: string) => ["lectures", courseName] as const +} \ No newline at end of file diff --git a/nextjs/src/models/local/lecture.ts b/nextjs/src/models/local/lecture.ts new file mode 100644 index 0000000..e5d7517 --- /dev/null +++ b/nextjs/src/models/local/lecture.ts @@ -0,0 +1,5 @@ +export interface Lecture { + name: string + date: string + content: string +} \ No newline at end of file diff --git a/nextjs/src/services/fileStorage/lectureFileStorageService.ts b/nextjs/src/services/fileStorage/lectureFileStorageService.ts new file mode 100644 index 0000000..518f7c8 --- /dev/null +++ b/nextjs/src/services/fileStorage/lectureFileStorageService.ts @@ -0,0 +1,62 @@ +"use server"; +import path from "path"; +import { basePath } from "./utils/fileSystemUtils"; +import fs from "fs/promises"; +import { Lecture } from "@/models/local/lecture"; +import { extractLabelValue } from "@/models/local/assignment/utils/markdownUtils"; + +export async function getLectures(courseName: string) { + const courseLectureRoot = path.join(basePath, courseName, "lectures"); + if (!(await directoryExists(courseLectureRoot))) { + return []; + } + + const entries = await fs.readdir(courseLectureRoot, { withFileTypes: true }); + const lectureWeekFolders = entries + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name); + + const lecturesByWeek = await Promise.all( + lectureWeekFolders.map(async (weekName) => { + const weekBasePath = path.join(courseLectureRoot, weekName); + const fileNames = await fs.readdir(weekBasePath); + const lectures = await Promise.all( + fileNames.map(async (fileName) => { + const filePath = path.join(weekBasePath, fileName); + const fileContent = await fs.readFile(filePath, "utf-8"); + const lecture = parseLecture(fileContent); + return lecture; + }) + ); + + return { + weekName, + lectures, + }; + }) + ); + return lecturesByWeek; +} + +function parseLecture(fileContent: string): Lecture { + const settings = fileContent.split("---\n")[0]; + const name = extractLabelValue(settings, "Name"); + const date = extractLabelValue(settings, "Date"); + + const content = fileContent.split("---\n")[1].trim(); + + return { + name, + date, + content, + }; +} + +const directoryExists = async (path: string): Promise => { + try { + const stat = await fs.stat(path); + return stat.isDirectory(); + } catch { + return false; + } +}; diff --git a/nextjs/src/services/fileStorage/moduleFileStorageService.ts b/nextjs/src/services/fileStorage/moduleFileStorageService.ts index 89fcc7d..f014017 100644 --- a/nextjs/src/services/fileStorage/moduleFileStorageService.ts +++ b/nextjs/src/services/fileStorage/moduleFileStorageService.ts @@ -14,7 +14,9 @@ export const moduleFileStorageService = { .map((dirent) => dirent.name); const modules = await Promise.all(modulePromises); - return modules.sort((a, b) => a.localeCompare(b)); + return modules + .filter((m) => m !== "lectures") + .sort((a, b) => a.localeCompare(b)); }, async createModule(courseName: string, moduleName: string) { const courseDirectory = path.join(basePath, courseName);