diff --git a/nextjs/build.sh b/nextjs/build.sh
index d6a3ca2..d271be9 100755
--- a/nextjs/build.sh
+++ b/nextjs/build.sh
@@ -1,7 +1,7 @@
#!/bin/bash
MAJOR_VERSION="2"
-MINOR_VERSION="2"
+MINOR_VERSION="3"
VERSION="$MAJOR_VERSION.$MINOR_VERSION"
docker build -t canvas_management:$VERSION .
diff --git a/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx b/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx
index 8c2fdfd..4e60196 100644
--- a/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx
+++ b/nextjs/src/app/course/[courseName]/lecture/[lectureDay]/EditLecture.tsx
@@ -1,7 +1,10 @@
"use client";
import { MonacoEditor } from "@/components/editor/MonacoEditor";
-import { useLecturesSuspenseQuery, useLectureUpdateMutation } from "@/hooks/localCourse/lectureHooks";
+import {
+ useLecturesSuspenseQuery,
+ useLectureUpdateMutation,
+} from "@/hooks/localCourse/lectureHooks";
import {
lectureToString,
parseLecture,
@@ -10,11 +13,16 @@ import { useEffect, useState } from "react";
import LecturePreview from "./LecturePreview";
import EditLectureTitle from "./EditLectureTitle";
import LectureButtons from "./LectureButtons";
-import { trpc } from "@/services/trpc/utils";
import { useCourseContext } from "../../context/courseContext";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
export default function EditLecture({ lectureDay }: { lectureDay: string }) {
+ const [_, {dataUpdatedAt}] = useLecturesSuspenseQuery();
+ return (
+
+ );
+}
+export function InnerEditLecture({ lectureDay }: { lectureDay: string }) {
const { courseName } = useCourseContext();
const [settings] = useLocalCourseSettingsQuery();
const [weeks] = useLecturesSuspenseQuery();
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 5028508..b5a97dd 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
@@ -26,6 +26,22 @@ 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();
diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/page/[pageName]/EditPage.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/page/[pageName]/EditPage.tsx
index d94b20b..46fa15b 100644
--- a/nextjs/src/app/course/[courseName]/modules/[moduleName]/page/[pageName]/EditPage.tsx
+++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/page/[pageName]/EditPage.tsx
@@ -21,10 +21,28 @@ export default function EditPage({
}: {
pageName: string;
moduleName: string;
+}) {
+ const [_, { dataUpdatedAt }] = usePageQuery(moduleName, pageName);
+
+ return (
+
+ );
+}
+
+function InnerEditPage({
+ moduleName,
+ pageName,
+}: {
+ pageName: string;
+ moduleName: string;
}) {
const router = useRouter();
const { courseName } = useCourseContext();
- const [page] = usePageQuery(moduleName, pageName);
+ const [page, pageQuery] = usePageQuery(moduleName, pageName);
const updatePage = useUpdatePageMutation();
const [pageText, setPageText] = useState(
localPageMarkdownUtils.toMarkdown(page)
@@ -32,6 +50,10 @@ export default function EditPage({
const [error, setError] = useState("");
const [settings] = useLocalCourseSettingsQuery();
+ useEffect(() => {
+ console.log("page data updated on sever", pageQuery.dataUpdatedAt);
+ }, [pageQuery.dataUpdatedAt]);
+
useEffect(() => {
const delay = 500;
const handler = setTimeout(() => {
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 941abe2..22722a6 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
@@ -61,6 +61,22 @@ export default function EditQuiz({
}: {
quizName: string;
moduleName: string;
+}) {
+ const [_, {dataUpdatedAt}] = useQuizQuery(moduleName, quizName);
+ return (
+
+ );
+}
+export function InnerEditQuiz({
+ moduleName,
+ quizName,
+}: {
+ quizName: string;
+ moduleName: string;
}) {
const router = useRouter();
const { courseName } = useCourseContext();
diff --git a/nextjs/src/app/realtime/ClientCacheInvalidation.tsx b/nextjs/src/app/realtime/ClientCacheInvalidation.tsx
index accfa53..2eb961b 100644
--- a/nextjs/src/app/realtime/ClientCacheInvalidation.tsx
+++ b/nextjs/src/app/realtime/ClientCacheInvalidation.tsx
@@ -1,7 +1,8 @@
"use client";
-import React, { useEffect } from 'react';
-import { io, Socket } from 'socket.io-client';
+import { trpc } from "@/services/trpc/utils";
+import React, { useEffect } from "react";
+import { io, Socket } from "socket.io-client";
interface ServerToClientEvents {
message: (data: string) => void;
@@ -12,28 +13,106 @@ interface ClientToServerEvents {
sendMessage: (data: string) => void;
}
-const socket: Socket = io('/');
+const socket: Socket = io("/");
+
+function removeFileExtension(fileName: string): string {
+ const lastDotIndex = fileName.lastIndexOf(".");
+ if (lastDotIndex > 0) {
+ return fileName.substring(0, lastDotIndex);
+ }
+ return fileName;
+}
export function ClientCacheInvalidation() {
+ const utils = trpc.useUtils();
useEffect(() => {
- socket.on('message', (data) => {
- console.log('Received message:', data);
+ socket.on("message", (data) => {
+ console.log("Received message:", data);
});
- socket.on('fileChanged', (filePath) => {
- console.log('File changed:', filePath);
+ socket.on("fileChanged", (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) => {
- console.error('Connection error:', error);
+ socket.on("connect_error", (error) => {
+ console.error("Connection error:", error);
});
return () => {
- socket.off('message');
- socket.off('fileChanged');
- socket.off('connect_error');
+ socket.off("message");
+ socket.off("fileChanged");
+ 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 <>>;
}
diff --git a/nextjs/src/websocket.js b/nextjs/src/websocket.js
index 179efde..d214c4d 100644
--- a/nextjs/src/websocket.js
+++ b/nextjs/src/websocket.js
@@ -12,7 +12,7 @@ const port = 3000;
const app = next({ dev, hostname, port });
const handler = app.getRequestHandler();
-const folderToWatch = path.join(process.cwd(), "./storage");
+const folderToWatch = path.join(process.cwd(), "./storage", "/");
console.log("watching folder", folderToWatch);
app.prepare().then(() => {
@@ -23,21 +23,14 @@ app.prepare().then(() => {
io.on("connection", (socket) => {
console.log("websocket connection created");
-
- // Define a change event handler
const changeHandler = (filePath) => {
- const relativePath = filePath.replace(folderToWatch, "")
+ const relativePath = filePath.replace(folderToWatch, "");
console.log(`Sending file changed websocket message: ${relativePath}`);
-
-
- socket.emit("fileChanged", `File changed: ${filePath}`);
+ socket.emit("fileChanged", relativePath);
};
-
- // Attach the change event handler
watcher.on("change", changeHandler);
- // Clean up the change event handler when the client disconnects
socket.on("disconnect", () => {
console.log("Client disconnected");
watcher.off("change", changeHandler); // Remove the event listener