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