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 { ResponsePost } from "@/api/resources/SinglePayment"; import { createSinglePayment } from "@/services/singlePayment"; import Modal from "@/components/Modal"; import Title from "@/components/Title"; import PaymentForm from "@/components/pages/SinglePaymentPage/PaymentForm"; import { getPriceCentsToDollars } from "@/services/price"; import { IMessage } from "@/api/resources/ChatMessages"; const returnUrl = `${window.location.protocol}//${ window.location.host }${routes.client.chatsExpert()}`; function ExpertChat() { 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 [isLoadingLatestMessages, setIsLoadingLatestMessages] = useState(false); 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 [paymentIntent, setPaymentIntent] = useState(null); const [isLoadingPayment, setIsLoadingPayment] = useState(false); const [isError, setIsError] = useState(false); const [currentProduct, setCurrentProduct] = useState( null ); 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: EPlacementKeys["aura.placement.chat"], }); 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 { _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" ); }; return (
{!isLoading && paymentIntent && "paymentIntent" in paymentIntent && !!tokenFromStore.length && currentProduct && ( <> setPaymentIntent(null)} containerClassName={styles.modal} > {getPriceCentsToDollars(currentProduct.price || 0)}$ )} {isLoading && ( )} {!isLoading && ( )} {!isLoading && !!messages.length && (
{messages.map((message) => ( showModals()} /> ))} {isLoadingAdvisorMessage && !isLoadingSelfMessage && ( } isSelf={false} backgroundTextColor={"#c9c9c9"} textColor={"#000"} /> )} {/*
{isLoadingLatestMessages && }
*/}
)} {!messages.length && !isLoading && }
{!isLoading && ( )} {!isLoading && ( <> {isShowRefillCreditsModal && ( setIsShowRefillCreditsModal(false)}> )} {isShowOutOfCreditsModal && ( setIsShowOutOfCreditsModal(false)}> { if (isLoadingPayment) return; setIsShowOutOfCreditsModal(false); setIsShowRefillProductsModal(true); }} /> )} {isShowRefillProductsModal && ( setIsShowRefillProductsModal(false)} > )} )}
); } export default ExpertChat;