moving v2 to top level

This commit is contained in:
2024-12-17 09:19:21 -07:00
parent 5f0b3554dc
commit 576ee02afb
468 changed files with 79 additions and 15430 deletions

View File

@@ -0,0 +1,65 @@
"use client";
import { ReactNode, useEffect, useState } from "react";
import { DraggingContext } from "./draggingContext";
import { useDragStyleContext } from "./dragStyleContext";
import { useModal } from "@/components/Modal";
import { LectureReplaceModal } from "../LectureReplaceModal";
import { useItemDropOnModule } from "./useItemDropOnModule";
import { useItemDropOnDay } from "./useItemDropOnDay";
export default function DraggingContextProvider({
children,
}: {
children: ReactNode;
}) {
const { setIsDragging } = useDragStyleContext();
const [isLoading, setIsLoading] = useState(false);
const [modalText, setModalText] = useState("");
const modal = useModal();
const [modalCallback, setModalCallback] = useState<() => void>(() => {});
useEffect(() => {
const handleDrop = () => {
console.log("drop on window");
setIsDragging(false);
};
const preventDefault = (e: globalThis.DragEvent) => e.preventDefault();
if (typeof window !== "undefined") {
window.addEventListener("drop", handleDrop);
window.addEventListener("dragover", preventDefault);
}
return () => {
window.removeEventListener("drop", handleDrop);
window.addEventListener("dragover", preventDefault);
};
}, [setIsDragging]);
const itemDropOnModule = useItemDropOnModule({
setIsDragging,
});
const itemDropOnDay = useItemDropOnDay({
setIsDragging,
setModalText,
setModalCallback,
setIsLoading,
modal,
});
return (
<DraggingContext.Provider
value={{
itemDropOnDay,
itemDropOnModule,
}}
>
<LectureReplaceModal
modal={modal}
modalText={modalText}
modalCallback={modalCallback}
isLoading={isLoading}
/>
{children}
</DraggingContext.Provider>
);
}

View File

@@ -0,0 +1,39 @@
"use client";
import {
createContext,
useContext,
ReactNode,
useState,
SetStateAction,
Dispatch,
} from "react";
export interface DraggingStyleContextInterface {
setIsDragging: Dispatch<SetStateAction<boolean>>;
}
const defaultDraggingValue: DraggingStyleContextInterface = {
setIsDragging: () => {},
};
export const DragStyleContext =
createContext<DraggingStyleContextInterface>(defaultDraggingValue);
export function useDragStyleContext() {
return useContext(DragStyleContext);
}
export function DragStyleContextProvider({
children,
}: {
children: ReactNode;
}) {
const [isDragging, setIsDragging] = useState(false);
return (
<DragStyleContext.Provider value={{ setIsDragging }}>
<div
className={"h-full flex flex-col " + (isDragging ? " dragging " : "")}
>
{children}
</div>
</DragStyleContext.Provider>
);
}

View File

@@ -0,0 +1,24 @@
"use client";
import { IModuleItem } from "@/models/local/IModuleItem";
import { createContext, useContext, DragEvent } from "react";
export interface DraggableItem {
item: IModuleItem;
sourceModuleName: string | undefined; // undefined for lectures
type: "quiz" | "assignment" | "page" | "lecture";
}
export interface DraggingContextInterface {
itemDropOnDay: (e: DragEvent, droppedOnDay: string) => void;
itemDropOnModule: (e: DragEvent, moduleName: string) => void;
}
const defaultDraggingValue: DraggingContextInterface = {
itemDropOnDay: () => {},
itemDropOnModule: () => {},
};
export const DraggingContext =
createContext<DraggingContextInterface>(defaultDraggingValue);
export function useDraggingContext() {
return useContext(DraggingContext);
}

View File

@@ -0,0 +1,25 @@
"use client";
import { getDateFromStringOrThrow, dateToMarkdownString } from "@/models/local/utils/timeUtils";
export function getNewLockDate(
originalDueDate: string,
originalLockDate: string | undefined,
dayAsDate: Date
): string | undefined {
// todo: preserve previous due date / lock date offset
const dueDate = getDateFromStringOrThrow(originalDueDate, "dueAt date");
const lockDate = originalLockDate === undefined
? undefined
: getDateFromStringOrThrow(originalLockDate, "lockAt date");
const originalOffset = lockDate === undefined ? undefined : lockDate.getTime() - dueDate.getTime();
const newLockDate = originalOffset === undefined
? undefined
: new Date(dayAsDate.getTime() + originalOffset);
return newLockDate === undefined
? undefined
: dateToMarkdownString(newLockDate);
}

View File

@@ -0,0 +1,212 @@
"use client";
import { useUpdateAssignmentMutation } from "@/hooks/localCourse/assignmentHooks";
import {
useLecturesSuspenseQuery,
useLectureUpdateMutation,
} from "@/hooks/localCourse/lectureHooks";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { useUpdatePageMutation } from "@/hooks/localCourse/pageHooks";
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
import { Lecture } from "@/models/local/lecture";
import { getLectureForDay } from "@/models/local/utils/lectureUtils";
import { LocalCoursePage } from "@/models/local/page/localCoursePage";
import { LocalQuiz } from "@/models/local/quiz/localQuiz";
import {
getDateFromStringOrThrow,
getDateOnlyMarkdownString,
dateToMarkdownString,
} from "@/models/local/utils/timeUtils";
import { Dispatch, SetStateAction, useCallback, DragEvent } from "react";
import { DraggableItem } from "./draggingContext";
import { getNewLockDate } from "./getNewLockDate";
import { useUpdateQuizMutation } from "@/hooks/localCourse/quizHooks";
import { useCourseContext } from "../courseContext";
export function useItemDropOnDay({
setIsDragging,
setModalText,
setModalCallback,
setIsLoading,
modal,
}: {
setIsDragging: Dispatch<SetStateAction<boolean>>;
setModalText: Dispatch<SetStateAction<string>>;
setModalCallback: Dispatch<SetStateAction<() => void>>;
setIsLoading: Dispatch<SetStateAction<boolean>>;
modal: { isOpen: boolean; openModal: () => void; closeModal: () => void };
}) {
const [settings] = useLocalCourseSettingsQuery();
const { courseName } = useCourseContext();
// const { data: weeks } = useLecturesByWeekQuery();
const [weeks] = useLecturesSuspenseQuery();
const updateQuizMutation = useUpdateQuizMutation();
const updateLectureMutation = useLectureUpdateMutation();
const updateAssignmentMutation = useUpdateAssignmentMutation();
const updatePageMutation = useUpdatePageMutation();
return useCallback(
(e: DragEvent, day: string) => {
const rawData = e.dataTransfer.getData("draggableItem");
if (!rawData) return;
const itemBeingDragged: DraggableItem = JSON.parse(rawData);
if (itemBeingDragged) {
const dayAsDate = getDateWithDefaultDueTime();
if (itemBeingDragged.type === "quiz") {
updateQuiz(dayAsDate);
} else if (itemBeingDragged.type === "assignment") {
updateAssignment(dayAsDate);
} else if (itemBeingDragged.type === "page") {
updatePage(dayAsDate);
} else if (itemBeingDragged.type === "lecture") {
updateLecture(dayAsDate);
}
}
setIsDragging(false);
function getDateWithDefaultDueTime() {
const dayAsDate = getDateFromStringOrThrow(day, "in drop callback");
dayAsDate.setHours(settings.defaultDueTime.hour);
dayAsDate.setMinutes(settings.defaultDueTime.minute);
dayAsDate.setSeconds(0);
return dayAsDate;
}
function updateLecture(dayAsDate: Date) {
const { dueAt, ...lecture } = itemBeingDragged.item as Lecture & {
dueAt: string;
};
console.log("dropped lecture on day");
const existingLecture = getLectureForDay(weeks, dayAsDate);
if (existingLecture) {
console.log("attempting to drop on existing lecture");
setModalText(
`Are you sure you want to replace ${
existingLecture?.name || "Un-named Lecture"
} with ${lecture.name}? This will delete ${
existingLecture?.name || "Un-named Lecture"
}.`
);
setModalCallback(() => async () => {
// because sometimes setStates receive a function
console.log("running callback");
setIsLoading(true);
await updateLectureMutation.mutateAsync({
previousDay: lecture.date,
lecture: {
...lecture,
date: getDateOnlyMarkdownString(dayAsDate),
},
courseName,
settings,
});
setModalText("");
setModalCallback(() => {});
modal.closeModal();
setIsLoading(false);
});
modal.openModal();
} else {
console.log("updating lecture on unique day");
updateLectureMutation.mutate({
previousDay: lecture.date,
lecture: {
...lecture,
date: getDateOnlyMarkdownString(dayAsDate),
},
courseName,
settings,
});
}
}
function updateQuiz(dayAsDate: Date) {
const previousQuiz = itemBeingDragged.item as LocalQuiz;
if (!itemBeingDragged.sourceModuleName) {
console.error(
"error dropping quiz on day, sourceModuleName is undefined"
);
return;
}
const quiz: LocalQuiz = {
...previousQuiz,
dueAt: dateToMarkdownString(dayAsDate),
lockAt: getNewLockDate(
previousQuiz.dueAt,
previousQuiz.lockAt,
dayAsDate
),
};
updateQuizMutation.mutate({
quiz,
quizName: quiz.name,
moduleName: itemBeingDragged.sourceModuleName,
previousModuleName: itemBeingDragged.sourceModuleName,
previousQuizName: quiz.name,
courseName: settings.name,
});
}
function updatePage(dayAsDate: Date) {
const previousPage = itemBeingDragged.item as LocalCoursePage;
if (!itemBeingDragged.sourceModuleName) {
console.error(
"error dropping page on day, sourceModuleName is undefined"
);
return;
}
const page: LocalCoursePage = {
...previousPage,
dueAt: dateToMarkdownString(dayAsDate),
};
updatePageMutation.mutate({
page,
moduleName: itemBeingDragged.sourceModuleName,
pageName: page.name,
previousPageName: page.name,
previousModuleName: itemBeingDragged.sourceModuleName,
courseName: settings.name,
});
}
function updateAssignment(dayAsDate: Date) {
if (!itemBeingDragged.sourceModuleName) {
console.error(
"error dropping assignment on day, sourceModuleName is undefined"
);
return;
}
const previousAssignment = itemBeingDragged.item as LocalAssignment;
const assignment: LocalAssignment = {
...previousAssignment,
dueAt: dateToMarkdownString(dayAsDate),
lockAt: getNewLockDate(
previousAssignment.dueAt,
previousAssignment.lockAt,
dayAsDate
),
};
updateAssignmentMutation.mutate({
assignment,
previousModuleName: itemBeingDragged.sourceModuleName,
moduleName: itemBeingDragged.sourceModuleName,
assignmentName: assignment.name,
previousAssignmentName: assignment.name,
courseName: settings.name,
});
}
},
[
courseName,
modal,
setIsDragging,
setIsLoading,
setModalCallback,
setModalText,
settings,
updateAssignmentMutation,
updateLectureMutation,
updatePageMutation,
updateQuizMutation,
weeks,
]
);
}

View File

@@ -0,0 +1,105 @@
"use client";
import { useUpdateAssignmentMutation } from "@/hooks/localCourse/assignmentHooks";
import { useUpdatePageMutation } from "@/hooks/localCourse/pageHooks";
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
import { LocalCoursePage } from "@/models/local/page/localCoursePage";
import { LocalQuiz } from "@/models/local/quiz/localQuiz";
import { Dispatch, SetStateAction, useCallback, DragEvent } from "react";
import { DraggableItem } from "./draggingContext";
import { useCourseContext } from "../courseContext";
import { useUpdateQuizMutation } from "@/hooks/localCourse/quizHooks";
export function useItemDropOnModule({
setIsDragging,
}: {
setIsDragging: Dispatch<SetStateAction<boolean>>;
}) {
const updateQuizMutation = useUpdateQuizMutation();
const updateAssignmentMutation = useUpdateAssignmentMutation();
const updatePageMutation = useUpdatePageMutation();
const { courseName } = useCourseContext();
return useCallback(
(e: DragEvent, dropModuleName: string) => {
console.log("dropping on module");
const rawData = e.dataTransfer.getData("draggableItem");
if (!rawData) return;
const itemBeingDragged: DraggableItem = JSON.parse(rawData);
if (itemBeingDragged) {
if (itemBeingDragged.type === "quiz") {
updateQuiz();
} else if (itemBeingDragged.type === "assignment") {
updateAssignment();
} else if (itemBeingDragged.type === "page") {
updatePage();
} else if (itemBeingDragged.type === "lecture") {
console.log("cannot drop lecture on module, only on days");
}
}
setIsDragging(false);
function updateQuiz() {
const quiz = itemBeingDragged.item as LocalQuiz;
if (itemBeingDragged.sourceModuleName) {
updateQuizMutation.mutate({
quiz,
quizName: quiz.name,
moduleName: dropModuleName,
previousModuleName: itemBeingDragged.sourceModuleName,
previousQuizName: quiz.name,
courseName,
});
} else {
console.error(
`error dropping quiz, sourceModuleName is undefined `,
quiz
);
}
}
function updateAssignment() {
const assignment = itemBeingDragged.item as LocalAssignment;
if (itemBeingDragged.sourceModuleName) {
updateAssignmentMutation.mutate({
assignment,
previousModuleName: itemBeingDragged.sourceModuleName,
moduleName: dropModuleName,
assignmentName: assignment.name,
previousAssignmentName: assignment.name,
courseName,
});
} else {
console.error(
`error dropping assignment, sourceModuleName is undefined `,
assignment
);
}
}
function updatePage() {
const page = itemBeingDragged.item as LocalCoursePage;
if (itemBeingDragged.sourceModuleName) {
updatePageMutation.mutate({
page,
moduleName: dropModuleName,
pageName: page.name,
previousPageName: page.name,
previousModuleName: itemBeingDragged.sourceModuleName,
courseName,
});
} else {
console.error(
`error dropping page, sourceModuleName is undefined `,
page
);
}
}
},
[
courseName,
setIsDragging,
updateAssignmentMutation,
updatePageMutation,
updateQuizMutation,
]
);
}