Proyectos de Subversion LeadersLinked - Backend

Rev

Rev 15836 | Rev 15844 | Ir a la última revisión | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
15834 stevensc 1
import React, { useEffect, useRef, useState } from "react";
15801 stevensc 2
import { axios } from "../../../utils";
15834 stevensc 3
import { useForm } from "react-hook-form";
11347 nelberth 4
 
5
import Emojione from "./emojione/Emojione";
6
import FileModal from "./fileModal/FileModal";
7
import Messages from "./messages/Messages";
15834 stevensc 8
import AttachFileIcon from "@mui/icons-material/AttachFile";
9
import InsertEmoticonIcon from "@mui/icons-material/InsertEmoticon";
15835 stevensc 10
import SendIcon from "@mui/icons-material/Send";
11347 nelberth 11
 
15834 stevensc 12
import styles from "./chat.module.scss";
13
 
11347 nelberth 14
const permittedFiles =
15
  "video/mp4, video/mpeg, video/webm, application/pdf, image/jpeg, image/png, image/jpg";
16
 
15834 stevensc 17
const Chat = ({ entity }) => {
15839 stevensc 18
  const [oldMessages, setOldMessages] = useState([]);
19
  const [messages, setmessages] = useState([]);
20
  const [totalPages, setTotalPages] = useState(1);
21
  const [currentPage, setCurrentPage] = useState(1);
22
  const [loading, setLoading] = useState(false);
23
 
11347 nelberth 24
  const [showEmojione, setShowEmojione] = useState(false);
25
  const [selectedFile, setSelectedFile] = useState("");
26
 
15834 stevensc 27
  const { handleSubmit, register } = useForm();
28
 
11347 nelberth 29
  const bottomToScroll = useRef(null);
30
  const inputTextEl = useRef(null);
31
  const fileInputEl = useRef(null);
32
  const loadMoreEl = useRef();
33
  const divToScroll = useRef(null);
15834 stevensc 34
 
35
  const {
36
    url_get_all_messages,
37
    url_send,
38
    url_upload,
39
    url_close,
40
    url_mark_seen,
41
    type,
42
  } = entity;
43
 
15839 stevensc 44
  useEffect(() => {
45
    let timeInterval;
46
    if (loading) return;
47
    timeInterval = setTimeout(() => chatHeartbeat(), 2000);
11347 nelberth 48
 
49
    return () => {
15839 stevensc 50
      clearTimeout(timeInterval);
11347 nelberth 51
    };
15839 stevensc 52
  }, [loading, entity]);
11347 nelberth 53
 
54
  // infiniteScroll
55
  useEffect(() => {
56
    const options = {
57
      root: null,
58
      rootMargin: "0px",
59
      threshold: 1.0,
60
    };
61
    const observer = new IntersectionObserver(handleObserver, options);
62
    if (loadMoreEl.current) {
63
      observer.observe(loadMoreEl.current);
64
    }
65
    return () => {
66
      observer.disconnect();
67
    };
15839 stevensc 68
  }, [totalPages]);
11347 nelberth 69
 
70
  useEffect(() => {
71
    loadOldMessages();
72
  }, [currentPage]);
73
 
15839 stevensc 74
  useEffect(() => {
75
    axios.post(url_mark_seen);
11347 nelberth 76
  }, []);
77
 
78
  // heartbeat function
15839 stevensc 79
  const chatHeartbeat = () => {
80
    setLoading(true);
81
    axios
82
      .get(url_get_all_messages)
83
      .then(({ data: response }) => {
84
        const { data, success } = response;
85
 
86
        if (!success) {
87
          return console.log("Ha ocurrido un error");
11347 nelberth 88
        }
15839 stevensc 89
 
90
        const messageResponse = [...data.items].reverse();
91
        const updatedMessages = messageResponse.reduce(
92
          (acum, updatedMessage) => {
93
            if (
94
              messages.findIndex(
95
                (message) => message.id === updatedMessage.id
96
              ) === -1
97
            ) {
98
              acum = [...acum, updatedMessage];
99
            }
100
            return acum;
101
          },
102
          []
103
        );
104
 
105
        if (updatedMessages.length > 0) {
106
          setmessages([...messages, ...updatedMessages]);
107
          setTotalPages(data.pages);
108
          scrollToBottom();
109
        }
110
      })
111
      .finally(() => setLoading(false));
11347 nelberth 112
  };
113
 
114
  // utilsFunctions
115
  const scrollToBottom = () => {
116
    const divToScrollEl = divToScroll.current;
117
    divToScrollEl.scrollIntoView({ behavior: "smooth" });
118
  };
119
 
120
  const onClickEmoji = (event) => {
121
    const shortname = event.currentTarget.dataset.shortname;
122
    const currentText = inputTextEl.current.value;
123
    let cursorPosition = inputTextEl.current.selectionStart;
124
    const textBehind = currentText.substring(0, cursorPosition);
125
    const textForward = currentText.substring(cursorPosition);
126
    inputTextEl.current.value = `${textBehind}${shortname}${textForward}`;
127
    inputTextEl.current.focus();
128
    inputTextEl.current.setSelectionRange(
129
      cursorPosition + shortname.length,
130
      cursorPosition + shortname.length
131
    );
132
  };
133
 
134
  const handleUploadFile = (e) => {
135
    const file = e.target.files[0];
136
    if (file) {
137
      setSelectedFile(file);
138
    }
139
  };
140
 
141
  const removeSelectedFile = () => {
142
    setSelectedFile("");
143
  };
144
 
145
  const handleObserver = async (entities) => {
146
    const target = entities[0];
147
    if (target.isIntersecting) {
15839 stevensc 148
      if (currentPage < totalPages) {
11347 nelberth 149
        setCurrentPage((prevState) => prevState + 1);
150
        bottomToScroll.current.scrollBy(0, 200);
151
      }
152
    }
153
  };
154
 
155
  const loadOldMessages = async () => {
15839 stevensc 156
    if (currentPage < totalPages && currentPage > 1) {
11347 nelberth 157
    }
158
    await axios
159
      .get(url_get_all_messages, {
160
        params: {
161
          page: currentPage,
162
        },
163
      })
164
      .then(async (response) => {
165
        const resData = response.data;
166
        if (resData.success) {
167
          if (resData.data.page > 1) {
168
            setOldMessages([
169
              ...resData.data.items.slice().reverse(),
170
              ...oldMessages,
171
            ]);
172
          }
173
        }
174
      });
175
  };
176
 
177
  // on send message
178
  const onHandleSubmit = (data, event) => {
179
    const formData = new FormData();
180
    Object.entries(data).map(([key, value]) => {
181
      formData.append(key, value);
182
    });
183
    event.target.reset();
184
    axios.post(url_send, formData).then((response) => {
185
      setShowEmojione(false);
186
    });
187
  };
188
 
189
  // on send file
190
  const handleSendFile = () => {
191
    const formData = new FormData();
192
    formData.append("file", selectedFile);
193
    axios.post(url_upload, formData).then(async (response) => {
194
      const resData = response.data;
195
      if (resData.success) {
196
        setSelectedFile("");
197
        setShowEmojione(false);
198
      }
199
    });
200
  };
201
 
202
  return (
203
    <div className={styles.chat}>
204
      <div className={styles.messagesContainer} ref={bottomToScroll}>
205
        <div className={styles.messageWrapper}>
15839 stevensc 206
          {currentPage < totalPages && (
15834 stevensc 207
            <p ref={loadMoreEl} className="mt-2">
11347 nelberth 208
              Cargando...
209
            </p>
210
          )}
211
          <Messages
212
            oldMessages={oldMessages}
15839 stevensc 213
            messages={messages}
11347 nelberth 214
            onScrollToBottom={scrollToBottom}
215
            chatType={type}
216
          />
217
          <div ref={divToScroll}></div>
218
        </div>
219
      </div>
15805 stevensc 220
      <div className={styles.chat__input__container}>
11347 nelberth 221
        {showEmojione && <Emojione onClickEmoji={onClickEmoji} />}
222
        <form
223
          onSubmit={handleSubmit(onHandleSubmit)}
224
          encType="multipart/form-data"
225
        >
226
          <button
227
            type="button"
15836 stevensc 228
            className={"btn " + styles.icon_btn}
15833 stevensc 229
            onClick={() => fileInputEl.current.click()}
230
          >
231
            <AttachFileIcon />
232
          </button>
11347 nelberth 233
          <button
234
            type="button"
15836 stevensc 235
            className={"btn " + styles.icon_btn}
15833 stevensc 236
            onClick={() => setShowEmojione(!showEmojione)}
237
          >
238
            <InsertEmoticonIcon />
239
          </button>
11347 nelberth 240
          <input
241
            type="file"
15833 stevensc 242
            ref={(e) => (fileInputEl.current = e)}
11347 nelberth 243
            accept={permittedFiles}
15833 stevensc 244
            onChange={handleUploadFile}
11347 nelberth 245
            hidden
246
          />
247
          <textarea
248
            className={styles.chatInput}
249
            placeholder="Escribe un mensaje"
250
            rows="1"
251
            ref={inputTextEl}
15833 stevensc 252
          />
15836 stevensc 253
          <button type="submit" className={"btn " + styles.send_btn}>
15835 stevensc 254
            <SendIcon />
11347 nelberth 255
          </button>
256
        </form>
257
      </div>
258
      {selectedFile && (
259
        <FileModal
260
          file={selectedFile}
261
          onCancel={removeSelectedFile}
262
          onSend={handleSendFile}
263
        />
264
      )}
265
    </div>
266
  );
267
};
268
 
269
export default Chat;