Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

Rev 7326 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
7317 stevensc 1
import React, { useEffect, useRef, useState } from 'react'
2
import { axios } from '../../../utils'
3
import { useSelector } from 'react-redux'
7326 stevensc 4
import { Avatar, IconButton } from '@mui/material'
7317 stevensc 5
import styled from 'styled-components'
7326 stevensc 6
import Add from '@mui/icons-material/Add'
7
import ArrowBackIos from '@mui/icons-material/ArrowBackIos'
8
import PersonRemoveIcon from '@mui/icons-material/PersonRemove'
7317 stevensc 9
 
10
import TextBox from './TextBox'
11
import ChatList from './ChatList'
12
import EmptySection from '../../UI/EmptySection'
13
import ConfirmModal from '../../modals/ConfirmModal'
14
import ConferenceModal from '../../modals/ConferenceModal'
7322 stevensc 15
import SearchInput from '../../UI/SearchInput'
7317 stevensc 16
 
17
const StyledShowOptions = styled.div`
7325 stevensc 18
  align-items: flex-start;
19
  display: flex;
20
  flex-direction: column;
7317 stevensc 21
  height: 342px;
22
  overflow-y: auto;
7325 stevensc 23
  ul {
24
    width: 100%;
25
  }
7317 stevensc 26
  &.show {
27
    display: flex;
28
  }
29
  &.hide {
30
    display: none;
31
  }
7324 stevensc 32
  .option-tab {
33
    align-items: center;
34
    border-bottom: 1px solid #e2e2e2;
7317 stevensc 35
    cursor: pointer;
7324 stevensc 36
    display: flex;
37
    gap: 0.5rem;
38
    padding: 0.5rem;
7325 stevensc 39
    width: 100%;
7324 stevensc 40
    &:hover {
41
      background-color: #e2e2e2;
7317 stevensc 42
    }
43
  }
7322 stevensc 44
  .contact-tab {
45
    align-items: center;
46
    border-bottom: 1px solid #e2e2e2;
7317 stevensc 47
    display: flex;
7322 stevensc 48
    justify-content: space-between;
49
    padding: 0.2rem 0.5rem;
50
    .info {
7317 stevensc 51
      display: flex;
52
      align-items: center;
7322 stevensc 53
      gap: 0.5rem;
7317 stevensc 54
    }
55
  }
56
`
57
 
7325 stevensc 58
const StyledComeBackButton = styled(IconButton)`
59
  border-radius: 0px !important;
60
  font-size: 1rem !important;
61
  width: -webkit-fill-available;
62
  justify-content: flex-start !important;
7324 stevensc 63
`
64
 
7317 stevensc 65
const OPTIONS = {
66
  GROUP: 'group',
67
  CONFERENCE: 'conference',
68
  ADD_CONTACTS: 'addContacts',
69
  LIST_CONTACTS: 'listContacts',
70
  INITIAL: null,
71
}
72
 
73
const PersonalChat = ({
74
  entity,
75
  onClose,
76
  onMinimize,
77
  onRead,
78
  not_seen_messages,
79
  minimized,
80
}) => {
81
  const [optionTab, setOptionTab] = useState(OPTIONS.INITIAL)
82
  const [availableContactsToAdd, setAvailableContactsToAdd] = useState([])
83
  const [groupContactsList, setGroupContactsList] = useState([])
84
  const [confirmModalShow, setConfirmModalShow] = useState(false)
85
  const [showConferenceModal, setShowConferenceModal] = useState(false)
86
  const [search, setSearch] = useState('')
87
  const [messages, setMessages] = useState([])
88
  const [oldMessages, setOldMessages] = useState([])
89
  const [currentPage, setCurrentPage] = useState(1)
90
  const [lastPage, setLastPage] = useState(1)
91
  const [loading, setLoading] = useState(false)
92
  const labels = useSelector(({ intl }) => intl.labels)
93
 
94
  const filtredGroupList = groupContactsList.filter((conversation) =>
95
    conversation.name.toLowerCase().includes(search.toLowerCase())
96
  )
97
 
98
  // refs
99
  const conversationListEl = useRef(null)
100
  const loader = useRef(null)
101
  const modalActionUrl = useRef('')
102
 
103
  const handleActive = () => {
104
    onRead(entity)
105
    onMinimize(entity)
106
  }
107
 
108
  const handleGetMessages = async () => {
109
    setLoading(true)
110
    const response = await axios.get(entity?.url_get_all_messages)
111
    const resData = response.data
112
    if (!resData.success) {
113
      return 'ha ocurrido un error'
114
    }
115
    const updatedMessages = [...resData.data.items].reverse()
116
    const newMessages = updatedMessages.reduce((acum, updatedMessage) => {
117
      if (
118
        messages.findIndex((message) => message.id === updatedMessage.id) === -1
119
      ) {
120
        acum = [...acum, updatedMessage]
121
      }
122
      return acum
123
    }, [])
124
 
125
    if (newMessages.length > 0) {
126
      setMessages([...messages, ...newMessages])
127
      setLoading(false)
128
      setLastPage(resData.data.pages)
129
      scrollToBottom()
130
    } else {
131
      setLoading(false)
132
    }
133
  }
134
 
135
  const handleLoadMore = async () => {
136
    await axios
137
      .get(`${entity?.url_get_all_messages}?page=${currentPage}`)
138
      .then((response) => {
139
        const resData = response.data
140
        if (resData.success) {
141
          if (resData.data.page > 1) {
142
            const updatedOldMessages = [...resData.data.items].reverse()
143
            setOldMessages([...updatedOldMessages, ...oldMessages])
144
            /* scrollDownBy(100); */
145
          }
146
        }
147
      })
148
  }
149
 
150
  const handleCloseChat = () => onClose(entity)
151
 
152
  const sendMessage = async (message) => {
153
    const formData = new FormData()
154
    formData.append('message', emojione.toShort(message))
155
    await axios.post(entity?.url_send, formData).then((response) => {
156
      const resData = response.data
157
      if (resData.success) {
158
        let newMessage = resData.data
159
 
160
        entity?.online
161
          ? (newMessage = { ...newMessage, not_received: false })
162
          : (newMessage = { ...newMessage, not_received: true })
163
 
164
        setMessages([...messages, newMessage])
165
      }
166
    })
167
    // await handleGetMessages()
168
    // setResponseMessage(null)
169
  }
170
 
171
  const showOptions = (tab = OPTIONS.INITIAL) => {
172
    onMinimize(entity, false)
173
    setOptionTab(tab)
174
  }
175
 
176
  const handleAddPersonToGroup = async (id) => {
177
    const formData = new FormData()
178
    formData.append('uid', id)
179
    await axios
180
      .post(entity?.url_add_user_to_group, formData)
181
      .then((response) => {
182
        const resData = response.data
183
        if (resData.success) {
184
          loadPersonsAvailable()
185
        }
186
      })
187
  }
188
 
189
  const handleConfirmModalAction = async () => {
190
    try {
191
      const { data } = await axios.post(modalActionUrl.current)
192
      if (!data.success) console.log('Error in confirm modal action')
193
      handleConfirmModalShow()
194
      onClose(entity)
195
      return
196
    } catch (error) {
197
      console.log(error)
198
    }
199
  }
200
 
201
  const handleObserver = (entities) => {
202
    const target = entities[0]
203
    if (target.isIntersecting) {
204
      setCurrentPage((prevState) => prevState + 1)
205
    }
206
  }
207
 
208
  const scrollToBottom = () => {
209
    if (conversationListEl.current) {
210
      conversationListEl.current.scrollTop =
211
        conversationListEl.current.scrollHeight * 9
212
    }
213
  }
214
 
215
  const handleConfirmModalShow = () => setConfirmModalShow(!confirmModalShow)
216
 
217
  const handleConfirmModalAccept = () => handleConfirmModalAction()
218
 
219
  /* const handleResponseMessage = (element) => {
220
    element.mtype === 'text'
221
      ? setResponseMessage(element)
222
      : setResponseMessage({ ...element, m: 'Archivo adjunto' })
223
 
224
    textAreaEl.current && textAreaEl.current.focus()
225
  } */
226
 
227
  const displayConferenceModal = () =>
228
    setShowConferenceModal(!showConferenceModal)
229
 
230
  const loadPersonsAvailable = async () => {
231
    await axios
232
      .get(entity?.url_get_contacts_availables_for_group)
233
      .then((response) => {
234
        const resData = response.data
235
        if (resData.success) {
236
          setAvailableContactsToAdd(resData.data)
237
        }
238
      })
239
  }
240
 
241
  const loadGroupContacts = async () => {
242
    await axios.get(entity?.url_get_contact_group_list).then((response) => {
243
      const resData = response.data
244
      if (resData.success) {
245
        setGroupContactsList(resData.data)
246
      }
247
    })
248
  }
249
 
250
  const handleDeletePersonFromGroup = async (urlDeletePersonFromGroup) => {
251
    await axios.post(urlDeletePersonFromGroup).then((response) => {
252
      const resData = response.data
253
      if (resData.success) {
254
        loadGroupContacts()
255
      }
256
    })
257
  }
258
 
259
  const tabsOptions = {
260
    group: (
261
      <ul>
262
        {entity?.url_get_contact_group_list && (
7324 stevensc 263
          <li>
264
            <button
265
              className="option-tab"
266
              onClick={() => showOptions(OPTIONS.LIST_CONTACTS)}
267
            >
7317 stevensc 268
              <i className="fa fa-group" />
269
              Integrantes
7324 stevensc 270
            </button>
7317 stevensc 271
          </li>
272
        )}
273
        {entity?.url_add_user_to_group && (
7324 stevensc 274
          <li>
275
            <button
276
              className="option-tab"
277
              onClick={() => showOptions(OPTIONS.ADD_CONTACTS)}
278
            >
7317 stevensc 279
              <i className="fa fa-user-plus"></i>
7324 stevensc 280
              {labels.add_contacts}
281
            </button>
7317 stevensc 282
          </li>
283
        )}
284
        {entity?.url_zoom && (
7324 stevensc 285
          <li>
286
            <button className="option-tab" onClick={displayConferenceModal}>
7317 stevensc 287
              <i className="fa fa-user-plus" />
7324 stevensc 288
              {labels.create_conference}
289
            </button>
7317 stevensc 290
          </li>
291
        )}
292
        {entity?.url_delete && (
7324 stevensc 293
          <li>
294
            <button
295
              className="option-tab"
296
              onClick={() => {
297
                handleConfirmModalShow()
298
                modalActionUrl.current = entity?.url_delete
299
              }}
300
            >
7317 stevensc 301
              <i className="fa fa-trash"></i>
7324 stevensc 302
              Eliminar grupo
303
            </button>
7317 stevensc 304
          </li>
305
        )}
306
        {!!entity?.url_leave && (
7324 stevensc 307
          <li>
308
            <button
309
              className="option-tab"
310
              onClick={() => {
311
                handleConfirmModalShow()
312
                modalActionUrl.current = entity?.url_leave
313
              }}
314
            >
7326 stevensc 315
              <PersonRemoveIcon />
7324 stevensc 316
              Dejar grupo
317
            </button>
7317 stevensc 318
          </li>
319
        )}
320
      </ul>
321
    ),
322
    addContacts: (
323
      <>
7322 stevensc 324
        {availableContactsToAdd.length ? (
7323 stevensc 325
          <ul>
326
            {availableContactsToAdd.map(({ image, name, id }) => (
327
              <li key={id}>
328
                <div className="contact-tab">
329
                  <div className="info">
330
                    <Avatar
331
                      src={image}
332
                      alt={`${name} profile image`}
333
                      sx={{
334
                        height: '36px',
335
                        width: '36px',
336
                      }}
337
                    />
338
                    <span>{name}</span>
7322 stevensc 339
                  </div>
7323 stevensc 340
                  <IconButton onClick={() => handleAddPersonToGroup(id)}>
341
                    <Add />
342
                  </IconButton>
343
                </div>
344
              </li>
345
            ))}
346
          </ul>
7322 stevensc 347
        ) : (
7320 stevensc 348
          <EmptySection message={labels.not_contacts} />
7317 stevensc 349
        )}
350
      </>
351
    ),
352
    listContacts: (
353
      <>
7322 stevensc 354
        <SearchInput onChange={(e) => setSearch(e.target.value)} />
7317 stevensc 355
        {filtredGroupList.length ? (
7322 stevensc 356
          <ul>
357
            {filtredGroupList.map(
358
              ({ image, name, url_remove_from_group, id }) => {
359
                return (
360
                  <li key={id}>
361
                    <div className="contact-tab">
362
                      <div className="info">
363
                        <Avatar
364
                          src={image}
365
                          alt={`${name} profile image`}
366
                          sx={{
367
                            height: '36px',
368
                            width: '36px',
369
                          }}
370
                        />
371
                        <span>{name}</span>
372
                      </div>
373
                      {url_remove_from_group && (
374
                        <IconButton
375
                          onClick={() =>
376
                            handleDeletePersonFromGroup(url_remove_from_group)
377
                          }
378
                        >
7327 stevensc 379
                          <PersonRemoveIcon />
7322 stevensc 380
                        </IconButton>
381
                      )}
382
                    </div>
383
                  </li>
384
                )
385
              }
386
            )}
387
          </ul>
7317 stevensc 388
        ) : (
7322 stevensc 389
          <EmptySection message={labels.not_contacts} />
7317 stevensc 390
        )}
391
      </>
392
    ),
393
  }
394
 
395
  // getMessageOnMaximize and subscribe to infinite Loader
396
  useEffect(async () => {
397
    const options = {
398
      root: null,
399
      rootMargin: '20px',
400
      treshold: 1.0,
401
    }
402
    const observer = new IntersectionObserver(handleObserver, options)
403
    if (!minimized) {
404
      await handleGetMessages()
405
      // loader observer
406
      if (loader.current) {
407
        observer.observe(loader.current)
408
      }
409
    }
410
    return () => {
411
      if (loader.current) {
412
        observer.unobserve(loader.current)
413
      }
414
    }
415
  }, [minimized])
416
 
417
  // LoadMore on change page
418
  useEffect(() => {
419
    let loadMore = () => handleLoadMore()
420
    loadMore()
421
    return () => {
422
      loadMore = null
423
    }
424
  }, [currentPage])
425
 
426
  // getMessagesInterval
427
  useEffect(() => {
428
    if (window.location.pathname === '/group/my-groups') {
429
      const items = document.getElementsByClassName('sc-jSgupP')
430
      if (items && items.length > 0) {
431
        items[0].style.display = 'none'
432
      }
433
    }
434
  }, [minimized])
435
 
436
  useEffect(() => {
437
    let timer
438
    if (!minimized && !loading) {
439
      timer = setTimeout(() => handleGetMessages(), 1000)
440
    }
441
    return () => {
442
      clearTimeout(timer)
443
    }
444
  }, [minimized, loading])
445
 
446
  // useEffect for tabs changing
447
  useEffect(() => {
448
    if (optionTab === 'addContacts') loadPersonsAvailable()
449
    if (optionTab === 'listContacts') loadGroupContacts()
450
  }, [optionTab])
451
 
452
  return (
453
    <>
454
      <div className="personal-chat">
455
        <div className={`chat-header ${not_seen_messages ? 'notify' : ''}`}>
7324 stevensc 456
          <Avatar
457
            src={entity?.image || '/images/users-group.png'}
458
            sx={{ width: '36px', height: '36px' }}
459
          />
7317 stevensc 460
          <div className="info-content">
461
            <a href={entity?.profile} target="_blank" rel="noreferrer">
7323 stevensc 462
              {entity?.name}
7317 stevensc 463
            </a>
464
            {entity?.type === 'user' && (
465
              <small className={entity?.online ? 'online' : 'offline'}>
466
                {entity?.online ? 'En línea' : 'Desconectado'}
467
              </small>
468
            )}
469
          </div>
470
          <div className="options ml-auto">
471
            <i
472
              className="cursor-pointer fa fa-gear"
473
              onClick={() =>
474
                showOptions(
475
                  entity?.type === 'user' ? OPTIONS.CONFERENCE : OPTIONS.GROUP
476
                )
477
              }
478
            />
479
            <i
480
              className={'cursor-pointer fa fa-minus-circle'}
481
              onClick={handleActive}
482
            />
483
            <i
484
              className="cursor-pointer fa fa-times-circle"
485
              onClick={handleCloseChat}
486
            />
487
          </div>
488
        </div>
489
 
490
        <div
491
          className="panel-body"
492
          style={{ display: !minimized ? 'block' : 'none' }}
493
        >
494
          {optionTab ? (
495
            <StyledShowOptions>
7325 stevensc 496
              <StyledComeBackButton
497
                onClick={() => showOptions(OPTIONS.INITIAL)}
498
              >
7323 stevensc 499
                <ArrowBackIos />
7324 stevensc 500
                {labels.back}
7325 stevensc 501
              </StyledComeBackButton>
502
              {tabsOptions[optionTab]}
7317 stevensc 503
            </StyledShowOptions>
504
          ) : (
505
            <>
506
              <ChatList
507
                messages={[...oldMessages, ...messages]}
508
                currentPage={currentPage}
509
                lastPage={lastPage}
510
                conversationList={conversationListEl}
511
                loader={loader}
512
              />
513
              <TextBox
514
                uploadUrl={entity?.url_upload}
515
                isNotSeen={not_seen_messages}
516
                markRead={() => onRead(entity)}
517
                onSend={sendMessage}
518
              />
519
            </>
520
          )}
521
        </div>
522
      </div>
523
      <ConfirmModal
524
        show={confirmModalShow}
525
        onClose={handleConfirmModalShow}
526
        onAccept={handleConfirmModalAccept}
527
      />
528
      <ConferenceModal
529
        show={showConferenceModal}
530
        zoomUrl={entity?.url_zoom}
531
        onClose={() => {
532
          showOptions()
533
          displayConferenceModal()
534
        }}
535
      />
536
    </>
537
  )
538
}
539
 
540
export default React.memo(PersonalChat)