mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
fixing updating between server and client
This commit is contained in:
@@ -15,6 +15,7 @@ import LectureButtons from "./LectureButtons";
|
|||||||
import { useCourseContext } from "../../context/courseContext";
|
import { useCourseContext } from "../../context/courseContext";
|
||||||
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
||||||
import { Lecture } from "@/models/local/lecture";
|
import { Lecture } from "@/models/local/lecture";
|
||||||
|
import { useAuthoritativeUpdates } from "../../utils/useAuthoritativeUpdates";
|
||||||
|
|
||||||
export default function EditLecture({ lectureDay }: { lectureDay: string }) {
|
export default function EditLecture({ lectureDay }: { lectureDay: string }) {
|
||||||
const { courseName } = useCourseContext();
|
const { courseName } = useCourseContext();
|
||||||
@@ -27,24 +28,15 @@ 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 startingText = getLectureTextOrDefault(lecture, lectureDay);
|
const { clientIsAuthoritative, text, textUpdate, monacoKey } =
|
||||||
|
useAuthoritativeUpdates({
|
||||||
const [text, setText] = useState(startingText);
|
serverUpdatedAt: serverDataUpdatedAt,
|
||||||
const [updateMonacoKey, setUpdateMonacoKey] = useState(1);
|
startingText: getLectureTextOrDefault(lecture, lectureDay),
|
||||||
|
});
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
const [clientDataUpdatedAt, setClientDataUpdatedAt] =
|
|
||||||
useState(serverDataUpdatedAt);
|
|
||||||
|
|
||||||
const textUpdate = useCallback((t: string) => {
|
|
||||||
setText(t);
|
|
||||||
setClientDataUpdatedAt(Date.now());
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
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 {
|
||||||
@@ -58,8 +50,7 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) {
|
|||||||
console.log(
|
console.log(
|
||||||
"client not authoritative, updating client with server data"
|
"client not authoritative, updating client with server data"
|
||||||
);
|
);
|
||||||
textUpdate(lectureToString(lecture));
|
textUpdate(lectureToString(lecture), true);
|
||||||
setUpdateMonacoKey((k) => k + 1);
|
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log(
|
||||||
"client not authoritative, but no lecture on server, this is a bug"
|
"client not authoritative, but no lecture on server, this is a bug"
|
||||||
@@ -76,18 +67,22 @@ 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">
|
||||||
<EditLectureTitle lectureDay={lectureDay} />
|
<EditLectureTitle lectureDay={lectureDay} />
|
||||||
<div className="sm:columns-2 min-h-0 flex-1">
|
<div className="sm:columns-2 min-h-0 flex-1">
|
||||||
<div className="flex-1 h-full">
|
<div className="flex-1 h-full">
|
||||||
<MonacoEditor
|
<MonacoEditor key={monacoKey} value={text} onChange={textUpdate} />
|
||||||
key={updateMonacoKey}
|
|
||||||
value={text}
|
|
||||||
onChange={textUpdate}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="h-full sm:block none overflow-auto">
|
<div className="h-full sm:block none overflow-auto">
|
||||||
<div className="text-red-300">{error && error}</div>
|
<div className="text-red-300">{error && error}</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
LocalAssignment,
|
LocalAssignment,
|
||||||
localAssignmentMarkdown,
|
localAssignmentMarkdown,
|
||||||
} from "@/models/local/assignment/localAssignment";
|
} from "@/models/local/assignment/localAssignment";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { 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";
|
||||||
@@ -19,6 +19,7 @@ import { AssignmentSubmissionType } from "@/models/local/assignment/assignmentSu
|
|||||||
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
|
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { AssignmentButtons } from "./AssignmentButtons";
|
import { AssignmentButtons } from "./AssignmentButtons";
|
||||||
|
import { useAuthoritativeUpdates } from "@/app/course/[courseName]/utils/useAuthoritativeUpdates";
|
||||||
|
|
||||||
export default function EditAssignment({
|
export default function EditAssignment({
|
||||||
moduleName,
|
moduleName,
|
||||||
@@ -34,31 +35,22 @@ export default function EditAssignment({
|
|||||||
useAssignmentQuery(moduleName, assignmentName);
|
useAssignmentQuery(moduleName, assignmentName);
|
||||||
const updateAssignment = useUpdateAssignmentMutation();
|
const updateAssignment = useUpdateAssignmentMutation();
|
||||||
|
|
||||||
const [assignmentText, setAssignmentText] = useState(
|
const { clientIsAuthoritative, text, textUpdate, monacoKey } =
|
||||||
localAssignmentMarkdown.toMarkdown(assignment)
|
useAuthoritativeUpdates({
|
||||||
);
|
serverUpdatedAt: serverDataUpdatedAt,
|
||||||
|
startingText: 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 =
|
||||||
localAssignmentMarkdown.parseMarkdown(assignmentText);
|
localAssignmentMarkdown.parseMarkdown(text);
|
||||||
if (
|
if (
|
||||||
localAssignmentMarkdown.toMarkdown(assignment) !==
|
localAssignmentMarkdown.toMarkdown(assignment) !==
|
||||||
localAssignmentMarkdown.toMarkdown(updatedAssignment)
|
localAssignmentMarkdown.toMarkdown(updatedAssignment)
|
||||||
@@ -89,8 +81,7 @@ export default function EditAssignment({
|
|||||||
console.log(
|
console.log(
|
||||||
"client not authoritative, updating client with server data"
|
"client not authoritative, updating client with server data"
|
||||||
);
|
);
|
||||||
textUpdate(localAssignmentMarkdown.toMarkdown(assignment));
|
textUpdate(localAssignmentMarkdown.toMarkdown(assignment), true);
|
||||||
setUpdateMonacoKey((k) => k + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setError("");
|
setError("");
|
||||||
@@ -105,10 +96,12 @@ export default function EditAssignment({
|
|||||||
}, [
|
}, [
|
||||||
assignment,
|
assignment,
|
||||||
assignmentName,
|
assignmentName,
|
||||||
assignmentText,
|
clientIsAuthoritative,
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
router,
|
router,
|
||||||
|
text,
|
||||||
|
textUpdate,
|
||||||
updateAssignment,
|
updateAssignment,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -121,11 +114,7 @@ export default function EditAssignment({
|
|||||||
</pre>
|
</pre>
|
||||||
)}
|
)}
|
||||||
<div className="flex-1 h-full">
|
<div className="flex-1 h-full">
|
||||||
<MonacoEditor
|
<MonacoEditor key={monacoKey} value={text} onChange={textUpdate} />
|
||||||
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>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import ClientOnly from "@/components/ClientOnly";
|
|||||||
import { getModuleItemUrl } from "@/services/urlUtils";
|
import { getModuleItemUrl } from "@/services/urlUtils";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||||
|
import { useAuthoritativeUpdates } from "@/app/course/[courseName]/utils/useAuthoritativeUpdates";
|
||||||
|
|
||||||
export default function EditPage({
|
export default function EditPage({
|
||||||
moduleName,
|
moduleName,
|
||||||
@@ -21,69 +22,61 @@ export default function EditPage({
|
|||||||
}: {
|
}: {
|
||||||
pageName: string;
|
pageName: string;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
}) {
|
|
||||||
const [_, { dataUpdatedAt }] = usePageQuery(moduleName, pageName);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<InnerEditPage
|
|
||||||
key={dataUpdatedAt}
|
|
||||||
pageName={pageName}
|
|
||||||
moduleName={moduleName}
|
|
||||||
></InnerEditPage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function InnerEditPage({
|
|
||||||
moduleName,
|
|
||||||
pageName,
|
|
||||||
}: {
|
|
||||||
pageName: string;
|
|
||||||
moduleName: string;
|
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { courseName } = useCourseContext();
|
const { courseName } = useCourseContext();
|
||||||
const [page, pageQuery] = usePageQuery(moduleName, pageName);
|
const [page, { dataUpdatedAt }] = usePageQuery(
|
||||||
const updatePage = useUpdatePageMutation();
|
moduleName,
|
||||||
const [pageText, setPageText] = useState(
|
pageName
|
||||||
localPageMarkdownUtils.toMarkdown(page)
|
|
||||||
);
|
);
|
||||||
|
const updatePage = useUpdatePageMutation();
|
||||||
|
|
||||||
|
const { clientIsAuthoritative, text, textUpdate, monacoKey } =
|
||||||
|
useAuthoritativeUpdates({
|
||||||
|
serverUpdatedAt: dataUpdatedAt,
|
||||||
|
startingText: localPageMarkdownUtils.toMarkdown(page),
|
||||||
|
});
|
||||||
|
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [settings] = useLocalCourseSettingsQuery();
|
const [settings] = useLocalCourseSettingsQuery();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log("page data updated on sever", pageQuery.dataUpdatedAt);
|
|
||||||
}, [pageQuery.dataUpdatedAt]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const delay = 500;
|
const delay = 500;
|
||||||
const handler = setTimeout(() => {
|
const handler = setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
const updatedPage = localPageMarkdownUtils.parseMarkdown(pageText);
|
const updatedPage = localPageMarkdownUtils.parseMarkdown(text);
|
||||||
if (
|
if (
|
||||||
localPageMarkdownUtils.toMarkdown(page) !==
|
localPageMarkdownUtils.toMarkdown(page) !==
|
||||||
localPageMarkdownUtils.toMarkdown(updatedPage)
|
localPageMarkdownUtils.toMarkdown(updatedPage)
|
||||||
) {
|
) {
|
||||||
console.log("updating page");
|
if (clientIsAuthoritative) {
|
||||||
updatePage
|
console.log("updating page");
|
||||||
.mutateAsync({
|
updatePage
|
||||||
page: updatedPage,
|
.mutateAsync({
|
||||||
moduleName,
|
page: updatedPage,
|
||||||
pageName: updatedPage.name,
|
moduleName,
|
||||||
previousModuleName: moduleName,
|
pageName: updatedPage.name,
|
||||||
previousPageName: pageName,
|
previousModuleName: moduleName,
|
||||||
courseName,
|
previousPageName: pageName,
|
||||||
})
|
courseName,
|
||||||
.then(() => {
|
})
|
||||||
if (updatedPage.name !== pageName)
|
.then(() => {
|
||||||
router.replace(
|
if (updatedPage.name !== pageName)
|
||||||
getModuleItemUrl(
|
router.replace(
|
||||||
courseName,
|
getModuleItemUrl(
|
||||||
moduleName,
|
courseName,
|
||||||
"page",
|
moduleName,
|
||||||
updatedPage.name
|
"page",
|
||||||
)
|
updatedPage.name
|
||||||
);
|
)
|
||||||
});
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
"client not authoritative, updating client with server data"
|
||||||
|
);
|
||||||
|
textUpdate(localPageMarkdownUtils.toMarkdown(page), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setError("");
|
setError("");
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -94,13 +87,23 @@ function InnerEditPage({
|
|||||||
return () => {
|
return () => {
|
||||||
clearTimeout(handler);
|
clearTimeout(handler);
|
||||||
};
|
};
|
||||||
}, [courseName, moduleName, page, pageName, pageText, router, updatePage]);
|
}, [
|
||||||
|
clientIsAuthoritative,
|
||||||
|
courseName,
|
||||||
|
moduleName,
|
||||||
|
page,
|
||||||
|
pageName,
|
||||||
|
router,
|
||||||
|
text,
|
||||||
|
textUpdate,
|
||||||
|
updatePage,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col">
|
<div className="h-full flex flex-col">
|
||||||
<div className="columns-2 min-h-0 flex-1">
|
<div className="columns-2 min-h-0 flex-1">
|
||||||
<div className="flex-1 h-full">
|
<div className="flex-1 h-full">
|
||||||
<MonacoEditor value={pageText} onChange={setPageText} />
|
<MonacoEditor key={monacoKey} value={text} onChange={textUpdate} />
|
||||||
</div>
|
</div>
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
<div className="text-red-300">{error && error}</div>
|
<div className="text-red-300">{error && error}</div>
|
||||||
|
|||||||
@@ -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 { useCallback, useEffect, useState } from "react";
|
import { 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";
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
useQuizQuery,
|
useQuizQuery,
|
||||||
useUpdateQuizMutation,
|
useUpdateQuizMutation,
|
||||||
} from "@/hooks/localCourse/quizHooks";
|
} from "@/hooks/localCourse/quizHooks";
|
||||||
|
import { useAuthoritativeUpdates } from "../../../../utils/useAuthoritativeUpdates";
|
||||||
|
|
||||||
const helpString = `QUESTION REFERENCE
|
const helpString = `QUESTION REFERENCE
|
||||||
---
|
---
|
||||||
@@ -61,22 +62,6 @@ export default function EditQuiz({
|
|||||||
}: {
|
}: {
|
||||||
quizName: string;
|
quizName: string;
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
}) {
|
|
||||||
const [_, { dataUpdatedAt }] = useQuizQuery(moduleName, quizName);
|
|
||||||
return (
|
|
||||||
<InnerEditQuiz
|
|
||||||
key={dataUpdatedAt}
|
|
||||||
quizName={quizName}
|
|
||||||
moduleName={moduleName}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export function InnerEditQuiz({
|
|
||||||
moduleName,
|
|
||||||
quizName,
|
|
||||||
}: {
|
|
||||||
quizName: string;
|
|
||||||
moduleName: string;
|
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { courseName } = useCourseContext();
|
const { courseName } = useCourseContext();
|
||||||
@@ -85,34 +70,25 @@ export function InnerEditQuiz({
|
|||||||
quizName
|
quizName
|
||||||
);
|
);
|
||||||
const updateQuizMutation = useUpdateQuizMutation();
|
const updateQuizMutation = useUpdateQuizMutation();
|
||||||
const [quizText, setQuizText] = useState(quizMarkdownUtils.toMarkdown(quiz));
|
const { clientIsAuthoritative, text, textUpdate, monacoKey } =
|
||||||
|
useAuthoritativeUpdates({
|
||||||
|
serverUpdatedAt: serverDataUpdatedAt,
|
||||||
|
startingText: 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 {
|
||||||
if (
|
if (
|
||||||
quizMarkdownUtils.toMarkdown(quiz) !==
|
quizMarkdownUtils.toMarkdown(quiz) !==
|
||||||
quizMarkdownUtils.toMarkdown(
|
quizMarkdownUtils.toMarkdown(quizMarkdownUtils.parseMarkdown(text))
|
||||||
quizMarkdownUtils.parseMarkdown(quizText)
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
if (clientIsAuthoritative) {
|
if (clientIsAuthoritative) {
|
||||||
const updatedQuiz = quizMarkdownUtils.parseMarkdown(quizText);
|
const updatedQuiz = quizMarkdownUtils.parseMarkdown(text);
|
||||||
await updateQuizMutation
|
await updateQuizMutation
|
||||||
.mutateAsync({
|
.mutateAsync({
|
||||||
quiz: updatedQuiz,
|
quiz: updatedQuiz,
|
||||||
@@ -137,8 +113,7 @@ export function InnerEditQuiz({
|
|||||||
console.log(
|
console.log(
|
||||||
"client not authoritative, updating client with server data"
|
"client not authoritative, updating client with server data"
|
||||||
);
|
);
|
||||||
textUpdate(quizMarkdownUtils.toMarkdown(quiz));
|
textUpdate(quizMarkdownUtils.toMarkdown(quiz), true);
|
||||||
setUpdateMonacoKey((k) => k + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setError("");
|
setError("");
|
||||||
@@ -151,19 +126,17 @@ export function InnerEditQuiz({
|
|||||||
clearTimeout(handler);
|
clearTimeout(handler);
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
clientDataUpdatedAt,
|
clientIsAuthoritative,
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
quiz,
|
quiz,
|
||||||
quizName,
|
quizName,
|
||||||
quizText,
|
|
||||||
router,
|
router,
|
||||||
serverDataUpdatedAt,
|
text,
|
||||||
textUpdate,
|
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"}>
|
||||||
@@ -173,11 +146,7 @@ export function InnerEditQuiz({
|
|||||||
</pre>
|
</pre>
|
||||||
)}
|
)}
|
||||||
<div className="flex-1 h-full">
|
<div className="flex-1 h-full">
|
||||||
<MonacoEditor
|
<MonacoEditor key={monacoKey} value={text} onChange={textUpdate} />
|
||||||
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>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"use client";
|
||||||
|
import { useState, useMemo, useCallback } from "react";
|
||||||
|
|
||||||
|
export function useAuthoritativeUpdates({
|
||||||
|
serverUpdatedAt,
|
||||||
|
startingText,
|
||||||
|
}: {
|
||||||
|
serverUpdatedAt: number;
|
||||||
|
startingText: string;
|
||||||
|
}) {
|
||||||
|
const [text, setText] = useState(startingText);
|
||||||
|
const [clientDataUpdatedAt, setClientDataUpdatedAt] =
|
||||||
|
useState(serverUpdatedAt);
|
||||||
|
const [updateMonacoKey, setUpdateMonacoKey] = useState(1);
|
||||||
|
|
||||||
|
const clientIsAuthoritative = useMemo(
|
||||||
|
() => serverUpdatedAt <= clientDataUpdatedAt,
|
||||||
|
[clientDataUpdatedAt, serverUpdatedAt]
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("client is authoritative", clientIsAuthoritative);
|
||||||
|
const textUpdate = useCallback((t: string, updateMonaco: boolean = false) => {
|
||||||
|
setText(t);
|
||||||
|
setClientDataUpdatedAt(Date.now());
|
||||||
|
if (updateMonaco) setUpdateMonacoKey((t) => t + 1);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
clientIsAuthoritative,
|
||||||
|
textUpdate,
|
||||||
|
text,
|
||||||
|
monacoKey: updateMonacoKey,
|
||||||
|
}),
|
||||||
|
[clientIsAuthoritative, text, textUpdate, updateMonacoKey]
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user