import { Box, FormControl, IconButton, InputAdornment, InputBase, SxProps, Theme, Typography, useMediaQuery, useTheme, } from "@mui/material" import { TicketMessage } from "@frontend/kitui" import { addOrUpdateUnauthMessages, useUnauthTicketStore, incrementUnauthMessageApiPage, setUnauthIsPreventAutoscroll, setUnauthSessionData, setIsMessageSending, setUnauthTicketMessageFetchState, } from "@root/stores/unauthTicket" import { enqueueSnackbar } from "notistack" import { useCallback, useEffect, useMemo, useRef, useState } from "react" import ChatMessage from "../ChatMessage" import SendIcon from "../icons/SendIcon" import UserCircleIcon from "./UserCircleIcon" import { throttle } from "@frontend/kitui" import { useTicketMessages, getMessageFromFetchError, useSSESubscription, useEventListener, createTicket, } from "@frontend/kitui" import { sendTicketMessage } from "@root/api/ticket" interface Props { sx?: SxProps; } export default function Chat({ sx }: Props) { const theme = useTheme() const upMd = useMediaQuery(theme.breakpoints.up("md")) const [messageField, setMessageField] = useState("") const sessionData = useUnauthTicketStore((state) => state.sessionData) const messages = useUnauthTicketStore((state) => state.messages) const messageApiPage = useUnauthTicketStore((state) => state.apiPage) const messagesPerPage = useUnauthTicketStore( (state) => state.messagesPerPage ) const isMessageSending = useUnauthTicketStore( (state) => state.isMessageSending ) const isPreventAutoscroll = useUnauthTicketStore( (state) => state.isPreventAutoscroll ) const lastMessageId = useUnauthTicketStore((state) => state.lastMessageId) const fetchState = useUnauthTicketStore( (state) => state.unauthTicketMessageFetchState ) const chatBoxRef = useRef(null) useTicketMessages({ url: process.env.REACT_APP_DOMAIN + "/heruvym/getMessages", isUnauth: true, ticketId: sessionData?.ticketId, messagesPerPage, messageApiPage, onSuccess: useCallback((messages) => { if (chatBoxRef.current && chatBoxRef.current.scrollTop < 1) chatBoxRef.current.scrollTop = 1 addOrUpdateUnauthMessages(messages) }, []), onError: useCallback((error: Error) => { const message = getMessageFromFetchError(error) if (message) enqueueSnackbar(message) }, []), onFetchStateChange: setUnauthTicketMessageFetchState, }) useSSESubscription({ enabled: Boolean(sessionData), url: process.env.REACT_APP_DOMAIN + `/heruvym/ticket?ticket=${sessionData?.ticketId}&s=${sessionData?.sessionId}`, onNewData: addOrUpdateUnauthMessages, onDisconnect: useCallback(() => { setUnauthIsPreventAutoscroll(false) }, []), marker: "ticket", }) const throttledScrollHandler = useMemo( () => throttle(() => { const chatBox = chatBoxRef.current if (!chatBox) return const scrollBottom = chatBox.scrollHeight - chatBox.scrollTop - chatBox.clientHeight const isPreventAutoscroll = scrollBottom > chatBox.clientHeight setUnauthIsPreventAutoscroll(isPreventAutoscroll) if (fetchState !== "idle") return if (chatBox.scrollTop < chatBox.clientHeight) { incrementUnauthMessageApiPage() } }, 200), [fetchState] ) useEventListener("scroll", throttledScrollHandler, chatBoxRef) useEffect( function scrollOnNewMessage() { if (!chatBoxRef.current) return if (!isPreventAutoscroll) { setTimeout(() => { scrollToBottom() }, 50) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [lastMessageId] ) async function handleSendMessage() { if (!messageField || isMessageSending) return if (!sessionData) { setIsMessageSending(true) createTicket({ url: process.env.REACT_APP_DOMAIN + "/heruvym/create", body: { Title: "Unauth title", Message: messageField, }, useToken: false, }) .then((response) => { setUnauthSessionData({ ticketId: response.Ticket, sessionId: response.sess, }) }) .catch((error) => { const errorMessage = getMessageFromFetchError(error) if (errorMessage) enqueueSnackbar(errorMessage) }) .finally(() => { setMessageField("") setIsMessageSending(false) }) } else { setIsMessageSending(true) const [_, sendTicketMessageError] = await sendTicketMessage( sessionData.ticketId, messageField ) if (sendTicketMessageError) { enqueueSnackbar(sendTicketMessageError) } setMessageField("") setIsMessageSending(false) } } function scrollToBottom(behavior?: ScrollBehavior) { if (!chatBoxRef.current) return const chatBox = chatBoxRef.current chatBox.scroll({ left: 0, top: chatBox.scrollHeight, behavior, }) } const handleTextfieldKeyPress: React.KeyboardEventHandler< HTMLInputElement | HTMLTextAreaElement > = (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault() handleSendMessage() } } return ( Мария онлайн-консультант {sessionData && messages.map((message) => ( ))} setMessageField(e.target.value)} endAdornment={ } /> ) }