From fd65bf710de706330816fd6d542caa1327ff0b02 Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Sat, 2 Nov 2024 13:59:25 -0600 Subject: [PATCH] refactored drag provider --- .../context/DraggingContextProvider.tsx | 328 ++---------------- .../context/LectureReplaceModal.tsx | 39 +++ .../[courseName]/context/draggingContext.tsx | 4 +- .../context/draggingContextUtils.ts | 310 +++++++++++++++++ 4 files changed, 372 insertions(+), 309 deletions(-) create mode 100644 nextjs/src/app/course/[courseName]/context/LectureReplaceModal.tsx create mode 100644 nextjs/src/app/course/[courseName]/context/draggingContextUtils.ts diff --git a/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx b/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx index 14f94cf..deadfd9 100644 --- a/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx +++ b/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx @@ -1,27 +1,10 @@ "use client"; -import { ReactNode, useCallback, DragEvent, useEffect, useState } from "react"; -import { DraggableItem, DraggingContext } from "./draggingContext"; -import { useUpdateQuizMutation } from "@/hooks/localCourse/quizHooks"; -import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; -import { LocalQuiz } from "@/models/local/quiz/localQuiz"; -import { - getDateFromStringOrThrow, - dateToMarkdownString, - getDateOnlyMarkdownString, -} from "@/models/local/timeUtils"; -import { LocalAssignment } from "@/models/local/assignment/localAssignment"; -import { useUpdateAssignmentMutation } from "@/hooks/localCourse/assignmentHooks"; -import { useUpdatePageMutation } from "@/hooks/localCourse/pageHooks"; -import { LocalCoursePage } from "@/models/local/page/localCoursePage"; +import { ReactNode, useEffect, useState } from "react"; +import { DraggingContext } from "./draggingContext"; import { useDragStyleContext } from "./dragStyleContext"; -import { Lecture } from "@/models/local/lecture"; -import { - useLecturesByWeekQuery, - useLectureUpdateMutation, -} from "@/hooks/localCourse/lectureHooks"; -import { getLectureForDay } from "@/models/local/lectureUtils"; -import Modal, { useModal } from "@/components/Modal"; -import { Spinner } from "@/components/Spinner"; +import { useModal } from "@/components/Modal"; +import { LectureReplaceModal } from "./LectureReplaceModal"; +import { useItemDropOnDay, useItemDropOnModule } from "./draggingContextUtils"; export default function DraggingContextProvider({ children, @@ -29,12 +12,6 @@ export default function DraggingContextProvider({ children: ReactNode; }) { const { setIsDragging } = useDragStyleContext(); - const updateQuizMutation = useUpdateQuizMutation(); - const updateLectureMutation = useLectureUpdateMutation(); - const updateAssignmentMutation = useUpdateAssignmentMutation(); - const updatePageMutation = useUpdatePageMutation(); - const { data: settings } = useLocalCourseSettingsQuery(); - const { data: weeks } = useLecturesByWeekQuery(); const [isLoading, setIsLoading] = useState(false); const [modalText, setModalText] = useState(""); const modal = useModal(); @@ -56,237 +33,17 @@ export default function DraggingContextProvider({ }; }, [setIsDragging]); - const itemDropOnModule = 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); + const itemDropOnModule = useItemDropOnModule({ + setIsDragging, + }); - if (itemBeingDragged) { - if (itemBeingDragged.type === "quiz") { - updateQuiz(); - } else if (itemBeingDragged.type === "assignment") { - updateAssignment(); - } else if (itemBeingDragged.type === "page") { - updatePage(); - } else if (itemBeingDragged.type === "lecture") { - // const lecture = itemBeingDragged.item as Lecture & { dueAt: string }; - 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({ - item: quiz, - itemName: quiz.name, - moduleName: dropModuleName, - previousModuleName: itemBeingDragged.sourceModuleName, - previousItemName: quiz.name, - }); - } else { - console.error( - `error dropping quiz, sourceModuleName is undefined `, - quiz - ); - } - } - function updateAssignment() { - const assignment = itemBeingDragged.item as LocalAssignment; - if (itemBeingDragged.sourceModuleName) { - updateAssignmentMutation.mutate({ - item: assignment, - previousModuleName: itemBeingDragged.sourceModuleName, - moduleName: dropModuleName, - itemName: assignment.name, - previousItemName: assignment.name, - }); - } else { - console.error( - `error dropping assignment, sourceModuleName is undefined `, - assignment - ); - } - } - function updatePage() { - const page = itemBeingDragged.item as LocalCoursePage; - if (itemBeingDragged.sourceModuleName) { - updatePageMutation.mutate({ - item: page, - moduleName: dropModuleName, - itemName: page.name, - previousItemName: page.name, - previousModuleName: itemBeingDragged.sourceModuleName, - }); - } else { - console.error( - `error dropping page, sourceModuleName is undefined `, - page - ); - } - } - }, - [ - setIsDragging, - updateAssignmentMutation, - updatePageMutation, - updateQuizMutation, - ] - ); - - const itemDropOnDay = 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} with ${lecture.name}? This will delete ${existingLecture.name}.` - ); - - 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), - }, - }); - 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), - }, - }); - } - } - 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({ - item: quiz, - itemName: quiz.name, - moduleName: itemBeingDragged.sourceModuleName, - previousModuleName: itemBeingDragged.sourceModuleName, - previousItemName: quiz.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({ - item: page, - moduleName: itemBeingDragged.sourceModuleName, - itemName: page.name, - previousItemName: page.name, - previousModuleName: itemBeingDragged.sourceModuleName, - }); - } - 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({ - item: assignment, - previousModuleName: itemBeingDragged.sourceModuleName, - moduleName: itemBeingDragged.sourceModuleName, - itemName: assignment.name, - previousItemName: assignment.name, - }); - } - }, - [ - modal, - setIsDragging, - settings.defaultDueTime.hour, - settings.defaultDueTime.minute, - updateAssignmentMutation, - updateLectureMutation, - updatePageMutation, - updateQuizMutation, - weeks, - ] - ); + const itemDropOnDay = useItemDropOnDay({ + setIsDragging, + setModalText, + setModalCallback, + setIsLoading, + modal, + }); return ( - - {({ closeModal }) => ( -
-
{modalText}
-
-
- - -
- {isLoading && } -
- )} -
+ {children}
); } - -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); -} diff --git a/nextjs/src/app/course/[courseName]/context/LectureReplaceModal.tsx b/nextjs/src/app/course/[courseName]/context/LectureReplaceModal.tsx new file mode 100644 index 0000000..e0e5358 --- /dev/null +++ b/nextjs/src/app/course/[courseName]/context/LectureReplaceModal.tsx @@ -0,0 +1,39 @@ +"use client"; +import Modal, { ModalControl } from "@/components/Modal"; +import { Spinner } from "@/components/Spinner"; + +export function LectureReplaceModal({ + modal, modalText, modalCallback, isLoading, +}: { + modal: ModalControl; + modalText: string; + modalCallback: () => void; + isLoading: boolean; +}) { + return ( + + {({ closeModal }) => ( +
+
{modalText}
+
+
+ + +
+ {isLoading && } +
+ )} +
+ ); +} diff --git a/nextjs/src/app/course/[courseName]/context/draggingContext.tsx b/nextjs/src/app/course/[courseName]/context/draggingContext.tsx index 6fa64b8..814ba8e 100644 --- a/nextjs/src/app/course/[courseName]/context/draggingContext.tsx +++ b/nextjs/src/app/course/[courseName]/context/draggingContext.tsx @@ -9,8 +9,8 @@ export interface DraggableItem { } export interface DraggingContextInterface { - itemDropOnDay: (e: DragEvent, droppedOnDay: string) => void; - itemDropOnModule: (e: DragEvent, moduleName: string) => void; + itemDropOnDay: (e: DragEvent, droppedOnDay: string) => void; + itemDropOnModule: (e: DragEvent, moduleName: string) => void; } const defaultDraggingValue: DraggingContextInterface = { itemDropOnDay: () => {}, diff --git a/nextjs/src/app/course/[courseName]/context/draggingContextUtils.ts b/nextjs/src/app/course/[courseName]/context/draggingContextUtils.ts new file mode 100644 index 0000000..6227933 --- /dev/null +++ b/nextjs/src/app/course/[courseName]/context/draggingContextUtils.ts @@ -0,0 +1,310 @@ +"use client"; + +import { useUpdateAssignmentMutation } from "@/hooks/localCourse/assignmentHooks"; +import { + useLecturesByWeekQuery, + useLectureUpdateMutation, +} from "@/hooks/localCourse/lectureHooks"; +import { useUpdatePageMutation } from "@/hooks/localCourse/pageHooks"; +import { useUpdateQuizMutation } from "@/hooks/localCourse/quizHooks"; +import { LocalAssignment } from "@/models/local/assignment/localAssignment"; +import { Lecture } from "@/models/local/lecture"; +import { getLectureForDay } from "@/models/local/lectureUtils"; +import { LocalCoursePage } from "@/models/local/page/localCoursePage"; +import { LocalQuiz } from "@/models/local/quiz/localQuiz"; +import { + getDateFromStringOrThrow, + getDateOnlyMarkdownString, + dateToMarkdownString, +} from "@/models/local/timeUtils"; +import { Dispatch, SetStateAction, useCallback, DragEvent } from "react"; +import { DraggableItem } from "./draggingContext"; +import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; + +export function useItemDropOnDay({ + setIsDragging, + setModalText, + setModalCallback, + setIsLoading, + modal, +}: { + setIsDragging: Dispatch>; + setModalText: Dispatch>; + setModalCallback: Dispatch void>>; + setIsLoading: Dispatch>; + modal: { isOpen: boolean; openModal: () => void; closeModal: () => void }; +}) { + const { data: settings } = useLocalCourseSettingsQuery(); + const { data: weeks } = useLecturesByWeekQuery(); + 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} with ${lecture.name}? This will delete ${existingLecture.name}.` + ); + + 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), + }, + }); + 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), + }, + }); + } + } + 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({ + item: quiz, + itemName: quiz.name, + moduleName: itemBeingDragged.sourceModuleName, + previousModuleName: itemBeingDragged.sourceModuleName, + previousItemName: quiz.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({ + item: page, + moduleName: itemBeingDragged.sourceModuleName, + itemName: page.name, + previousItemName: page.name, + previousModuleName: itemBeingDragged.sourceModuleName, + }); + } + 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({ + item: assignment, + previousModuleName: itemBeingDragged.sourceModuleName, + moduleName: itemBeingDragged.sourceModuleName, + itemName: assignment.name, + previousItemName: assignment.name, + }); + } + }, + [ + modal, + setIsDragging, + setIsLoading, + setModalCallback, + setModalText, + settings.defaultDueTime.hour, + settings.defaultDueTime.minute, + updateAssignmentMutation, + updateLectureMutation, + updatePageMutation, + updateQuizMutation, + weeks, + ] + ); +} +export function useItemDropOnModule({ + setIsDragging, +}: { + setIsDragging: Dispatch>; +}) { + const updateQuizMutation = useUpdateQuizMutation(); + const updateAssignmentMutation = useUpdateAssignmentMutation(); + const updatePageMutation = useUpdatePageMutation(); + + 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({ + item: quiz, + itemName: quiz.name, + moduleName: dropModuleName, + previousModuleName: itemBeingDragged.sourceModuleName, + previousItemName: quiz.name, + }); + } else { + console.error( + `error dropping quiz, sourceModuleName is undefined `, + quiz + ); + } + } + function updateAssignment() { + const assignment = itemBeingDragged.item as LocalAssignment; + if (itemBeingDragged.sourceModuleName) { + updateAssignmentMutation.mutate({ + item: assignment, + previousModuleName: itemBeingDragged.sourceModuleName, + moduleName: dropModuleName, + itemName: assignment.name, + previousItemName: assignment.name, + }); + } else { + console.error( + `error dropping assignment, sourceModuleName is undefined `, + assignment + ); + } + } + function updatePage() { + const page = itemBeingDragged.item as LocalCoursePage; + if (itemBeingDragged.sourceModuleName) { + updatePageMutation.mutate({ + item: page, + moduleName: dropModuleName, + itemName: page.name, + previousItemName: page.name, + previousModuleName: itemBeingDragged.sourceModuleName, + }); + } else { + console.error( + `error dropping page, sourceModuleName is undefined `, + page + ); + } + } + }, + [ + setIsDragging, + updateAssignmentMutation, + updatePageMutation, + updateQuizMutation, + ] + ); +} +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); +}