From e47810a74147f13ff7f1a83ed5deafcf35451d3d Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Tue, 19 Nov 2024 17:07:26 -0700 Subject: [PATCH] disable useeffecto for now --- .../[courseName]/calendar/day/DayTitle.tsx | 1 + .../[assignmentName]/AssignmentPreview.tsx | 2 + .../[assignmentName]/EditAssignment.tsx | 14 ++++- .../components/SuspenseAndErrorHandling.tsx | 11 ++-- .../src/hooks/localCourse/assignmentHooks.ts | 60 +++++++++++++++++++ nextjs/src/services/htmlMarkdownUtils.ts | 47 ++++++++++++--- 6 files changed, 119 insertions(+), 16 deletions(-) diff --git a/nextjs/src/app/course/[courseName]/calendar/day/DayTitle.tsx b/nextjs/src/app/course/[courseName]/calendar/day/DayTitle.tsx index 76766e7..792f93b 100644 --- a/nextjs/src/app/course/[courseName]/calendar/day/DayTitle.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/day/DayTitle.tsx @@ -22,6 +22,7 @@ export function DayTitle({ day, dayAsDate }: { day: string; dayAsDate: Date }) { { if (todaysLecture) { diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentPreview.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentPreview.tsx index 9badab1..592a8d6 100644 --- a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentPreview.tsx +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentPreview.tsx @@ -1,3 +1,5 @@ +import ClientOnly from "@/components/ClientOnly"; +import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling"; import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; import { LocalAssignment } from "@/models/local/assignment/localAssignment"; import { rubricItemIsExtraCredit } from "@/models/local/assignment/rubricItem"; diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx index c8cfbda..71d15b3 100644 --- a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/EditAssignment.tsx @@ -3,6 +3,7 @@ import { MonacoEditor } from "@/components/editor/MonacoEditor"; import { useAssignmentQuery, useUpdateAssignmentMutation, + useUpdateImageSettingsForAssignment, } from "@/hooks/localCourse/assignmentHooks"; import { LocalAssignment, @@ -31,9 +32,12 @@ export default function EditAssignment({ const router = useRouter(); const { courseName } = useCourseContext(); const [settings] = useLocalCourseSettingsQuery(); - const [assignment, { dataUpdatedAt: serverDataUpdatedAt, isFetching: assignmentIsFetching }] = - useAssignmentQuery(moduleName, assignmentName); + const [ + assignment, + { dataUpdatedAt: serverDataUpdatedAt, isFetching: assignmentIsFetching }, + ] = useAssignmentQuery(moduleName, assignmentName); const updateAssignment = useUpdateAssignmentMutation(); + useUpdateImageSettingsForAssignment({ moduleName, assignmentName }); const { clientIsAuthoritative, @@ -138,7 +142,11 @@ export default function EditAssignment({
{error && error}
- + + + + +
diff --git a/nextjs/src/components/SuspenseAndErrorHandling.tsx b/nextjs/src/components/SuspenseAndErrorHandling.tsx index 82268b4..9879fcb 100644 --- a/nextjs/src/components/SuspenseAndErrorHandling.tsx +++ b/nextjs/src/components/SuspenseAndErrorHandling.tsx @@ -5,16 +5,19 @@ import { ErrorBoundary } from "react-error-boundary"; import { Spinner } from "./Spinner"; import toast from "react-hot-toast"; -export const SuspenseAndErrorHandling: FC<{ children: ReactNode }> = ({ - children, -}) => { +export const SuspenseAndErrorHandling: FC<{ + children: ReactNode; + showToast?: boolean; +}> = ({ children, showToast = true }) => { return ( {({ reset }) => ( { - toast.error(getErrorMessage(props.error)); + if (showToast) { + toast.error(getErrorMessage(props.error)); + } return (
{getErrorMessage(props.error)}
diff --git a/nextjs/src/hooks/localCourse/assignmentHooks.ts b/nextjs/src/hooks/localCourse/assignmentHooks.ts index ff018c1..734d884 100644 --- a/nextjs/src/hooks/localCourse/assignmentHooks.ts +++ b/nextjs/src/hooks/localCourse/assignmentHooks.ts @@ -1,6 +1,15 @@ "use client"; import { trpc } from "@/services/serverFunctions/trpcClient"; import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; +import { + useLocalCourseSettingsQuery, + useUpdateLocalCourseSettingsMutation, +} from "./localCoursesHooks"; +import { + extractImageSources, + markdownToHtmlNoImages, +} from "@/services/htmlMarkdownUtils"; +import { useActionState, useEffect, useState } from "react"; export const useAssignmentQuery = ( moduleName: string, @@ -14,6 +23,57 @@ export const useAssignmentQuery = ( }); }; +export const useUpdateImageSettingsForAssignment = ({ + moduleName, + assignmentName, +}: { + moduleName: string; + assignmentName: string; +}) => { + const [settings] = useLocalCourseSettingsQuery(); + const [assignment] = useAssignmentQuery(moduleName, assignmentName); + const updateSettings = useUpdateLocalCourseSettingsMutation(); + const [isUpdatingSettings, setIsUpdatingSettings] = useState(false); + + // useEffect(() => { + // if (isUpdatingSettings) { + // console.log("not updating image assets, still loading"); + // return; + // } + // setIsUpdatingSettings(true); + // const assignmentMarkdown = markdownToHtmlNoImages(assignment.description); + + // const imageSources = extractImageSources(assignmentMarkdown); + // const imagesToUpdate = imageSources.filter((source) => + // settings.assets.every((a) => a.sourceUrl !== source) + // ); + // console.log("images to update", imagesToUpdate); + + + // Promise.all( + // imagesToUpdate.map(async (source) => { + // // todo: get canvas url + // const canvasUrl = ""; + // return { sourceUrl: source, canvasUrl }; + // }) + // ).then(async (newAssets) => { + // await updateSettings.mutateAsync({ + // settings: { + // ...settings, + // assets: [...settings.assets, ...newAssets], + // }, + // }); + // setIsUpdatingSettings(false); + // }); + // }, [ + // assignment.description, + // isUpdatingSettings, + // settings, + // settings.assets, + // updateSettings, + // ]); +}; + export const useAssignmentNamesQuery = (moduleName: string) => { const { courseName } = useCourseContext(); return trpc.assignment.getAllAssignments.useSuspenseQuery( diff --git a/nextjs/src/services/htmlMarkdownUtils.ts b/nextjs/src/services/htmlMarkdownUtils.ts index 631d89d..ff9dc90 100644 --- a/nextjs/src/services/htmlMarkdownUtils.ts +++ b/nextjs/src/services/htmlMarkdownUtils.ts @@ -3,18 +3,40 @@ import { marked } from "marked"; import * as DOMPurify from "isomorphic-dompurify"; import { LocalCourseSettings } from "@/models/local/localCourseSettings"; -function extractImageSources(html: string) { - const parser = new DOMParser(); - const doc = parser.parseFromString(html, "text/html"); - const imgElements = doc.querySelectorAll("img"); - const srcUrls = Array.from(imgElements).map((img) => img.src); +export function extractImageSources(htmlString: string) { + const srcUrls = []; + const regex = /]+src=["']?([^"'>]+)["']?/g; + let match; + + while ((match = regex.exec(htmlString)) !== null) { + srcUrls.push(match[1]); + } + return srcUrls; } - -function handleImages(html: string, settings: LocalCourseSettings) { +export function convertImagesToCanvasImages( + html: string, + settings: LocalCourseSettings +) { const imageSources = extractImageSources(html); - console.log(imageSources); + let mutableHtml = html; + // console.log(imageSources); + + const imageLookup = settings.assets.reduce((acc, asset) => { + return { ...acc, [asset.sourceUrl]: asset.canvasUrl }; + }, {} as { [key: string]: string }); + + for (const imageSrc of imageSources) { + const destinationUrl = imageLookup[imageSrc]; + if (typeof destinationUrl === "undefined") { + throw `cannot convert to html, no canvas url for ${imageSrc} in settings`; + } + mutableHtml = mutableHtml.replaceAll(imageSrc, destinationUrl); + } + // console.log(imageSources, imageLookup, mutableHtml); + return mutableHtml; } + export function markdownToHTMLSafe( markdownString: string, settings: LocalCourseSettings @@ -22,6 +44,13 @@ export function markdownToHTMLSafe( const clean = DOMPurify.sanitize( marked.parse(markdownString, { async: false, pedantic: false, gfm: true }) ); - handleImages(clean, settings); + return convertImagesToCanvasImages(clean, settings); + // return clean; +} + +export function markdownToHtmlNoImages(markdownString: string) { + const clean = DOMPurify.sanitize( + marked.parse(markdownString, { async: false, pedantic: false, gfm: true }) + ); return clean; }