Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

Rev 5933 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 5933 Rev 6753
Línea 1... Línea -...
1
/* eslint-disable react/prop-types */
-
 
2
import React, { useEffect, useRef, useState } from 'react'
1
import React, { useEffect, useRef, useState } from 'react'
3
import { ArrowLeft } from '@mui/icons-material'
2
import { axios, scrollToBottom } from '../../utils'
4
import { useDispatch } from 'react-redux'
3
import { useDispatch } from 'react-redux'
5
import { addNotification } from '../../redux/notification/notification.actions'
4
import { addNotification } from '../../redux/notification/notification.actions'
6
import { axios, scrollToBottom } from '../../utils'
5
import IconButton from '@mui/material/IconButton'
7
import { fetchMessages, getMessagesDifferences } from '../../services/chat'
6
import MoreVertIcon from '@mui/icons-material/MoreVert'
-
 
7
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
8
import QuestionAnswerRoundedIcon from '@mui/icons-material/QuestionAnswerRounded'
8
import QuestionAnswerRoundedIcon from '@mui/icons-material/QuestionAnswerRounded'
9
import SpeakerNotesOffRoundedIcon from '@mui/icons-material/SpeakerNotesOffRounded'
-
 
10
import EmptySection from '../../shared/empty-section/EmptySection'
-
 
-
 
9
 
11
import MessageBox from './MessageBox'
10
import MessageBox from './MessageBox'
12
import MessageTemplate from './MessageTemplate'
11
import MessageTemplate from './MessageTemplate'
-
 
12
import EmptySection from '../../shared/empty-section/EmptySection'
13
import ConfirmModal from '../../shared/confirm-modal/ConfirmModal'
13
import ConfirmModal from '../../shared/confirm-modal/ConfirmModal'
14
import IconButton from '@mui/material/IconButton'
14
import LoaderContainer from '../../app/components/UI/LoaderContainer'
15
import MoreVertIcon from '@mui/icons-material/MoreVert'
15
import Spinner from '../../shared/loading-spinner/Spinner'
-
 
16
import { getMessagesDifferences } from '../../services/chat'
Línea 16... Línea 17...
16
 
17
 
Línea 17... Línea 18...
17
const DEFAULT_PAGES = { current: 1, last: 1 }
18
const DEFAULT_PAGES = { current: 1, last: 1 }
-
 
19
 
18
 
20
const Chatmail = ({
19
const Chatmail = ({
21
  messagesUrl = '',
20
  selectedConversation = null,
22
  selectedConversation = null,
21
  setConversation = () => null,
23
  setConversation = () => null,
22
}) => {
24
}) => {
23
  const [oldMessages, setOldMessages] = useState([])
25
  const [oldMessages, setOldMessages] = useState([])
24
  const [messages, setMessages] = useState([])
26
  const [messages, setMessages] = useState([])
25
  const [pages, setPages] = useState(DEFAULT_PAGES)
27
  const [pages, setPages] = useState(DEFAULT_PAGES)
26
  const [loading, setLoading] = useState(false)
28
  const [loading, setLoading] = useState(false)
27
  const [displayOptions, setDisplayOptions] = useState(false)
-
 
28
  const [isShowConfirm, setIsShowConfirm] = useState(false)
29
  const [isGetting, setIsGetting] = useState(false)
29
  const lastMessage = useRef(null)
30
  const [isShowConfirm, setIsShowConfirm] = useState(false)
Línea 30... Línea 31...
30
  const messagesList = useRef(null)
31
  const messagesList = useRef(null)
31
  const dispatch = useDispatch()
32
  const dispatch = useDispatch()
32
 
33
 
33
  const getMoreMessages = async (url = '', page = pages.current) => {
34
  const getMessages = () => {
34
    try {
35
    try {
35
      setLoading(true)
36
      const controller = new AbortController()
36
      const response = await fetchMessages(url, page)
-
 
37
      if (!response.success) {
-
 
38
        const errorMessage = response.data
37
      const query = (url = '', page = pages.current) =>
39
 
38
        axios
40
        dispatch(
39
          .get(`${url}?page=${page}`, {
41
          addNotification({ style: 'danger', msg: errorMessage.message })
-
 
Línea 42... Línea -...
42
        )
-
 
43
        return
-
 
44
      }
-
 
45
 
-
 
46
      if (response.pagination.current > 1) {
-
 
47
        setOldMessages((prevOldMessages) => [
-
 
48
          ...prevOldMessages,
-
 
49
          ...response.data,
-
 
50
        ])
40
            signal: controller.signal,
51
      }
-
 
52
      setPages((prevPages) => ({
-
 
53
        ...prevPages,
-
 
54
        last: response.pagination.last,
41
          })
55
      }))
42
          .then((response) => response.data)
56
 
-
 
57
      return
-
 
58
    } catch (error) {
43
 
59
      const errorMessage = new Error(error)
44
      return { query, controller }
60
      console.log('Request canceled', errorMessage)
45
    } catch (error) {
Línea 61... Línea 46...
61
    } finally {
46
      dispatch(addNotification({ style: 'danger', message: 'Error: ' + error }))
62
      setLoading(false)
-
 
63
    }
47
      throw new Error(error)
64
  }
-
 
65
 
-
 
66
  const hearBeat = async () => {
-
 
67
    try {
-
 
68
      setLoading(true)
-
 
69
      const response = await fetchMessages(
-
 
70
        selectedConversation.messages_link,
-
 
Línea -... Línea 48...
-
 
48
    }
-
 
49
  }
-
 
50
 
71
        1
51
  const getMoreMessages = () => {
-
 
52
    setLoading(true)
72
      )
53
 
-
 
54
    const { query } = getMessages()
73
 
55
    query(messagesUrl, pages.current)
-
 
56
      .then(({ success, data, pagination }) => {
-
 
57
        if (!success) {
-
 
58
          const errorMessage = data ?? 'Ha ocurrido un error'
-
 
59
          dispatch(addNotification({ style: 'danger', msg: errorMessage }))
-
 
60
          return
74
      if (!response.success) {
61
        }
-
 
62
 
-
 
63
        if (pagination.current > 1) {
-
 
64
          setOldMessages((prevOldMessages) => [
-
 
65
            ...data.reverse(),
-
 
66
            ...prevOldMessages,
-
 
67
          ])
-
 
68
        }
-
 
69
 
-
 
70
        setPages((prevPages) => ({
-
 
71
          ...prevPages,
-
 
72
          last: pagination.last,
75
        const errorMessage = response.data
73
        }))
-
 
74
      })
-
 
75
      .catch((error) => {
Línea -... Línea 76...
-
 
76
        const errorMessage = 'Error: ' + error
-
 
77
        dispatch(addNotification({ style: 'danger', message: errorMessage }))
-
 
78
        throw new Error(error)
-
 
79
      })
-
 
80
      .finally(() => setLoading(false))
-
 
81
  }
-
 
82
 
-
 
83
  const hearbeat = () => {
-
 
84
    setIsGetting(true)
-
 
85
    const { query } = getMessages()
-
 
86
 
-
 
87
    query(messagesUrl, 1)
76
 
88
      .then(({ success, data, pagination }) => {
-
 
89
        if (!success) {
-
 
90
          const errorMessage = data ?? 'Ha ocurrido un error'
-
 
91
          dispatch(addNotification({ style: 'danger', msg: errorMessage }))
-
 
92
          return
-
 
93
        }
-
 
94
 
-
 
95
        const newMessages = getMessagesDifferences(messages, data)
-
 
96
 
-
 
97
        console.log(newMessages)
-
 
98
        if (newMessages.length) {
-
 
99
          setMessages((prevMessages) => [
-
 
100
            ...prevMessages,
-
 
101
            ...newMessages.reverse(),
-
 
102
          ])
-
 
103
        }
-
 
104
 
-
 
105
        setPages((prevPages) => ({
-
 
106
          ...prevPages,
-
 
107
          last: pagination.last,
-
 
108
        }))
-
 
109
      })
Línea 77... Línea 110...
77
        dispatch(
110
      .catch((err) => {
78
          addNotification({ style: 'danger', msg: errorMessage.message })
-
 
79
        )
111
        const errorMessage = 'Error: ' + err
80
        return
-
 
81
      }
112
        dispatch(addNotification({ style: 'danger', message: errorMessage }))
82
 
113
        throw new Error(err)
Línea 83... Línea 114...
83
      const newMessages = getMessagesDifferences(messages, response.data)
114
      })
84
 
-
 
85
      if (newMessages.length) {
115
      .finally(() => setIsGetting(false))
86
        setMessages([...newMessages, ...messages])
-
 
87
        scrollToBottom()
-
 
88
      } else {
-
 
89
        setMessages(response.data)
-
 
90
      }
-
 
91
 
-
 
92
      setPages((prevPages) => ({
-
 
93
        ...prevPages,
116
  }
Línea 94... Línea 117...
94
        last: response.pagination.last,
117
 
95
      }))
118
  const loadMore = () => {
96
    } catch (error) {
119
    setLoading(true)
Línea 97... Línea 120...
97
      const errorMessage = new Error(error)
120
    setPages((prevPages) => ({ ...prevPages, current: prevPages.current + 1 }))
98
      console.log('Request canceled', errorMessage)
121
  }
99
    } finally {
122
 
Línea 100... Línea 123...
100
      setLoading(false)
123
  const toggleConfirmModal = () => {
-
 
124
    setIsShowConfirm(!isShowConfirm)
101
    }
125
  }
102
  }
126
 
103
 
127
  const sendMessage = async (sendUrl = '', message = {}) => {
104
  const handleSend = async (sendUrl = '', message = {}) => {
128
    try {
105
    try {
129
      const formData = new FormData()
106
      const formData = new FormData()
130
 
Línea 107... Línea -...
107
 
-
 
108
      Object.entries(message).forEach(([key, value]) =>
-
 
109
        formData.append(key, value)
-
 
110
      )
-
 
111
 
-
 
112
      const { data: response } = await axios.post(sendUrl, formData)
-
 
113
      setMessages((prev) => [response.data, ...prev])
-
 
114
    } catch (error) {
-
 
115
      const errorMessage = new Error(error)
131
      Object.entries(message).forEach(([key, value]) =>
116
      dispatch(addNotification({ style: 'danger', msg: errorMessage.message }))
132
        formData.append(key, value)
117
    }
133
      )
Línea 118... Línea 134...
118
  }
134
 
119
 
135
      await axios.post(sendUrl, formData)
120
  const loadMore = async () => {
136
 
121
    setPages((prevPages) => ({ ...prevPages, current: prevPages.current + 1 }))
137
      scrollToBottom(messagesList)
Línea 122... Línea 138...
122
  }
138
    } catch (error) {
-
 
139
      const errorMessage = new Error(error)
123
 
140
      dispatch(addNotification({ style: 'danger', msg: errorMessage.message }))
124
  const toggleConfirmModal = () => {
141
    }
125
    setIsShowConfirm(!isShowConfirm)
142
  }
Línea 126... Línea 143...
126
  }
143
 
127
 
144
  const deleteConversation = () => {
128
  const deleteConversation = () => {
145
    axios.post(selectedConversation.delete_link).then(({ data: response }) => {
Línea 129... Línea 146...
129
    axios.post(selectedConversation.delete_link).then(({ data: response }) => {
146
      const { success, data } = response
130
      const { success, data } = response
147
 
131
 
148
      if (!success) {
132
      if (!success) {
149
        dispatch(addNotification({ style: 'danger', msg: data }))
133
        dispatch(addNotification({ style: 'danger', msg: data }))
150
        return
Línea 134... Línea 151...
134
        return
151
      }
135
      }
152
 
136
 
-
 
137
      dispatch(addNotification({ style: 'success', msg: data }))
-
 
Línea 138... Línea -...
138
      setConversation(null)
-
 
139
    })
153
      dispatch(addNotification({ style: 'success', msg: data }))
140
  }
-
 
141
 
154
      toggleConfirmModal()
142
  useEffect(() => {
-
 
143
    if (!loading && selectedConversation) setTimeout(() => hearBeat(), 3000)
155
      setConversation(null)
Línea 144... Línea 156...
144
  }, [loading, selectedConversation])
156
    })
145
 
157
  }
146
  useEffect(() => {
158
 
147
    if (messages) setMessages([])
159
  useEffect(() => {
148
    scrollToBottom(messagesList)
-
 
149
    setPages(DEFAULT_PAGES)
-
 
150
  }, [selectedConversation])
-
 
151
 
-
 
152
  useEffect(() => {
-
 
153
    if (selectedConversation)
-
 
154
      getMoreMessages(selectedConversation?.messages_link, pages.current)
-
 
155
  }, [pages.current])
-
 
156
 
-
 
Línea 157... Línea 160...
157
  useEffect(() => {
160
    getMoreMessages()
158
    const observer = new IntersectionObserver(([entry]) => {
161
  }, [pages.current])
159
      if (entry.isIntersecting) {
162
 
160
        loadMore()
-
 
161
      }
-
 
162
    })
163
  useEffect(() => {
163
 
-
 
164
    if (lastMessage.current) {
-
 
165
      observer.observe(lastMessage.current)
164
    setMessages([])
166
    }
165
    setOldMessages([])
167
  }, [messages])
166
    setPages(DEFAULT_PAGES)
168
 
-
 
169
  if (!selectedConversation) {
-
 
170
    return (
-
 
171
      <EmptySection
-
 
172
        message={LABELS.SELECT_CONVERSATION}
-
 
173
        Icon={<QuestionAnswerRoundedIcon />}
-
 
174
      />
167
  }, [selectedConversation])
175
    )
-
 
176
  }
-
 
177
 
168
 
178
  return (
169
  useEffect(() => {
179
    <>
170
    if (isGetting) return
180
      <div className="chat">
-
 
181
        <span className="icon-hide" onClick={() => setConversation(null)}>
-
 
182
          <ArrowLeft />
171
 
183
          {LABELS.RETURN}
172
    const messagesInterval = setTimeout(() => {
184
        </span>
173
      hearbeat()
185
        <div className="chat_header">
174
    }, 2000)
186
          <a href={selectedConversation.profile}>
-
 
187
            <h2 className="chat-header">{selectedConversation.name}</h2>
-
 
188
          </a>
175
 
189
          <div className="header-options">
-
 
190
            <IconButton onClick={() => setDisplayOptions(!displayOptions)}>
176
    return () => {
191
              <MoreVertIcon />
177
      clearTimeout(messagesInterval)
192
            </IconButton>
178
    }
193
            <div className="position-relative">
179
  }, [isGetting])
194
              <div className={`feed-options ${displayOptions ? 'active' : ''}`}>
-
 
195
                <ul>
180
 
196
                  <li>
181
  return (
197
                    <button className="option-btn" onClick={toggleConfirmModal}>
182
    <>
198
                      <i className="fa fa-trash-o mr-1" />
183
      <div className="chat">
199
                      Borrar
184
        <Chatmail.Header
200
                    </button>
-
 
201
                  </li>
185
          name={selectedConversation.name}
202
                </ul>
186
          profile={selectedConversation.profile}
203
              </div>
187
          options={[
204
            </div>
188
            {
205
          </div>
189
              url: selectedConversation.delete_link,
206
        </div>
190
              label: 'Borrar convesación',
207
        <div className="messages-line" ref={messagesList}>
191
              action: toggleConfirmModal,
208
          {messages.length ? (
192
            },
209
            [...oldMessages, ...messages].map((element, index) => (
193
          ]}
210
              <MessageTemplate
194
          changeTab={() => setConversation(null)}
211
                key={index}
195
        />
Línea 236... Línea 220...
236
      />
220
      />
237
    </>
221
    </>
238
  )
222
  )
239
}
223
}
Línea -... Línea 224...
-
 
224
 
-
 
225
const Header = ({ name, profile, options, changeTab }) => {
-
 
226
  return (
-
 
227
    <>
-
 
228
      <div className="chat_header">
-
 
229
        <IconButton onClick={changeTab}>
-
 
230
          <ArrowBackIcon />
-
 
231
        </IconButton>
-
 
232
        <a href={profile}>
-
 
233
          <h2>{name}</h2>
-
 
234
        </a>
-
 
235
        <Header.Options options={options} />
-
 
236
      </div>
-
 
237
    </>
-
 
238
  )
-
 
239
}
-
 
240
 
-
 
241
const List = ({
-
 
242
  messages,
-
 
243
  onIntersection,
-
 
244
  isLastPage,
-
 
245
  scrollRef,
-
 
246
  isLoading,
-
 
247
}) => {
-
 
248
  const loadMoreEl = useRef(null)
-
 
249
 
-
 
250
  useEffect(() => {
-
 
251
    const observer = new IntersectionObserver((entries) => {
-
 
252
      if (entries[0].isIntersecting && !isLoading) {
-
 
253
        onIntersection()
-
 
254
      }
-
 
255
    })
-
 
256
 
-
 
257
    if (loadMoreEl.current === null) {
-
 
258
      return
-
 
259
    }
-
 
260
 
-
 
261
    observer.observe(loadMoreEl.current)
-
 
262
 
-
 
263
    return () => {
-
 
264
      observer.disconnect()
-
 
265
    }
-
 
266
  }, [isLoading, onIntersection])
-
 
267
 
-
 
268
  return (
-
 
269
    <div className="messages_container" ref={scrollRef}>
-
 
270
      <div className="message_wrapper">
-
 
271
        {!isLastPage && <p ref={loadMoreEl}>Cargando...</p>}
-
 
272
        {isLoading && (
-
 
273
          <LoaderContainer>
-
 
274
            <Spinner />
-
 
275
          </LoaderContainer>
-
 
276
        )}
-
 
277
        {messages.map((message, index) => (
-
 
278
          <MessageTemplate message={message} key={JSON.stringify(message)} />
-
 
279
        ))}
-
 
280
      </div>
-
 
281
    </div>
-
 
282
  )
-
 
283
}
-
 
284
 
-
 
285
const Options = ({ options }) => {
-
 
286
  const [isShowMenu, setIsShowMenu] = useState(false)
-
 
287
 
-
 
288
  const toggleOptions = () => {
-
 
289
    setIsShowMenu(!isShowMenu)
-
 
290
  }
-
 
291
 
-
 
292
  return (
-
 
293
    <div className="header-options">
-
 
294
      <IconButton onClick={toggleOptions}>
-
 
295
        <MoreVertIcon />
-
 
296
      </IconButton>
-
 
297
      <div className="position-relative">
-
 
298
        <div className={`feed-options ${isShowMenu ? 'active' : ''}`}>
-
 
299
          <ul>
-
 
300
            {options.map((option, index) => {
-
 
301
              if (!option.url) {
-
 
302
                return null
-
 
303
              }
-
 
304
 
-
 
305
              return (
-
 
306
                <li key={index}>
-
 
307
                  <button
-
 
308
                    className="btn option-btn"
-
 
309
                    onClick={() => {
-
 
310
                      option.action()
-
 
311
                      toggleOptions()
-
 
312
                    }}
-
 
313
                  >
-
 
314
                    {option.label}
-
 
315
                  </button>
-
 
316
                </li>
-
 
317
              )
-
 
318
            })}
-
 
319
          </ul>
-
 
320
        </div>
-
 
321
      </div>
-
 
322
    </div>
-
 
323
  )
-
 
324
}
-
 
325
 
-
 
326
Chatmail.Header = Header
-
 
327
Chatmail.List = List
-
 
328
Header.Options = Options
240
 
329