| "use client"; | |
| import * as React from "react"; | |
| import { LoadingButton } from "@/components/Button/LoadingButton"; | |
| import { setAgentConnected, setMobileActiveTab } from "@/store/reducers/global"; | |
| import { | |
| useAppDispatch, | |
| useAppSelector, | |
| apiPing, | |
| apiStartService, | |
| apiStopService, | |
| MOBILE_ACTIVE_TAB_MAP, | |
| EMobileActiveTab, | |
| isEditModeOn, | |
| } from "@/common"; | |
| import { toast } from "sonner"; | |
| import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; | |
| import { cn } from "@/lib/utils"; | |
| import { RemotePropertyCfgSheet } from "@/components/Chat/ChatCfgPropertySelect"; | |
| import { RemoteGraphSelect } from "@/components/Chat/ChatCfgGraphSelect"; | |
| import { RemoteModuleCfgSheet } from "@/components/Chat/ChatCfgModuleSelect"; | |
| import { TrulienceCfgSheet } from "../Chat/ChatCfgTrulienceSetting"; | |
| let intervalId: NodeJS.Timeout | null = null; | |
| export default function Action(props: { className?: string }) { | |
| const { className } = props; | |
| const dispatch = useAppDispatch(); | |
| const agentConnected = useAppSelector((state) => state.global.agentConnected); | |
| const channel = useAppSelector((state) => state.global.options.channel); | |
| const userId = useAppSelector((state) => state.global.options.userId); | |
| const language = useAppSelector((state) => state.global.language); | |
| const voiceType = useAppSelector((state) => state.global.voiceType); | |
| const graphName = useAppSelector((state) => state.global.selectedGraphId); | |
| const mobileActiveTab = useAppSelector( | |
| (state) => state.global.mobileActiveTab | |
| ); | |
| const [loading, setLoading] = React.useState(false); | |
| React.useEffect(() => { | |
| if (channel) { | |
| checkAgentConnected(); | |
| } | |
| }, [channel]); | |
| const checkAgentConnected = async () => { | |
| const res: any = await apiPing(channel); | |
| if (res?.code == 0) { | |
| dispatch(setAgentConnected(true)); | |
| } | |
| }; | |
| const onClickConnect = async () => { | |
| if (loading) { | |
| return; | |
| } | |
| setLoading(true); | |
| if (agentConnected) { | |
| await apiStopService(channel); | |
| dispatch(setAgentConnected(false)); | |
| toast.success("Agent disconnected"); | |
| stopPing(); | |
| } else { | |
| const res = await apiStartService({ | |
| channel, | |
| userId, | |
| graphName, | |
| language, | |
| voiceType, | |
| }); | |
| const { code, msg } = res || {}; | |
| if (code != 0) { | |
| if (code == "10001") { | |
| toast.error( | |
| "The number of users experiencing the program simultaneously has exceeded the limit. Please try again later." | |
| ); | |
| } else { | |
| toast.error(`code:${code},msg:${msg}`); | |
| } | |
| setLoading(false); | |
| throw new Error(msg); | |
| } | |
| dispatch(setAgentConnected(true)); | |
| toast.success("Agent connected"); | |
| startPing(); | |
| } | |
| setLoading(false); | |
| }; | |
| const startPing = () => { | |
| if (intervalId) { | |
| stopPing(); | |
| } | |
| intervalId = setInterval(() => { | |
| apiPing(channel); | |
| }, 3000); | |
| }; | |
| const stopPing = () => { | |
| if (intervalId) { | |
| clearInterval(intervalId); | |
| intervalId = null; | |
| } | |
| }; | |
| const onChangeMobileActiveTab = (tab: string) => { | |
| dispatch(setMobileActiveTab(tab as EMobileActiveTab)); | |
| }; | |
| return ( | |
| <> | |
| {/* Action Bar */} | |
| <div | |
| className={cn( | |
| "mx-2 mt-2 flex items-center justify-between rounded-t-lg bg-[#181a1d] p-2 md:m-2 md:rounded-lg", | |
| className | |
| )} | |
| > | |
| {/* -- Description Part */} | |
| <div className="hidden md:block"> | |
| <span className="text-sm font-bold"></span> | |
| <span className="ml-2 text-xs text-muted-foreground whitespace-nowrap"> | |
| </span> | |
| </div> | |
| <div className="flex w-full flex-col md:flex-row md:items-center justify-between md:justify-end"> | |
| {/* -- Tabs Section */} | |
| <Tabs | |
| defaultValue={mobileActiveTab} | |
| className="md:hidden w-full md:flex-row" | |
| onValueChange={onChangeMobileActiveTab} | |
| > | |
| <TabsList className="flex justify-center md:justify-start"> | |
| {Object.values(EMobileActiveTab).map((tab) => ( | |
| <TabsTrigger key={tab} value={tab} className="w-24 text-sm"> | |
| {MOBILE_ACTIVE_TAB_MAP[tab]} | |
| </TabsTrigger> | |
| ))} | |
| </TabsList> | |
| </Tabs> | |
| {/* -- Graph Select Part */} | |
| <div className="flex flex-wrap items-center justify-between w-full md:w-auto gap-2 mt-2 md:mt-0"> | |
| <RemoteGraphSelect /> | |
| {isEditModeOn && ( | |
| <> | |
| <TrulienceCfgSheet /> | |
| <RemoteModuleCfgSheet /> | |
| <RemotePropertyCfgSheet /> | |
| </> | |
| )} | |
| {/* -- Action Button */} | |
| <div className="ml-auto flex items-center gap-2"> | |
| <LoadingButton | |
| onClick={onClickConnect} | |
| variant={!agentConnected ? "default" : "destructive"} | |
| size="sm" | |
| disabled={graphName === "" && !agentConnected} | |
| className="w-fit min-w-24" | |
| loading={loading} | |
| svgProps={{ className: "h-4 w-4 text-muted-foreground" }} | |
| > | |
| {loading | |
| ? "Connecting" | |
| : !agentConnected | |
| ? "Connect" | |
| : "Disconnect"} | |
| </LoadingButton> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </> | |
| ); | |
| } | |