/
nayvok
/
livecoding
Обзор
Документация
Войти
/
nayvok
/
livecoding
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
frontend/src/components/TaskPanel.tsx
97 строк
3 KB
nayvok
fix: harden sessions and WebRTC recovery
17 июн 2026, 22:02
17 июн 2026, 22:02
37b9a92
Код
Авторство
О чём код?
import { useEffect, useRef, useState } from 'react' import { useSession } from '@/context/useSession' import { patchTask } from '@/lib/api' import { Textarea } from '@/components/ui/textarea' import { Card, CardContent, CardHeader, CardTitle, } from '@/components/ui/card' import { ScrollArea } from '@/components/ui/scroll-area' import { toast } from 'sonner' import type { SignalingMessage, TaskUpdateMessage } from '@/types/session' const SAVE_DEBOUNCE_MS = 800 export function TaskPanel() { const { metadata, role, teacherToken, sessionId, subscribeSignaling, } = useSession() const [task, setTask] = useState<string>(metadata.task) const [saving, setSaving] = useState(false) const lastSavedRef = useRef<string>(metadata.task) // Обновления задания. useEffect(() => { if (role === 'teacher') return const unsubscribe = subscribeSignaling('task-update', (msg: SignalingMessage) => { if (msg.type === 'task-update') { const updated = (msg as TaskUpdateMessage).task setTask(updated) lastSavedRef.current = updated } }) return unsubscribe }, [role, subscribeSignaling]) // Сохраняем через backend. useEffect(() => { if (role !== 'teacher' || !teacherToken) return if (task === lastSavedRef.current) return const handle = setTimeout(async () => { setSaving(true) try { await patchTask(sessionId, teacherToken, task) lastSavedRef.current = task } catch (err) { const message = err instanceof Error ? err.message : 'Ошибка сохранения' toast.error(`Не удалось сохранить задание, ${message}`) } finally { setSaving(false) } }, SAVE_DEBOUNCE_MS) return () => clearTimeout(handle) }, [task, role, teacherToken, sessionId]) return ( <Card className="h-full flex flex-col gap-0 rounded-md py-0"> <CardHeader className="border-b px-3 py-2 pb-2!"> <CardTitle className="flex items-center justify-between gap-2 text-xs font-medium uppercase tracking-wider text-muted-foreground"> <span>Задание</span> {role === 'teacher' && saving && ( <span className="text-xs font-normal normal-case tracking-normal text-muted-foreground"> сохранение... </span> )} </CardTitle> </CardHeader> <CardContent className="p-0 flex-1 min-h-0"> {role === 'teacher' ? ( <Textarea value={task} onChange={(e) => setTask(e.target.value)} placeholder="Введите текст задания" className="h-full w-full resize-none border-0 rounded-none focus-visible:ring-0 focus-visible:ring-offset-0" /> ) : ( <ScrollArea className="h-full"> <pre className="whitespace-pre-wrap text-sm p-3 font-sans leading-relaxed"> {task || ( <span className="text-muted-foreground italic"> Преподаватель пока не задал задание. </span> )} </pre> </ScrollArea> )} </CardContent> </Card> ) }