working pages and app router

This commit is contained in:
2024-09-07 08:52:03 -06:00
parent 3c86d3be88
commit 5b610e2777
135 changed files with 16129 additions and 4 deletions

View File

@@ -0,0 +1,12 @@
import { canvasService } from "@/services/canvas/canvasService";
import { useSuspenseQuery } from "@tanstack/react-query";
export const canvasCourseKeys = {
courseDetails: (canavasId: number) => ["canvas course", canavasId] as const,
};
export const useCanvasCourseQuery = (canvasId: number) =>
useSuspenseQuery({
queryKey: canvasCourseKeys.courseDetails(canvasId),
queryFn: async () => await canvasService.getCourse(canvasId),
});

View File

@@ -0,0 +1,141 @@
import { QueryClient } from "@tanstack/react-query";
import { localCourseKeys } from "./localCourse/localCourseKeys";
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
// https://tanstack.com/query/latest/docs/framework/react/guides/ssr
export const hydrateCourses = async (queryClient: QueryClient) => {
const allCourseNames = await fileStorageService.getCourseNames();
await queryClient.prefetchQuery({
queryKey: localCourseKeys.allCourses,
queryFn: () => allCourseNames,
});
await Promise.all(
allCourseNames.map(async (c) => await hydrateCourse(queryClient, c))
);
};
export const hydrateCourse = async (
queryClient: QueryClient,
courseName: string
) => {
const settings = await fileStorageService.getCourseSettings(courseName);
const moduleNames = await fileStorageService.getModuleNames(courseName);
const modulesData = await Promise.all(
moduleNames.map(async (moduleName) => {
const [assignmentNames, pageNames, quizNames] = await Promise.all([
await fileStorageService.getAssignmentNames(courseName, moduleName),
await fileStorageService.getPageNames(courseName, moduleName),
await fileStorageService.getQuizNames(courseName, moduleName),
]);
const [assignments, quizzes, pages] = await Promise.all([
await Promise.all(
assignmentNames.map(
async (assignmentName) =>
await fileStorageService.getAssignment(
courseName,
moduleName,
assignmentName
)
)
),
await Promise.all(
quizNames.map(
async (quizName) =>
await fileStorageService.getQuiz(courseName, moduleName, quizName)
)
),
await Promise.all(
pageNames.map(
async (pageName) =>
await fileStorageService.getPage(courseName, moduleName, pageName)
)
),
]);
return {
moduleName,
assignmentNames,
pageNames,
quizNames,
assignments,
quizzes,
pages,
};
})
);
await queryClient.prefetchQuery({
queryKey: localCourseKeys.settings(courseName),
queryFn: () => settings,
});
await queryClient.prefetchQuery({
queryKey: localCourseKeys.moduleNames(courseName),
queryFn: () => moduleNames,
});
await Promise.all(
modulesData.map(
async ({
moduleName,
assignmentNames,
pageNames,
quizNames,
assignments,
quizzes,
pages,
}) => {
await queryClient.prefetchQuery({
queryKey: localCourseKeys.assignmentNames(courseName, moduleName),
queryFn: () => assignmentNames,
});
await Promise.all(
assignments.map(
async (assignment) =>
await queryClient.prefetchQuery({
queryKey: localCourseKeys.assignment(
courseName,
moduleName,
assignment.name
),
queryFn: () => assignment,
})
)
);
await queryClient.prefetchQuery({
queryKey: localCourseKeys.quizNames(courseName, moduleName),
queryFn: () => quizNames,
});
await Promise.all(
quizzes.map(
async (quiz) =>
await queryClient.prefetchQuery({
queryKey: localCourseKeys.quiz(
courseName,
moduleName,
quiz.name
),
queryFn: () => quiz,
})
)
);
await queryClient.prefetchQuery({
queryKey: localCourseKeys.pageNames(courseName, moduleName),
queryFn: () => pageNames,
});
await Promise.all(
pages.map(
async (page) =>
await queryClient.prefetchQuery({
queryKey: localCourseKeys.page(
courseName,
moduleName,
page.name
),
queryFn: () => page,
})
)
);
}
)
);
};

View File

@@ -0,0 +1,120 @@
"use client";
import axios from "axios";
import { localCourseKeys } from "./localCourseKeys";
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
import {
useSuspenseQuery,
useSuspenseQueries,
useQueryClient,
useMutation,
} from "@tanstack/react-query";
import { useCourseContext } from "@/components/contexts/courseContext";
export const useAssignmentNamesQuery = (moduleName: string) => {
const { courseName } = useCourseContext();
return useSuspenseQuery({
queryKey: localCourseKeys.assignmentNames(courseName, moduleName),
queryFn: async (): Promise<string[]> => {
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/assignments";
const response = await axios.get(url);
return response.data;
},
});
};
const getAssignmentQueryConfig = (
courseName: string,
moduleName: string,
assignmentName: string
) => {
return {
queryKey: localCourseKeys.assignment(
courseName,
moduleName,
assignmentName
),
queryFn: async (): Promise<LocalAssignment> => {
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/assignments/" +
encodeURIComponent(assignmentName);
const response = await axios.get(url);
return response.data;
},
};
};
export const useAssignmentQuery = (
moduleName: string,
assignmentName: string
) => {
const { courseName } = useCourseContext();
return useSuspenseQuery(
getAssignmentQueryConfig(courseName, moduleName, assignmentName)
);
};
export const useAssignmentsQueries = (
moduleName: string,
assignmentNames: string[]
) => {
const { courseName } = useCourseContext();
return useSuspenseQueries({
queries: assignmentNames.map((name) =>
getAssignmentQueryConfig(courseName, moduleName, name)
),
combine: (results) => ({
data: results.map((r) => r.data),
pending: results.some((r) => r.isPending),
}),
});
};
export const useUpdateAssignmentMutation = () => {
const { courseName } = useCourseContext();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
assignment,
moduleName,
assignmentName,
}: {
assignment: LocalAssignment;
moduleName: string;
assignmentName: string;
}) => {
queryClient.setQueryData(
localCourseKeys.assignment(courseName, moduleName, assignmentName),
assignment
);
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/assignments/" +
encodeURIComponent(assignmentName);
await axios.put(url, assignment);
},
onSuccess: (_, { moduleName, assignmentName }) => {
queryClient.invalidateQueries({
queryKey: localCourseKeys.assignment(
courseName,
moduleName,
assignmentName
),
});
queryClient.invalidateQueries({
queryKey: localCourseKeys.assignmentNames(courseName, moduleName),
});
},
});
};

View File

@@ -0,0 +1,70 @@
export const localCourseKeys = {
allCourses: ["all courses"] as const,
settings: (courseName: string) =>
["course details", courseName, "settings"] as const,
moduleNames: (courseName: string) =>
[
"course details",
courseName,
"modules",
{ type: "names" } as const,
] as const,
assignmentNames: (courseName: string, moduleName: string) =>
[
"course details",
courseName,
"modules",
moduleName,
"assignments",
{ type: "names" },
] as const,
quizNames: (courseName: string, moduleName: string) =>
[
"course details",
courseName,
"modules",
moduleName,
"quizzes",
{ type: "names" },
] as const,
pageNames: (courseName: string, moduleName: string) =>
[
"course details",
courseName,
"modules",
moduleName,
"pages",
{ type: "names" },
] as const,
assignment: (
courseName: string,
moduleName: string,
assignmentName: string
) =>
[
"course details",
courseName,
"modules",
moduleName,
"assignments",
assignmentName,
] as const,
quiz: (courseName: string, moduleName: string, quizName: string) =>
[
"course details",
courseName,
"modules",
moduleName,
"quizzes",
quizName,
] as const,
page: (courseName: string, moduleName: string, pageName: string) =>
[
"course details",
courseName,
"modules",
moduleName,
"pages",
pageName,
] as const,
};

View File

@@ -0,0 +1,91 @@
"use client";
import { LocalCourseSettings } from "@/models/local/localCourse";
import { useSuspenseQuery } from "@tanstack/react-query";
import axios from "axios";
import { localCourseKeys } from "./localCourseKeys";
import { useCourseContext } from "@/components/contexts/courseContext";
export const useLocalCourseNamesQuery = () =>
useSuspenseQuery({
queryKey: localCourseKeys.allCourses,
queryFn: async (): Promise<string[]> => {
const url = `/api/courses`;
const response = await axios.get(url);
return response.data;
},
});
export const useLocalCourseSettingsQuery = () => {
const { courseName } = useCourseContext();
return useSuspenseQuery({
queryKey: localCourseKeys.settings(courseName),
queryFn: async (): Promise<LocalCourseSettings> => {
const url = `/api/courses/${courseName}/settings`;
const response = await axios.get(url);
return response.data;
},
});
};
export const useModuleNamesQuery = () => {
const { courseName } = useCourseContext();
return useSuspenseQuery({
queryKey: localCourseKeys.moduleNames(courseName),
queryFn: async (): Promise<string[]> => {
const url = `/api/courses/${courseName}/modules`;
const response = await axios.get(url);
return response.data;
},
});
};
// dangerous? really slowed down page...
// maybe it only slowed down with react query devtools...
// export const useModuleDataQuery = (moduleName: string) => {
// console.log("running");
// const { data: assignmentNames } = useAssignmentNamesQuery(moduleName);
// const { data: quizNames } = useQuizNamesQuery(moduleName);
// const { data: pageNames } = usePageNamesQuery(moduleName);
// const { data: assignments } = useAssignmentsQueries(
// moduleName,
// assignmentNames
// );
// const { data: quizzes } = useQuizzesQueries(moduleName, quizNames);
// const { data: pages } = usePagesQueries(moduleName, pageNames);
// return {
// assignments,
// quizzes,
// pages,
// };
// // return useMemo(
// // () => ({
// // assignments,
// // quizzes,
// // pages,
// // }),
// // [assignments, pages, quizzes]
// // );
// };
// export const useUpdateCourseMutation = (courseName: string) => {
// const queryClient = useQueryClient();
// return useMutation({
// mutationFn: async (body: {
// updatedCourse: LocalCourse;
// previousCourse: LocalCourse;
// }) => {
// const url = `/api/courses/${courseName}`;
// await axios.put(url, body);
// },
// onSuccess: () => {
// queryClient.invalidateQueries({
// queryKey: localCourseKeys.settings(courseName),
// });
// },
// scope: {
// id: "all courses",
// },
// });
// };

View File

@@ -0,0 +1,108 @@
"use client";
import { LocalCoursePage } from "@/models/local/page/localCoursePage";
import {
useMutation,
useQueryClient,
useSuspenseQueries,
useSuspenseQuery,
} from "@tanstack/react-query";
import axios from "axios";
import { localCourseKeys } from "./localCourseKeys";
import { useCourseContext } from "@/components/contexts/courseContext";
export const usePageNamesQuery = (moduleName: string) => {
const { courseName } = useCourseContext();
return useSuspenseQuery({
queryKey: localCourseKeys.pageNames(courseName, moduleName),
queryFn: async (): Promise<string[]> => {
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/pages";
const response = await axios.get(url);
return response.data;
},
});
};
export const usePageQuery = (moduleName: string, pageName: string) => {
const { courseName } = useCourseContext();
return useSuspenseQuery(getPageQueryConfig(courseName, moduleName, pageName));
};
export const usePagesQueries = (moduleName: string, pageNames: string[]) => {
const { courseName } = useCourseContext();
return useSuspenseQueries({
queries: pageNames.map((name) =>
getPageQueryConfig(courseName, moduleName, name)
),
combine: (results) => ({
data: results.map((r) => r.data),
pending: results.some((r) => r.isPending),
}),
});
};
function getPageQueryConfig(
courseName: string,
moduleName: string,
pageName: string
) {
return {
queryKey: localCourseKeys.page(courseName, moduleName, pageName),
queryFn: async (): Promise<LocalCoursePage> => {
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/pages/" +
encodeURIComponent(pageName);
try {
const response = await axios.get(url);
return response.data;
} catch (e) {
console.log("error getting page", e, url);
throw e;
}
},
};
}
export const useUpdatePageMutation = () => {
const { courseName } = useCourseContext();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
page,
moduleName,
pageName,
}: {
page: LocalCoursePage;
moduleName: string;
pageName: string;
}) => {
queryClient.setQueryData(
localCourseKeys.page(courseName, moduleName, pageName),
page
);
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/pages/" +
encodeURIComponent(pageName);
await axios.put(url, page);
},
onSuccess: (_, { moduleName, pageName }) => {
queryClient.invalidateQueries({
queryKey: localCourseKeys.page(courseName, moduleName, pageName),
});
queryClient.invalidateQueries({
queryKey: localCourseKeys.pageNames(courseName, moduleName),
});
},
});
};

View File

@@ -0,0 +1,104 @@
"use client";
import { LocalQuiz } from "@/models/local/quiz/localQuiz";
import {
useMutation,
useQueryClient,
useSuspenseQueries,
useSuspenseQuery,
} from "@tanstack/react-query";
import axios from "axios";
import { localCourseKeys } from "./localCourseKeys";
import { useCourseContext } from "@/components/contexts/courseContext";
export const useQuizNamesQuery = (moduleName: string) => {
const { courseName } = useCourseContext();
return useSuspenseQuery({
queryKey: localCourseKeys.quizNames(courseName, moduleName),
queryFn: async (): Promise<string[]> => {
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/quizzes";
const response = await axios.get(url);
return response.data;
},
});
};
export const useQuizQuery = (moduleName: string, quizName: string) => {
const { courseName } = useCourseContext();
return useSuspenseQuery(getQuizQueryConfig(courseName, moduleName, quizName));
};
export const useQuizzesQueries = (moduleName: string, quizNames: string[]) => {
const { courseName } = useCourseContext();
return useSuspenseQueries({
queries: quizNames.map((name) =>
getQuizQueryConfig(courseName, moduleName, name)
),
combine: (results) => ({
data: results.map((r) => r.data),
pending: results.some((r) => r.isPending),
}),
});
};
function getQuizQueryConfig(
courseName: string,
moduleName: string,
quizName: string
) {
return {
queryKey: localCourseKeys.quiz(courseName, moduleName, quizName),
queryFn: async (): Promise<LocalQuiz> => {
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/quizzes/" +
encodeURIComponent(quizName);
const response = await axios.get(url);
return response.data;
},
};
}
export const useUpdateQuizMutation = () => {
const { courseName } = useCourseContext();
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
quiz,
moduleName,
quizName,
}: {
quiz: LocalQuiz;
moduleName: string;
quizName: string;
}) => {
queryClient.setQueryData(
localCourseKeys.quiz(courseName, moduleName, quizName),
quiz
);
const url =
"/api/courses/" +
encodeURIComponent(courseName) +
"/modules/" +
encodeURIComponent(moduleName) +
"/quizzes/" +
encodeURIComponent(quizName);
await axios.put(url, quiz);
},
onSuccess: (_, { moduleName, quizName }) => {
queryClient.invalidateQueries({
queryKey: localCourseKeys.quiz(courseName, moduleName, quizName),
});
queryClient.invalidateQueries({
queryKey: localCourseKeys.quizNames(courseName, moduleName),
});
},
});
};