refactor: отчёт сессии загружается одним hook useSessionReport с повтором 409
This commit is contained in:
parent
93c5f063f3
commit
f6d0653e68
7 changed files with 106 additions and 79 deletions
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { useSessionReport } from "@/shared/api/http";
|
||||
import { type ChannelStatus, callChannel } from "@/shared/api/ws";
|
||||
import { Ambience } from "@/shared/audio/ambience";
|
||||
import { type Capture, startCapture } from "@/shared/audio/capture";
|
||||
|
|
@ -18,7 +19,6 @@ import type {
|
|||
CardBriefing,
|
||||
DDSCode,
|
||||
ServerToTrainee,
|
||||
SessionReport,
|
||||
TimerSnapshot,
|
||||
} from "@/shared/types/generated";
|
||||
|
||||
|
|
@ -48,8 +48,7 @@ export function useCall(sessionId: string | null) {
|
|||
const [checklist, setChecklist] = useState<ChecklistItem[]>([]);
|
||||
const [selfAssessed, setSelfAssessed] = useState(false);
|
||||
const [scoreReady, setScoreReady] = useState(false);
|
||||
const [report, setReport] = useState<SessionReport | null>(null);
|
||||
const [reportError, setReportError] = useState<string | null>(null);
|
||||
const { report, error: reportError } = useSessionReport(sessionId, scoreReady);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const channel = useRef<ReturnType<typeof callChannel> | null>(null);
|
||||
|
|
@ -153,16 +152,6 @@ export function useCall(sessionId: string | null) {
|
|||
break;
|
||||
case "score.ready":
|
||||
setScoreReady(true);
|
||||
setReportError(null);
|
||||
void fetch(`/api/sessions/${sessionId}/report`)
|
||||
.then((response) => {
|
||||
if (response.status === 403) throw new Error("Отчёт недоступен: занятие привязано к другому обучающемуся.");
|
||||
if (response.status === 409) throw new Error("Оценка ещё не готова. Обновите страницу позже.");
|
||||
if (!response.ok) throw new Error(`Не удалось загрузить отчёт (HTTP ${response.status}).`);
|
||||
return response.json();
|
||||
})
|
||||
.then(setReport)
|
||||
.catch((cause: unknown) => setReportError(cause instanceof Error ? cause.message : "Не удалось загрузить отчёт."));
|
||||
break;
|
||||
case "error":
|
||||
setError(event.message);
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@
|
|||
// и он должен показать текущее состояние, а не ждать событий. Отправлять в этот
|
||||
// канал нечего и нельзя: тип `Out` — `never` (docs/arch/CONTRACT.md).
|
||||
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { type CardState, applyState, empty as emptyCard } from "@/features/kio-card/merge";
|
||||
import { useSessionReport } from "@/shared/api/http";
|
||||
import { observeChannel, type ChannelStatus } from "@/shared/api/ws";
|
||||
import type {
|
||||
SessionMode,
|
||||
|
|
@ -43,7 +45,12 @@ export function useObserve(sessionId: string | null): ObservedSession {
|
|||
const [hintsUsed, setHintsUsed] = useState(0);
|
||||
const [notes, setNotes] = useState<Map<string, string>>(new Map());
|
||||
const [ended, setEnded] = useState(false);
|
||||
const [report, setReport] = useState<SessionReport | null>(null);
|
||||
// Сессия, для которой отчёт уже стоит просить. Привязка к id, а не флаг:
|
||||
// при переключении истории старый флаг не должен дёрнуть запрос новой сессии.
|
||||
const [reportFor, setReportFor] = useState<string | null>(null);
|
||||
// Ошибку отчёта наблюдение не показывает: пока оценки нет, это не ошибка.
|
||||
const { report } = useSessionReport(sessionId, reportFor === sessionId);
|
||||
const queries = useQueryClient();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -58,7 +65,6 @@ export function useObserve(sessionId: string | null): ObservedSession {
|
|||
setHintsUsed(0);
|
||||
setNotes(new Map());
|
||||
setEnded(false);
|
||||
setReport(null);
|
||||
setError(null);
|
||||
if (!sessionId) return;
|
||||
let active = true;
|
||||
|
|
@ -66,23 +72,7 @@ export function useObserve(sessionId: string | null): ObservedSession {
|
|||
// could only return score_not_ready, competing with the snapshot under load.
|
||||
// Ask for the report only after completion or when no live session exists
|
||||
// (e.g. history opened after a backend restart).
|
||||
let reportLoading = false;
|
||||
let reportLoaded = false;
|
||||
const loadReport = () => {
|
||||
if (reportLoading || reportLoaded) return;
|
||||
reportLoading = true;
|
||||
void fetch(`/api/sessions/${sessionId}/report`, { credentials: "same-origin" })
|
||||
.then((response) => (response.ok ? response.json() : null))
|
||||
.then((saved) => {
|
||||
if (!active) return;
|
||||
if (saved) {
|
||||
reportLoaded = true;
|
||||
setReport(saved as SessionReport);
|
||||
}
|
||||
})
|
||||
.catch(() => undefined)
|
||||
.finally(() => { reportLoading = false; });
|
||||
};
|
||||
const loadReport = () => setReportFor(sessionId);
|
||||
const channel = observeChannel(sessionId, {
|
||||
onStatus: (next) => { if (active) setStatus(next); },
|
||||
onEvent: (event) => {
|
||||
|
|
@ -128,12 +118,14 @@ export function useObserve(sessionId: string | null): ObservedSession {
|
|||
break;
|
||||
case "score.ready":
|
||||
loadReport();
|
||||
// После session.ended hook мог исчерпать повторы 409 до готовности оценки.
|
||||
void queries.invalidateQueries({ queryKey: ["session-report", sessionId] });
|
||||
break;
|
||||
}
|
||||
},
|
||||
}).connect();
|
||||
return () => { active = false; channel.close(); };
|
||||
}, [sessionId]);
|
||||
}, [sessionId, queries]);
|
||||
|
||||
return { status, title, mode, trainee, card, requiredFields, transcript, timers, hintsUsed, notes, ended, report, error };
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue