import { useNavigate } from "react-router-dom"; import styles from "./styles.module.scss"; import { ChangeEvent, useCallback, useEffect, useRef, useState } from "react"; import Loader, { LoaderColor } from "@/components/Loader"; import ChatHeader from "./components/ChatHeader"; import InputMessage from "./components/InputMessage"; import routes from "@/routes"; import Message from "./components/Message"; import LoaderDots from "./components/LoaderDots"; import StartInfo from "./components/StartInfo"; import RefillCreditsModal from "./components/RefillCreditsModal"; import useChatSocket from "@/hooks/chatsSocket/useChatsSocket"; import { useDispatch, useSelector } from "react-redux"; import { actions, selectors } from "@/store"; import BottomModal from "../../components/BottomModal"; import OutOfCreditsModal from "./components/OutOfCreditsModal"; import RefillProductsModal from "./components/RefillProductsModal"; import { Products, useApi, useApiCall } from "@/api"; import { usePaywall } from "@/hooks/paywall/usePaywall"; import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall"; import { useAuth } from "@/auth"; import { createSinglePayment } from "@/services/singlePayment"; import { IMessage } from "@/api/resources/ChatMessages"; import { useTranslations } from "@/hooks/translations"; import { ELocalesPlacement } from "@/locales"; import { usePayment } from "@/hooks/payment/nmi/usePayment"; const returnUrl = `${window.location.protocol}//${window.location.host }${routes.client.chatsExpert()}`; const placementKey = EPlacementKeys["aura.placement.chat"]; function ExpertChat() { const { translate } = useTranslations(ELocalesPlacement.Chats); const api = useApi(); const dispatch = useDispatch(); const navigate = useNavigate(); const assistant = useSelector(selectors.selectCurrentAssistant); const chatId = useSelector(selectors.selectCurrentChatId); const userId = useSelector(selectors.selectUserId); const [messageText, setMessageText] = useState(""); const [textareaRows, setTextareaRows] = useState(1); const messagesEndRef = useRef(null); const [isShowRefillCreditsModal, setIsShowRefillCreditsModal] = useState(false); const [isShowOutOfCreditsModal, setIsShowOutOfCreditsModal] = useState(false); const [isShowRefillProductsModal, setIsShowRefillProductsModal] = useState(false); const { isLoading, isLoadingSelfMessage, isLoadingAdvisorMessage, messages, isAvailableChatting, messagesAfterEnd, initialBalance, sendMessage, readMessage, } = useChatSocket(userId, chatId); // Payment const { user: userFromStore } = useAuth(); const tokenFromStore = useSelector(selectors.selectToken); const [isLoadingPayment, setIsLoadingPayment] = useState(false); const [isError, setIsError] = useState(false); const currentProduct = useSelector(selectors.selectActiveProduct); const setCurrentProduct = (product: IPaywallProduct) => { dispatch(actions.payment.update({ activeProduct: product })); }; const { error, isPaymentSuccess, showCreditCardForm, } = usePayment({ placementKey, activeProduct: currentProduct!, paymentFormType: "lightbox" }); const isPayedFirstPurchase = useSelector( selectors.selectIsPayedFirstPurchase ); const checkIsPayedFirstPurchase = useCallback(async () => { if (isPayedFirstPurchase) return; const isPayed = await api.checkProductPurchased({ token: tokenFromStore, productKey: "credits.100", }); if (isPayed && "active" in isPayed && isPayed.active) { dispatch(actions.chat.updateIsPayedFirstPurchase(true)); } return isPayed; // eslint-disable-next-line react-hooks/exhaustive-deps }, [ api, dispatch, isPayedFirstPurchase, tokenFromStore, isAvailableChatting, ]); const { data: isPayedFirstPurchaseResponse } = useApiCall< Products.ResponseGet | undefined >(checkIsPayedFirstPurchase); const { products } = usePaywall({ placementKey, }); const scrollToBottom = () => { setTimeout(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, 100); }; useEffect(() => { if (!isLoading && !!messages.length) scrollToBottom(); }, [messages, isLoading]); const handleChangeMessageText = (e: ChangeEvent) => { const { scrollHeight, clientHeight, value } = e.target; if ( scrollHeight > clientHeight && textareaRows < 5 && value.length > messageText.length ) { setTextareaRows((prev) => prev + 1); } if ( scrollHeight === clientHeight && textareaRows > 1 && value.length < messageText.length ) { setTextareaRows((prev) => prev - 1); } setMessageText(e.target.value); }; const getIsSelfMessage = (role: string) => role === "user"; const deleteDataFromMessage = (messageText: string) => { const splittedText = messageText?.split("#"); if (splittedText?.length > 2) { return splittedText?.slice(2).join("#"); } return messageText; }; const endChat = () => { navigate(routes.client.chatsCategories()); }; const handleRefillModalClickButton = () => { createPayment("credits.100"); }; const handleOutOfCreditsModalClickButton = () => { createPayment("credits.80"); }; const handleRefillProductsModalClickButton = (productKey: string) => { createPayment(productKey); }; const handleSendMessage = (messageText: string) => { if (!isAvailableChatting) { showModals(); } setMessageText(""); sendMessage(messageText); }; const closeModals = useCallback(() => { setIsShowRefillProductsModal(false); setIsShowOutOfCreditsModal(false); setIsShowRefillCreditsModal(false); }, []); const createPayment = useCallback( async (productKey: string) => { try { if (!userFromStore || !productKey.length) return; const currentProduct = products?.find((product) => product.key === productKey) || null; if (!currentProduct) return; setCurrentProduct(currentProduct); setIsLoadingPayment(true); const isPaymentMethodExist = await api.getPaymentMethods({ token: tokenFromStore }); if (isPaymentMethodExist.status === "error") { return showCreditCardForm(); } const { _id, key } = currentProduct; const paymentInfo = { productId: _id, key, }; const paymentIntent = await createSinglePayment( userFromStore, paymentInfo, tokenFromStore, userFromStore.email, userFromStore.profile.full_name, userFromStore.profile.birthday, returnUrl, api ); // setPaymentIntent(paymentIntent); if ("payment" in paymentIntent) { if (paymentIntent.payment.status === "paid") return closeModals(); return setIsError(true); } } catch (error) { console.log(error); setIsError(true); } finally { setIsLoadingPayment(false); } }, [api, closeModals, products, tokenFromStore, userFromStore] ); const showModals = useCallback(() => { if (isPayedFirstPurchase) { return setIsShowOutOfCreditsModal(true); } if ( isPayedFirstPurchase || !isPayedFirstPurchaseResponse || !("active" in isPayedFirstPurchaseResponse) || !isPayedFirstPurchaseResponse ) { return setIsShowOutOfCreditsModal(true); } return setIsShowRefillCreditsModal(true); }, [isPayedFirstPurchase, isPayedFirstPurchaseResponse]); useEffect(() => { if (isAvailableChatting && !isLoading) { closeModals(); } }, [closeModals, isAvailableChatting, isLoading]); useEffect(() => { if (initialBalance !== null && !isLoading && !initialBalance) { showModals(); } }, [initialBalance, isLoading, showModals]); const isBlurMessage = (message: IMessage) => { return ( !!messagesAfterEnd.find((item) => item.id === message.id) && message.role === "assistant" ); }; const onPaymentError = () => { return setIsError(true); } const onPaymentSuccess = () => { setIsLoadingPayment(false); return closeModals(); } useEffect(() => { if (error) { onPaymentError(); } }, [error]) useEffect(() => { if (isPaymentSuccess) { onPaymentSuccess(); } }, [isPaymentSuccess]) return (
{isLoading && ( )} {!isLoading && ( )} {!isLoading && !!messages.length && (
{messages.map((message) => ( showModals()} /> ))} {isLoadingAdvisorMessage && !isLoadingSelfMessage && ( } isSelf={false} backgroundTextColor={"#c9c9c9"} textColor={"#000"} /> )}
)} {!messages.length && !isLoading && }
{!isLoading && ( )} {!isLoading && ( <> {isShowRefillCreditsModal && ( setIsShowRefillCreditsModal(false)}> )} {isShowOutOfCreditsModal && ( setIsShowOutOfCreditsModal(false)}> { if (isLoadingPayment) return; setIsShowOutOfCreditsModal(false); setIsShowRefillProductsModal(true); }} /> )} {isShowRefillProductsModal && ( setIsShowRefillProductsModal(false)} > )} )}
); } export default ExpertChat;