Proyectos de Subversion LeadersLinked - Backend

Rev

Rev 16288 | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |

import React, { useEffect, useRef, useState } from "react";
import { axios } from "../../../utils";
import { useForm } from "react-hook-form";

import Emojione from "./emojione/Emojione";
import FileModal from "./fileModal/FileModal";
import MessagesList from "./messages/MessagesList";
import ConferenceModal from "./components/ConferenceModal";
import IconButton from "@mui/material/IconButton";
import AttachFileIcon from "@mui/icons-material/AttachFile";
import InsertEmoticonIcon from "@mui/icons-material/InsertEmoticon";
import SendIcon from "@mui/icons-material/Send";
import MoreVertIcon from "@mui/icons-material/MoreVert";

import styles from "./chat.module.scss";

const permittedFiles =
  "video/mp4, video/mpeg, video/webm, application/pdf, image/jpeg, image/png, image/jpg";

const Chat = ({ entity, timezones }) => {
  const [oldMessages, setOldMessages] = useState([]);
  const [messages, setMessages] = useState([]);
  const [totalPages, setTotalPages] = useState(1);
  const [currentPage, setCurrentPage] = useState(1);

  const [loading, setLoading] = useState(false);
  const [isGettingMessages, setIsGettingMessages] = useState(false);

  const [showEmojione, setShowEmojione] = useState(false);
  const [selectedFile, setSelectedFile] = useState("");

  const { handleSubmit } = useForm();

  const scrollList = useRef(null);
  const inputTextEl = useRef(null);
  const fileInputEl = useRef(null);

  const {
    url_get_all_messages,
    url_send,
    url_upload,
    url_close,
    url_mark_seen,
    url_zoom,
    type,
  } = entity;

  // Get messages
  const getMessages = () => {
    setLoading(true);
    axios
      .get(url_get_all_messages)
      .then(({ data: response }) => {
        const { data, success } = response;

        if (!success) {
          return console.log("Ha ocurrido un error");
        }

        const messageResponse = [...data.items].reverse();
        const updatedMessages = messageResponse.reduce(
          (acum, updatedMessage) => {
            if (
              messages.findIndex(
                (message) => message.id === updatedMessage.id
              ) === -1
            ) {
              acum = [...acum, updatedMessage];
            }
            return acum;
          },
          []
        );

        if (updatedMessages.length > 0) {
          setMessages((prevMessages) => [...prevMessages, ...updatedMessages]);
          setTotalPages(data.pages);
          scrollTo(scrollList);
        }
      })
      .finally(() => setLoading(false));
  };

  const onIntersection = (entities) => {
    const target = entities[0];
    if (target.isIntersecting && currentPage < totalPages) {
      setIsGettingMessages(true);
      setCurrentPage((prevState) => prevState + 1);
      scrollTo(scrollList, 200);
    }
  };

  const getOldMessages = () => {
    setIsGettingMessages(true);
    axios
      .get(`${url_get_all_messages}?page=${currentPage}`)
      .then(({ data: response }) => {
        const { data, success } = response;
        if (success && data.page > 1) {
          setOldMessages([...data.items.slice().reverse(), ...oldMessages]);
        }
      })
      .finally(() => setIsGettingMessages(false));
  };

  //Utilitys
  const scrollTo = (element, distance) => {
    const divToScrollEl = element.current;
    const options = {
      top: distance,
      behavior: "smooth",
    };

    if (!distance) {
      divToScrollEl.scrollBy({ ...options, top: divToScrollEl.scrollHeight });
      return;
    }
    divToScrollEl.scrollBy(options);
  };

  const onClickEmoji = (event) => {
    const shortname = event.currentTarget.dataset.shortname;
    const currentText = inputTextEl.current.value;
    const cursorPosition = inputTextEl.current.selectionStart;
    const textBehind = currentText.substring(0, cursorPosition);
    const textForward = currentText.substring(cursorPosition);
    const unicode = emojione.shortnameToUnicode(shortname);
    inputTextEl.current.value = `${textBehind}${unicode}${textForward}`;
    inputTextEl.current.focus();
    inputTextEl.current.setSelectionRange(
      cursorPosition + unicode.length,
      cursorPosition + unicode.length
    );
  };

  const handleUploadFile = ({ target }) => {
    const file = target.files[0];
    if (!file) return;
    setSelectedFile(file);
  };

  const removeSelectedFile = () => {
    setSelectedFile("");
  };

  // On send
  const handleKeyDown = (e) => {
    if (e.key !== "Enter") return false;
    e.preventDefault();
    onHandleSubmit();
  };

  const onHandleSubmit = () => {
    const formData = new FormData();
    formData.append("message", emojione.toShort(inputTextEl.current.value));
    axios.post(url_send, formData).then(({ data: response }) => {
      const { data, success } = response;
      if (!success) {
        console.log("Ha ocurrido un error: " + data);
        return;
      }
      inputTextEl.current.value = "";
      setShowEmojione(false);
      scrollTo(scrollList);
    });
  };

  const handleSendFile = () => {
    const formData = new FormData();
    formData.append("file", selectedFile);
    axios.post(url_upload, formData).then(({ data: response }) => {
      const { success, data } = response;
      if (!success) {
        console.log("Ha ocurrido un error: " + data);
        return;
      }
      setSelectedFile("");
      setShowEmojione(false);
      scrollTo(scrollList);
    });
  };

  useEffect(() => {
    let timeInterval;
    if (loading) return;
    timeInterval = setTimeout(() => getMessages(), 2000);

    return () => {
      clearTimeout(timeInterval);
    };
  }, [loading]);

  useEffect(() => {
    setMessages([]);
    setOldMessages([]);
    setTotalPages(1);
    setCurrentPage(1);
  }, [entity]);

  useEffect(() => getOldMessages(), [currentPage]);

  useEffect(() => axios.post(url_mark_seen), []);

  return (
    <div className={styles.chat}>
      <Chat.Header
        name={entity.name}
        conferenceUrl={url_zoom}
        timezones={timezones}
      />
      <MessagesList
        isLastPage={currentPage >= totalPages}
        messages={[...oldMessages, ...messages]}
        onIntersection={onIntersection}
        scrollRef={scrollList}
        isLoading={isGettingMessages}
      />
      <div className={styles.chat__input__container}>
        {showEmojione && <Emojione onClickEmoji={onClickEmoji} />}
        <form
          onSubmit={handleSubmit(onHandleSubmit)}
          encType="multipart/form-data"
        >
          <button
            type="button"
            className={"btn " + styles.icon_btn}
            onClick={() => fileInputEl.current.click()}
          >
            <AttachFileIcon />
          </button>
          <button
            type="button"
            className={"btn " + styles.icon_btn}
            onClick={() => setShowEmojione(!showEmojione)}
          >
            <InsertEmoticonIcon />
          </button>
          <input
            type="file"
            ref={(e) => (fileInputEl.current = e)}
            accept={permittedFiles}
            onChange={handleUploadFile}
            hidden
          />
          <textarea
            className={styles.chatInput}
            placeholder="Escribe un mensaje"
            onKeyDown={handleKeyDown}
            ref={inputTextEl}
            rows="1"
          />
          <button type="submit" className={"btn " + styles.send_btn}>
            <SendIcon />
          </button>
        </form>
      </div>
      {selectedFile && (
        <FileModal
          file={selectedFile}
          onCancel={removeSelectedFile}
          onSend={handleSendFile}
        />
      )}
    </div>
  );
};

const Header = ({ name, conferenceUrl, timezones }) => {
  const [isShowConferenceModal, setisShowConferenceModal] = useState(false);

  const toggleConferenceModal = () =>
    setisShowConferenceModal(!isShowConferenceModal);

  const options = [
    { label: "Crear Conferencia", action: toggleConferenceModal },
  ];

  return (
    <div className={styles.chat_header}>
      <h2>{name}</h2>
      <Header.Options options={options} />
      <ConferenceModal
        isShow={isShowConferenceModal}
        onClose={toggleConferenceModal}
        timezones={timezones}
        zoomUrl={conferenceUrl}
        onCreate={toggleConferenceModal}
      />
    </div>
  );
};

const Options = ({ options }) => {
  const [isShowMenu, setIsShowMenu] = useState(false);

  const toggleOptions = () => {
    setIsShowMenu(!isShowMenu);
  };

  return (
    <div className="header-options">
      <IconButton onClick={toggleOptions}>
        <MoreVertIcon />
      </IconButton>
      <div className="position-relative">
        <div className={`feed-options ${isShowMenu ? "active" : ""}`}>
          <ul>
            {options.map((option, index) => (
              <li key={index}>
                <button
                  className="btn option-btn"
                  onClick={() => {
                    toggleOptions();
                    option.action();
                  }}
                >
                  {option.label}
                </button>
              </li>
            ))}
          </ul>
        </div>
      </div>
    </div>
  );
};

Chat.Header = Header;
Header.Options = Options;

export default Chat;