Proyectos de Subversion LeadersLinked - SPA

Rev

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

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