Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

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