frontPanel/src/stores/questions/actions.ts
2023-11-14 23:15:52 +03:00

140 lines
4.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 { questionApi } from "@api/question";
import { devlog } from "@frontend/kitui";
import { RawQuestion, rawQuestionToQuestion } from "@model/question/question";
import { produce } from "immer";
import { enqueueSnackbar } from "notistack";
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
import { QuestionsStore, useQuestionsStore } from "./store";
import { questionToEditQuestionRequest } from "@model/question/edit";
import { AnyQuizQuestion } from "@model/questionTypes/shared";
export const setQuestions = (questions: RawQuestion[] | null) => setProducedState(state => {
state.questionsById = {};
if (questions === null) return;
questions.forEach(question => state.questionsById[question.id] = rawQuestionToQuestion(question));
}, {
type: "setQuestions",
questions,
});
export const setQuestion = (question: AnyQuizQuestion) => setProducedState(state => {
state.questionsById[question.id] = question;
}, {
type: "setQuestion",
question,
});
export const setQuestionField = <T extends keyof AnyQuizQuestion>(
questionId: number,
field: T,
value: AnyQuizQuestion[T],
) => setProducedState(state => {
const question = state.questionsById[questionId];
if (!question) return;
question[field] = value;
}, {
type: "setQuestionField",
questionId,
field,
value,
});
let savedOriginalQuestion: AnyQuizQuestion | null = null;
let controller: AbortController | null = null;
export const setQuestionFieldOptimistic = async <T extends keyof AnyQuizQuestion>(
questionId: number,
field: T,
value: AnyQuizQuestion[T],
) => {
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 {
const { updated } = await questionApi.edit(
questionToEditQuestionRequest(currentUpdatedQuestion),
controller.signal,
);
setQuestionField(question.id, "id", updated);
controller = null;
savedOriginalQuestion = null;
} catch (error) {
if (isAxiosCanceledError(error)) return;
devlog("Error editing question", { error, question, currentUpdatedQuestion });
enqueueSnackbar("Не удалось сохранить вопрос");
if (!savedOriginalQuestion) {
devlog("Cannot rollback question");
throw new Error("Cannot rollback question");
}
setQuestion(savedOriginalQuestion);
controller = null;
savedOriginalQuestion = null;
}
};
export const updateQuestionWithFn = (
questionId: number,
updateFn: (question: AnyQuizQuestion) => void,
) => setProducedState(state => {
const question = state.questionsById[questionId];
if (!question) return;
updateFn(question);
}, {
type: "updateQuestion",
questionId,
updateFn: updateFn.toString(),
});
export const createQuestion = async (quizId: number) => {
try {
const question = await questionApi.create({
quiz_id: quizId,
});
setQuestion(rawQuestionToQuestion(question));
} 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);
}