Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

Rev 7230 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
7149 stevensc 1
import React, { useEffect, useState } from 'react'
6830 stevensc 2
import { axios } from '../../../utils'
3
import { feedTypes } from '../../../redux/feed/feed.types'
4
import { deleteFeed } from '../../../redux/feed/feed.actions'
5
import { openShareModal } from '../../../redux/share-modal/shareModal.actions'
6
import { shareModalTypes } from '../../../redux/share-modal/shareModal.types'
7
import { addNotification } from '../../../redux/notification/notification.actions'
8
import { connect, useDispatch, useSelector } from 'react-redux'
9
import parse from 'html-react-parser'
10
import TungstenIcon from '@mui/icons-material/Tungsten'
11
import FavoriteIcon from '@mui/icons-material/FavoriteTwoTone'
12
import RecommendIcon from '@mui/icons-material/Recommend'
13
import AccessTimeIcon from '@mui/icons-material/AccessTime'
14
import SendOutlinedIcon from '@mui/icons-material/SendOutlined'
15
import ChatOutlinedIcon from '@mui/icons-material/ChatOutlined'
16
import ShareOutlinedIcon from '@mui/icons-material/ShareOutlined'
17
import EmojiEmotionsIcon from '@mui/icons-material/EmojiEmotions'
18
import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism'
19
 
20
import InputOption from './InputOption'
21
import ConfirmModal from '../../modals/ConfirmModal'
7227 stevensc 22
import { CommentForm, CommentsList } from '../CommentSection'
6830 stevensc 23
import withExternalShare from './withExternalShare'
24
import FeedModal from '../FeedModal'
25
import SurveyForm from '../../survey-form/SurveyForm'
26
 
6835 stevensc 27
import './Feed.scss'
7142 stevensc 28
import ReactionsButton from '../../UI/buttons/ReactionsButton'
7145 stevensc 29
import Options from '../../UI/Option'
30
import { Avatar } from '@mui/material'
7149 stevensc 31
import { Link } from 'react-router-dom'
6835 stevensc 32
 
6830 stevensc 33
const Feed = (props) => {
34
  const {
35
    isShare = false,
36
    feed_unique,
37
    feed_share_url,
38
    feed_share_external_url,
39
    feed_delete_url,
40
    feed_my_reaction,
41
    feed_reactions,
42
    owner_url,
7136 stevensc 43
    image,
6830 stevensc 44
    owner_image,
45
    owner_name,
46
    owner_description,
47
    owner_shared,
48
    owner_comments,
49
    owner_time_elapse,
50
    owner_file_image_preview,
51
    owner_file_video,
52
    owner_file_image,
53
    owner_file_document,
54
    comment_add_url,
7227 stevensc 55
    comments: defaultComments,
6830 stevensc 56
    shared_name,
57
    shared_image,
58
    shared_time_elapse,
59
    shared_description,
60
    shared_file_video,
61
    shared_file_image_preview,
62
    shared_file_image,
63
    owner_external_shared,
64
    shared_file_document,
65
    shared_url,
66
    feed_increment_external_counter_url,
67
    feed_content_type,
68
    feed_vote_url,
7144 stevensc 69
    feed_save_reaction_url,
70
    feed_delete_reaction_url,
6830 stevensc 71
    openShareModal, // REDUX ACTION
72
  } = props
7227 stevensc 73
  const [comments, setComments] = useState([])
6830 stevensc 74
  const [ownerReactions, setOwnerReaction] = useState(feed_reactions)
75
  const [totalComments, setTotalComments] = useState(owner_comments)
76
  const [externalShare, setExternalShare] = useState(owner_external_shared)
77
  const [sharedState, setSharedState] = useState(owner_shared)
7227 stevensc 78
  const [totalReactions, setTotalReactions] = useState(0)
79
 
6830 stevensc 80
  const [showComments, setShowComments] = useState(false)
81
  const [showModal, setShowModal] = useState(false)
6832 stevensc 82
  const labels = useSelector(({ intl }) => intl.labels)
7227 stevensc 83
  const dispatch = useDispatch()
6830 stevensc 84
 
85
  const reactionsOptions = [
86
    {
87
      type: 'r',
88
      icon: <RecommendIcon style={{ color: '#7405f9' }} />,
89
    },
90
    {
91
      type: 's',
92
      icon: <VolunteerActivismIcon style={{ color: '#6495ED' }} />,
93
    },
94
    {
95
      type: 'l',
96
      icon: <FavoriteIcon style={{ color: '#DF704D' }} />,
97
    },
98
    {
99
      type: 'i',
100
      icon: (
101
        <TungstenIcon
102
          style={{ color: '#F5BB5C', transform: 'rotate(180deg)' }}
103
        />
104
      ),
105
    },
106
    {
107
      type: 'f',
108
      icon: <EmojiEmotionsIcon style={{ color: '#FF7F50' }} />,
109
    },
110
  ]
111
 
7227 stevensc 112
  const handleShare = () => {
6830 stevensc 113
    openShareModal(
114
      feed_share_url,
115
      shareModalTypes.SHARE,
116
      feedTypes.DASHBOARD,
117
      feed_unique
118
    )
7227 stevensc 119
  }
6830 stevensc 120
 
7227 stevensc 121
  const handleExternalShare = (value) => {
122
    setExternalShare(value)
123
  }
6830 stevensc 124
 
7227 stevensc 125
  const displayCommentSection = () => {
126
    setShowComments(!showComments)
127
  }
128
 
129
  const addComment = ({ comment }) => {
130
    const formData = new FormData()
131
    formData.append('comment', comment)
132
 
133
    axios.post(comment_add_url, formData).then((response) => {
134
      const { success, data, total_comments } = response.data
135
 
136
      if (!success) {
137
        const errorMessage =
138
          typeof data === 'string' ? data : 'Error interno. Intente más tarde.'
139
 
140
        dispatch(addNotification({ style: 'danger', msg: errorMessage }))
141
        return
142
      }
143
 
144
      setComments((prevMessages) => [...prevMessages, data])
145
      setTotalComments(total_comments)
146
    })
147
  }
148
 
149
  const deleteComment = (commentUnique, deleteCommentUrl) => {
150
    axios
151
      .post(deleteCommentUrl)
152
      .then((response) => {
153
        const { success, data, total_comments } = response.data
154
 
155
        if (!success) {
156
          const errorMessage =
157
            typeof data === 'string'
158
              ? data
159
              : 'Error interno. Intente más tarde.'
160
 
161
          dispatch(addNotification({ style: 'danger', msg: errorMessage }))
162
          return
163
        }
164
 
165
        dispatch(addNotification({ style: 'success', msg: data }))
166
 
167
        setComments((prevComments) =>
168
          prevComments.filter((comment) => comment.unique !== commentUnique)
169
        )
170
        setTotalComments(total_comments)
171
      })
172
      .catch((error) => {
173
        dispatch(addNotification({ style: 'danger', msg: error }))
174
        throw new Error(error)
175
      })
176
  }
177
 
6830 stevensc 178
  const ExternalShareButton = withExternalShare(
179
    InputOption,
180
    feed_share_external_url,
181
    {
182
      Icon: SendOutlinedIcon,
183
      color: 'gray',
184
      title: 'Send',
185
      shareUrl: feed_increment_external_counter_url,
186
      setValue: handleExternalShare,
187
      withTitle: true,
188
    }
189
  )
190
 
191
  useEffect(() => setSharedState(owner_shared), [owner_shared])
192
 
193
  useEffect(() => {
194
    const feedReactions = ownerReactions?.reduce(
195
      (acc, reaction) => acc + Number(reaction.total),
196
 
197
    )
198
    setTotalReactions(feedReactions)
199
  }, [ownerReactions])
200
 
7227 stevensc 201
  useEffect(() => {
202
    setComments(defaultComments)
203
  }, [defaultComments])
204
 
6830 stevensc 205
  return (
206
    <>
207
      <div className="feed">
208
        <Feed.Header
209
          image={owner_image}
210
          name={owner_name}
211
          timeElapsed={owner_time_elapse}
212
          viewUrl={owner_url}
213
          deleteUrl={feed_delete_url}
214
          feedUnique={feed_unique}
215
        />
216
 
217
        <div
218
          className="feed__body"
219
          onClick={() =>
220
            (owner_file_image || owner_file_video || owner_file_document) &&
221
            setShowModal(true)
222
          }
223
        >
224
          <Feed.Content
225
            description={owner_description}
226
            image={owner_file_image}
227
            imagePreview={owner_file_image_preview}
228
            video={owner_file_video}
229
            document={owner_file_document}
230
            sharedItem={{
231
              name: shared_name,
232
              image: shared_image,
233
              time_elapse: shared_time_elapse,
234
              description: shared_description,
235
              file_video: shared_file_video,
236
              file_image_preview: shared_file_image_preview,
237
              file_image: shared_file_image,
238
              file_document: shared_file_document,
239
              shared_url,
240
            }}
241
            type={feed_content_type}
242
            voteUrl={feed_vote_url}
243
          />
244
        </div>
245
 
246
        {!isShare && feed_content_type !== 'fast-survey' && (
247
          <div className="px-3 d-flex align-items-center justify-content-between">
248
            <div className="reactions-counter">
249
              {reactionsOptions
250
                .filter((option) =>
251
                  ownerReactions.find(
252
                    (reaction) => reaction.reaction === option.type
253
                  )
254
                )
255
                .map((reaction) => reaction.icon)}
256
              <span>{totalReactions} reacciones</span>
257
            </div>
258
            <div
259
              className="d-inline-flex align-items-center"
260
              style={{ gap: '5px' }}
261
            >
262
              {!!totalComments && (
6832 stevensc 263
                <span>{`${totalComments} ${labels.comments?.toLowerCase()}`}</span>
6830 stevensc 264
              )}
265
              {!!sharedState && (
6832 stevensc 266
                <span>{`${sharedState} ${labels.shared?.toLowerCase()}`}</span>
6830 stevensc 267
              )}
268
              {!!externalShare && (
6832 stevensc 269
                <span>{`${externalShare} ${labels.sends?.toLowerCase()}`}</span>
6830 stevensc 270
              )}
271
            </div>
272
          </div>
273
        )}
274
 
275
        {!isShare && feed_content_type !== 'fast-survey' && (
276
          <div className="feed__buttons">
7142 stevensc 277
            <ReactionsButton
7143 stevensc 278
              className="feed__share-option position-relative"
7142 stevensc 279
              currentReaction={feed_my_reaction}
7144 stevensc 280
              saveUrl={feed_save_reaction_url}
281
              deleteUrl={feed_delete_reaction_url}
7230 stevensc 282
              onChange={({ reactions }) => setOwnerReaction(reactions)}
7142 stevensc 283
              withLabel
284
            />
6830 stevensc 285
            <InputOption
286
              Icon={ChatOutlinedIcon}
6832 stevensc 287
              title={labels.comment}
6830 stevensc 288
              color="gray"
289
              onClick={displayCommentSection}
290
              withTitle
291
            />
292
            <InputOption
293
              Icon={ShareOutlinedIcon}
6832 stevensc 294
              title={labels.share}
6830 stevensc 295
              color="gray"
296
              onClick={handleShare}
297
              withTitle
298
            />
299
            <ExternalShareButton />
300
          </div>
301
        )}
7227 stevensc 302
        {showComments && (
303
          <div className="px-2 pb-2">
304
            <CommentForm onSubmit={addComment} image={image} />
305
            <CommentsList comments={comments} onDelete={deleteComment} />
306
          </div>
307
        )}
6830 stevensc 308
      </div>
7152 stevensc 309
      <FeedModal
310
        show={showModal}
311
        feed={props}
312
        onClose={() => setShowModal(false)}
313
      />
6830 stevensc 314
    </>
315
  )
316
}
317
 
318
const Content = ({
319
  description,
320
  image,
321
  imagePreview,
322
  video,
323
  document,
324
  sharedItem,
325
  type,
326
  voteUrl,
327
}) => {
7147 stevensc 328
  const [readMore, setReadMore] = useState(false)
6832 stevensc 329
  const labels = useSelector(({ intl }) => intl.labels)
6830 stevensc 330
 
7147 stevensc 331
  const onReadMore = () => {
332
    setReadMore(!readMore)
333
  }
6830 stevensc 334
 
6831 stevensc 335
  const htmlParsedText = (fullStringText = '') => {
6830 stevensc 336
    const fullText = parse(fullStringText)
337
    if (fullStringText.length > 500) {
338
      const shortenedString = fullStringText.substr(0, 500)
339
      const shortenedText = parse(`${shortenedString}... `)
340
      return (
341
        <>
7147 stevensc 342
          {readMore ? fullText : shortenedText}
343
          <span className="cursor-pointer" onClick={onReadMore}>
344
            {readMore ? labels.read_less : labels.read_more}
6830 stevensc 345
          </span>
346
        </>
347
      )
348
    }
7384 stevensc 349
    return fullText
6830 stevensc 350
  }
351
 
352
  return (
353
    <>
354
      {type !== 'fast-survey' ? (
355
        htmlParsedText(description)
356
      ) : (
357
        <SurveyForm
358
          active={description.active}
359
          question={description.question}
360
          answers={[
361
            description.answer1,
362
            description.answer2,
363
            description.answer3,
364
            description.answer4,
365
            description.answer5,
366
          ]}
367
          votes={
368
            description.votes1 && [
369
              description.votes1,
370
              description.votes2,
371
              description.votes3,
372
              description.votes4,
373
              description.votes5,
374
            ]
375
          }
376
          time={description.time_remaining}
377
          voteUrl={voteUrl}
378
          resultType={description.result_type}
379
        />
380
      )}
381
      {image && <img src={image} className="Entradas" loading="lazy" />}
382
      {video && (
383
        <video src={video} controls poster={imagePreview} preload="none" />
384
      )}
385
      {document && (
7150 stevensc 386
        <a href={document} target="_blank" rel="noreferrer">
7140 stevensc 387
          <img className="pdf" src="/images/extension/pdf.png" alt="pdf" />
6830 stevensc 388
        </a>
389
      )}
390
      {sharedItem.name && (
391
        <div className="py-3 px-md-3">
392
          <Feed
393
            isShare={true}
394
            owner_name={sharedItem.name}
395
            owner_image={sharedItem.image}
396
            owner_time_elapse={sharedItem.time_elapse}
397
            owner_description={sharedItem.description}
398
            owner_file_video={sharedItem.file_video}
399
            owner_file_image_preview={sharedItem.file_image_preview}
400
            owner_file_image={sharedItem.file_image}
401
            owner_file_document={sharedItem.file_document}
402
            owner_url={sharedItem.shared_url}
403
          />
404
        </div>
405
      )}
406
    </>
407
  )
408
}
409
 
410
const Header = ({
411
  image = '',
412
  name = '',
413
  timeElapsed = '',
414
  deleteUrl = '',
415
  viewUrl = '',
416
  feedUnique = '',
417
}) => {
418
  const [showConfirmModal, setShowConfirmModal] = useState(false)
7149 stevensc 419
  const [options, setOptions] = useState([])
6832 stevensc 420
  const labels = useSelector(({ intl }) => intl.labels)
6830 stevensc 421
  const dispatch = useDispatch()
422
 
7145 stevensc 423
  const toggleConfirm = () => {
424
    setShowConfirmModal(!showConfirmModal)
425
  }
6830 stevensc 426
 
7145 stevensc 427
  const onDelete = () => {
428
    axios.post(deleteUrl).then((response) => {
429
      const { data, success } = response.data
430
 
431
      if (!success) {
432
        dispatch(addNotification({ style: 'danger', msg: data }))
6830 stevensc 433
        return
434
      }
7145 stevensc 435
 
6830 stevensc 436
      dispatch(deleteFeed(feedUnique))
7145 stevensc 437
      toggleConfirm()
438
      dispatch(addNotification({ style: 'success', msg: data }))
6830 stevensc 439
    })
440
  }
441
 
442
  useEffect(() => {
7145 stevensc 443
    if (deleteUrl) {
444
      const deleteOption = { action: toggleConfirm, label: labels.delete }
7149 stevensc 445
      setOptions([...options, deleteOption])
6830 stevensc 446
    }
7145 stevensc 447
  }, [deleteUrl])
6830 stevensc 448
 
449
  return (
450
    <div className="feed__header">
7145 stevensc 451
      <div className="d-inline-flex">
7148 stevensc 452
        <Avatar
453
          src={image}
454
          alt={name}
455
          sx={{ width: '60px', height: '60px', marginRight: '.5rem' }}
456
        />
6830 stevensc 457
        <div className="feed__info">
7149 stevensc 458
          <Link to={viewUrl}>
6830 stevensc 459
            <h2>{name}</h2>
7149 stevensc 460
          </Link>
6830 stevensc 461
          <div className="time__elapse">
462
            <p>{timeElapsed}</p>
463
            <AccessTimeIcon className="time__elapse-icon" />
464
          </div>
465
        </div>
466
      </div>
7149 stevensc 467
      {!!options.length && <Options options={options} />}
7145 stevensc 468
      <ConfirmModal
469
        show={showConfirmModal}
470
        onClose={toggleConfirm}
471
        onAccept={onDelete}
472
      />
6830 stevensc 473
    </div>
474
  )
475
}
476
 
477
Feed.Content = Content
478
Feed.Header = Header
479
 
480
const mapDispatchToProps = {
481
  addNotification: (notification) => addNotification(notification),
482
  openShareModal: (postUrl, modalType, feedType) =>
483
    openShareModal(postUrl, modalType, feedType),
484
}
485
 
486
export default connect(null, mapDispatchToProps)(Feed)