frontPanel/src/stores/quizes/actions.ts

319 lines
7.8 KiB
TypeScript
Raw Normal View History

2023-11-13 18:04:51 +00:00
import { quizApi } from "@api/quiz";
2024-05-15 12:37:42 +00:00
import { devlog } from "@frontend/kitui";
2023-11-27 23:07:24 +00:00
import { quizToEditQuizRequest } from "@model/quiz/edit";
2023-11-13 18:04:51 +00:00
import { Quiz, RawQuiz, rawQuizToQuiz } from "@model/quiz/quiz";
import { maxQuizSetupSteps, QuizConfig } from "@model/quizSettings";
2024-02-26 09:51:33 +00:00
import { createUntypedQuestion, updateQuestion } from "@root/questions/actions";
import { useQuestionsStore } from "@root/questions/store";
2023-11-13 18:04:51 +00:00
import { produce } from "immer";
import { enqueueSnackbar } from "notistack";
import { NavigateFunction } from "react-router-dom";
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
import { RequestQueue } from "../../utils/requestQueue";
import { QuizStore, useQuizStore } from "./store";
2023-11-13 18:04:51 +00:00
2023-12-31 02:53:25 +00:00
export const setEditQuizId = (quizId: number | null) =>
setProducedState(
(state) => {
state.editQuizId = quizId;
},
{
type: "setEditQuizId",
quizId,
},
);
export const resetEditConfig = () =>
setProducedState((state) => {
2023-11-14 14:22:10 +00:00
state.editQuizId = null;
2023-11-27 23:07:24 +00:00
state.currentStep = 0;
2023-12-31 02:53:25 +00:00
}, "resetEditConfig");
export const setQuizes = (quizes: RawQuiz[] | null) =>
setProducedState(
(state) => {
state.quizes = quizes?.map(rawQuizToQuiz) ?? [];
},
{
type: "setQuizes",
quizes,
},
);
const addQuiz = (quiz: Quiz) =>
setProducedState(
(state) => {
state.quizes.push(quiz);
},
{
type: "addQuiz",
quiz,
},
);
const removeQuiz = (quizId: string) =>
setProducedState(
(state) => {
const index = state.quizes.findIndex((q) => q.id === quizId);
if (index === -1) return;
state.quizes.splice(index, 1);
},
{
type: "removeQuiz",
quizId,
},
);
const setQuizBackendId = (quizId: string, backendId: number) =>
setProducedState(
(state) => {
const quiz = state.quizes.find((q) => q.id === quizId);
if (!quiz) return;
quiz.backendId = backendId;
},
{
type: "setQuizBackendId",
quizId,
backendId,
},
);
export const incrementCurrentStep = () =>
setProducedState(
(state) => {
state.currentStep = Math.min(
maxQuizSetupSteps - 1,
state.currentStep + 1,
);
},
{
type: "incrementCurrentStep",
},
);
export const decrementCurrentStep = () =>
setProducedState(
(state) => {
state.currentStep = Math.max(0, state.currentStep - 1);
},
{
type: "decrementCurrentStep",
},
);
export const setCurrentStep = (step: number) =>
setProducedState(
(state) => {
state.currentStep = Math.max(0, Math.min(maxQuizSetupSteps - 1, step));
},
{
type: "setCurrentStep",
step,
},
);
export const setQuizType = (quizId: string, quizType: QuizConfig["type"]) => {
updateQuiz(quizId, (quiz) => {
quiz.config.type = quizType;
});
2023-11-13 18:04:51 +00:00
};
export const setQuizStartpageType = (
2023-12-31 02:53:25 +00:00
quizId: string,
startpageType: QuizConfig["startpageType"],
2023-11-13 18:04:51 +00:00
) => {
2023-12-31 02:53:25 +00:00
updateQuiz(quizId, (quiz) => {
quiz.config.startpageType = startpageType;
});
2023-11-13 18:04:51 +00:00
};
2023-11-27 23:07:24 +00:00
const REQUEST_DEBOUNCE = 200;
const requestQueue = new RequestQueue();
let requestTimeoutId: ReturnType<typeof setTimeout>;
2023-11-13 18:04:51 +00:00
2023-12-08 16:23:06 +00:00
export const updateQuiz = (
2023-12-31 02:53:25 +00:00
quizId: string | null | undefined,
updateFn: (quiz: Quiz) => void,
2023-11-13 18:04:51 +00:00
) => {
2023-12-31 02:53:25 +00:00
if (!quizId) return;
setProducedState(
(state) => {
const quiz = state.quizes.find((q) => q.id === quizId);
if (!quiz) return;
updateFn(quiz);
},
{
type: "updateQuiz",
quizId,
updateFn: updateFn.toString(),
},
);
clearTimeout(requestTimeoutId);
requestTimeoutId = setTimeout(async () => {
2024-05-13 13:24:41 +00:00
requestQueue.enqueue(`updateQuiz-${quizId}`, async () => {
const quiz = useQuizStore.getState().quizes.find((q) => q.id === quizId);
if (!quiz) return;
const [editedQuiz, editedQuizError] = await quizApi.edit(
quizToEditQuizRequest(quiz),
);
if (editedQuizError || !editedQuiz) {
devlog("Error editing quiz", editedQuizError, quizId);
enqueueSnackbar(editedQuizError);
return;
}
setQuizBackendId(quizId, editedQuiz.updated);
setEditQuizId(editedQuiz.updated);
});
2023-12-31 02:53:25 +00:00
}, REQUEST_DEBOUNCE);
2023-11-13 18:04:51 +00:00
};
2023-12-31 02:53:25 +00:00
export const createQuiz = async (navigate: NavigateFunction) =>
2024-02-26 09:51:33 +00:00
requestQueue.enqueue("createQuiz", async () => {
2024-05-13 13:24:41 +00:00
const [rawQuiz, createQuizError] = await quizApi.create();
if (createQuizError || !rawQuiz) {
devlog("Error creating quiz", createQuizError);
enqueueSnackbar(createQuizError);
return;
2023-11-14 16:43:21 +00:00
}
2024-05-13 13:24:41 +00:00
const quiz = rawQuizToQuiz(rawQuiz);
addQuiz(quiz);
setEditQuizId(quiz.backendId);
navigate("/edit");
createUntypedQuestion(rawQuiz.id);
2023-12-31 02:53:25 +00:00
});
2023-11-27 23:07:24 +00:00
2023-12-31 02:53:25 +00:00
export const deleteQuiz = async (quizId: string) =>
2024-02-26 09:51:33 +00:00
requestQueue.enqueue(`deleteQuiz-${quizId}`, async () => {
2023-12-31 02:53:25 +00:00
const quiz = useQuizStore.getState().quizes.find((q) => q.id === quizId);
2023-11-27 23:07:24 +00:00
if (!quiz) return;
2023-11-14 16:43:21 +00:00
2024-05-13 13:24:41 +00:00
const [_, deleteQuizError] = await quizApi.delete(quiz.backendId);
if (deleteQuizError) {
devlog("Error deleting quiz", deleteQuizError);
2023-11-14 16:43:21 +00:00
2024-05-13 13:24:41 +00:00
enqueueSnackbar(deleteQuizError);
2023-11-14 16:43:21 +00:00
2024-05-13 13:24:41 +00:00
return;
2023-11-14 16:43:21 +00:00
}
2024-05-13 13:24:41 +00:00
removeQuiz(quizId);
2023-12-31 02:53:25 +00:00
});
export const updateRootContentId = (quizId: string, id: string) => {
2023-12-31 02:53:25 +00:00
if (id.length === 0) {
//дерева больше не существует, все результаты неактивны кроме результата линейности
useQuestionsStore.getState().questions.forEach((q) => {
if (q.type !== null && q.type === "result") {
if (q.content.rule.parentId === "line") {
if (q.content.usage === false)
updateQuestion(q.id, (q) => {
q.content.usage = true;
});
} else {
updateQuestion(q.id, (q) => {
q.content.usage = false;
});
}
}
});
} else {
//было создано дерево, результат линейности неактивен
useQuestionsStore.getState().questions.forEach((q) => {
if (q.type !== null && q.content.rule.parentId === "line") {
updateQuestion(q.id, (q) => {
q.content.usage = false;
});
}
});
}
2023-12-31 02:53:25 +00:00
updateQuiz(quizId, (quiz) => {
quiz.config.haveRoot = id;
});
};
2023-12-04 13:33:43 +00:00
export const copyQuiz = async (quizId: string) =>
requestQueue.enqueue(`copyQuiz`, async () => {
const quiz = useQuizStore.getState().quizes.find((q) => q.id === quizId);
if (!quiz) return;
2024-05-13 13:24:41 +00:00
const [copiedQuiz, copyError] = await quizApi.copy(quiz.backendId);
if (copyError || !copiedQuiz) {
devlog("Error copying quiz", copyError);
enqueueSnackbar(copyError);
2024-05-13 13:24:41 +00:00
return;
}
2024-05-13 13:24:41 +00:00
let newQuiz: Quiz = {
...quiz,
id: String(copiedQuiz.updated),
session_count: 0,
passed_count: 0,
};
setProducedState(
(state) => {
state.quizes.unshift(newQuiz);
},
{ type: "addQuiz", quiz },
);
});
2023-11-14 16:43:21 +00:00
2023-12-01 18:05:59 +00:00
export const uploadQuizImage = async (
2023-12-31 02:53:25 +00:00
quizId: string,
blob: Blob,
updateFn: (quiz: Quiz, imageId: string) => void,
2023-12-01 18:05:59 +00:00
) => {
2023-12-31 02:53:25 +00:00
const quiz = useQuizStore.getState().quizes.find((q) => q.id === quizId);
if (!quiz) return;
2023-12-01 18:05:59 +00:00
2024-05-13 13:24:41 +00:00
const [addedImages, addImagesError] = await quizApi.addImages(
quiz.backendId,
blob,
);
2023-12-01 18:05:59 +00:00
2024-05-13 13:24:41 +00:00
if (addImagesError || !addedImages) {
devlog("Error uploading quiz image", addImagesError);
enqueueSnackbar(addImagesError);
2023-12-01 18:05:59 +00:00
2024-05-13 13:24:41 +00:00
return;
}
2023-12-01 18:05:59 +00:00
2024-05-13 13:24:41 +00:00
const values = Object.values(addedImages);
if (values.length !== 1) {
console.warn("Error uploading image");
return;
2023-12-31 02:53:25 +00:00
}
2024-05-13 13:24:41 +00:00
const imageId = values[0];
updateQuiz(quizId, (quiz) => {
updateFn(
quiz,
`https://storage.yandexcloud.net/squizimages/${quiz.qid}/${imageId}`,
);
});
2023-12-01 18:05:59 +00:00
};
2023-12-31 02:53:25 +00:00
function setProducedState<A extends string | { type: unknown }>(
recipe: (state: QuizStore) => void,
action?: A,
2023-11-13 18:04:51 +00:00
) {
2023-12-31 02:53:25 +00:00
useQuizStore.setState((state) => produce(state, recipe), false, action);
2023-11-13 18:04:51 +00:00
}