frontPanel/src/stores/quizes/actions.ts
2023-11-28 02:07:24 +03:00

184 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { quizApi } from "@api/quiz";
import { devlog, getMessageFromFetchError } from "@frontend/kitui";
import { quizToEditQuizRequest } from "@model/quiz/edit";
import { Quiz, RawQuiz, rawQuizToQuiz } from "@model/quiz/quiz";
import { QuizConfig, maxQuizSetupSteps } from "@model/quizSettings";
import { createQuestion } from "@root/questions/actions";
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";
export const setEditQuizId = (quizId: number | null) => setProducedState(state => {
state.editQuizId = quizId;
}, {
type: "setEditQuizId",
quizId,
});
export const resetEditConfig = () => setProducedState(state => {
state.editQuizId = null;
state.currentStep = 0;
});
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));
});
export const setQuizType = (
quizId: string,
quizType: QuizConfig["type"],
) => {
updateQuiz(
quizId,
quiz => {
quiz.config.type = quizType;
},
);
};
export const setQuizStartpageType = (
quizId: string,
startpageType: QuizConfig["startpageType"],
) => {
updateQuiz(
quizId,
quiz => {
quiz.config.startpageType = startpageType;
},
);
};
const REQUEST_DEBOUNCE = 200;
const requestQueue = new RequestQueue();
let requestTimeoutId: ReturnType<typeof setTimeout>;
export const updateQuiz = async (
quizId: string | null | undefined,
updateFn: (quiz: Quiz) => void,
) => {
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 () => {
requestQueue.enqueue(async () => {
const quiz = useQuizStore.getState().quizes.find(q => q.id === quizId);
if (!quiz) return;
const response = await quizApi.edit(quizToEditQuizRequest(quiz));
setQuizBackendId(quizId, response.updated);
setEditQuizId(response.updated);
}).catch(error => {
if (isAxiosCanceledError(error)) return;
devlog("Error editing quiz", error, quizId);
enqueueSnackbar("Не удалось сохранить настройки квиза");
});
}, REQUEST_DEBOUNCE);
};
export const createQuiz = async (navigate: NavigateFunction) => requestQueue.enqueue(async () => {
try {
const rawQuiz = await quizApi.create();
const quiz = rawQuizToQuiz(rawQuiz);
addQuiz(quiz);
setEditQuizId(quiz.backendId);
navigate("/edit");
await createQuestion(rawQuiz.id);
} catch (error) {
devlog("Error creating quiz", error);
const message = getMessageFromFetchError(error) ?? "";
enqueueSnackbar(`Не удалось создать квиз. ${message}`);
}
});
export const deleteQuiz = async (quizId: string) => requestQueue.enqueue(async () => {
const quiz = useQuizStore.getState().quizes.find(q => q.id === quizId);
if (!quiz) return;
try {
await quizApi.delete(quiz.backendId);
removeQuiz(quizId);
} catch (error) {
devlog("Error deleting quiz", error);
const message = getMessageFromFetchError(error) ?? "";
enqueueSnackbar(`Не удалось удалить квиз. ${message}`);
}
});
// TODO copy quiz
function setProducedState<A extends string | { type: unknown; }>(
recipe: (state: QuizStore) => void,
action?: A,
) {
useQuizStore.setState(state => produce(state, recipe), false, action);
}