import { useState, useEffect, useMemo } from "react"; import { featureEnabled } from "../../domainContext"; import type { components } from "../../reflector-api"; type GetTranscript = components["schemas"]["GetTranscript"]; type GetTranscriptTopic = components["schemas"]["GetTranscriptTopic"]; import { BoxProps, Button, Dialog, CloseButton, Text, Box, Flex, Checkbox, Combobox, Spinner, useFilter, useListCollection, } from "@chakra-ui/react"; import { TbBrandZulip } from "react-icons/tb"; import { useZulipStreams, useZulipTopics, useTranscriptPostToZulip, } from "../../lib/apiHooks"; type ShareZulipProps = { transcriptResponse: GetTranscript; topicsResponse: GetTranscriptTopic[]; disabled: boolean; }; interface Stream { stream_id: number; name: string; } export default function ShareZulip(props: ShareZulipProps & BoxProps) { const [showModal, setShowModal] = useState(false); const [stream, setStream] = useState(undefined); const [selectedStreamId, setSelectedStreamId] = useState(null); const [topic, setTopic] = useState(undefined); const [includeTopics, setIncludeTopics] = useState(false); const { data: streams = [], isLoading: isLoadingStreams } = useZulipStreams(); const { data: topics = [] } = useZulipTopics(selectedStreamId); const postToZulipMutation = useTranscriptPostToZulip(); const { contains } = useFilter({ sensitivity: "base" }); const streamItems = useMemo(() => { return streams.map((stream: Stream) => ({ label: stream.name, value: stream.name, })); }, [streams]); const topicItems = useMemo(() => { return topics.map(({ name }) => ({ label: name, value: name, })); }, [topics]); const { collection: streamItemsCollection, filter: streamItemsFilter } = useListCollection({ initialItems: streamItems, filter: contains, }); const { collection: topicItemsCollection, filter: topicItemsFilter } = useListCollection({ initialItems: topicItems, filter: contains, }); // Update selected stream ID when stream changes useEffect(() => { if (stream && streams) { const selectedStream = streams.find((s: Stream) => s.name === stream); setSelectedStreamId(selectedStream ? selectedStream.stream_id : null); } else { setSelectedStreamId(null); } }, [stream, streams]); const handleSendToZulip = async () => { if (!props.transcriptResponse) return; if (stream && topic) { try { await postToZulipMutation.mutateAsync({ params: { path: { transcript_id: props.transcriptResponse.id, }, query: { stream, topic, include_topics: includeTopics, }, }, }); setShowModal(false); } catch (error) { console.error("Error posting to Zulip:", error); } } }; if (!featureEnabled("sendToZulip")) return null; return ( <> setShowModal(e.open)} size="md" > Send to Zulip {isLoadingStreams ? ( ) : ( <> setIncludeTopics(!!e.checked)} > Include topics # { setTopic(undefined); setStream(e.value[0]); }} onInputValueChange={(e) => streamItemsFilter(e.inputValue) } openOnClick={true} positioning={{ strategy: "fixed", hideWhenDetached: true, }} > No streams found {streamItemsCollection.items.map((item) => ( {item.label} ))} {stream && ( # setTopic(e.value[0])} onInputValueChange={(e) => topicItemsFilter(e.inputValue) } openOnClick selectionBehavior="replace" skipAnimationOnMount={true} closeOnSelect={true} positioning={{ strategy: "fixed", hideWhenDetached: true, }} > No topics found {topicItemsCollection.items.map((item) => ( {item.label} ))} )} )} ); }