Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

Rev 6921 | Rev 6926 | 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'
7
import styled 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'
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`
29
  border-bottom: 1px solid var(--border-primary);
30
  padding: 1rem 0.5rem;
31
  position: relative;
32
  text-align: center;
33
 
34
  & > button {
35
    position: absolute;
36
    left: 1rem;
37
    top: 50%;
38
    transform: translateY(-50%);
39
    display: none;
40
  }
41
`
42
 
43
const StyledTitle = styled.h2`
44
  font-size: 1.5rem;
45
  font-weight: 500;
46
  width: fit-content;
47
  max-width: 30ch;
48
  text-align: center;
49
`
50
 
6922 stevensc 51
const StyledMessageList = styled.div`
52
  gap: 0.5rem;
6921 stevensc 53
  display: flex;
6922 stevensc 54
  flex-direction: column-reverse;
6921 stevensc 55
  height: -webkit-fill-available;
56
  padding: 0.5rem 0;
57
  overflow: auto;
58
`
59
 
6911 stevensc 60
const Chat = ({ children }) => {
61
  return <StyledChatContainer>{children}</StyledChatContainer>
62
}
63
 
64
const Header = ({ children, options, onClose }) => {
65
  return (
6921 stevensc 66
    <StyledChatHeader>
6911 stevensc 67
      <IconButton onClick={onClose}>
68
        <ArrowBackIcon />
69
      </IconButton>
70
      {children}
71
      {options && <Options options={options} />}
6921 stevensc 72
    </StyledChatHeader>
6911 stevensc 73
  )
74
}
75
 
6921 stevensc 76
const Title = ({ children, url }) => {
6911 stevensc 77
  if (!url) {
6921 stevensc 78
    return <StyledTitle>{children}</StyledTitle>
6911 stevensc 79
  }
80
 
81
  return (
6921 stevensc 82
    <a href={url} style={{ width: 'fit-content' }}>
83
      <StyledTitle>{children}</StyledTitle>
6911 stevensc 84
    </a>
85
  )
86
}
87
 
88
const List = ({
89
  messages,
90
  pages,
91
  currentPage,
92
  onPagination,
93
  scrollRef,
94
  loading,
95
}) => {
6921 stevensc 96
  const loadMoreEl = useRef(null)
6911 stevensc 97
 
98
  useEffect(() => {
99
    const observer = new IntersectionObserver(onPagination)
100
 
101
    if (loadMoreEl.current) {
102
      observer.observe(loadMoreEl.current)
103
    }
104
 
105
    return () => {
106
      observer.disconnect()
107
    }
108
  }, [messages])
109
 
110
  return (
6922 stevensc 111
    <StyledMessageList ref={scrollRef}>
112
      {messages.map((message) => (
113
        <List.Message message={message} key={message.id} />
114
      ))}
115
      {pages > currentPage && !loading && <p ref={loadMoreEl}>Cargando...</p>}
116
    </StyledMessageList>
6911 stevensc 117
  )
118
}
119
 
120
const Message = ({ message }) => {
121
  const senderName = (message) => {
122
    if (message.type === 'group' && !message.u === 1) return message.user_name
123
  }
124
 
125
  const messagesContent = {
126
    // eslint-disable-next-line no-undef
127
    text: <p>{parse(emojione.shortnameToImage(message.m))}</p>,
128
    image: <img src={message.m} alt="chat_image" />,
129
    video: <video src={message.m} preload="none" controls />,
130
    document: (
131
      <img
132
        className="pdf"
133
        src="/storage/type/default/filename/pdf.png"
134
        alt="pdf"
135
      />
136
    ),
137
  }
138
 
139
  return (
140
    <div
141
      className={`message_container ${message.u === 1 ? 'sent' : 'received'}`}
142
    >
143
      <span className="user_name">{senderName(message)}</span>
144
      <div className={`message ${message.u === 1 ? 'sent' : 'received'}`}>
145
        {messagesContent[message.mtype]}
146
        <label className="message_time">
147
          {!message.not_received && (
148
            <i
149
              className="fa fa-check"
150
              style={message.seen ? { color: 'blue' } : { color: 'gray' }}
151
            />
152
          )}
153
          {message.time}
154
        </label>
155
      </div>
156
    </div>
157
  )
158
}
159
 
160
const SubmitForm = ({ sendUrl, uploadUrl, onSubmit: onComplete }) => {
161
  const [showEmojione, setShowEmojione] = useState(false)
162
  const [isShowFileModal, setIsShowFileModal] = useState(false)
163
  const [isSending, setIsSending] = useState(false)
164
  const dispatch = useDispatch()
165
 
166
  const { handleSubmit, setValue, register, reset, getValues } = useForm()
167
 
168
  const onSubmit = handleSubmit(({ message }) => {
169
    const formData = new FormData()
170
    // eslint-disable-next-line no-undef
171
    formData.append('message', emojione.toShort(message))
172
 
173
    axios.post(sendUrl, formData).then((response) => {
174
      const { success, data } = response.data
175
 
176
      if (!success) {
177
        const errorMessage =
178
          typeof data === 'string' ? data : 'Ha ocurrido un error'
179
 
180
        setShowEmojione(false)
181
        dispatch(addNotification({ style: 'danger', msg: errorMessage }))
182
        return
183
      }
184
 
185
      setShowEmojione(false)
186
      onComplete()
187
      reset()
188
    })
189
  })
190
 
191
  const sendFile = (file) => {
192
    setIsSending(true)
193
    const formData = new FormData()
194
    formData.append('file', file)
195
 
196
    axios
197
      .post(uploadUrl, formData)
198
      .then(({ data: response }) => {
199
        const { success, data } = response
200
        if (!success) {
201
          const errorMessage =
202
            typeof data === 'string' ? data : 'Ha ocurrido un error'
203
          dispatch(addNotification({ style: 'success', msg: errorMessage }))
204
          return
205
        }
206
 
207
        toggleFileModal()
208
        onComplete()
209
      })
210
      .finally(() => setIsSending(false))
211
  }
212
 
213
  const toggleEmojione = () => {
214
    setShowEmojione(!showEmojione)
215
  }
216
 
217
  const toggleFileModal = () => {
218
    setIsShowFileModal(!isShowFileModal)
219
  }
220
 
221
  const onClickEmoji = (event) => {
222
    const shortname = event.currentTarget.dataset.shortname
223
    const currentMessage = getValues('message')
224
    // eslint-disable-next-line no-undef
225
    const unicode = emojione.shortnameToUnicode(shortname)
226
    setValue('message', `${currentMessage}${unicode}`)
227
  }
228
 
229
  return (
230
    <>
231
      <form className="chat__input-container" onSubmit={onSubmit}>
232
        {showEmojione && <Emojione onClickEmoji={onClickEmoji} />}
233
        <IconButton onClick={toggleFileModal}>
234
          <AttachFileIcon />
235
        </IconButton>
236
        <IconButton onClick={toggleEmojione}>
237
          <InsertEmoticonIcon />
238
        </IconButton>
239
        <input
240
          type="text"
241
          name="message"
242
          placeholder="Escribe un mensaje"
243
          ref={register({ required: true })}
244
        />
245
        <IconButton type="submit">
246
          <SendIcon />
247
        </IconButton>
248
      </form>
249
      <FileModal
250
        isShow={isShowFileModal}
251
        onHide={toggleFileModal}
252
        onComplete={sendFile}
253
        loading={isSending}
254
      />
255
    </>
256
  )
257
}
258
 
259
Chat.Header = Header
260
Chat.Title = Title
261
Chat.List = List
262
List.Message = Message
263
Chat.SubmitForm = SubmitForm
264
 
265
export default Chat