Rev 4872 | Autoría | Ultima modificación | Ver Log |
/* eslint-disable react/prop-types */
import React from "react";
import { useState, useEffect } from "react";
import { axios } from "../../utils";
import Contacts from "./contacts/Contacts";
import NotificationAlert from "../../shared/notification/NotificationAlert";
import Groups from "./groups/Groups";
import PersonalChat from "./personal-chat/PersonalChat";
import { FiMaximize2 } from 'react-icons/fi'
import { addNotification } from "../../redux/notification/notification.actions";
import { useDispatch } from "react-redux";
import { Modal } from "react-bootstrap";
import Spinner from "../../shared/loading-spinner/Spinner";
import SendIcon from '@mui/icons-material/Send'
import AddIcon from '@mui/icons-material/Add'
const notifyAudio = new Audio("/audio/chat.mp3");
const Chat = (props) => {
// states
const [contacts, setContacts] = useState([]);
const [groups, setGroups] = useState([]);
const [activeChats, setActiveChats] = useState([]);
const [isChatOpen, setIsChatOpen] = useState(false);
const [isMuted, setIsMuted] = useState(false);
const [showModal, setShowModal] = useState(false);
const [activeTab, setActiveTab] = useState("user");
const [search, setSearch] = useState('');
const [loading, setLoading] = useState(false);
const [pendingConversation, setPendingConversation] = useState('')
const { defaultNetwork } = props
const filtredContacts = contacts.filter(({ name }) => name.toLowerCase().includes(search.toLowerCase()))
const filtredGroups = groups.filter(({ name }) => name.toLowerCase().includes(search.toLowerCase()))
const handleEntities = entities => {
let newUserContacts = [];
let newGroups = [];
entities.map((entity) => {
if (entity.not_received_messages) handleNewMessage(entity)
if (entity.not_seen_messages) handleNotSeenMessage(entity)
if (entity.is_open) handleOpenConversation(entity, false);
switch (entity.type) {
case "user":
newUserContacts = [...newUserContacts, entity];
handleUpdateOnline(entity);
break;
case "group":
newGroups = [...newGroups, entity];
break;
default:
break;
}
});
setContacts(newUserContacts);
setGroups(newGroups);
}
const heartBeat = async () => {
try {
const { data } = await axios.get("/chat/heart-beat")
if (data.success) {
handleEntities(data.data)
}
return data.data;
} catch (error) {
console.log('>>: chat error > ', error)
}
};
const handleUpdateOnline = (entity) => {
const existingChatId = activeChats.findIndex(
(activeChat) => activeChat.id === entity.id
);
if (existingChatId >= 0) {
if (activeChats[existingChatId].online !== entity.online) {
const newActiveChats = [...activeChats];
newActiveChats[existingChatId].online = entity.online;
setActiveChats(newActiveChats);
}
}
};
const handleNewMessage = async (unseeEntity) => {
await axios.post(unseeEntity.url_mark_received)
if (!activeChats.some((activeChat) => activeChat.id === unseeEntity.id)) {
setActiveChats([...activeChats, { ...unseeEntity, minimized: false }]);
playNotifyAudio();
} else {
const existingChatId = activeChats.findIndex(
(activeChat) => activeChat.id === unseeEntity.id
);
if (!activeChats[existingChatId].unsee_messages) {
const newActiveChats = [...activeChats];
newActiveChats[existingChatId].unsee_messages = true;
setActiveChats(newActiveChats);
playNotifyAudio(newActiveChats[existingChatId].minimized);
}
}
};
const handleCloseConversation = async (entity) => {
const { data } = await axios.post(entity.url_close)
if (!data.success) console.log('Error in entity close')
setActiveChats(activeChats.filter(prevActiveChats => prevActiveChats.id !== entity.id))
}
const handleOpenConversation = async (entity, minimized = false) => {
if (activeChats.length < 3 && !activeChats.some((el) => el.id === entity.id)) {
setActiveChats([...activeChats, { ...entity, minimized: minimized }]);
}
if (activeChats.length >= 3 && !activeChats.some((el) => el.id === entity.id)) {
await handleCloseConversation(activeChats[0])
setActiveChats(prevActiveChats => [...prevActiveChats, { ...entity, minimized: minimized }]);
}
};
const handleReadConversation = async (entity) => {
try {
const { data } = await axios.post(entity.url_mark_seen)
if (!data.success) console.log('Ha ocurrido un error')
setActiveChats(prevActiveChats => [...prevActiveChats].map(chat => {
if (entity.id === chat.id) return { ...chat, not_seen_messages: false }
return chat;
}))
} catch (error) {
console.log(`Error: ${error}`)
}
};
const handleMinimizeConversation = (entity, minimized = null) => {
return setActiveChats(prevActiveChats => [...prevActiveChats].map(chat => {
if (entity.id === chat.id) return { ...chat, minimized: minimized ?? !chat.minimized }
return chat
}))
}
const handleNotSeenMessage = (entity) => {
const index = activeChats.findIndex(chat => chat.id === entity.id)
if (index !== -1) {
setActiveChats(prev => [...prev].map(chat => {
if (chat.id === entity.id) {
return {
...chat,
not_seen_messages: entity.not_seen_messages
}
}
return chat;
}))
}
}
const playNotifyAudio = (minimized = true) => {
if (!isMuted && minimized) {
notifyAudio.play();
}
};
const handleMute = () => {
setIsMuted(!isMuted);
if (isMuted) {
notifyAudio.play();
}
};
const handleChangeTab = (tab) => setActiveTab(tab)
useEffect(() => {
if (!loading) {
const fetchData = async () => {
setLoading(true)
const entities = await heartBeat() || [];
setLoading(false)
return entities
}
setTimeout(() => {
fetchData()
}, "2000")
}
}, [loading]);
useEffect(() => {
if (pendingConversation) {
const pendingChat = contacts.find(contact => contact.url_send === pendingConversation)
if (pendingChat) {
handleOpenConversation(pendingChat)
setPendingConversation('')
}
}
}, [pendingConversation, contacts])
useEffect(() => {
emojione.imageType = "png";
emojione.sprites = false;
emojione.ascii = true;
emojione.imagePathPNG = props.emojiOnePath;
}, []);
return (window.innerWidth > 1000 && window.location.pathname !== '/chat') ? (
<React.Fragment>
<div id="drupalchat-wrapper">
<div id="drupalchat">
<div className="item-list" id="chatbox_chatlist">
<ul id="mainpanel">
<li id="chatpanel" className="first last">
<div className="subpanel">
<div
className="subpanel_title"
onClick={(e) => (e.currentTarget === e.target) && setIsChatOpen(!isChatOpen)}
>
<a
href="/chat"
className="text-chat-title"
>
Chat
<FiMaximize2 className="ml-3" />
</a>
<div className="subpanel_title-icons">
<i
className={`icon ${isMuted ? "icon-volume-off" : "icon-volume-2"} text-20`}
onClick={handleMute}
/>
<i
className={`fa ${isChatOpen ? "fa-angle-down" : "fa-angle-up"} text-20`}
onClick={() => setIsChatOpen(!isChatOpen)}
/>
</div>
</div>
<div
id="showhidechatlist"
style={{ display: isChatOpen ? "grid" : "none" }}
>
<div className="drupalchat_search_main chatboxinput">
<input
className="drupalchat_searchinput live-search-box"
id="live-search-box"
type="text"
name='search'
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<i className="searchbutton fas fa-search" />
</div>
{defaultNetwork !== 'y' &&
<div
className="d-flex align-items-center cursor-pointer"
style={{ gap: '.5rem', background: '#fff' }}
onClick={() => setShowModal(true)}
>
<AddIcon />
<span>Iniciar conversación</span>
</div>
}
<div className="drupalchat_search_main chatboxinput">
<button
className={`${activeTab === 'user' ? 'active' : ''}`}
onClick={() => handleChangeTab("user")}
>
Contactos
</button>
<button
className={`${activeTab === 'group' ? 'active' : ''}`}
onClick={() => handleChangeTab("group")}
>
Grupos
</button>
</div>
<div
className="contact-list chatboxcontent"
style={{ display: activeTab === "user" ? "block" : "none", }}
>
<Contacts
contacts={filtredContacts}
onOpenConversation={handleOpenConversation}
/>
</div>
<div
className="group-list chatboxcontent"
style={{ display: activeTab === "group" ? "block" : "none", }}
>
<ul id="group-list-ul" className="live-search-list-group">
<Groups
groups={filtredGroups}
onOpenConversation={handleOpenConversation}
/>
</ul>
</div>
<div
className="group-contacts-list chatboxcontent"
style={{ display: "none" }}
>
<div style={{ textAlign: "center", fontSize: "13px" }}>
Integrantes del grupo
</div>
<ul
id="contact-group-list-ul"
className="live-search-list"
></ul>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<div className="active_chats-list">
{activeChats.map((entity, index) => (
<PersonalChat
index={index}
key={entity.id}
entity={entity}
not_seen_messages={entity.not_seen_messages}
minimized={entity.minimized}
onClose={handleCloseConversation}
onMinimize={handleMinimizeConversation}
onRead={handleReadConversation}
timezones={props.timezones}
/>
))}
</div>
<Chat.ContactsModal
show={showModal}
setConversation={(url) => setPendingConversation(url)}
/>
<NotificationAlert />
</React.Fragment >
) : (
""
);
}
const ContactsModal = ({ show, setConversation }) => {
let axiosThrottle = null
const [isShow, setIsShow] = useState(show)
const [searchResult, setSearchResult] = useState({})
const [loading, setLoading] = useState(false)
const dispatch = useDispatch()
const closeModal = () => {
setIsShow(false)
setSearchResult({})
}
const handleSearch = (contact) => {
clearTimeout(axiosThrottle)
axiosThrottle = setTimeout(() => {
fetchContacts(contact)
}, 500);
};
const fetchContacts = async (searchParam = '') => {
setLoading(true)
await axios
.get(`/chat/users?search=${searchParam.toLowerCase()}`)
.then(({ data: response }) => {
if (!response.success) return dispatch(addNotification({ style: 'danger', msg: 'Ha ocurrido un error' }))
setSearchResult(response.data)
})
.finally(() => setLoading(false))
}
const startConversation = async (send_url = '', name = '') => {
const formData = new FormData()
const fullName = name.split(' ')
formData.append("message", `Hola, ${fullName[0]}`)
setLoading(true)
await axios
.post(send_url, formData)
.then(({ data: response }) => {
if (response.success) {
setConversation(send_url)
closeModal()
}
})
.finally(() => setLoading(false))
}
useEffect(() => {
setIsShow(show)
}, [show])
return (
<Modal
show={isShow}
onHide={closeModal}
>
<Modal.Header closeButton>
<h3 className="card-title font-weight-bold">Iniciar conversación</h3>
</Modal.Header>
<div className="form-group">
<label htmlFor="search-people">Escribe el nombre</label>
<input
type="text"
className="form-control"
placeholder="Escribe el nombre de la persona"
onChange={(e) => handleSearch(e.target.value)}
/>
</div>
<div className='container'>
{loading
? <Spinner />
: Object.entries(searchResult).map(([key, value]) => {
return (
<div className='row' key={key}>
<div className='col-8'>
<p>{value}</p>
</div>
<div className='col-4'>
<button
className='btn btn-primary'
onClick={() => startConversation(key, value)}
>
<SendIcon />
</button>
</div>
</div>
)
})}
</div>
</Modal >
)
}
Chat.ContactsModal = ContactsModal
export default Chat;