Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

Rev 6932 | Rev 6934 | Ir a la última revisión | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
6932 stevensc 1
import React, { memo, useEffect, useRef, useState } from 'react'
6911 stevensc 2
import { axios } from '../../utils'
3
import { useForm } from 'react-hook-form'
4
import { useDispatch } from 'react-redux'
5
import { addNotification } from '../../redux/notification/notification.actions'
6
import parse from 'html-react-parser'
6927 stevensc 7
import styled, { css } from 'styled-components'
6911 stevensc 8
import SendIcon from '@mui/icons-material/Send'
9
import IconButton from '@mui/material/IconButton'
10
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
11
import AttachFileIcon from '@mui/icons-material/AttachFile'
12
import InsertEmoticonIcon from '@mui/icons-material/InsertEmoticon'
13
 
14
import Options from '../UI/Option'
15
import Emojione from './emojione/Emojione'
16
import FileModal from '../modals/FileModal'
17
 
18
const StyledChatContainer = styled.div`
6921 stevensc 19
  background-color: var(--bg-color);
20
  border-radius: var(--border-radius);
21
  border: 1px solid var(--border-primary);
6911 stevensc 22
  height: 80vh;
23
  display: flex;
24
  flex-direction: column;
6922 stevensc 25
  flex-grow: 1;
6911 stevensc 26
`
27
 
6921 stevensc 28
const StyledChatHeader = styled.div`
6926 stevensc 29
  align-items: center;
6921 stevensc 30
  border-bottom: 1px solid var(--border-primary);
6926 stevensc 31
  display: flex;
32
  justify-content: center;
6921 stevensc 33
  padding: 1rem 0.5rem;
34
  position: relative;
35
 
36
  & > button {
37
    position: absolute;
38
    left: 1rem;
39
    top: 50%;
40
    transform: translateY(-50%);
41
    display: none;
42
  }
43
`
44
 
45
const StyledTitle = styled.h2`
46
  font-size: 1.5rem;
47
  font-weight: 500;
48
  width: fit-content;
49
  max-width: 30ch;
50
  text-align: center;
51
`
52
 
6922 stevensc 53
const StyledMessageList = styled.div`
54
  gap: 0.5rem;
6921 stevensc 55
  display: flex;
6922 stevensc 56
  flex-direction: column-reverse;
6921 stevensc 57
  height: -webkit-fill-available;
58
  padding: 0.5rem 0;
59
  overflow: auto;
60
`
61
 
6927 stevensc 62
const StyledMessage = styled.div`
63
  box-shadow: var(--light-shadow);
64
  display: inline-flex;
65
  flex-direction: column;
66
  gap: 0.5rem;
67
  margin-bottom: 0.5rem;
68
  max-width: 70%;
69
  min-width: 4rem;
70
  padding: 0.5rem;
71
  position: relative;
72
 
73
  & > p {
74
    color: var(--chat-color);
75
    max-width: 100%;
76
    overflow: hidden;
77
    text-overflow: ellipsis;
78
    word-break: break-word;
79
  }
80
 
81
  & > img {
82
    max-width: 250px;
83
    max-height: 250px;
84
    object-fit: contain;
85
  }
86
 
87
  &:first-child {
88
    margin-top: 0.5rem;
89
  }
90
 
91
  .time {
92
    color: $subtitle-color;
93
    font-size: 0.8rem;
94
  }
95
 
96
  .emojione {
97
    width: 1rem;
98
    height: 1rem;
99
  }
100
 
6930 stevensc 101
  ${(props) => {
6932 stevensc 102
    console.log('Render')
103
    if (props.send) {
6930 stevensc 104
      return css`
105
        align-self: flex-end;
106
        background-color: var(--chat-send);
107
        border-radius: 10px 0 10px 10px;
108
      `
109
    }
110
    return css`
111
      align-self: flex-start;
112
      background-color: var(--chat-received);
113
      border-radius: 0 10px 10px 10px;
114
    `
115
  }}
6927 stevensc 116
`
117
 
6933 stevensc 118
function messageAreEqual(oldProps, newProps) {
119
  const messageDifference = oldProps.messages?.filter(
120
    (message, index) => newProps.messages[index].id !== message.id && message
6932 stevensc 121
  )
122
 
123
  return Boolean(messageDifference.length)
124
}
125
 
6911 stevensc 126
const Chat = ({ children }) => {
127
  return <StyledChatContainer>{children}</StyledChatContainer>
128
}
129
 
130
const Header = ({ children, options, onClose }) => {
131
  return (
6921 stevensc 132
    <StyledChatHeader>
6911 stevensc 133
      <IconButton onClick={onClose}>
134
        <ArrowBackIcon />
135
      </IconButton>
136
      {children}
137
      {options && <Options options={options} />}
6921 stevensc 138
    </StyledChatHeader>
6911 stevensc 139
  )
140
}
141
 
6921 stevensc 142
const Title = ({ children, url }) => {
6911 stevensc 143
  if (!url) {
6921 stevensc 144
    return <StyledTitle>{children}</StyledTitle>
6911 stevensc 145
  }
146
 
147
  return (
6921 stevensc 148
    <a href={url} style={{ width: 'fit-content' }}>
149
      <StyledTitle>{children}</StyledTitle>
6911 stevensc 150
    </a>
151
  )
152
}
153
 
6932 stevensc 154
const MemoList = memo(function List({
6933 stevensc 155
  messages = [],
6911 stevensc 156
  pages,
157
  currentPage,
158
  onPagination,
159
  scrollRef,
160
  loading,
6932 stevensc 161
}) {
6921 stevensc 162
  const loadMoreEl = useRef(null)
6911 stevensc 163
 
164
  useEffect(() => {
165
    const observer = new IntersectionObserver(onPagination)
166
 
167
    if (loadMoreEl.current) {
168
      observer.observe(loadMoreEl.current)
169
    }
170
 
171
    return () => {
172
      observer.disconnect()
173
    }
174
  }, [messages])
175
 
176
  return (
6922 stevensc 177
    <StyledMessageList ref={scrollRef}>
178
      {messages.map((message) => (
6932 stevensc 179
        <MemoList.Message message={message} key={message.id} />
6922 stevensc 180
      ))}
181
      {pages > currentPage && !loading && <p ref={loadMoreEl}>Cargando...</p>}
182
    </StyledMessageList>
6911 stevensc 183
  )
6932 stevensc 184
},
185
messageAreEqual)
6911 stevensc 186
 
187
const Message = ({ message }) => {
188
  const senderName = (message) => {
189
    if (message.type === 'group' && !message.u === 1) return message.user_name
190
  }
191
 
192
  const messagesContent = {
193
    // eslint-disable-next-line no-undef
194
    text: <p>{parse(emojione.shortnameToImage(message.m))}</p>,
195
    image: <img src={message.m} alt="chat_image" />,
196
    video: <video src={message.m} preload="none" controls />,
197
    document: (
198
      <img
199
        className="pdf"
200
        src="/storage/type/default/filename/pdf.png"
201
        alt="pdf"
202
      />
203
    ),
204
  }
205
 
206
  return (
6927 stevensc 207
    <>
6911 stevensc 208
      <span className="user_name">{senderName(message)}</span>
6927 stevensc 209
      <StyledMessage send={message.u === 1}>
6911 stevensc 210
        {messagesContent[message.mtype]}
6927 stevensc 211
        <label className="time">
6911 stevensc 212
          {!message.not_received && (
213
            <i
214
              className="fa fa-check"
215
              style={message.seen ? { color: 'blue' } : { color: 'gray' }}
216
            />
217
          )}
218
          {message.time}
219
        </label>
6927 stevensc 220
      </StyledMessage>
221
    </>
6911 stevensc 222
  )
223
}
224
 
225
const SubmitForm = ({ sendUrl, uploadUrl, onSubmit: onComplete }) => {
226
  const [showEmojione, setShowEmojione] = useState(false)
227
  const [isShowFileModal, setIsShowFileModal] = useState(false)
228
  const [isSending, setIsSending] = useState(false)
229
  const dispatch = useDispatch()
230
 
231
  const { handleSubmit, setValue, register, reset, getValues } = useForm()
232
 
233
  const onSubmit = handleSubmit(({ message }) => {
234
    const formData = new FormData()
235
    // eslint-disable-next-line no-undef
236
    formData.append('message', emojione.toShort(message))
237
 
238
    axios.post(sendUrl, formData).then((response) => {
239
      const { success, data } = response.data
240
 
241
      if (!success) {
242
        const errorMessage =
243
          typeof data === 'string' ? data : 'Ha ocurrido un error'
244
 
245
        setShowEmojione(false)
246
        dispatch(addNotification({ style: 'danger', msg: errorMessage }))
247
        return
248
      }
249
 
250
      setShowEmojione(false)
251
      onComplete()
252
      reset()
253
    })
254
  })
255
 
256
  const sendFile = (file) => {
257
    setIsSending(true)
258
    const formData = new FormData()
259
    formData.append('file', file)
260
 
261
    axios
262
      .post(uploadUrl, formData)
263
      .then(({ data: response }) => {
264
        const { success, data } = response
265
        if (!success) {
266
          const errorMessage =
267
            typeof data === 'string' ? data : 'Ha ocurrido un error'
268
          dispatch(addNotification({ style: 'success', msg: errorMessage }))
269
          return
270
        }
271
 
272
        toggleFileModal()
273
        onComplete()
274
      })
275
      .finally(() => setIsSending(false))
276
  }
277
 
278
  const toggleEmojione = () => {
279
    setShowEmojione(!showEmojione)
280
  }
281
 
282
  const toggleFileModal = () => {
283
    setIsShowFileModal(!isShowFileModal)
284
  }
285
 
286
  const onClickEmoji = (event) => {
287
    const shortname = event.currentTarget.dataset.shortname
288
    const currentMessage = getValues('message')
289
    // eslint-disable-next-line no-undef
290
    const unicode = emojione.shortnameToUnicode(shortname)
291
    setValue('message', `${currentMessage}${unicode}`)
292
  }
293
 
294
  return (
295
    <>
296
      <form className="chat__input-container" onSubmit={onSubmit}>
297
        {showEmojione && <Emojione onClickEmoji={onClickEmoji} />}
298
        <IconButton onClick={toggleFileModal}>
299
          <AttachFileIcon />
300
        </IconButton>
301
        <IconButton onClick={toggleEmojione}>
302
          <InsertEmoticonIcon />
303
        </IconButton>
304
        <input
305
          type="text"
306
          name="message"
307
          placeholder="Escribe un mensaje"
308
          ref={register({ required: true })}
309
        />
310
        <IconButton type="submit">
311
          <SendIcon />
312
        </IconButton>
313
      </form>
314
      <FileModal
315
        isShow={isShowFileModal}
316
        onHide={toggleFileModal}
317
        onComplete={sendFile}
318
        loading={isSending}
319
      />
320
    </>
321
  )
322
}
323
 
324
Chat.Header = Header
325
Chat.Title = Title
6932 stevensc 326
Chat.List = MemoList
327
MemoList.Message = Message
6911 stevensc 328
Chat.SubmitForm = SubmitForm
329
 
330
export default Chat