From 9f3360261e0a3e3011c84e1d471f9140272f291d Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Fri, 15 Nov 2024 09:20:39 -0700 Subject: [PATCH] fixing authority on server quizzes still broken --- .../lecture/[lectureDay]/EditLecture.tsx | 19 ++-- .../[assignmentName]/EditAssignment.tsx | 89 ++++++++++--------- .../[moduleName]/quiz/[quizName]/EditQuiz.tsx | 82 +++++++++++------ 3 files changed, 111 insertions(+), 79 deletions(-) diff --git a/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx b/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx index 1c44e04..c2666af 100644 --- a/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx +++ b/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx @@ -27,16 +27,14 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) { .flatMap(({ lectures }) => lectures.map((lecture) => lecture)) .find((l) => l.date === lectureDay); - const serverVersionOfText = getLectureTextOrDefault(lecture, lectureDay); + const startingText = getLectureTextOrDefault(lecture, lectureDay); - const [text, setText] = useState(serverVersionOfText); + const [text, setText] = useState(startingText); const [updateMonacoKey, setUpdateMonacoKey] = useState(1); const [error, setError] = useState(""); const [clientDataUpdatedAt, setClientDataUpdatedAt] = useState(serverDataUpdatedAt); - const clientIsAuthoritative = serverDataUpdatedAt <= clientDataUpdatedAt; - console.log("client it authoritative", clientIsAuthoritative); const textUpdate = useCallback((t: string) => { setText(t); @@ -45,6 +43,9 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) { useEffect(() => { const delay = 500; + const clientIsAuthoritative = serverDataUpdatedAt <= clientDataUpdatedAt; + console.log("client is authoritative", clientIsAuthoritative); + const handler = setTimeout(() => { try { const parsed = parseLecture(text); @@ -75,15 +76,7 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) { return () => { clearTimeout(handler); }; - }, [ - clientIsAuthoritative, - courseName, - lecture, - settings, - text, - textUpdate, - updateLecture, - ]); + }, [courseName, lecture, settings, text, textUpdate, updateLecture]); return (
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 b5a97dd..b0bc08b 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 @@ -8,7 +8,7 @@ import { LocalAssignment, localAssignmentMarkdown, } from "@/models/local/assignment/localAssignment"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import AssignmentPreview from "./AssignmentPreview"; import { getModuleItemUrl } from "@/services/urlUtils"; import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; @@ -26,38 +26,35 @@ export default function EditAssignment({ }: { assignmentName: string; moduleName: string; -}) { - const [_, {dataUpdatedAt}] = useAssignmentQuery(moduleName, assignmentName); - return ( - - ); -} -export function InnerEditAssignment({ - moduleName, - assignmentName, -}: { - assignmentName: string; - moduleName: string; }) { const router = useRouter(); const { courseName } = useCourseContext(); const [settings] = useLocalCourseSettingsQuery(); - const [assignment] = useAssignmentQuery(moduleName, assignmentName); + const [assignment, { dataUpdatedAt: serverDataUpdatedAt }] = + useAssignmentQuery(moduleName, assignmentName); const updateAssignment = useUpdateAssignmentMutation(); const [assignmentText, setAssignmentText] = useState( localAssignmentMarkdown.toMarkdown(assignment) ); + const [updateMonacoKey, setUpdateMonacoKey] = useState(1); + const [clientDataUpdatedAt, setClientDataUpdatedAt] = + useState(serverDataUpdatedAt); + + const textUpdate = useCallback((t: string) => { + setAssignmentText(t); + setClientDataUpdatedAt(Date.now()); + }, []); + const [error, setError] = useState(""); const [showHelp, setShowHelp] = useState(false); useEffect(() => { const delay = 500; + const clientIsAuthoritative = serverDataUpdatedAt <= clientDataUpdatedAt; + console.log("client is authoritative", clientIsAuthoritative); + const handler = setTimeout(() => { try { const updatedAssignment: LocalAssignment = @@ -66,27 +63,35 @@ export function InnerEditAssignment({ localAssignmentMarkdown.toMarkdown(assignment) !== localAssignmentMarkdown.toMarkdown(updatedAssignment) ) { - console.log("updating assignment"); - updateAssignment - .mutateAsync({ - assignment: updatedAssignment, - moduleName, - assignmentName: updatedAssignment.name, - previousModuleName: moduleName, - previousAssignmentName: assignmentName, - courseName, - }) - .then(() => { - if (updatedAssignment.name !== assignmentName) - router.replace( - getModuleItemUrl( - courseName, - moduleName, - "assignment", - updatedAssignment.name - ) - ); - }); + if (clientIsAuthoritative) { + console.log("updating assignment"); + updateAssignment + .mutateAsync({ + assignment: updatedAssignment, + moduleName, + assignmentName: updatedAssignment.name, + previousModuleName: moduleName, + previousAssignmentName: assignmentName, + courseName, + }) + .then(() => { + if (updatedAssignment.name !== assignmentName) + router.replace( + getModuleItemUrl( + courseName, + moduleName, + "assignment", + updatedAssignment.name + ) + ); + }); + } else { + console.log( + "client not authoritative, updating client with server data" + ); + textUpdate(localAssignmentMarkdown.toMarkdown(assignment)); + setUpdateMonacoKey((k) => k + 1); + } } setError(""); } catch (e: any) { @@ -116,7 +121,11 @@ export function InnerEditAssignment({ )}
- +
{error && error}
diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiz.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiz.tsx index 22722a6..ec2cde3 100644 --- a/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiz.tsx +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/quiz/[quizName]/EditQuiz.tsx @@ -1,7 +1,7 @@ "use client"; import { MonacoEditor } from "@/components/editor/MonacoEditor"; import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import QuizPreview from "./QuizPreview"; import { QuizButtons } from "./QuizButton"; import ClientOnly from "@/components/ClientOnly"; @@ -62,7 +62,7 @@ export default function EditQuiz({ quizName: string; moduleName: string; }) { - const [_, {dataUpdatedAt}] = useQuizQuery(moduleName, quizName); + const [_, { dataUpdatedAt }] = useQuizQuery(moduleName, quizName); return ( { + setQuizText(t); + setClientDataUpdatedAt(Date.now()); + }, []); + useEffect(() => { const delay = 1000; + const clientIsAuthoritative = serverDataUpdatedAt <= clientDataUpdatedAt; + console.log("client is authoritative", clientIsAuthoritative); + const handler = setTimeout(async () => { try { - console.log("checking if the same..."); if ( quizMarkdownUtils.toMarkdown(quiz) !== quizMarkdownUtils.toMarkdown( quizMarkdownUtils.parseMarkdown(quizText) ) ) { - const updatedQuiz = quizMarkdownUtils.parseMarkdown(quizText); - updateQuizMutation - .mutateAsync({ - quiz: updatedQuiz, - moduleName, - quizName: updatedQuiz.name, - previousModuleName: moduleName, - previousQuizName: quizName, - courseName, - }) - .then(() => { - if (updatedQuiz.name !== quizName) - router.replace( - getModuleItemUrl( - courseName, - moduleName, - "quiz", - updatedQuiz.name - ) - ); - }); + if (clientIsAuthoritative) { + const updatedQuiz = quizMarkdownUtils.parseMarkdown(quizText); + await updateQuizMutation + .mutateAsync({ + quiz: updatedQuiz, + moduleName, + quizName: updatedQuiz.name, + previousModuleName: moduleName, + previousQuizName: quizName, + courseName, + }) + .then(() => { + if (updatedQuiz.name !== quizName) + router.replace( + getModuleItemUrl( + courseName, + moduleName, + "quiz", + updatedQuiz.name + ) + ); + }); + } else { + console.log( + "client not authoritative, updating client with server data" + ); + textUpdate(quizMarkdownUtils.toMarkdown(quiz)); + setUpdateMonacoKey((k) => k + 1); + } } setError(""); } catch (e: any) { @@ -129,15 +151,19 @@ export function InnerEditQuiz({ clearTimeout(handler); }; }, [ + clientDataUpdatedAt, courseName, moduleName, quiz, quizName, quizText, router, + serverDataUpdatedAt, + textUpdate, updateQuizMutation, ]); + console.log("updateMonacoKey", updateMonacoKey); return (
@@ -147,7 +173,11 @@ export function InnerEditQuiz({ )}
- +
{error && error}