frontPanel/src/stores/questions/actions.ts

140 lines
4.1 KiB
TypeScript
Raw Normal View History

2023-11-02 16:45:28 +00:00
import { questionApi } from "@api/question";
import { devlog } from "@frontend/kitui";
2023-11-14 20:15:52 +00:00
import { RawQuestion, rawQuestionToQuestion } from "@model/question/question";
2023-11-02 16:45:28 +00:00
import { produce } from "immer";
import { enqueueSnackbar } from "notistack";
2023-11-14 16:44:27 +00:00
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
import { QuestionsStore, useQuestionsStore } from "./store";
2023-11-14 20:15:52 +00:00
import { questionToEditQuestionRequest } from "@model/question/edit";
import { AnyQuizQuestion } from "@model/questionTypes/shared";
2023-11-02 16:45:28 +00:00
2023-11-14 20:15:52 +00:00
export const setQuestions = (questions: RawQuestion[] | null) => setProducedState(state => {
2023-11-14 16:44:27 +00:00
state.questionsById = {};
2023-11-14 20:15:52 +00:00
if (questions === null) return;
2023-11-02 16:45:28 +00:00
2023-11-14 20:15:52 +00:00
questions.forEach(question => state.questionsById[question.id] = rawQuestionToQuestion(question));
2023-11-14 16:44:27 +00:00
}, {
2023-11-14 20:15:52 +00:00
type: "setQuestions",
questions,
2023-11-14 16:44:27 +00:00
});
2023-11-02 16:45:28 +00:00
2023-11-14 20:15:52 +00:00
export const setQuestion = (question: AnyQuizQuestion) => setProducedState(state => {
2023-11-02 16:45:28 +00:00
state.questionsById[question.id] = question;
}, {
type: "setQuestion",
question,
});
2023-11-14 20:15:52 +00:00
export const setQuestionField = <T extends keyof AnyQuizQuestion>(
2023-11-02 16:45:28 +00:00
questionId: number,
field: T,
2023-11-14 20:15:52 +00:00
value: AnyQuizQuestion[T],
2023-11-02 16:45:28 +00:00
) => setProducedState(state => {
const question = state.questionsById[questionId];
if (!question) return;
question[field] = value;
}, {
type: "setQuestionField",
questionId,
field,
value,
});
2023-11-14 20:15:52 +00:00
let savedOriginalQuestion: AnyQuizQuestion | null = null;
2023-11-02 16:45:28 +00:00
let controller: AbortController | null = null;
2023-11-14 20:15:52 +00:00
export const setQuestionFieldOptimistic = async <T extends keyof AnyQuizQuestion>(
2023-11-02 16:45:28 +00:00
questionId: number,
field: T,
2023-11-14 20:15:52 +00:00
value: AnyQuizQuestion[T],
2023-11-02 16:45:28 +00:00
) => {
const question = useQuestionsStore.getState().questionsById[questionId] ?? null;
if (!question) return;
const currentUpdatedQuestion = produce(question, draft => {
draft[field] = value;
});
controller?.abort();
controller = new AbortController();
savedOriginalQuestion ??= question;
setQuestion(currentUpdatedQuestion);
try {
2023-11-14 20:15:52 +00:00
const { updated } = await questionApi.edit(
questionToEditQuestionRequest(currentUpdatedQuestion),
controller.signal,
);
2023-11-02 16:45:28 +00:00
2023-11-14 20:15:52 +00:00
setQuestionField(question.id, "id", updated);
2023-11-02 16:45:28 +00:00
controller = null;
savedOriginalQuestion = null;
} catch (error) {
if (isAxiosCanceledError(error)) return;
2023-11-13 18:04:51 +00:00
devlog("Error editing question", { error, question, currentUpdatedQuestion });
2023-11-02 16:45:28 +00:00
enqueueSnackbar("Не удалось сохранить вопрос");
if (!savedOriginalQuestion) {
devlog("Cannot rollback question");
throw new Error("Cannot rollback question");
}
setQuestion(savedOriginalQuestion);
controller = null;
savedOriginalQuestion = null;
}
};
export const updateQuestionWithFn = (
questionId: number,
2023-11-14 20:15:52 +00:00
updateFn: (question: AnyQuizQuestion) => void,
2023-11-02 16:45:28 +00:00
) => setProducedState(state => {
const question = state.questionsById[questionId];
if (!question) return;
updateFn(question);
}, {
type: "updateQuestion",
questionId,
updateFn: updateFn.toString(),
});
2023-11-14 16:44:27 +00:00
export const createQuestion = async (quizId: number) => {
2023-11-02 16:45:28 +00:00
try {
2023-11-14 16:44:27 +00:00
const question = await questionApi.create({
quiz_id: quizId,
});
2023-11-02 16:45:28 +00:00
2023-11-14 20:15:52 +00:00
setQuestion(rawQuestionToQuestion(question));
2023-11-02 16:45:28 +00:00
} catch (error) {
devlog("Error creating question", error);
enqueueSnackbar("Не удалось создать вопрос");
}
};
export const deleteQuestion = async (questionId: number) => {
try {
await questionApi.delete(questionId);
removeQuestion(questionId);
} catch (error) {
devlog("Error deleting question", error);
enqueueSnackbar("Не удалось удалить вопрос");
}
};
export const removeQuestion = (questionId: number) => setProducedState(state => {
delete state.questionsById[questionId];
}, {
type: "removeQuestion",
questionId,
});
function setProducedState<A extends string | { type: unknown; }>(
recipe: (state: QuestionsStore) => void,
action?: A,
) {
useQuestionsStore.setState(state => produce(state, recipe), false, action);
}