Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

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;