Files
reflector/www/app/(app)/transcripts/[transcriptId]/processing/page.tsx
Igor Loskutov 499de45fdb fix: processing page reads DAG status from user room WS fallback
The processing page only read dagStatus from the transcript room WS,
which loses events during page navigation and React strict mode
double-mounting (WS torn down and reconnected, historical replay
skips DAG_STATUS). Now also consumes useDagStatusMap() from
UserEventsProvider (user room), which uses a singleton WS that
survives remounts.

Priority: transcript room WS > user room WS > REST API.
2026-02-09 15:09:29 -05:00

134 lines
3.6 KiB
TypeScript

"use client";
import { useEffect, use } from "react";
import {
Heading,
Text,
VStack,
Spinner,
Button,
Center,
} from "@chakra-ui/react";
import { useRouter } from "next/navigation";
import { useTranscriptGet } from "../../../../lib/apiHooks";
import { parseNonEmptyString } from "../../../../lib/utils";
import { useWebSockets } from "../../useWebSockets";
import type { DagTask } from "../../useWebSockets";
import { useDagStatusMap } from "../../../../lib/UserEventsProvider";
import DagProgressTable from "./DagProgressTable";
type TranscriptProcessing = {
params: Promise<{
transcriptId: string;
}>;
};
export default function TranscriptProcessing(details: TranscriptProcessing) {
const params = use(details.params);
const transcriptId = parseNonEmptyString(params.transcriptId);
const router = useRouter();
const transcript = useTranscriptGet(transcriptId);
const { status: wsStatus, dagStatus: wsDagStatus } =
useWebSockets(transcriptId);
const userDagStatusMap = useDagStatusMap();
const userDagStatus = userDagStatusMap.get(transcriptId) ?? null;
const restDagStatus: DagTask[] | null =
((transcript.data as Record<string, unknown>)?.dag_status as
| DagTask[]
| null) ?? null;
// Prefer transcript room WS (most granular), then user room WS, then REST
const dagStatus = wsDagStatus ?? userDagStatus ?? restDagStatus;
useEffect(() => {
const status = wsStatus?.value ?? transcript.data?.status;
if (!status) return;
if (status === "ended" || status === "error") {
router.replace(`/transcripts/${transcriptId}`);
} else if (status === "recording") {
router.replace(`/transcripts/${transcriptId}/record`);
} else if (status === "idle") {
const dest =
transcript.data?.source_kind === "file"
? `/transcripts/${transcriptId}/upload`
: `/transcripts/${transcriptId}/record`;
router.replace(dest);
}
}, [
wsStatus?.value,
transcript.data?.status,
transcript.data?.source_kind,
router,
transcriptId,
]);
if (transcript.isLoading) {
return (
<VStack align="center" py={8}>
<Heading size="lg">Loading transcript...</Heading>
</VStack>
);
}
if (transcript.error) {
return (
<VStack align="center" py={8}>
<Heading size="lg">Transcript not found</Heading>
<Text>We couldn't load this transcript.</Text>
</VStack>
);
}
return (
<>
<VStack
align={"left"}
minH="100vh"
pt={4}
mx="auto"
w={{ base: "full", md: "container.xl" }}
>
<Center h={"full"} w="full">
<VStack
gap={10}
bg="gray.100"
p={10}
borderRadius="md"
maxW="600px"
w="full"
>
{dagStatus ? (
<>
<Heading size={"md"} textAlign="center">
Processing recording
</Heading>
<DagProgressTable tasks={dagStatus} />
</>
) : (
<>
<Spinner size="xl" color="blue.500" />
<Heading size={"md"} textAlign="center">
Processing recording
</Heading>
</>
)}
<Text color="gray.600" textAlign="center">
You can safely return to the library while your recording is being
processed.
</Text>
<Button
onClick={() => {
router.push("/browse");
}}
>
Browse
</Button>
</VStack>
</Center>
</VStack>
</>
);
}