fixing authority on server quizzes still broken

This commit is contained in:
2024-11-15 09:20:39 -07:00
parent 2641289b12
commit 9f3360261e
3 changed files with 111 additions and 79 deletions

View File

@@ -27,16 +27,14 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) {
.flatMap(({ lectures }) => lectures.map((lecture) => lecture)) .flatMap(({ lectures }) => lectures.map((lecture) => lecture))
.find((l) => l.date === lectureDay); .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 [updateMonacoKey, setUpdateMonacoKey] = useState(1);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [clientDataUpdatedAt, setClientDataUpdatedAt] = const [clientDataUpdatedAt, setClientDataUpdatedAt] =
useState(serverDataUpdatedAt); useState(serverDataUpdatedAt);
const clientIsAuthoritative = serverDataUpdatedAt <= clientDataUpdatedAt;
console.log("client it authoritative", clientIsAuthoritative);
const textUpdate = useCallback((t: string) => { const textUpdate = useCallback((t: string) => {
setText(t); setText(t);
@@ -45,6 +43,9 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) {
useEffect(() => { useEffect(() => {
const delay = 500; const delay = 500;
const clientIsAuthoritative = serverDataUpdatedAt <= clientDataUpdatedAt;
console.log("client is authoritative", clientIsAuthoritative);
const handler = setTimeout(() => { const handler = setTimeout(() => {
try { try {
const parsed = parseLecture(text); const parsed = parseLecture(text);
@@ -75,15 +76,7 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) {
return () => { return () => {
clearTimeout(handler); clearTimeout(handler);
}; };
}, [ }, [courseName, lecture, settings, text, textUpdate, updateLecture]);
clientIsAuthoritative,
courseName,
lecture,
settings,
text,
textUpdate,
updateLecture,
]);
return ( return (
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">

View File

@@ -8,7 +8,7 @@ import {
LocalAssignment, LocalAssignment,
localAssignmentMarkdown, localAssignmentMarkdown,
} from "@/models/local/assignment/localAssignment"; } from "@/models/local/assignment/localAssignment";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import AssignmentPreview from "./AssignmentPreview"; import AssignmentPreview from "./AssignmentPreview";
import { getModuleItemUrl } from "@/services/urlUtils"; import { getModuleItemUrl } from "@/services/urlUtils";
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext"; import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
@@ -26,38 +26,35 @@ export default function EditAssignment({
}: { }: {
assignmentName: string; assignmentName: string;
moduleName: string; moduleName: string;
}) {
const [_, {dataUpdatedAt}] = useAssignmentQuery(moduleName, assignmentName);
return (
<InnerEditAssignment
key={dataUpdatedAt}
assignmentName={assignmentName}
moduleName={moduleName}
/>
);
}
export function InnerEditAssignment({
moduleName,
assignmentName,
}: {
assignmentName: string;
moduleName: string;
}) { }) {
const router = useRouter(); const router = useRouter();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const [settings] = useLocalCourseSettingsQuery();
const [assignment] = useAssignmentQuery(moduleName, assignmentName); const [assignment, { dataUpdatedAt: serverDataUpdatedAt }] =
useAssignmentQuery(moduleName, assignmentName);
const updateAssignment = useUpdateAssignmentMutation(); const updateAssignment = useUpdateAssignmentMutation();
const [assignmentText, setAssignmentText] = useState( const [assignmentText, setAssignmentText] = useState(
localAssignmentMarkdown.toMarkdown(assignment) 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 [error, setError] = useState("");
const [showHelp, setShowHelp] = useState(false); const [showHelp, setShowHelp] = useState(false);
useEffect(() => { useEffect(() => {
const delay = 500; const delay = 500;
const clientIsAuthoritative = serverDataUpdatedAt <= clientDataUpdatedAt;
console.log("client is authoritative", clientIsAuthoritative);
const handler = setTimeout(() => { const handler = setTimeout(() => {
try { try {
const updatedAssignment: LocalAssignment = const updatedAssignment: LocalAssignment =
@@ -66,27 +63,35 @@ export function InnerEditAssignment({
localAssignmentMarkdown.toMarkdown(assignment) !== localAssignmentMarkdown.toMarkdown(assignment) !==
localAssignmentMarkdown.toMarkdown(updatedAssignment) localAssignmentMarkdown.toMarkdown(updatedAssignment)
) { ) {
console.log("updating assignment"); if (clientIsAuthoritative) {
updateAssignment console.log("updating assignment");
.mutateAsync({ updateAssignment
assignment: updatedAssignment, .mutateAsync({
moduleName, assignment: updatedAssignment,
assignmentName: updatedAssignment.name, moduleName,
previousModuleName: moduleName, assignmentName: updatedAssignment.name,
previousAssignmentName: assignmentName, previousModuleName: moduleName,
courseName, previousAssignmentName: assignmentName,
}) courseName,
.then(() => { })
if (updatedAssignment.name !== assignmentName) .then(() => {
router.replace( if (updatedAssignment.name !== assignmentName)
getModuleItemUrl( router.replace(
courseName, getModuleItemUrl(
moduleName, courseName,
"assignment", moduleName,
updatedAssignment.name "assignment",
) updatedAssignment.name
); )
}); );
});
} else {
console.log(
"client not authoritative, updating client with server data"
);
textUpdate(localAssignmentMarkdown.toMarkdown(assignment));
setUpdateMonacoKey((k) => k + 1);
}
} }
setError(""); setError("");
} catch (e: any) { } catch (e: any) {
@@ -116,7 +121,11 @@ export function InnerEditAssignment({
</pre> </pre>
)} )}
<div className="flex-1 h-full"> <div className="flex-1 h-full">
<MonacoEditor value={assignmentText} onChange={setAssignmentText} /> <MonacoEditor
key={updateMonacoKey}
value={assignmentText}
onChange={textUpdate}
/>
</div> </div>
<div className="flex-1 h-full"> <div className="flex-1 h-full">
<div className="text-red-300">{error && error}</div> <div className="text-red-300">{error && error}</div>

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import { MonacoEditor } from "@/components/editor/MonacoEditor"; import { MonacoEditor } from "@/components/editor/MonacoEditor";
import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils"; import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import QuizPreview from "./QuizPreview"; import QuizPreview from "./QuizPreview";
import { QuizButtons } from "./QuizButton"; import { QuizButtons } from "./QuizButton";
import ClientOnly from "@/components/ClientOnly"; import ClientOnly from "@/components/ClientOnly";
@@ -62,7 +62,7 @@ export default function EditQuiz({
quizName: string; quizName: string;
moduleName: string; moduleName: string;
}) { }) {
const [_, {dataUpdatedAt}] = useQuizQuery(moduleName, quizName); const [_, { dataUpdatedAt }] = useQuizQuery(moduleName, quizName);
return ( return (
<InnerEditQuiz <InnerEditQuiz
key={dataUpdatedAt} key={dataUpdatedAt}
@@ -80,44 +80,66 @@ export function InnerEditQuiz({
}) { }) {
const router = useRouter(); const router = useRouter();
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [quiz] = useQuizQuery(moduleName, quizName); const [quiz, { dataUpdatedAt: serverDataUpdatedAt }] = useQuizQuery(
moduleName,
quizName
);
const updateQuizMutation = useUpdateQuizMutation(); const updateQuizMutation = useUpdateQuizMutation();
const [quizText, setQuizText] = useState(quizMarkdownUtils.toMarkdown(quiz)); const [quizText, setQuizText] = useState(quizMarkdownUtils.toMarkdown(quiz));
const [updateMonacoKey, setUpdateMonacoKey] = useState(1);
const [clientDataUpdatedAt, setClientDataUpdatedAt] =
useState(serverDataUpdatedAt);
const [error, setError] = useState(""); const [error, setError] = useState("");
const [showHelp, setShowHelp] = useState(false); const [showHelp, setShowHelp] = useState(false);
const textUpdate = useCallback((t: string) => {
setQuizText(t);
setClientDataUpdatedAt(Date.now());
}, []);
useEffect(() => { useEffect(() => {
const delay = 1000; const delay = 1000;
const clientIsAuthoritative = serverDataUpdatedAt <= clientDataUpdatedAt;
console.log("client is authoritative", clientIsAuthoritative);
const handler = setTimeout(async () => { const handler = setTimeout(async () => {
try { try {
console.log("checking if the same...");
if ( if (
quizMarkdownUtils.toMarkdown(quiz) !== quizMarkdownUtils.toMarkdown(quiz) !==
quizMarkdownUtils.toMarkdown( quizMarkdownUtils.toMarkdown(
quizMarkdownUtils.parseMarkdown(quizText) quizMarkdownUtils.parseMarkdown(quizText)
) )
) { ) {
const updatedQuiz = quizMarkdownUtils.parseMarkdown(quizText); if (clientIsAuthoritative) {
updateQuizMutation const updatedQuiz = quizMarkdownUtils.parseMarkdown(quizText);
.mutateAsync({ await updateQuizMutation
quiz: updatedQuiz, .mutateAsync({
moduleName, quiz: updatedQuiz,
quizName: updatedQuiz.name, moduleName,
previousModuleName: moduleName, quizName: updatedQuiz.name,
previousQuizName: quizName, previousModuleName: moduleName,
courseName, previousQuizName: quizName,
}) courseName,
.then(() => { })
if (updatedQuiz.name !== quizName) .then(() => {
router.replace( if (updatedQuiz.name !== quizName)
getModuleItemUrl( router.replace(
courseName, getModuleItemUrl(
moduleName, courseName,
"quiz", moduleName,
updatedQuiz.name "quiz",
) updatedQuiz.name
); )
}); );
});
} else {
console.log(
"client not authoritative, updating client with server data"
);
textUpdate(quizMarkdownUtils.toMarkdown(quiz));
setUpdateMonacoKey((k) => k + 1);
}
} }
setError(""); setError("");
} catch (e: any) { } catch (e: any) {
@@ -129,15 +151,19 @@ export function InnerEditQuiz({
clearTimeout(handler); clearTimeout(handler);
}; };
}, [ }, [
clientDataUpdatedAt,
courseName, courseName,
moduleName, moduleName,
quiz, quiz,
quizName, quizName,
quizText, quizText,
router, router,
serverDataUpdatedAt,
textUpdate,
updateQuizMutation, updateQuizMutation,
]); ]);
console.log("updateMonacoKey", updateMonacoKey);
return ( return (
<div className="h-full flex flex-col align-middle px-1"> <div className="h-full flex flex-col align-middle px-1">
<div className={"min-h-96 h-full flex flex-row w-full"}> <div className={"min-h-96 h-full flex flex-row w-full"}>
@@ -147,7 +173,11 @@ export function InnerEditQuiz({
</pre> </pre>
)} )}
<div className="flex-1 h-full"> <div className="flex-1 h-full">
<MonacoEditor value={quizText} onChange={setQuizText} /> <MonacoEditor
key={updateMonacoKey}
value={quizText}
onChange={textUpdate}
/>
</div> </div>
<div className="flex-1 h-full"> <div className="flex-1 h-full">
<div className="text-red-300">{error && error}</div> <div className="text-red-300">{error && error}</div>