154 lines
4.8 KiB
TypeScript
154 lines
4.8 KiB
TypeScript
import { QuestionVariant } from "@model/questionTypes/shared";
|
|
import { QuizStep } from "@model/settingsData";
|
|
import type { Moment } from "moment";
|
|
import { nanoid } from "nanoid";
|
|
import { createContext, useContext } from "react";
|
|
import { createStore, useStore } from "zustand";
|
|
import { immer } from "zustand/middleware/immer";
|
|
import { devtools } from "zustand/middleware";
|
|
|
|
export type Answer = string | string[] | Moment;
|
|
|
|
export type QuestionAnswer = {
|
|
questionId: string;
|
|
answer: Answer;
|
|
};
|
|
|
|
export type OwnVariant = {
|
|
id: string;
|
|
variant: QuestionVariant;
|
|
};
|
|
|
|
interface QuizViewStore {
|
|
answers: QuestionAnswer[];
|
|
ownVariants: OwnVariant[];
|
|
pointsSum: number;
|
|
points: Record<string, number>;
|
|
currentQuizStep: QuizStep;
|
|
}
|
|
|
|
interface QuizViewActions {
|
|
updateAnswer: (questionId: string, answer: string | string[] | Moment, points: number) => void;
|
|
deleteAnswer: (questionId: string) => void;
|
|
updateOwnVariant: (id: string, answer: string, extendedText?: string, originalImageUrl?: string) => void;
|
|
deleteOwnVariant: (id: string) => void;
|
|
setCurrentQuizStep: (step: QuizStep) => void;
|
|
}
|
|
|
|
export const QuizViewContext = createContext<ReturnType<typeof createQuizViewStore> | null>(null);
|
|
|
|
export function useQuizViewStore<U>(selector: (state: QuizViewStore & QuizViewActions) => U): U {
|
|
const store = useContext(QuizViewContext);
|
|
if (!store) throw new Error("QuizViewStore context is null");
|
|
|
|
return useStore(store, selector);
|
|
}
|
|
|
|
export const createQuizViewStore = () =>
|
|
createStore<QuizViewStore & QuizViewActions>()(
|
|
immer(
|
|
devtools(
|
|
(set, get) => ({
|
|
answers: [],
|
|
ownVariants: [],
|
|
points: {},
|
|
pointsSum: 0,
|
|
currentQuizStep: "startpage",
|
|
updateAnswer(questionId, answer, points) {
|
|
set(
|
|
(state) => {
|
|
const index = state.answers.findIndex((answer) => questionId === answer.questionId);
|
|
|
|
if (index < 0) {
|
|
state.answers.push({ questionId, answer });
|
|
} else {
|
|
state.answers[index] = { questionId, answer };
|
|
}
|
|
|
|
state.points = { ...state.points, ...{ [questionId]: points } };
|
|
|
|
state.pointsSum = Object.values(state.points).reduce((sum, value) => sum + value);
|
|
},
|
|
false,
|
|
{
|
|
type: "updateAnswer",
|
|
questionId,
|
|
answer,
|
|
points,
|
|
}
|
|
);
|
|
},
|
|
deleteAnswer(questionId) {
|
|
set(
|
|
(state) => {
|
|
state.answers = state.answers.filter((answer) => questionId !== answer.questionId);
|
|
},
|
|
false,
|
|
{
|
|
type: "deleteAnswer",
|
|
questionId,
|
|
}
|
|
);
|
|
},
|
|
updateOwnVariant(id, answer, extendedText, originalImageUrl) {
|
|
set(
|
|
(state) => {
|
|
const index = state.ownVariants.findIndex((variant) => variant.id === id);
|
|
|
|
if (index < 0) {
|
|
state.ownVariants.push({
|
|
id,
|
|
variant: {
|
|
id: id,
|
|
answer,
|
|
extendedText: extendedText || "",
|
|
hints: "",
|
|
originalImageUrl: originalImageUrl || "",
|
|
},
|
|
});
|
|
} else {
|
|
state.ownVariants[index].variant.answer = answer;
|
|
if (extendedText) {
|
|
state.ownVariants[index].variant.extendedText = extendedText;
|
|
}
|
|
if (originalImageUrl) {
|
|
state.ownVariants[index].variant.originalImageUrl = originalImageUrl;
|
|
}
|
|
}
|
|
},
|
|
false,
|
|
{
|
|
type: "updateOwnVariant",
|
|
id,
|
|
answer,
|
|
}
|
|
);
|
|
},
|
|
deleteOwnVariant(id) {
|
|
set(
|
|
(state) => {
|
|
state.ownVariants = state.ownVariants.filter((variant) => variant.id !== id);
|
|
},
|
|
false,
|
|
{
|
|
type: "deleteOwnVariant",
|
|
id,
|
|
}
|
|
);
|
|
},
|
|
setCurrentQuizStep(step) {
|
|
set({ currentQuizStep: step }, false, {
|
|
type: "setCurrentQuizStep",
|
|
step,
|
|
});
|
|
},
|
|
}),
|
|
{
|
|
name: "QuizViewStore-" + nanoid(4),
|
|
enabled: import.meta.env.DEV,
|
|
trace: import.meta.env.DEV,
|
|
}
|
|
)
|
|
)
|
|
);
|