timer
This commit is contained in:
parent
3bb940047c
commit
13de6c7f1c
@ -66,16 +66,20 @@ export function parseQuizData(quizDataResponse: GetQuizDataResponse): Omit<QuizS
|
||||
readyData.questions = items;
|
||||
|
||||
if (quizDataResponse?.settings !== undefined) {
|
||||
const parsedCfg = JSON.parse(quizDataResponse?.settings.cfg);
|
||||
|
||||
readyData.settings = {
|
||||
fp: quizDataResponse.settings.fp,
|
||||
rep: quizDataResponse.settings.rep,
|
||||
name: quizDataResponse.settings.name,
|
||||
cfg: JSON.parse(quizDataResponse?.settings.cfg),
|
||||
cfg: parsedCfg,
|
||||
lim: quizDataResponse.settings.lim,
|
||||
due: quizDataResponse.settings.due,
|
||||
delay: quizDataResponse.settings.delay,
|
||||
pausable: quizDataResponse.settings.pausable,
|
||||
status: quizDataResponse.settings.status,
|
||||
// Автоматически включаем таймер, если time_of_passing > 0
|
||||
questionTimerEnabled: parsedCfg.time_of_passing > 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -54,6 +54,8 @@ export type QuizSettingsConfig = {
|
||||
pausable: boolean;
|
||||
cfg: QuizConfig;
|
||||
status: Status;
|
||||
// Таймер вопросов
|
||||
questionTimerEnabled?: boolean;
|
||||
};
|
||||
|
||||
export type QuizSettings = {
|
||||
@ -65,6 +67,7 @@ export type QuizSettings = {
|
||||
};
|
||||
|
||||
export interface QuizConfig {
|
||||
time_of_passing?: number;
|
||||
isUnSc?: boolean;
|
||||
spec: undefined | true;
|
||||
type: QuizType;
|
||||
|
||||
@ -9,10 +9,19 @@ import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
||||
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
||||
import { useQuestionTimer } from "./useQuestionTimer";
|
||||
|
||||
export function useAIQuiz() {
|
||||
//Получаем инфо о квизе и список вопросов.
|
||||
const { settings, questions, quizId, cnt, quizStep } = useQuizStore();
|
||||
const { settings, questions, quizId, cnt, quizStep, preview } = useQuizStore();
|
||||
|
||||
// Отладочная информация о настройках таймера
|
||||
console.log("🤖 AI Quiz settings:", {
|
||||
questionTimerEnabled: settings.questionTimerEnabled,
|
||||
time_of_passing: settings.cfg.time_of_passing,
|
||||
timerEnabled: Boolean(settings.questionTimerEnabled),
|
||||
timerSeconds: settings.cfg.time_of_passing ?? 0,
|
||||
});
|
||||
|
||||
//Список ответов на вопрос. Мы записываем ответы локально, параллельно отправляя на бек информацию о ответах
|
||||
const answers = useQuizViewStore((state) => state.answers);
|
||||
@ -96,6 +105,19 @@ export function useAIQuiz() {
|
||||
nextQuestion: questions[quizStep - 1],
|
||||
});
|
||||
|
||||
// Таймер авто-перехода между вопросами (AI)
|
||||
useQuestionTimer({
|
||||
enabled: Boolean(settings.questionTimerEnabled),
|
||||
seconds: settings.cfg.time_of_passing ?? 0,
|
||||
quizId,
|
||||
preview,
|
||||
currentQuestion,
|
||||
onNext: () => {
|
||||
console.log("🤖 AI Quiz: Timer triggered moveToNextQuestion");
|
||||
moveToNextQuestion();
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
currentQuestion,
|
||||
currentQuestionStepNumber: null,
|
||||
|
||||
@ -9,10 +9,19 @@ import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
||||
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
||||
import { useQuestionTimer } from "./useQuestionTimer";
|
||||
|
||||
export function useBranchingQuiz() {
|
||||
//Получаем инфо о квизе и список вопросов.
|
||||
const { settings, questions, quizId, cnt } = useQuizStore();
|
||||
const { settings, questions, quizId, cnt, preview } = useQuizStore();
|
||||
|
||||
// Отладочная информация о настройках таймера
|
||||
console.log("🌳 Branching Quiz settings:", {
|
||||
questionTimerEnabled: settings.questionTimerEnabled,
|
||||
time_of_passing: settings.cfg.time_of_passing,
|
||||
timerEnabled: Boolean(settings.questionTimerEnabled),
|
||||
timerSeconds: settings.cfg.time_of_passing ?? 0,
|
||||
});
|
||||
|
||||
//Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page.
|
||||
//За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page
|
||||
@ -166,6 +175,19 @@ export function useBranchingQuiz() {
|
||||
return next;
|
||||
}, [nextQuestionId, findResultPointsLogic, linearQuestionIndex, sortedQuestions, settings.cfg.score]);
|
||||
|
||||
// Таймер авто-перехода между вопросами
|
||||
useQuestionTimer({
|
||||
enabled: Boolean(settings.questionTimerEnabled),
|
||||
seconds: settings.cfg.time_of_passing ?? 0,
|
||||
quizId,
|
||||
preview,
|
||||
currentQuestion,
|
||||
onNext: () => {
|
||||
console.log("🌳 Branching Quiz: Timer triggered moveToNextQuestion");
|
||||
moveToNextQuestion();
|
||||
},
|
||||
});
|
||||
|
||||
//Показать визуалом юзеру результат
|
||||
const showResult = useCallback(() => {
|
||||
if (nextQuestion?.type !== "result") throw new Error("Current question is not result");
|
||||
|
||||
@ -9,10 +9,19 @@ import { useQuizViewStore } from "@stores/quizView";
|
||||
|
||||
import { useVkMetricsGoals } from "@/utils/hooks/metrics/useVkMetricsGoals";
|
||||
import { useYandexMetricsGoals } from "@/utils/hooks/metrics/useYandexMetricsGoals";
|
||||
import { useQuestionTimer } from "./useQuestionTimer";
|
||||
|
||||
export function useLinearQuiz() {
|
||||
//Получаем инфо о квизе и список вопросов.
|
||||
const { settings, questions, quizId, cnt } = useQuizStore();
|
||||
const { settings, questions, quizId, cnt, preview } = useQuizStore();
|
||||
|
||||
// Отладочная информация о настройках таймера
|
||||
console.log("📏 Linear Quiz settings:", {
|
||||
questionTimerEnabled: settings.questionTimerEnabled,
|
||||
time_of_passing: settings.cfg.time_of_passing,
|
||||
timerEnabled: Boolean(settings.questionTimerEnabled),
|
||||
timerSeconds: settings.cfg.time_of_passing ?? 0,
|
||||
});
|
||||
|
||||
//Когда квиз линейный, не ветвящийся, мы идём по вопросам по их порядковому номеру. Это их page.
|
||||
//За корректность page отвечает конструктор квизов. Интересный факт, если в конструкторе удалить из середины вопрос, то случится куча запросов изменения вопросов с изменением этого page
|
||||
@ -166,6 +175,20 @@ export function useLinearQuiz() {
|
||||
return next;
|
||||
}, [nextQuestionId, findResultPointsLogic, linearQuestionIndex, sortedQuestions, settings.cfg.score]);
|
||||
|
||||
// Таймер авто-перехода между вопросами
|
||||
useQuestionTimer({
|
||||
enabled: Boolean(settings.questionTimerEnabled),
|
||||
seconds: settings.cfg.time_of_passing ?? 0,
|
||||
quizId,
|
||||
preview,
|
||||
currentQuestion,
|
||||
onNext: () => {
|
||||
console.log("📏 Linear Quiz: Timer triggered moveToNextQuestion");
|
||||
// Программный переход к следующему вопросу
|
||||
moveToNextQuestion();
|
||||
},
|
||||
});
|
||||
|
||||
//Показать визуалом юзеру результат
|
||||
const showResult = useCallback(() => {
|
||||
if (nextQuestion?.type !== "result") throw new Error("Current question is not result");
|
||||
|
||||
91
lib/utils/hooks/FlowControlLogic/useQuestionTimer.ts
Normal file
91
lib/utils/hooks/FlowControlLogic/useQuestionTimer.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
|
||||
import { useQuizViewStore } from "@/stores/quizView";
|
||||
import { sendQuestionAnswer } from "@/utils/sendQuestionAnswer";
|
||||
|
||||
type Params = {
|
||||
enabled: boolean;
|
||||
seconds: number;
|
||||
quizId: string;
|
||||
preview: boolean;
|
||||
currentQuestion: any; // Using any to avoid tight coupling with question union types
|
||||
onNext: () => void;
|
||||
};
|
||||
|
||||
export function useQuestionTimer({ enabled, seconds, quizId, preview, currentQuestion, onNext }: Params) {
|
||||
const ownVariants = useQuizViewStore((state) => state.ownVariants);
|
||||
const currentQuizStep = useQuizViewStore((state) => state.currentQuizStep);
|
||||
const timeoutRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("🕐 useQuestionTimer useEffect triggered", {
|
||||
enabled,
|
||||
seconds,
|
||||
quizId,
|
||||
preview,
|
||||
currentQuestionId: currentQuestion?.id,
|
||||
currentQuestionType: currentQuestion?.type,
|
||||
currentQuizStep,
|
||||
hasCurrentQuestion: !!currentQuestion,
|
||||
});
|
||||
|
||||
if (!enabled) {
|
||||
console.log("❌ Timer disabled");
|
||||
return;
|
||||
}
|
||||
if (!seconds || seconds <= 0) {
|
||||
console.log("❌ Invalid seconds:", seconds);
|
||||
return;
|
||||
}
|
||||
if (!currentQuestion) {
|
||||
console.log("❌ No current question");
|
||||
return;
|
||||
}
|
||||
if (currentQuizStep !== "question") {
|
||||
console.log("❌ Not on question step:", currentQuizStep);
|
||||
return;
|
||||
}
|
||||
if (currentQuestion.type === "result") {
|
||||
console.log("❌ Question is result type");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("✅ Starting timer for", seconds, "seconds");
|
||||
|
||||
// Сбрасываем предыдущий таймер
|
||||
if (timeoutRef.current) {
|
||||
console.log("🔄 Clearing previous timer");
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
|
||||
timeoutRef.current = window.setTimeout(async () => {
|
||||
console.log("⏰ Timer expired! Auto-advancing to next question");
|
||||
try {
|
||||
if (!preview) {
|
||||
console.log("📤 Sending empty answer for question:", currentQuestion.id);
|
||||
// Отправляем пустую строку в ответе (questionAnswer === undefined)
|
||||
await sendQuestionAnswer(quizId, currentQuestion, undefined, ownVariants);
|
||||
console.log("✅ Empty answer sent successfully");
|
||||
} else {
|
||||
console.log("👀 Preview mode - skipping answer send");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("❌ Error sending empty timed answer", e);
|
||||
enqueueSnackbar("Ошибка при отправке ответа по таймеру");
|
||||
} finally {
|
||||
console.log("➡️ Calling onNext()");
|
||||
onNext();
|
||||
}
|
||||
}, seconds * 1000);
|
||||
|
||||
return () => {
|
||||
console.log("🧹 Cleaning up timer");
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [enabled, seconds, quizId, preview, currentQuestion?.id, currentQuizStep, onNext]);
|
||||
}
|
||||
@ -11,6 +11,7 @@ export async function sendQuestionAnswer(
|
||||
ownVariants: OwnVariant[]
|
||||
) {
|
||||
if (!questionAnswer) {
|
||||
console.log("📤 sendQuestionAnswer: Sending empty answer for question", question.id);
|
||||
return sendAnswer({
|
||||
questionId: question.id,
|
||||
body: "",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user