frontPanel/src/stores/questions.ts

386 lines
12 KiB
TypeScript
Raw Normal View History

2023-08-10 13:45:44 +00:00
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
2023-10-02 19:43:07 +00:00
import type {
2023-11-15 18:38:02 +00:00
AnyQuizQuestion
2023-10-02 19:43:07 +00:00
} from "../model/questionTypes/shared";
2023-11-15 18:38:02 +00:00
import { QuestionType } from "@model/question/question";
import { produce, setAutoFreeze } from "immer";
2023-10-03 14:03:57 +00:00
import { QUIZ_QUESTION_BASE } from "../constants/base";
import { QUIZ_QUESTION_DATE } from "../constants/date";
import { QUIZ_QUESTION_EMOJI } from "../constants/emoji";
import { QUIZ_QUESTION_FILE } from "../constants/file";
import { QUIZ_QUESTION_IMAGES } from "../constants/images";
import { QUIZ_QUESTION_NUMBER } from "../constants/number";
import { QUIZ_QUESTION_PAGE } from "../constants/page";
import { QUIZ_QUESTION_RATING } from "../constants/rating";
import { QUIZ_QUESTION_SELECT } from "../constants/select";
import { QUIZ_QUESTION_TEXT } from "../constants/text";
import { QUIZ_QUESTION_VARIANT } from "../constants/variant";
import { QUIZ_QUESTION_VARIMG } from "../constants/varimg";
2023-10-13 12:11:00 +00:00
setAutoFreeze(false);
interface QuestionStore {
listQuestions: Record<string, AnyQuizQuestion[]>;
}
2023-09-27 14:14:48 +00:00
let isFirstPartialize = true;
2023-09-20 12:42:14 +00:00
2023-11-20 17:22:13 +00:00
/** @deprecated */
export const questionStore = create<QuestionStore>()(
persist(
devtools(
() => ({
listQuestions: {},
}),
{
name: "Question",
enabled: process.env.NODE_ENV === "development",
trace: process.env.NODE_ENV === "development",
2023-10-23 15:48:27 +00:00
actionsBlacklist: "ignored",
2023-10-16 10:20:07 +00:00
}
),
{
name: "question",
partialize: (state: QuestionStore) => {
if (isFirstPartialize) {
isFirstPartialize = false;
Object.keys(state.listQuestions).forEach((quizId) => {
2023-11-27 23:07:24 +00:00
[...state.listQuestions[quizId]].forEach(({ backendId: id, deleted }) => {
if (deleted) {
const removedItemIndex = state.listQuestions[quizId].findIndex(
2023-11-27 23:07:24 +00:00
(item) => item.backendId === id
);
state.listQuestions[quizId].splice(removedItemIndex, 1);
2023-10-13 12:11:00 +00:00
}
});
});
2023-10-16 10:20:07 +00:00
}
return state;
},
merge: (persistedState, currentState) => {
const state = persistedState as QuestionStore;
// replace blob urls with ""
Object.values(state.listQuestions).forEach(questions => {
questions.forEach(question => {
if (question.type === "page") {
if (question.content.picture.startsWith("blob:")) {
question.content.picture = "";
2023-10-13 12:11:00 +00:00
}
}
if (question.type === "images") {
question.content.variants.forEach(variant => {
if (variant.extendedText.startsWith("blob:")) {
variant.extendedText = "";
}
if (variant.originalImageUrl.startsWith("blob:")) {
variant.originalImageUrl = "";
}
});
}
if (question.type === "varimg") {
question.content.variants.forEach(variant => {
if (variant.extendedText.startsWith("blob:")) {
variant.extendedText = "";
}
if (variant.originalImageUrl.startsWith("blob:")) {
variant.originalImageUrl = "";
}
});
}
});
2023-10-13 12:11:00 +00:00
});
return {
...currentState,
...state,
};
},
}
)
2023-08-10 13:45:44 +00:00
);
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-10-02 19:43:07 +00:00
export const updateQuestionsList = <T = AnyQuizQuestion>(
quizId: number,
index: number,
data: Partial<T>
2023-09-06 11:51:08 +00:00
) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
questionListClone[quizId][index] = {
...questionListClone[quizId][index],
...data,
};
questionStore.setState({ listQuestions: questionListClone });
};
2023-11-20 17:22:13 +00:00
/** @deprecated */
export const updateQuestion = <T extends AnyQuizQuestion>(
quizId: number,
questionIndex: number,
recipe: (question: T) => void,
) => setProducedState(state => {
const question = state.listQuestions[quizId][questionIndex] as T;
recipe(question);
}, {
type: "updateQuestion",
quizId,
questionIndex,
recipe,
});
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-10-23 15:33:56 +00:00
export const removeQuestionsByQuizId = (quizId: number) => setProducedState(state => {
delete state.listQuestions[quizId];
}, "removeQuestionsByQuizId");
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-10-23 15:48:27 +00:00
export const setVariantImageUrl = (
quizId: number,
questionIndex: number,
variantIndex: number,
url: string,
) => setProducedState(state => {
const question = state.listQuestions[quizId][questionIndex];
if (!("variants" in question.content)) return;
const variant = question.content.variants[variantIndex];
if (!("originalImageUrl" in variant)) return;
if (variant.extendedText === url) return;
if (variant.extendedText !== variant.originalImageUrl) URL.revokeObjectURL(variant.extendedText);
variant.extendedText = url;
}, {
type: "setVariantImageUrl",
quizId,
questionIndex,
variantIndex,
url,
});
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-10-23 15:48:27 +00:00
export const setVariantOriginalImageUrl = (
quizId: number,
questionIndex: number,
variantIndex: number,
url: string,
) => setProducedState(state => {
const question = state.listQuestions[quizId][questionIndex];
if (!("variants" in question.content)) return;
const variant = question.content.variants[variantIndex];
if (!("originalImageUrl" in variant)) return;
if (variant.originalImageUrl === url) return;
URL.revokeObjectURL(variant.originalImageUrl);
variant.originalImageUrl = url;
}, {
type: "setVariantOriginalImageUrl",
quizId,
questionIndex,
variantIndex,
url,
});
2023-11-20 17:22:13 +00:00
/** @deprecated */
export const setPageQuestionPicture = (
quizId: number,
questionIndex: number,
url: string,
) => setProducedState(state => {
const question = state.listQuestions[quizId][questionIndex];
if (question.type !== "page") return;
if (question.content.picture === url) return;
if (
question.content.picture !== question.content.originalPicture
) URL.revokeObjectURL(question.content.picture);
question.content.picture = url;
});
2023-11-20 17:22:13 +00:00
/** @deprecated */
export const setPageQuestionOriginalPicture = (
quizId: number,
questionIndex: number,
url: string,
) => setProducedState(state => {
const question = state.listQuestions[quizId][questionIndex];
if (question.type !== "page") return;
if (question.content.originalPicture === url) return;
URL.revokeObjectURL(question.content.originalPicture);
question.content.originalPicture = url;
});
2023-11-20 17:22:13 +00:00
/** @deprecated */
export const setQuestionBackgroundImage = (
quizId: number,
questionIndex: number,
url: string,
) => setProducedState(state => {
const question = state.listQuestions[quizId][questionIndex];
if (question.content.back === url) return;
if (
question.content.back !== question.content.originalBack
) URL.revokeObjectURL(question.content.back);
question.content.back = url;
})
2023-11-20 17:22:13 +00:00
/** @deprecated */
export const setQuestionOriginalBackgroundImage = (
quizId: number,
questionIndex: number,
url: string,
) => setProducedState(state => {
const question = state.listQuestions[quizId][questionIndex];
if (question.content.originalBack === url) return;
URL.revokeObjectURL(question.content.originalBack);
question.content.originalBack = url;
})
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-08-11 06:15:04 +00:00
export const updateQuestionsListDragAndDrop = (
quizId: number,
updatedQuestions: AnyQuizQuestion[]
2023-08-11 06:15:04 +00:00
) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
questionStore.setState({
listQuestions: { ...questionListClone, [quizId]: updatedQuestions },
});
};
2023-10-23 15:48:27 +00:00
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-10-23 15:48:27 +00:00
export const reorderVariants = (
quizId: number,
2023-10-23 15:48:27 +00:00
questionIndex: number,
sourceIndex: number,
destinationIndex: number,
) => setProducedState(state => {
2023-10-23 15:48:27 +00:00
if (sourceIndex === destinationIndex) return;
const question = state.listQuestions[quizId][questionIndex];
if (!("variants" in question.content)) return;
const [removed] = question.content.variants.splice(sourceIndex, 1);
question.content.variants.splice(destinationIndex, 0, removed);
}, {
type: sourceIndex === destinationIndex ? "reorderVariants" : "ignored",
quizId,
questionIndex,
sourceIndex,
destinationIndex,
});
2023-08-18 11:16:56 +00:00
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-10-02 19:43:07 +00:00
export const createQuestion = (
quizId: number,
2023-11-15 18:38:02 +00:00
questionType: QuestionType = "variant",
placeIndex = -1
2023-10-02 19:43:07 +00:00
) => {
const id = getRandom();
const newData = { ...questionStore.getState()["listQuestions"] };
2023-09-06 13:20:12 +00:00
if (!newData[quizId]) {
newData[quizId] = [];
}
const defaultObject = [
QUIZ_QUESTION_BASE,
QUIZ_QUESTION_DATE,
QUIZ_QUESTION_EMOJI,
QUIZ_QUESTION_FILE,
QUIZ_QUESTION_IMAGES,
QUIZ_QUESTION_NUMBER,
QUIZ_QUESTION_PAGE,
QUIZ_QUESTION_RATING,
QUIZ_QUESTION_SELECT,
QUIZ_QUESTION_TEXT,
QUIZ_QUESTION_VARIANT,
QUIZ_QUESTION_VARIMG,
].find((defaultObjectItem) => defaultObjectItem.type === questionType);
2023-09-06 13:20:12 +00:00
if (defaultObject) {
newData[quizId].splice(
placeIndex < 0 ? newData[quizId].length : placeIndex,
0,
2023-11-27 23:07:24 +00:00
{ ...JSON.parse(JSON.stringify(defaultObject)), backendId: id }
);
questionStore.setState({ listQuestions: newData });
}
};
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-09-06 13:20:12 +00:00
export const copyQuestion = (quizId: number, copiedQuestionIndex: number) => {
const listQuestions = { ...questionStore.getState()["listQuestions"] };
2023-08-11 07:25:28 +00:00
2023-10-16 10:20:07 +00:00
const copiedQuiz = { ...listQuestions[quizId][copiedQuestionIndex] };
listQuestions[quizId].splice(copiedQuestionIndex, 0, {
...copiedQuiz,
2023-11-27 23:07:24 +00:00
backendId: getRandom(),
2023-10-16 10:20:07 +00:00
});
2023-08-11 07:25:28 +00:00
questionStore.setState({ listQuestions });
2023-08-11 07:25:28 +00:00
};
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-09-27 14:14:48 +00:00
export const removeQuestionForce = (quizId: number, removedId: number) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
const removedItemIndex = questionListClone[quizId].findIndex(
2023-11-27 23:07:24 +00:00
({ backendId: id }) => id === removedId
);
questionListClone[quizId].splice(removedItemIndex, 1);
questionStore.setState({ listQuestions: questionListClone });
2023-09-27 14:14:48 +00:00
};
2023-09-06 11:51:08 +00:00
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-09-27 14:14:48 +00:00
export const removeQuestion = (quizId: number, index: number) => {
const questionListClone = { ...questionStore.getState()["listQuestions"] };
questionListClone[quizId][index].deleted = true;
questionStore.setState({ listQuestions: questionListClone });
};
2023-11-20 17:22:13 +00:00
/** @deprecated */
2023-09-06 13:20:12 +00:00
export const findQuestionById = (quizId: number) => {
let found = null;
questionStore
.getState()
2023-10-02 19:43:07 +00:00
["listQuestions"][quizId].some((quiz: AnyQuizQuestion, index: number) => {
2023-11-27 23:07:24 +00:00
if (quiz.backendId === quizId) {
found = { quiz, index };
return true;
}
return false;
2023-08-11 06:15:04 +00:00
});
return found;
};
function getRandom() {
const min = Math.ceil(1000000);
const max = Math.floor(10000000);
return Math.floor(Math.random() * (max - min)) + min;
}
function setProducedState<A extends string | { type: unknown; }>(
recipe: (state: QuestionStore) => void,
action?: A,
) {
questionStore.setState(state => produce(state, recipe), false, action);
2023-10-16 10:20:07 +00:00
}