Proyectos de Subversion LeadersLinked - Backend

Rev

Rev 15839 | Rev 15851 | 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([]);
15844 stevensc 19
  const [messages, setMessages] = useState([]);
15839 stevensc 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
 
15844 stevensc 44
  // Get messages
45
  const getMessages = () => {
15839 stevensc 46
    setLoading(true);
47
    axios
48
      .get(url_get_all_messages)
49
      .then(({ data: response }) => {
50
        const { data, success } = response;
51
 
52
        if (!success) {
53
          return console.log("Ha ocurrido un error");
11347 nelberth 54
        }
15839 stevensc 55
 
56
        const messageResponse = [...data.items].reverse();
57
        const updatedMessages = messageResponse.reduce(
58
          (acum, updatedMessage) => {
59
            if (
60
              messages.findIndex(
61
                (message) => message.id === updatedMessage.id
62
              ) === -1
63
            ) {
64
              acum = [...acum, updatedMessage];
65
            }
66
            return acum;
67
          },
68
          []
69
        );
70
 
71
        if (updatedMessages.length > 0) {
15844 stevensc 72
          setMessages([...messages, ...updatedMessages]);
15839 stevensc 73
          setTotalPages(data.pages);
74
          scrollToBottom();
75
        }
76
      })
77
      .finally(() => setLoading(false));
11347 nelberth 78
  };
79
 
15844 stevensc 80
  const onIntersection = async (entities) => {
81
    const target = entities[0];
82
    if (target.isIntersecting) {
83
      if (currentPage < totalPages) {
84
        setCurrentPage((prevState) => prevState + 1);
85
        bottomToScroll.current.scrollBy(0, 200);
86
      }
87
    }
88
  };
89
 
90
  const getAllMessages = () => {
91
    setLoading(true);
92
    axios
93
      .get(url_get_all_messages, {
94
        params: {
95
          page: currentPage,
96
        },
97
      })
98
      .then(({ data: response }) => {
99
        const { data, success } = response;
100
        if (success && data.page > 1) {
101
          setOldMessages([...data.items.slice().reverse(), ...oldMessages]);
102
        }
103
      })
104
      .finally(() => setLoading(false));
105
  };
106
 
107
  //Utilitys
11347 nelberth 108
  const scrollToBottom = () => {
109
    const divToScrollEl = divToScroll.current;
110
    divToScrollEl.scrollIntoView({ behavior: "smooth" });
111
  };
112
 
113
  const onClickEmoji = (event) => {
114
    const shortname = event.currentTarget.dataset.shortname;
115
    const currentText = inputTextEl.current.value;
116
    let cursorPosition = inputTextEl.current.selectionStart;
117
    const textBehind = currentText.substring(0, cursorPosition);
118
    const textForward = currentText.substring(cursorPosition);
119
    inputTextEl.current.value = `${textBehind}${shortname}${textForward}`;
120
    inputTextEl.current.focus();
121
    inputTextEl.current.setSelectionRange(
122
      cursorPosition + shortname.length,
123
      cursorPosition + shortname.length
124
    );
125
  };
126
 
127
  const handleUploadFile = (e) => {
128
    const file = e.target.files[0];
15844 stevensc 129
    if (file) setSelectedFile(file);
11347 nelberth 130
  };
131
 
132
  const removeSelectedFile = () => {
133
    setSelectedFile("");
134
  };
135
 
15844 stevensc 136
  // On send
11347 nelberth 137
  const onHandleSubmit = (data, event) => {
138
    const formData = new FormData();
139
    Object.entries(data).map(([key, value]) => {
140
      formData.append(key, value);
141
    });
142
    event.target.reset();
143
    axios.post(url_send, formData).then((response) => {
144
      setShowEmojione(false);
145
    });
146
  };
147
 
148
  const handleSendFile = () => {
149
    const formData = new FormData();
150
    formData.append("file", selectedFile);
151
    axios.post(url_upload, formData).then(async (response) => {
152
      const resData = response.data;
153
      if (resData.success) {
154
        setSelectedFile("");
155
        setShowEmojione(false);
156
      }
157
    });
158
  };
159
 
15844 stevensc 160
  useEffect(() => {
161
    let timeInterval;
162
    if (loading) return;
163
    timeInterval = setTimeout(() => getMessages(), 2000);
164
 
165
    return () => {
166
      clearTimeout(timeInterval);
167
    };
168
  }, [loading, entity]);
169
 
170
  // infiniteScroll
171
  useEffect(() => {
172
    const options = {
173
      root: null,
174
      rootMargin: "0px",
175
      threshold: 1.0,
176
    };
177
    const observer = new IntersectionObserver(onIntersection, options);
178
    if (loadMoreEl.current) {
179
      observer.observe(loadMoreEl.current);
180
    }
181
    return () => {
182
      observer.disconnect();
183
    };
184
  }, [totalPages]);
185
 
186
  useEffect(() => {
187
    getAllMessages();
188
  }, [currentPage]);
189
 
190
  useEffect(() => {
191
    axios.post(url_mark_seen);
192
  }, []);
193
 
11347 nelberth 194
  return (
195
    <div className={styles.chat}>
196
      <div className={styles.messagesContainer} ref={bottomToScroll}>
197
        <div className={styles.messageWrapper}>
15839 stevensc 198
          {currentPage < totalPages && (
15834 stevensc 199
            <p ref={loadMoreEl} className="mt-2">
11347 nelberth 200
              Cargando...
201
            </p>
202
          )}
203
          <Messages
204
            oldMessages={oldMessages}
15844 stevensc 205
            newMessages={messages}
11347 nelberth 206
            onScrollToBottom={scrollToBottom}
207
            chatType={type}
208
          />
209
          <div ref={divToScroll}></div>
210
        </div>
211
      </div>
15805 stevensc 212
      <div className={styles.chat__input__container}>
11347 nelberth 213
        {showEmojione && <Emojione onClickEmoji={onClickEmoji} />}
214
        <form
215
          onSubmit={handleSubmit(onHandleSubmit)}
216
          encType="multipart/form-data"
217
        >
218
          <button
219
            type="button"
15836 stevensc 220
            className={"btn " + styles.icon_btn}
15833 stevensc 221
            onClick={() => fileInputEl.current.click()}
222
          >
223
            <AttachFileIcon />
224
          </button>
11347 nelberth 225
          <button
226
            type="button"
15836 stevensc 227
            className={"btn " + styles.icon_btn}
15833 stevensc 228
            onClick={() => setShowEmojione(!showEmojione)}
229
          >
230
            <InsertEmoticonIcon />
231
          </button>
11347 nelberth 232
          <input
233
            type="file"
15833 stevensc 234
            ref={(e) => (fileInputEl.current = e)}
11347 nelberth 235
            accept={permittedFiles}
15833 stevensc 236
            onChange={handleUploadFile}
11347 nelberth 237
            hidden
238
          />
239
          <textarea
240
            className={styles.chatInput}
241
            placeholder="Escribe un mensaje"
242
            rows="1"
243
            ref={inputTextEl}
15833 stevensc 244
          />
15836 stevensc 245
          <button type="submit" className={"btn " + styles.send_btn}>
15835 stevensc 246
            <SendIcon />
11347 nelberth 247
          </button>
248
        </form>
249
      </div>
250
      {selectedFile && (
251
        <FileModal
252
          file={selectedFile}
253
          onCancel={removeSelectedFile}
254
          onSend={handleSendFile}
255
        />
256
      )}
257
    </div>
258
  );
259
};
260
 
261
export default Chat;