Proyectos de Subversion LeadersLinked - Backend

Rev

Rev 16006 | Rev 16227 | 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";
15851 stevensc 7
import MessagesList from "./messages/MessagesList";
16226 stevensc 8
import IconButton from "@mui/material/IconButton";
15834 stevensc 9
import AttachFileIcon from "@mui/icons-material/AttachFile";
10
import InsertEmoticonIcon from "@mui/icons-material/InsertEmoticon";
15835 stevensc 11
import SendIcon from "@mui/icons-material/Send";
16226 stevensc 12
import MoreVertIcon from "@mui/icons-material/MoreVert";
11347 nelberth 13
 
15834 stevensc 14
import styles from "./chat.module.scss";
15
 
11347 nelberth 16
const permittedFiles =
17
  "video/mp4, video/mpeg, video/webm, application/pdf, image/jpeg, image/png, image/jpg";
18
 
15834 stevensc 19
const Chat = ({ entity }) => {
15839 stevensc 20
  const [oldMessages, setOldMessages] = useState([]);
15844 stevensc 21
  const [messages, setMessages] = useState([]);
15839 stevensc 22
  const [totalPages, setTotalPages] = useState(1);
23
  const [currentPage, setCurrentPage] = useState(1);
16004 stevensc 24
 
15839 stevensc 25
  const [loading, setLoading] = useState(false);
16004 stevensc 26
  const [isGettingMessages, setIsGettingMessages] = useState(false);
15839 stevensc 27
 
11347 nelberth 28
  const [showEmojione, setShowEmojione] = useState(false);
29
  const [selectedFile, setSelectedFile] = useState("");
30
 
16006 stevensc 31
  const { handleSubmit } = useForm();
15834 stevensc 32
 
16001 stevensc 33
  const scrollList = useRef(null);
11347 nelberth 34
  const inputTextEl = useRef(null);
35
  const fileInputEl = useRef(null);
15834 stevensc 36
 
37
  const {
38
    url_get_all_messages,
39
    url_send,
40
    url_upload,
41
    url_close,
42
    url_mark_seen,
43
    type,
44
  } = entity;
45
 
15844 stevensc 46
  // Get messages
47
  const getMessages = () => {
15839 stevensc 48
    setLoading(true);
49
    axios
50
      .get(url_get_all_messages)
51
      .then(({ data: response }) => {
52
        const { data, success } = response;
53
 
54
        if (!success) {
55
          return console.log("Ha ocurrido un error");
11347 nelberth 56
        }
15839 stevensc 57
 
58
        const messageResponse = [...data.items].reverse();
59
        const updatedMessages = messageResponse.reduce(
60
          (acum, updatedMessage) => {
61
            if (
62
              messages.findIndex(
63
                (message) => message.id === updatedMessage.id
64
              ) === -1
65
            ) {
66
              acum = [...acum, updatedMessage];
67
            }
68
            return acum;
69
          },
70
          []
71
        );
72
 
73
        if (updatedMessages.length > 0) {
15975 stevensc 74
          setMessages((prevMessages) => [...prevMessages, ...updatedMessages]);
15839 stevensc 75
          setTotalPages(data.pages);
16002 stevensc 76
          scrollTo(scrollList);
15839 stevensc 77
        }
78
      })
79
      .finally(() => setLoading(false));
11347 nelberth 80
  };
81
 
15851 stevensc 82
  const onIntersection = (entities) => {
15844 stevensc 83
    const target = entities[0];
15880 stevensc 84
    if (target.isIntersecting && currentPage < totalPages) {
16004 stevensc 85
      setIsGettingMessages(true);
15880 stevensc 86
      setCurrentPage((prevState) => prevState + 1);
16002 stevensc 87
      scrollTo(scrollList, 200);
15844 stevensc 88
    }
89
  };
90
 
15860 stevensc 91
  const getOldMessages = () => {
16004 stevensc 92
    setIsGettingMessages(true);
15844 stevensc 93
    axios
15860 stevensc 94
      .get(`${url_get_all_messages}?page=${currentPage}`)
15844 stevensc 95
      .then(({ data: response }) => {
96
        const { data, success } = response;
97
        if (success && data.page > 1) {
98
          setOldMessages([...data.items.slice().reverse(), ...oldMessages]);
99
        }
100
      })
16004 stevensc 101
      .finally(() => setIsGettingMessages(false));
15844 stevensc 102
  };
103
 
104
  //Utilitys
16001 stevensc 105
  const scrollTo = (element, distance) => {
106
    const divToScrollEl = element.current;
16003 stevensc 107
    const options = {
108
      top: distance,
109
      behavior: "smooth",
110
    };
111
 
16001 stevensc 112
    if (!distance) {
16003 stevensc 113
      divToScrollEl.scrollBy({ ...options, top: divToScrollEl.scrollHeight });
16001 stevensc 114
      return;
115
    }
16003 stevensc 116
    divToScrollEl.scrollBy(options);
11347 nelberth 117
  };
118
 
119
  const onClickEmoji = (event) => {
120
    const shortname = event.currentTarget.dataset.shortname;
15975 stevensc 121
    const currentText = inputTextEl.current.value;
15998 stevensc 122
    const cursorPosition = inputTextEl.current.selectionStart;
15975 stevensc 123
    const textBehind = currentText.substring(0, cursorPosition);
124
    const textForward = currentText.substring(cursorPosition);
15998 stevensc 125
    const unicode = emojione.shortnameToUnicode(shortname);
126
    inputTextEl.current.value = `${textBehind}${unicode}${textForward}`;
11347 nelberth 127
    inputTextEl.current.focus();
15975 stevensc 128
    inputTextEl.current.setSelectionRange(
15998 stevensc 129
      cursorPosition + unicode.length,
130
      cursorPosition + unicode.length
15975 stevensc 131
    );
11347 nelberth 132
  };
133
 
15880 stevensc 134
  const handleUploadFile = ({ target }) => {
135
    const file = target.files[0];
15975 stevensc 136
    if (!file) return;
15880 stevensc 137
    setSelectedFile(file);
11347 nelberth 138
  };
139
 
140
  const removeSelectedFile = () => {
141
    setSelectedFile("");
142
  };
143
 
15844 stevensc 144
  // On send
16001 stevensc 145
  const handleKeyDown = (e) => {
15999 stevensc 146
    if (e.key !== "Enter") return false;
16000 stevensc 147
    e.preventDefault();
15999 stevensc 148
    onHandleSubmit();
149
  };
150
 
15998 stevensc 151
  const onHandleSubmit = () => {
11347 nelberth 152
    const formData = new FormData();
15998 stevensc 153
    formData.append("message", emojione.toShort(inputTextEl.current.value));
154
    axios.post(url_send, formData).then(({ data: response }) => {
155
      const { data, success } = response;
156
      if (!success) {
157
        console.log("Ha ocurrido un error: " + data);
158
        return;
159
      }
160
      inputTextEl.current.value = "";
15943 stevensc 161
      setShowEmojione(false);
16002 stevensc 162
      scrollTo(scrollList);
15943 stevensc 163
    });
11347 nelberth 164
  };
165
 
166
  const handleSendFile = () => {
167
    const formData = new FormData();
168
    formData.append("file", selectedFile);
15880 stevensc 169
    axios.post(url_upload, formData).then(({ data: response }) => {
170
      const { success, data } = response;
171
      if (!success) {
172
        console.log("Ha ocurrido un error: " + data);
173
        return;
11347 nelberth 174
      }
15880 stevensc 175
      setSelectedFile("");
176
      setShowEmojione(false);
16002 stevensc 177
      scrollTo(scrollList);
11347 nelberth 178
    });
179
  };
180
 
15844 stevensc 181
  useEffect(() => {
182
    let timeInterval;
183
    if (loading) return;
184
    timeInterval = setTimeout(() => getMessages(), 2000);
185
 
186
    return () => {
187
      clearTimeout(timeInterval);
188
    };
15880 stevensc 189
  }, [loading]);
15844 stevensc 190
 
191
  useEffect(() => {
15880 stevensc 192
    setMessages([]);
193
    setOldMessages([]);
194
    setTotalPages(1);
195
    setCurrentPage(1);
196
  }, [entity]);
15844 stevensc 197
 
15880 stevensc 198
  useEffect(() => getOldMessages(), [currentPage]);
15844 stevensc 199
 
15880 stevensc 200
  useEffect(() => axios.post(url_mark_seen), []);
201
 
11347 nelberth 202
  return (
203
    <div className={styles.chat}>
16226 stevensc 204
      <Chat.Header name={entity.name} />
16001 stevensc 205
      <MessagesList
206
        isLastPage={currentPage >= totalPages}
207
        messages={[...oldMessages, ...messages]}
208
        onIntersection={onIntersection}
209
        scrollRef={scrollList}
16004 stevensc 210
        isLoading={isGettingMessages}
16001 stevensc 211
      />
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"
16002 stevensc 242
            onKeyDown={handleKeyDown}
11347 nelberth 243
            ref={inputTextEl}
16002 stevensc 244
            rows="1"
15833 stevensc 245
          />
15836 stevensc 246
          <button type="submit" className={"btn " + styles.send_btn}>
15835 stevensc 247
            <SendIcon />
11347 nelberth 248
          </button>
249
        </form>
250
      </div>
251
      {selectedFile && (
252
        <FileModal
253
          file={selectedFile}
254
          onCancel={removeSelectedFile}
255
          onSend={handleSendFile}
256
        />
257
      )}
258
    </div>
259
  );
260
};
261
 
16226 stevensc 262
const Header = ({ name, conferenceUrl }) => {
263
  const [isShowConferenceModal, setisShowConferenceModal] = useState(false);
264
 
265
  const toggleConferenceModal = () =>
266
    setisShowConferenceModal(!isShowConferenceModal);
267
 
268
  const options = [
269
    { label: "Crear Conferencia", action: toggleConferenceModal },
270
  ];
271
 
272
  return (
273
    <div className={styles.chat_header}>
274
      <h2>{name}</h2>
275
      <Header.Options options={options} />
276
    </div>
277
  );
278
};
279
 
280
const Options = ({ options }) => {
281
  const [isShowMenu, setIsShowMenu] = useState(false);
282
 
283
  const toggleOptions = () => {
284
    setIsShowMenu(!isShowMenu);
285
  };
286
 
287
  return (
288
    <div className="options">
289
      <IconButton onClick={toggleOptions}>
290
        <MoreVertIcon />
291
      </IconButton>
292
      <div className="position-relative">
293
        <div className={`feed-options ${isShowMenu ? "active" : ""}`}>
294
          <ul>
295
            {options.map((option, index) => (
296
              <li key={index}>
297
                <button className="option-btn" onClick={option.action}>
298
                  {option.label}
299
                </button>
300
              </li>
301
            ))}
302
          </ul>
303
        </div>
304
      </div>
305
    </div>
306
  );
307
};
308
 
309
Chat.Header = Header;
310
Header.Options = Options;
311
 
11347 nelberth 312
export default Chat;