import React, { useState, useEffect } from "react"; import { formatTime } from "../../lib/time"; import ScrollToBottom from "./scrollToBottom"; import { Topic } from "./webSocketTypes"; import { generateHighContrastColor } from "../../lib/utils"; import useParticipants from "./useParticipants"; import { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Box, Flex, Text, } from "@chakra-ui/react"; import { featureEnabled } from "../../domainContext"; type TopicListProps = { topics: Topic[]; useActiveTopic: [ Topic | null, React.Dispatch>, ]; autoscroll: boolean; transcriptId: string; status: string; currentTranscriptText: any; }; export function TopicList({ topics, useActiveTopic, autoscroll, transcriptId, status, currentTranscriptText, }: TopicListProps) { const [activeTopic, setActiveTopic] = useActiveTopic; const [autoscrollEnabled, setAutoscrollEnabled] = useState(true); const participants = useParticipants(transcriptId); const scrollToTopic = () => { const topicDiv = document.getElementById( `accordion-button-topic-${activeTopic?.id}`, ); setTimeout(() => { topicDiv?.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest", }); }, 200); }; useEffect(() => { if (activeTopic && autoscroll) scrollToTopic(); }, [activeTopic, autoscroll]); // scroll top is not rounded, heights are, so exact match won't work. // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#determine_if_an_element_has_been_totally_scrolled const toggleScroll = (element) => { const bottom = Math.abs( element.scrollHeight - element.clientHeight - element.scrollTop, ) < 2 || element.scrollHeight == element.clientHeight; if (!bottom && autoscrollEnabled) { setAutoscrollEnabled(false); } else if (bottom && !autoscrollEnabled) { setAutoscrollEnabled(true); } }; const handleScroll = (e) => { toggleScroll(e.target); }; useEffect(() => { if (autoscroll) { const topicsDiv = document.getElementById("scroll-div"); topicsDiv && toggleScroll(topicsDiv); } }, [activeTopic, autoscroll]); useEffect(() => { if (autoscroll && autoscrollEnabled) scrollToBottom(); }, [topics.length, currentTranscriptText]); const scrollToBottom = () => { const topicsDiv = document.getElementById("scroll-div"); if (topicsDiv) topicsDiv.scrollTop = topicsDiv.scrollHeight; }; const getSpeakerName = (speakerNumber: number) => { if (!participants.response) return; return ( participants.response.find( (participant) => participant.speaker == speakerNumber, )?.name || `Speaker ${speakerNumber}` ); }; const requireLogin = featureEnabled("requireLogin"); useEffect(() => { if (autoscroll) { setActiveTopic(topics[topics.length - 1]); } }, [topics, autoscroll]); return ( {autoscroll && ( )} {topics.length > 0 && ( topic.id == activeTopic?.id)} variant="custom" allowToggle > {topics.map((topic, index) => ( { setActiveTopic( activeTopic?.id == topic.id ? null : topic, ); }} > {topic.title}{" "}  [{formatTime(topic.timestamp)}] - [ {formatTime(topic.timestamp + (topic.duration || 0))}] {topic.segments ? ( <> {topic.segments.map((segment, index: number) => ( [{formatTime(segment.start)}] {" "} {getSpeakerName(segment.speaker)}: {" "} {segment.text} ))} ) : ( <>{topic.transcript} )} ))} )} {status == "recording" && ( {currentTranscriptText} )} {(status == "recording" || status == "idle") && currentTranscriptText.length == 0 && topics.length == 0 && ( Full discussion transcript will appear here after you start recording. It may take up to 5 minutes of conversation to first appear. )} {status == "processing" && ( We are processing the recording, please wait. {!requireLogin && ( Please do not navigate away from the page during this time. )} )} {status == "ended" && topics.length == 0 && ( Recording has ended without topics being found. )} {status == "error" && ( There was an error processing your recording )} ); }