server websockets

This commit is contained in:
2024-11-15 08:22:33 -07:00
parent a37071b584
commit 5c16280807
7 changed files with 161 additions and 27 deletions

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
MAJOR_VERSION="2" MAJOR_VERSION="2"
MINOR_VERSION="2" MINOR_VERSION="3"
VERSION="$MAJOR_VERSION.$MINOR_VERSION" VERSION="$MAJOR_VERSION.$MINOR_VERSION"
docker build -t canvas_management:$VERSION . docker build -t canvas_management:$VERSION .

View File

@@ -1,7 +1,10 @@
"use client"; "use client";
import { MonacoEditor } from "@/components/editor/MonacoEditor"; import { MonacoEditor } from "@/components/editor/MonacoEditor";
import { useLecturesSuspenseQuery, useLectureUpdateMutation } from "@/hooks/localCourse/lectureHooks"; import {
useLecturesSuspenseQuery,
useLectureUpdateMutation,
} from "@/hooks/localCourse/lectureHooks";
import { import {
lectureToString, lectureToString,
parseLecture, parseLecture,
@@ -10,11 +13,16 @@ import { useEffect, useState } from "react";
import LecturePreview from "./LecturePreview"; import LecturePreview from "./LecturePreview";
import EditLectureTitle from "./EditLectureTitle"; import EditLectureTitle from "./EditLectureTitle";
import LectureButtons from "./LectureButtons"; import LectureButtons from "./LectureButtons";
import { trpc } from "@/services/trpc/utils";
import { useCourseContext } from "../../context/courseContext"; import { useCourseContext } from "../../context/courseContext";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
export default function EditLecture({ lectureDay }: { lectureDay: string }) { export default function EditLecture({ lectureDay }: { lectureDay: string }) {
const [_, {dataUpdatedAt}] = useLecturesSuspenseQuery();
return (
<InnerEditLecture key={dataUpdatedAt} lectureDay={lectureDay} />
);
}
export function InnerEditLecture({ lectureDay }: { lectureDay: string }) {
const { courseName } = useCourseContext(); const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery(); const [settings] = useLocalCourseSettingsQuery();
const [weeks] = useLecturesSuspenseQuery(); const [weeks] = useLecturesSuspenseQuery();

View File

@@ -26,6 +26,22 @@ 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();

View File

@@ -21,10 +21,28 @@ 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] = usePageQuery(moduleName, pageName); const [page, pageQuery] = usePageQuery(moduleName, pageName);
const updatePage = useUpdatePageMutation(); const updatePage = useUpdatePageMutation();
const [pageText, setPageText] = useState( const [pageText, setPageText] = useState(
localPageMarkdownUtils.toMarkdown(page) localPageMarkdownUtils.toMarkdown(page)
@@ -32,6 +50,10 @@ export default function EditPage({
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(() => {

View File

@@ -61,6 +61,22 @@ 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();

View File

@@ -1,7 +1,8 @@
"use client"; "use client";
import React, { useEffect } from 'react'; import { trpc } from "@/services/trpc/utils";
import { io, Socket } from 'socket.io-client'; import React, { useEffect } from "react";
import { io, Socket } from "socket.io-client";
interface ServerToClientEvents { interface ServerToClientEvents {
message: (data: string) => void; message: (data: string) => void;
@@ -12,28 +13,106 @@ interface ClientToServerEvents {
sendMessage: (data: string) => void; sendMessage: (data: string) => void;
} }
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io('/'); const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io("/");
function removeFileExtension(fileName: string): string {
const lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex > 0) {
return fileName.substring(0, lastDotIndex);
}
return fileName;
}
export function ClientCacheInvalidation() { export function ClientCacheInvalidation() {
const utils = trpc.useUtils();
useEffect(() => { useEffect(() => {
socket.on('message', (data) => { socket.on("message", (data) => {
console.log('Received message:', data); console.log("Received message:", data);
}); });
socket.on('fileChanged', (filePath) => { socket.on("fileChanged", (filePath) => {
console.log('File changed:', filePath); const [courseName, moduleOrLectures, itemType, itemFile] =
filePath.split("/");
const itemName = removeFileExtension(itemFile);
const allParts = [courseName, moduleOrLectures, itemType, itemName];
if (moduleOrLectures === "settings.yml") {
utils.settings.allCoursesSettings.invalidate();
utils.settings.courseSettings.invalidate({ courseName });
return;
}
if (moduleOrLectures === "00 - lectures") {
console.log("lecture updated on FS ", allParts);
utils.lectures.getLectures.invalidate({ courseName });
return;
}
if (itemType === "assignments") {
console.log("assignment updated on FS ", allParts);
utils.assignment.getAllAssignments.invalidate({
courseName,
moduleName: moduleOrLectures,
});
utils.assignment.getAssignment.invalidate({
courseName,
moduleName: moduleOrLectures,
assignmentName: itemName,
});
return;
}
if (itemType === "quizzes") {
console.log("quiz updated on FS ", allParts);
utils.quiz.getAllQuizzes.invalidate({
courseName,
moduleName: moduleOrLectures,
});
utils.quiz.getQuiz.invalidate({
courseName,
moduleName: moduleOrLectures,
quizName: itemName,
});
return;
}
if (itemType === "pages") {
console.log("page updated on FS ", allParts);
utils.page.getAllPages.invalidate({
courseName,
moduleName: moduleOrLectures,
});
utils.page.getPage.invalidate({
courseName,
moduleName: moduleOrLectures,
pageName: itemName,
});
return;
}
}); });
socket.on('connect_error', (error) => { socket.on("connect_error", (error) => {
console.error('Connection error:', error); console.error("Connection error:", error);
}); });
return () => { return () => {
socket.off('message'); socket.off("message");
socket.off('fileChanged'); socket.off("fileChanged");
socket.off('connect_error'); socket.off("connect_error");
}; };
}, []); }, [
utils.assignment.getAllAssignments,
utils.assignment.getAssignment,
utils.lectures.getLectures,
utils.page.getAllPages,
utils.page.getPage,
utils.quiz.getAllQuizzes,
utils.quiz.getQuiz,
utils.settings.allCoursesSettings,
utils.settings.courseSettings,
]);
return <></>; return <></>;
} }

View File

@@ -12,7 +12,7 @@ const port = 3000;
const app = next({ dev, hostname, port }); const app = next({ dev, hostname, port });
const handler = app.getRequestHandler(); const handler = app.getRequestHandler();
const folderToWatch = path.join(process.cwd(), "./storage"); const folderToWatch = path.join(process.cwd(), "./storage", "/");
console.log("watching folder", folderToWatch); console.log("watching folder", folderToWatch);
app.prepare().then(() => { app.prepare().then(() => {
@@ -23,21 +23,14 @@ app.prepare().then(() => {
io.on("connection", (socket) => { io.on("connection", (socket) => {
console.log("websocket connection created"); console.log("websocket connection created");
// Define a change event handler
const changeHandler = (filePath) => { const changeHandler = (filePath) => {
const relativePath = filePath.replace(folderToWatch, "") const relativePath = filePath.replace(folderToWatch, "");
console.log(`Sending file changed websocket message: ${relativePath}`); console.log(`Sending file changed websocket message: ${relativePath}`);
socket.emit("fileChanged", relativePath);
socket.emit("fileChanged", `File changed: ${filePath}`);
}; };
// Attach the change event handler
watcher.on("change", changeHandler); watcher.on("change", changeHandler);
// Clean up the change event handler when the client disconnects
socket.on("disconnect", () => { socket.on("disconnect", () => {
console.log("Client disconnected"); console.log("Client disconnected");
watcher.off("change", changeHandler); // Remove the event listener watcher.off("change", changeHandler); // Remove the event listener