Files
challenge-pl/src/hooks/useSubmission.ts

172 lines
4.7 KiB
TypeScript

import { useCallback, useEffect, useMemo, useState } from 'react'
import {
useLazyCheckQueueStatusQuery,
useSubmitSolutionMutation,
} from '../__data__/api/api'
import type { ChallengeSubmission, QueueStatus } from '../__data__/types'
import { useChallenge } from '../context/ChallengeContext'
import { loadFinalAnswer, saveFinalAnswer } from '../utils/drafts'
interface UseSubmissionArgs {
taskId: string
}
interface SubmissionResult {
result: string
setResult: (value: string) => void
submit: () => Promise<void>
reset: () => void
queueStatus: QueueStatus | null
finalSubmission: ChallengeSubmission | null
isSubmitting: boolean
}
export const useSubmission = ({ taskId }: UseSubmissionArgs): SubmissionResult => {
const {
userId,
eventEmitter,
pollingManager,
behaviorTracker,
metricsCollector,
saveDraft,
loadDraft,
clearDraft,
} = useChallenge()
const [result, setResultState] = useState('')
const [queueId, setQueueId] = useState<string | null>(null)
const [queueStatus, setQueueStatus] = useState<QueueStatus | null>(null)
const [finalSubmission, setFinalSubmission] = useState<ChallengeSubmission | null>(null)
const [submitSolution, { isLoading: isSubmitting }] = useSubmitSolutionMutation()
const [triggerCheckStatus] = useLazyCheckQueueStatusQuery()
useEffect(() => {
behaviorTracker.reset()
// Сначала проверяем финальный ответ (если задание уже решалось)
const finalAnswer = loadFinalAnswer(taskId)
if (finalAnswer) {
setResultState(finalAnswer)
behaviorTracker.markDraftUsed()
} else {
// Если финального ответа нет, проверяем черновик
const draft = loadDraft(taskId)
if (draft) {
setResultState(draft)
behaviorTracker.markDraftUsed()
} else {
// Если ничего нет - пустое поле
setResultState('')
}
}
pollingManager.stop()
setQueueId(null)
setQueueStatus(null)
setFinalSubmission(null)
}, [behaviorTracker, loadDraft, pollingManager, taskId])
const setResult = useCallback(
(value: string) => {
setResultState(value)
behaviorTracker.onTextChange(value)
saveDraft(taskId, value)
},
[behaviorTracker, saveDraft, taskId],
)
const reset = useCallback(() => {
setResultState('')
setQueueId(null)
setQueueStatus(null)
setFinalSubmission(null)
behaviorTracker.reset()
pollingManager.stop()
clearDraft(taskId)
}, [behaviorTracker, clearDraft, pollingManager, taskId])
const submit = useCallback(async () => {
if (!userId) {
throw new Error('Пользователь не авторизован')
}
if (!result.trim()) {
return
}
pollingManager.stop()
setFinalSubmission(null)
setQueueStatus(null)
const { queueId: newQueueId } = await submitSolution({ userId, taskId, result }).unwrap()
setQueueId(newQueueId)
metricsCollector.startTracking(queueStatus?.position ?? 0)
pollingManager.start(async () => {
const status = await triggerCheckStatus(newQueueId, false).unwrap()
metricsCollector.incrementPoll()
setQueueStatus(status)
if (status.status === 'completed' && status.submission) {
const performanceMetrics = metricsCollector.getMetrics(status.submission)
const behaviorMetrics = behaviorTracker.getMetrics(result)
const eventPayload = {
submission: status.submission,
performanceMetrics,
behaviorMetrics,
}
eventEmitter.emit({
type: 'submission_completed',
timestamp: new Date().toISOString(),
userId,
data: eventPayload,
})
setFinalSubmission(status.submission)
// Сохраняем финальный ответ для восстановления при возврате
saveFinalAnswer(taskId, result)
// Очищаем черновик, так как теперь есть финальный ответ
clearDraft(taskId)
pollingManager.stop()
return false
}
if (status.status === 'error') {
pollingManager.stop()
return false
}
return true
})
}, [
behaviorTracker,
clearDraft,
eventEmitter,
metricsCollector,
pollingManager,
queueStatus?.position,
result,
submitSolution,
taskId,
triggerCheckStatus,
userId,
])
return useMemo(
() => ({
result,
setResult,
submit,
reset,
queueStatus,
finalSubmission,
isSubmitting,
}),
[finalSubmission, isSubmitting, queueStatus, reset, result, setResult, submit],
)
}