Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

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

Rev Autor Línea Nro. Línea
6911 stevensc 1
import React, { useEffect, useRef, useState } from 'react'
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) => {
6931 stevensc 102
    console.log(props.send)
103
    console.log(props.$send)
6930 stevensc 104
    if (props.$send) {
105
      return css`
106
        align-self: flex-end;
107
        background-color: var(--chat-send);
108
        border-radius: 10px 0 10px 10px;
109
      `
110
    }
111
    return css`
112
      align-self: flex-start;
113
      background-color: var(--chat-received);
114
      border-radius: 0 10px 10px 10px;
115
    `
116
  }}
6927 stevensc 117
`
118
 
6911 stevensc 119
const Chat = ({ children }) => {
120
  return <StyledChatContainer>{children}</StyledChatContainer>
121
}
122
 
123
const Header = ({ children, options, onClose }) => {
124
  return (
6921 stevensc 125
    <StyledChatHeader>
6911 stevensc 126
      <IconButton onClick={onClose}>
127
        <ArrowBackIcon />
128
      </IconButton>
129
      {children}
130
      {options && <Options options={options} />}
6921 stevensc 131
    </StyledChatHeader>
6911 stevensc 132
  )
133
}
134
 
6921 stevensc 135
const Title = ({ children, url }) => {
6911 stevensc 136
  if (!url) {
6921 stevensc 137
    return <StyledTitle>{children}</StyledTitle>
6911 stevensc 138
  }
139
 
140
  return (
6921 stevensc 141
    <a href={url} style={{ width: 'fit-content' }}>
142
      <StyledTitle>{children}</StyledTitle>
6911 stevensc 143
    </a>
144
  )
145
}
146
 
147
const List = ({
148
  messages,
149
  pages,
150
  currentPage,
151
  onPagination,
152
  scrollRef,
153
  loading,
154
}) => {
6921 stevensc 155
  const loadMoreEl = useRef(null)
6911 stevensc 156
 
157
  useEffect(() => {
158
    const observer = new IntersectionObserver(onPagination)
159
 
160
    if (loadMoreEl.current) {
161
      observer.observe(loadMoreEl.current)
162
    }
163
 
164
    return () => {
165
      observer.disconnect()
166
    }
167
  }, [messages])
168
 
169
  return (
6922 stevensc 170
    <StyledMessageList ref={scrollRef}>
171
      {messages.map((message) => (
172
        <List.Message message={message} key={message.id} />
173
      ))}
174
      {pages > currentPage && !loading && <p ref={loadMoreEl}>Cargando...</p>}
175
    </StyledMessageList>
6911 stevensc 176
  )
177
}
178
 
179
const Message = ({ message }) => {
180
  const senderName = (message) => {
181
    if (message.type === 'group' && !message.u === 1) return message.user_name
182
  }
183
 
184
  const messagesContent = {
185
    // eslint-disable-next-line no-undef
186
    text: <p>{parse(emojione.shortnameToImage(message.m))}</p>,
187
    image: <img src={message.m} alt="chat_image" />,
188
    video: <video src={message.m} preload="none" controls />,
189
    document: (
190
      <img
191
        className="pdf"
192
        src="/storage/type/default/filename/pdf.png"
193
        alt="pdf"
194
      />
195
    ),
196
  }
197
 
198
  return (
6927 stevensc 199
    <>
6911 stevensc 200
      <span className="user_name">{senderName(message)}</span>
6927 stevensc 201
      <StyledMessage send={message.u === 1}>
6911 stevensc 202
        {messagesContent[message.mtype]}
6927 stevensc 203
        <label className="time">
6911 stevensc 204
          {!message.not_received && (
205
            <i
206
              className="fa fa-check"
207
              style={message.seen ? { color: 'blue' } : { color: 'gray' }}
208
            />
209
          )}
210
          {message.time}
211
        </label>
6927 stevensc 212
      </StyledMessage>
213
    </>
6911 stevensc 214
  )
215
}
216
 
217
const SubmitForm = ({ sendUrl, uploadUrl, onSubmit: onComplete }) => {
218
  const [showEmojione, setShowEmojione] = useState(false)
219
  const [isShowFileModal, setIsShowFileModal] = useState(false)
220
  const [isSending, setIsSending] = useState(false)
221
  const dispatch = useDispatch()
222
 
223
  const { handleSubmit, setValue, register, reset, getValues } = useForm()
224
 
225
  const onSubmit = handleSubmit(({ message }) => {
226
    const formData = new FormData()
227
    // eslint-disable-next-line no-undef
228
    formData.append('message', emojione.toShort(message))
229
 
230
    axios.post(sendUrl, formData).then((response) => {
231
      const { success, data } = response.data
232
 
233
      if (!success) {
234
        const errorMessage =
235
          typeof data === 'string' ? data : 'Ha ocurrido un error'
236
 
237
        setShowEmojione(false)
238
        dispatch(addNotification({ style: 'danger', msg: errorMessage }))
239
        return
240
      }
241
 
242
      setShowEmojione(false)
243
      onComplete()
244
      reset()
245
    })
246
  })
247
 
248
  const sendFile = (file) => {
249
    setIsSending(true)
250
    const formData = new FormData()
251
    formData.append('file', file)
252
 
253
    axios
254
      .post(uploadUrl, formData)
255
      .then(({ data: response }) => {
256
        const { success, data } = response
257
        if (!success) {
258
          const errorMessage =
259
            typeof data === 'string' ? data : 'Ha ocurrido un error'
260
          dispatch(addNotification({ style: 'success', msg: errorMessage }))
261
          return
262
        }
263
 
264
        toggleFileModal()
265
        onComplete()
266
      })
267
      .finally(() => setIsSending(false))
268
  }
269
 
270
  const toggleEmojione = () => {
271
    setShowEmojione(!showEmojione)
272
  }
273
 
274
  const toggleFileModal = () => {
275
    setIsShowFileModal(!isShowFileModal)
276
  }
277
 
278
  const onClickEmoji = (event) => {
279
    const shortname = event.currentTarget.dataset.shortname
280
    const currentMessage = getValues('message')
281
    // eslint-disable-next-line no-undef
282
    const unicode = emojione.shortnameToUnicode(shortname)
283
    setValue('message', `${currentMessage}${unicode}`)
284
  }
285
 
286
  return (
287
    <>
288
      <form className="chat__input-container" onSubmit={onSubmit}>
289
        {showEmojione && <Emojione onClickEmoji={onClickEmoji} />}
290
        <IconButton onClick={toggleFileModal}>
291
          <AttachFileIcon />
292
        </IconButton>
293
        <IconButton onClick={toggleEmojione}>
294
          <InsertEmoticonIcon />
295
        </IconButton>
296
        <input
297
          type="text"
298
          name="message"
299
          placeholder="Escribe un mensaje"
300
          ref={register({ required: true })}
301
        />
302
        <IconButton type="submit">
303
          <SendIcon />
304
        </IconButton>
305
      </form>
306
      <FileModal
307
        isShow={isShowFileModal}
308
        onHide={toggleFileModal}
309
        onComplete={sendFile}
310
        loading={isSending}
311
      />
312
    </>
313
  )
314
}
315
 
316
Chat.Header = Header
317
Chat.Title = Title
318
Chat.List = List
319
List.Message = Message
320
Chat.SubmitForm = SubmitForm
321
 
322
export default Chat