Proyectos de Subversion LeadersLinked - Backend

Rev

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