UNPKG

amworkchat

Version:

Customizable React chat component for workplace communication

1 lines 213 kB
{"version":3,"file":"index.modern.mjs","sources":["../src/network.js","../src/ChatProvider.js","../src/AuthContext.js","../src/components/VideoPlayer.jsx","../src/components/AudioPlayer.jsx","../src/components/LinkHighlighter.jsx","../src/components/BeautifiedCodeViewer.jsx","../src/components/PreviewDialog.jsx","../src/components/MapCard.jsx","../src/ChatWindows.js","../src/Workchat.js","../src/global.js"],"sourcesContent":["import axios from \"axios\";\n\nexport const AXIOS = axios;\n\n// Create the custom Axios instance\nconst axiosInstance = axios.create({\n baseURL: \"https://chatapi.nte.ai/v3\",\n // baseURL: \"https://chatstagingapi.nte.ai\",\n\n headers: {\n 'Content-Type': 'application/json',\n },\n});\n\n// Add a request interceptor to attach the auth token\naxiosInstance.interceptors.request.use(\n (config) => {\n const token = localStorage.getItem('authToken')\n console.log('tokentoken', token)\n\n // const token = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZDEiOiI2ODEzMGEyNjdjMDY4NWY1ZTg3OGI4NTQiLCJpZDIiOiI2MTEzOGQ1YS1lMGUxLTcwODMtYmFkNC1jYWZmNjI1MTg1M2QiLCJpZDMiOiJhbm9vcG1vaGFuQHRhYnRyZWUuaW4iLCJpZDQiOiI2ODExZjEwMDQ4YjFiZGQ2MDcyMzE4ODUiLCJpYXQiOjE3NDcwNDcxMzh9.VO-ZQZlYWLQhzGqwHoljzbey9LbK2onjDHkDKJ6nlTE\";\n\n if (token) {\n config.headers.Authorization = token;\n }\n return config;\n },\n (error) => {\n return Promise.reject(error);\n }\n);\n\nexport default axiosInstance;\n","import React, { useState, useEffect, useRef, createContext, useContext, useCallback, useMemo } from 'react';\nimport { MessageSquare, Send, X, Phone, Video, Minimize, Maximize, ChevronUp, MessageSquareText, Settings, UserRound } from 'lucide-react';\nimport axiosInstance from './network';\nimport moment from 'moment';\nimport { io } from \"socket.io-client\";\n\n// Create a context for the chat state\nconst ChatContext = createContext();\n\n// Custom hook to use the chat context\nexport const useChatContext = () => useContext(ChatContext);\n\n\n// Chat Provider Component\nexport const ChatProvider = ({ children }) => {\n const [activeChats, setActiveChats] = useState([]);\n const [activeChatsIds, setActiveChatsIds] = useState({});\n const [activeChatsMsg, setActiveChatsMsg] = useState({});\n\n // console.log('activeChatsactiveChatsactiveChats', activeChats)\n\n const [minimizedChats, setMinimizedChats] = useState([]);\n\n\n\n\n const [minimizedContact, setMinimizedContact] = useState(true);\n const [newMessage, setNewMessage] = useState({});\n const [searchContactVal, setSearchContactVal] = useState(\"\");\n const [searchVal, setSearchVal] = useState(\"\");\n\n\n const textareaRef = useRef({});\n\n const messageEndRef = useRef({});\n // const MAX_CHATS = 3;\n const [activeTab, setActiveTab] = useState('chat');\n const [maxChat, setMaxChat] = useState(3)\n\n // State to hold the chat user list\n const [userList, setUserList] = useState([]);\n // State to hold the chat user list\n const [contactList, setContactList] = useState([]);\n const [ContactListSearch, setContactListSearch] = useState([]);\n const [chatListSearch, setChatListSearch] = useState([]);\n const [chatGroupMembers, setChatGroupMembers] = useState([])\n\n // State messages list\n const [messages, setMessages] = useState([])\n\n // state message input\n const [message, setMessage] = useState('')\n // state for chatid\n\n const [chatId, setChatId] = useState('')\n const [chats, setChats] = useState({})\n const [toUser, setToUser] = useState(\"\")\n const [socket, setSocket] = useState(null);\n const [messageAlert, setMessageAlert] = useState(false)\n const [messageWorkchatAlert, setMessageWorkchatAlert] = useState(false)\n const [messageWorkchatUserAlert, setMessageWorkchatUserAlert] = useState(false)\n const [updateUserListNotification, setUpdateUserListNotification] = useState(false)\n\n\n const activeChatRef = useRef(activeChats);\n const userChatRef = useRef(userList);\n const messageAlertRef = useRef(messageAlert);\n\n\n\n\n // FileUpload\n\n const [fileToUpload, setFileToUpload] = useState(null);\n const [showPreviewDialog, setShowPreviewDialog] = useState(false);\n const fileInputRef = useRef(null);\n const dropAreaRef = useRef(null);\n const [fileUrl, setFileUrl] = useState(\"\")\n const [isDragging, setIsDragging] = useState(false);\n const [activeGroupChat, setActiveGroupChat] = useState(\"\")\n const [activeBotChat, setActiveBotChat] = useState(\"\")\n const [activeUserChat, setActiveUserChat] = useState(\"\")\n\n\n // reply message\n\n const [replyMessageData, setReplyMessageData] = useState('')\n const messagesContainerRef = useRef(null);\n\n const [highlightReply, setHighlightReply] = useState('')\n\n\n // menton states\n const groupInputRef = useRef(null);\n const modalRef = useRef(null);\n\n\n const [showModal, setShowModal] = useState(false);\n const [modalPosition, setModalPosition] = useState({ top: 0, left: 0, width: 0 });\n const [mentionQuery, setMentionQuery] = useState('');\n const [mentionedUsers, setMentionedUsers] = useState([]);\n const filteredMentionGroupMembers = chatGroupMembers.filter(contact =>\n contact.userId.name.toLowerCase().includes(mentionQuery.toLowerCase())\n );\n // Cancel file upload\n const handleCancelFileUpload = () => {\n setFileToUpload(null);\n setShowPreviewDialog(false);\n fileInputRef.current.value = '';\n\n setNewMessage(prev => ({ ...prev, [toUser]: \"\" }));\n\n };\n\n // Trigger file input click\n const handleAttachmentClick = (id) => {\n setToUser(id)\n fileInputRef.current.click();\n };\n\n // Check if file is an image\n const isImageFile = (file) => {\n return file && file.type.startsWith('image/');\n };\n\n // Format file size\n const formatFileSize = (bytes) => {\n if (bytes < 1024) return bytes + ' B';\n else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';\n else return (bytes / 1048576).toFixed(1) + ' MB';\n };\n\n\n // FILE UPLOAD FUNCTIONS\n\n const handleDragOver = useCallback((e) => {\n e.preventDefault();\n e.stopPropagation();\n setIsDragging(true);\n }, []);\n\n const handleDragEnter = useCallback((e) => {\n e.preventDefault();\n e.stopPropagation();\n setIsDragging(true);\n }, []);\n\n const handleDragLeave = useCallback((e) => {\n e.preventDefault();\n e.stopPropagation();\n setIsDragging(false);\n }, []);\n\n const handleDrop = useCallback((e) => {\n e.preventDefault();\n e.stopPropagation();\n setIsDragging(false);\n\n if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n const file = e.dataTransfer.files[0];\n setFileToUpload(file);\n setShowPreviewDialog(true);\n }\n }, []);\n\n\n const handleFileSelect = (e) => {\n if (e.target.files.length > 0) {\n const file = e.target.files[0];\n setFileToUpload(file);\n setShowPreviewDialog(true);\n }\n };\n\n\n const fetchUploadFileApi = async (file) => {\n\n\n try {\n var formData = new FormData();\n formData.append(\"files\", fileToUpload);\n // const response = axiosInstance.post('chatFile/fileUpload', payload)\n const response = await axiosInstance.post(\n `chatFile/fileUpload`,\n formData,\n {\n headers: {\n 'Content-Type': 'multipart/form-data',\n },\n }\n );\n\n if (response.data.status === 'success') {\n // setMessage('A')\n console.log('fetchUploadFileApi', response.data)\n setFileUrl(response.data.url)\n setNewMessage(prev => ({ ...prev, [toUser]: fileToUpload.name, }));\n\n //\n\n // setMessage(\"F\")\n // setTimeout(() => {\n // setFileUrl(response.data.url)\n // }, 500);\n\n\n\n }\n\n\n\n\n\n } catch (error) {\n\n }\n\n }\n\n\n\n\n\n useEffect(() => {\n activeChatRef.current = activeChats;\n }, [activeChats]);\n useEffect(() => {\n userChatRef.current = userList;\n if (userList.length > 0) {\n localStorage.setItem('userListStore', JSON.stringify(userList));\n }\n }, [userList]);\n useEffect(() => {\n messageAlertRef.current = messageAlert;\n }, [messageAlert]);\n const handleSwitchTab = (event) => {\n setActiveTab(event);\n if (event === \"contact\") {\n fetchContacts()\n\n } else {\n fetchChatUserList()\n }\n };\n\n // Fetches the full user list\n const fetchChatUserList = async () => {\n // setIsLoading(true)\n try {\n const response = await axiosInstance.get(\"chatUser/userList\");\n\n if (response.data.status === \"success\") {\n setUserList(response.data.response);\n setChatListSearch(response.data.response);\n }\n } catch (error) {\n console.error(\"fetchChatUserList error:\", error);\n } finally {\n // setIsLoading(false)\n }\n };\n\n\n // Fetches the user list based on the search input\n\n const fetchSearchChatUserList = async () => {\n const trimmedVal = searchVal.trim();\n\n if (trimmedVal.length === 0) {\n fetchChatUserList();\n return;\n }\n\n try {\n const response = await axiosInstance.post(\"chatUser/search\", {\n name: trimmedVal,\n });\n\n console.log(\"fetchSearchChatUserList\", response.data);\n\n if (response.data.status === \"success\") {\n setUserList(response.data.userList || []);\n }\n } catch (error) {\n console.error(\"fetchSearchChatUserList error:\", error);\n }\n };\n\n // Fetches all contacts list\n\n const fetchContacts = async () => {\n try {\n const response = await axiosInstance.get(`chatUser/contactList`)\n\n console.log('fetchContacts', response.data)\n if (response.data.status === 'success') {\n setContactList(response.data.userData)\n setContactListSearch(response.data.userData)\n }\n\n } catch (error) {\n console.log('fetchContacts', error)\n\n } finally {\n // setIsLoading(false)\n }\n }\n\n\n\n const processChatHistory = (chatHistory, chatUserId, authuserId) => {\n const modifychat = chatHistory.map((msg) => {\n return {\n _id: msg._id,\n sender: msg.fromUserId._id !== authuserId ? 'reciver' : 'me',\n text: msg.messageContent,\n isRead: msg.isRead,\n user: msg.toUserId._id !== authuserId ? msg?.toUserId : msg?.fromUserId,\n time: moment(msg?.updatedAt).format('ddd hh:mm A'),\n type: msg?.messageType,\n link: msg?.fileUrl || \"\",\n fileType: msg?.fileType || \"\",\n createdAt: msg?.createdAt\n };\n });\n\n const grouped = {};\n modifychat.forEach((msg) => {\n const dateKey = moment(msg.createdAt).format(\"YYYY-MM-DD\");\n if (!grouped[dateKey]) {\n grouped[dateKey] = [];\n }\n grouped[dateKey].push(msg);\n });\n\n const data = Object.keys(grouped)\n .sort((a, b) => moment(b).diff(moment(a)))\n .map((date) => {\n const mDate = moment(date);\n let label = mDate.isSame(moment(), \"day\")\n ? \"Today\"\n : mDate.isSame(moment().subtract(1, \"day\"), \"day\")\n ? \"Yesterday\"\n : mDate.format(\"DD MMM YYYY\");\n\n return {\n date: label,\n chats: grouped[date],\n chatUserId: chatUserId\n };\n });\n\n return data;\n };\n\n\n\n\n\n // Fetch chat history \n const fetchChatHistory = async (val) => {\n try {\n\n let dada = {\n toUser: val\n }\n\n const response = await axiosInstance.post(\"chatUser/chatHistory\", dada);\n\n console.log('fetchChatHistory', response.data)\n\n if (response.data.status === \"success\") {\n setMessages(response.data.chatHistory)\n\n // to here change his as sepreate function\n if (response.data.chatUserId != \"\") {\n // setChatId(response.data.chatUserId)\n setActiveChatsIds(prev => {\n const existing = prev[val] || [];\n\n // Avoid adding duplicate\n if (existing.includes(response.data.chatUserId)) {\n return prev;\n }\n\n return {\n ...prev,\n [val]: [...existing, response.data.chatUserId],\n };\n });\n // fetchChatMedia(response.data.chatUserId)\n }\n }\n } catch (error) {\n console.log('fetchChatHistory', error)\n }\n }\n\n // Fetch group chat history \n const fetchGroupChatHistory = async (val) => {\n try {\n\n\n\n const response = await axiosInstance.get(`/group/history/${val.chatUserId}`);\n\n\n if (response.data.status === \"success\") {\n setActiveChatsIds(val.chatUserId)\n setMessages(response.data.chatHistory)\n\n // if (response.data.chatUserId != \"\") {\n // setChatId(response.data.chatUserId)\n // }\n }\n } catch (error) {\n console.log('fetchGroupChatHistory', error)\n }\n }\n\n // Fetch chat Group info\n const fetchChatGroupInfo = async (val) => {\n try {\n\n const response = await axiosInstance.get(`/group/groupInfo/${val}`);\n\n console.log('fetchChatGroupInfo', response.data)\n\n if (response.data.status === \"success\") {\n\n setChatGroupMembers(response.data.groupInfo.userList)\n\n } else {\n setChatGroupMembers([])\n }\n\n } catch (error) {\n console.log('fetchChatGroupInfo', error)\n }\n }\n\n const fetchBotChatHistory = async (val) => {\n try {\n\n\n\n const response = await axiosInstance.get(`/bot/chatHistory/${val.chatUserId}`);\n\n console.log('fetchBotChatHistory', response.data)\n\n if (response.data.status === \"success\") {\n setMessages(response.data.chatList)\n\n }\n } catch (error) {\n console.log('fetchBotChatHistory', error)\n }\n }\n\n\n useEffect(() => {\n const authuserId = localStorage.getItem(\"authUser\");\n\n\n if (toUser === \"\") return\n\n\n let uu = activeChats.includes(toUser)\n const found = activeChats.find(id => id === toUser) || toUser\n\n\n const modifychat = messages.map((msg) => {\n const senderId = msg.fromUserId?._id || msg?.userId?._id;\n const isSender = senderId === authuserId;\n const user =\n msg.toUserId?._id !== authuserId ? msg.toUserId : msg.fromUserId;\n\n if (activeUserChat.type === \"user\") {\n return {\n _id: msg._id,\n sender: isSender ? \"me\" : \"reciver\",\n text: msg.messageContent,\n isRead: msg.isRead,\n user,\n time: moment(msg?.updatedAt).format(\"ddd hh:mm A\"),\n type: msg?.messageType,\n link: msg?.fileUrl || \"\",\n fileType: msg?.fileType || \"\",\n createdAt: msg?.createdAt,\n isForwarded: msg.isForwarded,\n isDeleted: msg.isDeleted,\n isStared: msg.isStared,\n replyMessageId: msg.replyMessageId || \"\"\n };\n } else if (activeBotChat.type === \"bot\") {\n return {\n\n _id: msg._id,\n // groupId: msg.groupId,\n sender: isSender ? \"me\" : \"reciver\",\n text: msg.messageContent,\n // isRead: msg.isRead,\n // user,\n time: moment(msg?.updatedAt).format(\"ddd hh:mm A\"),\n type: msg?.messageType,\n link: msg?.fileUrl || \"\",\n fileType: msg?.fileType || \"\",\n createdAt: msg?.createdAt,\n // name: msg?.userId?.name,\n ctaInfo: msg?.ctaInfo || \"\",\n ctaData: msg.ctaData\n };\n } else {\n return {\n _id: msg._id,\n sender: isSender ? \"me\" : \"reciver\",\n text: msg.messageContent,\n isRead: msg.isRead,\n user: msg.userId,\n time: moment(msg?.updatedAt).format(\"ddd hh:mm A\"),\n type: msg?.messageType,\n link: msg?.fileUrl || \"\",\n fileType: msg?.fileType || \"\",\n createdAt: msg?.createdAt,\n name: msg?.userId?.name,\n isForwarded: msg.isForwarded,\n isDeleted: msg.isDeleted,\n isStared: msg.isStared,\n replyMessageId: msg.replyMessageId || \"\"\n\n }\n }\n });\n console.log('modifychat', modifychat);\n\n\n const grouped = {};\n\n\n\n modifychat.forEach((msg) => {\n const dateKey = moment(msg.createdAt).format(\"YYYY-MM-DD\"); // for grouping\n if (!grouped[dateKey]) {\n grouped[dateKey] = [];\n }\n grouped[dateKey].push(msg);\n });\n\n // Build the final array with label\n const data = Object.keys(grouped)\n .sort((a, b) => moment(b).diff(moment(a))) // Optional: latest date first\n .map((date) => {\n const mDate = moment(date);\n let label = mDate.isSame(moment(), \"day\")\n ? \"Today\"\n : mDate.isSame(moment().subtract(1, \"day\"), \"day\")\n ? \"Yesterday\"\n : mDate.format(\"DD MMM YYYY\");\n\n return {\n date: label,\n chats: grouped[date],\n chatUserId: activeChatsIds[toUser] //|| activeChatsIds[toUser][0]\n };\n });\n\n\n if (activeChats.length >= maxChat) {\n if (!activeChats.includes(found)) {\n const newChats = [...activeChats];\n const oldestChat = newChats.shift(); // Remove the oldest chat (first in array)\n newChats.push(found); // Add the new chat\n setActiveChats(newChats);\n }\n setChats(prev => ({ ...prev, [found]: data }));\n } else {\n // Otherwise just add the new chat\n\n setActiveChats(prev => (\n prev.includes(found) ? prev : [...prev, found]\n ));\n\n\n setChats(prev => ({ ...prev, [found]: data }));\n\n // console.log('[found]: data', found, { [found]: data })\n }\n\n // Focus on the new chat input after it's rendered\n setTimeout(() => {\n const inputElement = document.getElementById(`chat-input-${found}`);\n if (inputElement) {\n inputElement.focus();\n }\n }, 100);\n }, [messages, activeBotChat, activeUserChat])\n\n\n function moveObject(array, id, newIndex) {\n // console.log('array', array);\n // console.log(' id, ', id);\n // console.log(' newIndex', newIndex);\n\n if (!Array.isArray(array)) {\n console.error(\"Expected an array, but got:\", array);\n return [];\n }\n\n\n const elementIndex = array.findIndex((element) => element._id == id);\n\n if (elementIndex === -1) {\n return array;\n }\n\n const element = array.splice(elementIndex, 1)[0];\n array.splice(newIndex, 0, element);\n\n return array;\n }\n\n // useMemo(() => {\n // try {\n // Notification.requestPermission()\n // } catch (error) {\n // console.error(\"Notification.requestPermission()\")\n // }\n // }, [])\n\n useEffect(() => {\n const token = localStorage.getItem('authToken');\n const authuserId = localStorage.getItem(\"authUser\");\n\n if (!token) return;\n\n const apiUrl = `https://chatapi.nte.ai/chat`;\n // const apiUrl = `https://chatstagingapi.nte.ai/chat`;\n // const apiUrl = `http://172.16.5.25:3005/chat`;\n\n\n console.log('apiUrl', apiUrl)\n if (!apiUrl) {\n console.error(\"REACT_APP_API_URL not defined in environment\");\n return;\n }\n\n const newSocket = io(apiUrl, {\n transports: ['websocket'],\n auth: { token },\n // autoConnect: true,\n // reconnection: true,\n // reconnectionAttempts: 5,\n // reconnectionDelay: 1000\n });\n\n newSocket.on('connect', () => {\n console.log('Connected to socket server with ID:', newSocket.id);\n\n\n\n });\n\n\n newSocket.on('disconnect', (reason) => {\n console.log('Socket disconnected:', reason);\n // setSocketConnected(false);\n });\n\n newSocket.on('receiveMessage', (data) => {\n const authuserId = localStorage.getItem(\"authUser\");\n console.log('Received message:', data);\n\n // showNotification(data)\n\n if (!activeChatRef.current.includes(data.fromUserId._id)) {\n setMessageWorkchatAlert(true)\n }\n\n\n // Ensure it's an array before calling moveObject\n if (Array.isArray(userChatRef.current)) {\n const newArray = moveObject([...userChatRef.current], data.fromUserId._id, 0); // shallow copy for immutability\n\n\n\n const newData = newArray.map(obj =>\n obj._id == data.fromUserId._id ? { ...obj, isDeleted: false, messageContent: data.messageContent, messageId: data.messageId, createdAt: data.createdAt, msg: \"New\", unReadCount: activeChatRef.current.includes(data.fromUserId._id) ? 0 : (obj.unReadCount < 0 ? 1 : obj.unReadCount + 1) } : obj\n );\n // console.log('newDatanewDatanewData', newData)\n userChatRef.current = newData;\n\n setUserList(newData);\n } else {\n console.error(\"userChatRef.current is not an array:\", userChatRef.current);\n }\n setMessageAlert(false)\n\n // console.log('activeChatRef.current', activeChatRef.current.includes(data.fromUserId._id), activeChatRef.current, data.fromUserId._id, data.toUserId._id)\n if (!activeChatRef.current.includes(data.fromUserId._id)) return\n\n messageAlertRef.current = true;\n setMessageAlert(true)\n setMessages((prevMessages) => [data, ...prevMessages]);\n\n newSocket.emit(\"readMessage\", { user_id: data.fromUserId._id })\n\n\n\n });\n\n newSocket.on('redMessage', (payload) => {\n // console.log('redMessage received:', payload, activeChatRef.current);\n // alert(payload)\n\n setMessages((prevMessages) =>\n prevMessages.map((msg) =>\n activeChatRef.current.includes(payload) ? { ...msg, isRead: true } : msg\n )\n );\n\n\n\n });\n\n\n\n newSocket.on('ownMessage', (data,) => {\n\n console.log('ownMessage', data)\n\n\n\n if (Array.isArray(userChatRef.current)) {\n const newArray = moveObject([...userChatRef.current], data.toUserId._id, 0); // shallow copy for immutability\n\n\n\n const newData = newArray.map(obj =>\n obj._id == data.toUserId._id ? { ...obj, messageContent: data.messageContent, messageId: data.messageId, createdAt: data.createdAt, msg: \"Read\", isDeleted: false, } : obj\n );\n // console.log('newDatanewDatanewData', newData)\n userChatRef.current = newData;\n\n setUserList(newData);\n } else {\n console.error(\"userChatRef.current is not an array:\", userChatRef.current);\n }\n // console.log('activeChatRef.current', activeChatRef.current.includes(data.toUserId._id), activeChatRef.current, data.toUserId._id)\n\n if (!activeChatRef.current.includes(data.toUserId._id)) return\n\n setMessages((prevMessages) => [data, ...prevMessages,]);\n\n });\n\n\n newSocket.on('deletedMessage', (data) => {\n // \n console.log('deletedMessage response:', data, data.fromUserId, activeChatRef.current._id);\n setUserList((prevUsers) =>\n prevUsers.map((user) =>\n user.chatUserId === data.chatUserId && data._id === user.messageId\n ? { ...user, isDeleted: true }\n : user\n )\n\n );\n if (!activeChatRef.current.includes(data.fromUserId)) return\n // setMessages((prevMessages) =>\n // prevMessages.filter((msg) => msg._id !== data._id)\n // );\n setMessages((prevMessages) =>\n prevMessages.map((msg) =>\n msg._id === data._id ? { ...msg, isDeleted: true } : msg\n )\n );\n })\n\n\n newSocket.on('receiveGroupMessage', (data,) => {\n\n // console.log('receiveGroupMessage', data, data.groupId, activeChatRef, activeChatRef.current.includes(data.groupId)\n //activeGroupChatRef.current.chatUserId\n\n // )\n\n if (Array.isArray(userChatRef.current)) {\n const newArray = moveObject([...userChatRef.current], data.groupId, 0); // shallow copy for immutability\n\n // console.log('newDatanewDatanewDataarr', newArray)\n\n\n const newData = newArray.map(obj =>\n obj.chatUserId == data.groupId ? { ...obj, messageContent: data.messageContent, messageId: data.messageId, createdAt: data.createdAt, isDeleted: false } : obj\n );\n // console.log('newDatanewDatanewData', newData)\n userChatRef.current = newData;\n\n setUserList(newData);\n } else {\n console.error(\"newDatanewDatanewData.current is not an array:\", userChatRef.current);\n }\n\n if (data?.userId?._id !== authuserId) {\n // showGroupNotification(data)\n }\n\n\n // showGroupNotification(data)\n\n\n // if (data.groupId !== activeGroupChatRef.current.chatUserId) return old\n\n\n if (!activeChatRef.current.includes(data.groupId)) return\n // setMessages((prevMessages) => [...prevMessages, data]);\n setMessages((prevMessages) => [data, ...prevMessages,]);\n // fetchGroupChatMedia(data.groupId)\n\n\n\n });\n\n newSocket.on('deleteGroupMessage', (data) => {\n // setMessages((prevMessages) =>\n // prevMessages.filter((msg) => msg._id !== data.messageId)\n // );\n\n // setMessagesForSearch((prevMessages) =>\n // prevMessages.filter((msg) => msg._id !== data.messageId)\n // );\n console.log('deleteGroupMessage', data)\n setMessages((prevMessages) =>\n prevMessages.map((msg) =>\n msg._id === data.messageId ? { ...msg, isDeleted: true, } : msg\n )\n );\n\n if (Array.isArray(userChatRef.current)) {\n const newArray = moveObject([...userChatRef.current], data.groupId, 0); // shallow copy for immutability\n\n\n\n const newData = newArray.map(obj =>\n obj.chatUserId == data.groupId && obj.messageId === data.messageId ? { ...obj, createdAt: new Date(), isDeleted: true, } : obj\n );\n userChatRef.current = newData;\n\n setUserList(newData);\n } else {\n console.error(\"newDatanewDatanewData.current is not an array:\", userChatRef.current);\n }\n })\n\n newSocket.on('deleteGroupMessage', (data) => {\n // setMessages((prevMessages) =>\n // prevMessages.filter((msg) => msg._id !== data.messageId)\n // );\n\n // setMessagesForSearch((prevMessages) =>\n // prevMessages.filter((msg) => msg._id !== data.messageId)\n // );\n console.log('deleteGroupMessage', data)\n setMessages((prevMessages) =>\n prevMessages.map((msg) =>\n msg._id === data.messageId ? { ...msg, isDeleted: true } : msg\n )\n );\n\n\n\n if (Array.isArray(userChatRef.current)) {\n const newArray = moveObject([...userChatRef.current], data.groupId, 0); // shallow copy for immutability\n\n console.log('newDatanewDatanewDataarr', newArray)\n\n\n const newData = newArray.map(obj =>\n obj.chatUserId == data.groupId ? { ...obj, createdAt: new Date(), isDeleted: true } : obj\n );\n console.log('newDatanewDatanewsadasData', newData)\n userChatRef.current = newData;\n\n setUserList(newData);\n } else {\n console.error(\"newDatanewDatanewData.current is not an array:\", userChatRef.current);\n }\n\n\n\n })\n\n newSocket.on('botMessage', (data) => {\n console.log('botMessage response:', data);\n\n // if (data.toUser !== activeBotRef.current._id) return\n if (Array.isArray(userChatRef.current)) {\n const newArray = moveObject([...userChatRef.current], data.toUser, 0); // shallow copy for immutability\n\n console.log('newDatanewDatanewDataarr', newArray)\n\n\n const newData = newArray.map(obj =>\n obj._id == data.toUser ? { ...obj, messageContent: data.messageContent, createdAt: data.createdAt } : obj\n );\n console.log('newDatanewDatanewData', newData)\n userChatRef.current = newData;\n\n setUserList(newData);\n } else {\n console.error(\"newDatanewDatanewData.current is not an array:\", userChatRef.current);\n }\n\n if (!activeChatRef.current.includes(data.toUser)) return\n setMessages((prevMessages) => [data, ...prevMessages]);\n });\n\n\n newSocket.on('initiateBot', (data) => {\n console.log('botMessageinitiateBot response:', data);\n\n // if (data.toUser !== activeBotRef.current._id) return\n if (Array.isArray(userChatRef.current)) {\n const newArray = moveObject([...userChatRef.current], data.toUser, 0); // shallow copy for immutability\n\n console.log('newDatanewDatanewDataarr', newArray)\n\n\n const newData = newArray.map(obj =>\n obj._id == data.toUser ? { ...obj } : obj\n );\n console.log('newDatanewDatanewData', newData)\n userChatRef.current = newData;\n\n setUserList(newData);\n } else {\n console.error(\"newDatanewDatanewData.current is not an array:\", userChatRef.current);\n }\n });\n\n\n\n setSocket(newSocket);\n\n // Cleanup function\n return () => {\n if (newSocket) {\n newSocket.disconnect();\n }\n\n };\n }, []);\n\n\n\n const showNotification = (data) => {\n if ('Notification' in window && Notification.permission === 'granted') {\n // new Notification(title, {\n // body: body,\n // icon: '../../public/applogo.png' // Optional\n // });\n const notification = new Notification(`New message from ${data?.fromUserId?.name}`, {\n body: data.messageContent,\n icon: '/applogo.png',// fix: no `../../public`\n\n data: data // optional custom data\n });\n\n // Handle click\n notification.onclick = function (event) {\n event.preventDefault(); // prevent default behavior\n window.focus();\n const storedList = JSON.parse(localStorage.getItem('userListStore') || '[]');\n // console.log('storedListstoredList', storedList)\n let findUser = storedList.find(x => x._id === notification.data.fromUserId._id)\n if (findUser) {\n setMessageWorkchatAlert(false)\n setMinimizedChats([])\n handleUserClick(findUser);\n setUpdateUserListNotification(true)\n\n } else {\n console.log('User not found in list at the time of click.');\n }\n\n\n\n // window.location.href = notification.data.url || '/';\n };\n }\n\n\n };\n\n const showGroupNotification = (data) => {\n if ('Notification' in window && Notification.permission === 'granted') {\n\n const notification = new Notification(`New message from ${data.userId.name}`, {\n body: data.messageContent,\n icon: '/applogo.png',// fix: no `../../public`\n\n data: data // optional custom data\n });\n\n // Handle click\n notification.onclick = function (event) {\n event.preventDefault(); // prevent default behavior\n window.focus();\n\n\n const storedList = JSON.parse(localStorage.getItem('userListStore') || '[]');\n // console.log('storedListstoredList', storedList)\n let findUser = storedList.find(x => x._id === notification.data.userId._id)\n console.log('notification.data.url', findUser)\n if (findUser) {\n setMessageWorkchatAlert(false)\n setMinimizedChats([])\n handleUserClick(findUser);\n setUpdateUserListNotification(true)\n\n } else {\n console.log('User not found in list at the time of click.');\n }\n\n\n\n };\n }\n\n\n };\n\n useEffect(() => {\n\n const storedList = JSON.parse(localStorage.getItem('userListStore') || '[]');\n\n if (userList.length == 0 && updateUserListNotification) {\n setUserList(storedList)\n setTimeout(() => {\n setUpdateUserListNotification(false)\n }, 200);\n }\n\n\n\n }, [updateUserListNotification])\n\n // Store chat state in localStorage when it changes\n useEffect(() => {\n if (typeof window !== 'undefined') {\n localStorage.setItem('activeChats', JSON.stringify(activeChats));\n localStorage.setItem('minimizedChats', JSON.stringify(minimizedChats));\n localStorage.setItem('newMessage', JSON.stringify(chats));\n }\n }, [activeChats, minimizedChats, chats]);\n\n // Load chat state from localStorage on initial render\n useEffect(() => {\n if (typeof window !== 'undefined') {\n const storedActiveChats = localStorage.getItem('activeChats');\n const storedMinimizedChats = localStorage.getItem('minimizedChats');\n const storedNewMessage = localStorage.getItem('newMessage');\n\n if (storedActiveChats) setActiveChats(JSON.parse(storedActiveChats));\n if (storedMinimizedChats) setMinimizedChats(JSON.parse(storedMinimizedChats));\n if (storedNewMessage) setNewMessage(JSON.parse(storedNewMessage));\n }\n }, []);\n\n // Add or focus chat when a user is clicked\n const handleUserClick = (userId) => {\n setToUser(userId?._id)\n setShowModal(false)\n setMentionedUsers([])\n if (userId?.type === \"group\") {\n setActiveUserChat(\"\")\n\n fetchGroupChatHistory(userId)\n setActiveGroupChat(userId)\n fetchChatGroupInfo(userId?.chatUserId)\n\n } else if (userId?.type === \"bot\") {\n // setActiveGroupChat('')\n // setActiveUserChat(\"\")\n\n\n\n\n fetchBotChatHistory(userId)\n setActiveBotChat(userId)\n } else {\n setActiveGroupChat('')\n\n fetchChatHistory(userId?._id)\n setActiveUserChat(userId)\n\n }\n\n\n socket.emit(\"readMessage\", { user_id: userId?._id })\n // reset()\n const newData = userChatRef.current.map(obj =>\n obj._id == userId?._id\n ? { ...obj, msg: \"Read\", unReadCount: 0 }\n : obj\n );\n\n const isEqual = JSON.stringify(userList) === JSON.stringify(newData); // or use deepEqual if needed\n\n userChatRef.current = newData;\n // console.log(' ', newData[0])\n\n if (!isEqual) {\n setUserList(newData);\n }\n\n\n // const newData = userList.map(obj =>\n // obj._id == userId?._id ? { ...obj, msg: \"Read\" } : obj\n // );\n // console.log('userIduserId', userId)\n\n // setUserList(newData);\n // If chat is already open, just focus it\n\n };\n\n // FIXED: Toggle minimize chat function using functional updates\n const toggleMinimize = (userId) => {\n setMinimizedChats(prevMinimized => {\n if (prevMinimized.includes(userId)) {\n // Unminimize this specific chat\n return prevMinimized.filter(id => id !== userId);\n } else {\n // Minimize this specific chat only\n return [...prevMinimized, userId];\n }\n });\n };\n\n // Close a chat window with functional updates\n const closeChat = (userId) => {\n setActiveChats(prev => prev.filter(id => id !== userId));\n // Also remove from minimized if it was minimized\n setMinimizedChats(prev => prev.filter(id => id !== userId));\n };\n const autoResizeTextarea = (userId) => {\n const el = textareaRef.current[userId];\n if (el) {\n el.style.height = 'auto'; // Reset height\n el.style.height = `${el.scrollHeight}px`; // Set to scroll height\n }\n };\n\n const showMentionModal = () => {\n if (groupInputRef.current) {\n const rect = groupInputRef.current.getBoundingClientRect();\n\n setModalPosition({\n top: rect.top - 250,\n left: rect.left,\n width: rect.width\n });\n setShowModal(true);\n }\n };\n const handleMentionUserSelect = (contact) => {\n const message = newMessage[toUser] || '';\n const atIndex = message.lastIndexOf('@');\n\n if (atIndex !== -1) {\n const beforeAt = message.substring(0, atIndex);\n const afterMention = message.substring(atIndex + 1);\n const mentionText = `@${contact.userId.name} `;\n\n const remainingAfterMention = afterMention.split(' ').slice(1).join(' ');\n const updatedMessage = `${beforeAt}${mentionText}${remainingAfterMention ? ' ' + remainingAfterMention : ''}`;\n\n setNewMessage(prev => ({\n ...prev,\n [toUser]: updatedMessage,\n }));\n\n setMentionedUsers((prev) => {\n const exists = prev.some((u) => u.userId._id === contact.userId._id);\n return exists ? prev : [...prev, contact];\n });\n\n setShowModal(false);\n\n // Focus and resize\n requestAnimationFrame(() => {\n groupInputRef.current?.focus();\n autoResizeTextarea(toUser);\n });\n }\n };\n\n\n // Handle message input change\n const handleMessageChange = (userId, value) => {\n setToUser(userId);\n setNewMessage(prev => ({ ...prev, [userId]: value }));\n\n if (activeGroupChat.type !== 'group') return;\n\n const atIndex = value.lastIndexOf('@');\n\n console.log('valueatIndex', value, atIndex)\n\n if (atIndex !== -1) {\n const afterAt = value.substring(atIndex + 1);\n\n if (afterAt === '') {\n // Case: ends with @ → show modal with empty query\n setMentionQuery('');\n showMentionModal();\n } else if (!afterAt.includes(' ')) {\n // Case: text after @ is a valid query\n setMentionQuery(afterAt);\n if (!showModal) showMentionModal();\n } else {\n // Case: text after @ contains space → cancel mention\n setShowModal(false);\n }\n } else {\n // No @ → hide mention\n setShowModal(false);\n }\n\n // Remove users no longer mentioned\n setMentionedUsers((prev) =>\n prev.filter(user => value.includes(`@${user.userId.name}`))\n );\n };\n\n // Handles input changes for search\n const handleSearchUser = (e) => {\n setSearchVal(e.target.value);\n\n const value = e.target.value.trim();\n\n\n const filteredList = value\n ? chatListSearch.filter(({ name }) =>\n name.toLowerCase().includes(value.toLowerCase())\n )\n : chatListSearch;\n\n setUserList(filteredList)\n\n // setActiveChat('')\n\n };\n\n // handle conatact search\n\n const handleContactSearch = (e) => {\n const value = e.target.value.trim();\n setSearchContactVal(value);\n\n const filteredList = value\n ? ContactListSearch.filter(({ name }) =>\n name.toLowerCase().includes(value.toLowerCase())\n )\n : ContactListSearch;\n\n setContactList(filteredList);\n };\n\n // handle forward message\n const handleReplyMessage = (val) => {\n setReplyMessageData(val)\n }\n\n\n\n const fetchInitalFirstMessage = async (val, url) => {\n try {\n\n let dada = {\n toUser: val\n }\n // console.log('dadadada', dada)\n const response = await axiosInstance.post(\"chatUser/addChatUser\", dada);\n\n // console.log('hatUser/addChat', response.data)\n\n if (response.data.status === \"success\") {\n setActiveChatsIds(prev => {\n const existing = prev[val] || [];\n\n // Avoid adding duplicate\n if (existing.includes(response.data.chatUserId)) {\n return prev;\n }\n\n return {\n ...prev,\n [val]: [...existing, response.data.chatUserId],\n };\n });\n handleSocketData(val, response.data.chatUserId, url)\n // setChatId(response.data.chatUserId)\n\n }\n\n\n } catch (error) {\n console.log('d', error)\n }\n }\n\n const sendMessage = async (userId, url) => {\n\n\n\n\n const userMessages = activeChatsIds[userId] || [];\n\n\n // console.log('userMessages', userMessages)\n\n // const chatUserIds = [...new Set(userMessages.map(m => m.chatUserId))][0]\n // if (!message?.trim() || url == \"\") return;\n\n\n // return\n\n if (userMessages.length === 0) {\n await fetchInitalFirstMessage(userId, url);\n // console.log('fetchInitalFirstMessage1')\n } else {\n // console.log('fetchInitalFirstMessagefetchInitalFirstMessage2')\n\n handleSocketData(userId, userMessages[0], url);\n }\n\n\n\n\n\n\n };\n\n // handle send group message\n\n const handleSendGroupMessage = async (userId, url) => {\n const userMessages = activeChatsIds[userId] || [];\n\n\n // console.log('userGroupMessages', userMessages)\n\n\n handleSocketGroupData(userId, url);\n };\n\n\n // Send a new message\n const handleSocketData = (userId, chatId, url) => {\n\n // const authuserId = localStorage.getItem(\"authUser\"); 68130a267c0685f5e878b854\n // console.log(\"newMsgnewMsgnewMsg1\", userId, chatId, url)\n const authuserId = localStorage.getItem(\"authUser\");\n\n\n\n // console.log(\"newMsgnewMsgnewMsgchatUserIds\", chatId, newMessage, newMessage[userId]?.trim())\n\n\n\n\n\n if (newMessage[userId]?.trim()) {\n const newMsg = {\n\n user_id: userId,\n messageType: fileToUpload ? 'F' : 'M',\n message: newMessage[userId],\n chatUserId: chatId,\n fileType: fileToUpload ? fileToUpload.type : \"\",\n fileUrl: url ? url : \"\",\n replyMessageId: replyMessageData._id || \"\",\n\n };\n\n\n\n // console.log('newMessagenewMessagenewMessagedata', newMsg)\n // return\n socket.emit('sendMessage', newMsg);\n // mockMessages[userId] = [...mockMessages[userId], newMsg];\n setNewMessage(prev => ({ ...prev, [userId]: '' }));\n setFileUrl('')\n handleCancelFileUpload()\n setReplyMessageData('')\n const el = textareaRef.current[userId];\n if (el) {\n el.style.height = 'auto'; // Reset to original height\n }\n\n\n }\n };\n\n\n const handleSocketGroupData = async (userId, urls) => {\n // console.log(\"newMsgnewMsgnewMsg1aa\", userId, urls, newMessage[userId]?.trim())\n\n\n\n if (newMessage[userId]?.trim()) {\n const newMsg = {\n\n groupId: activeGroupChat?.chatUserId,\n messageType: fileToUpload ? 'F' : 'M',\n message: newMessage[userId],\n fileType: fileToUpload ? fileToUpload.type : \"\",\n fileUrl: urls ? urls : \"\",\n replyMessageId: replyMessageData._id || \"\",\n mentionedUsers: mentionedUsers.map((usr) => usr?.userId?._id)\n\n };\n // console.log('newMessagenewMessagenewMessagedata', newMsg)\n // return\n socket.emit('sendGroupMessage', newMsg);\n setNewMessage(prev => ({ ...prev, [activeGroupChat?.chatUserId]: '' }));\n setFileUrl('')\n setReplyMessageData('')\n handleCancelFileUpload()\n setMentionedUsers([])\n const el = textareaRef.current[activeGroupChat?.chatUserId];\n // if (el) {\n // el.style.height = 'auto'; // Reset to original height\n // }\n }\n\n };\n\n\n\n\n\n\n // Handle enter key press to send message\n const handleKeyDown = (userId, e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n sendMessage(userId);\n }\n };\n const scrollToMessage = (messageId) => {\n const messageElement = document.getElementById(`message-${messageId}`);\n console.log('messageElement', messageElement)\n\n if (messageElement && messagesContainerRef.current) {\n messageElement.scrollIntoView({ behavior: 'smooth', block: 'center' });\n setHighlightReply(messageId);\n // Remove highlight after 2 seconds\n setTimeout(() => {\n setHighlightReply('');\n }, 2000);\n }\n };\n\n\n // user chat delete\n const handleDeleteMessage = (selectDeleteMessageData) => {\n try {\n\n let payload = {\n id: selectDeleteMessageData._id\n }\n console.log('handleDelete', chatId)\n // return\n\n socket.emit('deleteMessage', payload)\n\n setMessages((prevMessages) =>\n