MainPostsFeed.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import { Card, Col, Row, Carousel, Empty, Button } from 'antd'
  2. import React, { createRef, useEffect, useState } from 'react'
  3. import { connect } from 'react-redux'
  4. import { Link } from 'react-router-dom'
  5. import { backURL } from '../../helpers'
  6. import { UserAvatar } from '../header/Header'
  7. import nodata from '../../images/nodata.png'
  8. import { HeartFilled, HeartOutlined, LeftCircleOutlined, RightCircleOutlined, SendOutlined, } from '@ant-design/icons'
  9. import Paragraph from 'antd/lib/typography/Paragraph'
  10. import Text from 'antd/lib/typography/Text'
  11. import TextArea from 'antd/lib/input/TextArea'
  12. import { actionDelLikePost, actionFullAddComment, actionLikePost, actionPostsFeed, actionRemovePostsFeedAC } from '../../actions'
  13. const PostTitle = ({ owner }) =>
  14. <Link to={`/profile/${owner?._id}`} className='owner'>
  15. <Row justify="start" align='middle'>
  16. <Col >
  17. <UserAvatar avatar={owner?.avatar} login={owner?.login} avatarSize={'45px'} nick={owner?.nick} />
  18. </Col>
  19. <Col offset={1}>
  20. <span>{owner?.nick ? owner.nick : owner?.login ? owner.login : 'Null'}</span>
  21. </Col>
  22. </Row>
  23. </Link >
  24. class PostImage extends React.Component {
  25. constructor(props) {
  26. super(props);
  27. this.carouselRef = createRef();
  28. this.state = {
  29. movePrev: false,
  30. moveNext: false
  31. }
  32. }
  33. handleNext = () => this.carouselRef.current.next(this);
  34. handlePrev = () => this.carouselRef.current.prev(this);
  35. moveOnDivArray = (length, index) => {
  36. if (length === 1) {
  37. this.setState({ movePrev: false, moveNext: false })
  38. } else if (index === 0) {
  39. this.setState({ movePrev: false, moveNext: true })
  40. } else if (index === length - 1 && length > 1) {
  41. this.setState({ movePrev: true, moveNext: false })
  42. } else {
  43. this.setState({ movePrev: true, moveNext: true })
  44. }
  45. }
  46. downOnDivArray = () => this.setState({ movePrev: false, moveNext: false })
  47. render() {
  48. const { images } = this.props
  49. return (
  50. <Carousel ref={this.carouselRef}
  51. effect="fade"
  52. infinite={false}
  53. dots={{ className: 'Post__dots' }
  54. }>
  55. {!!images ?
  56. images.map((i, index) => i?.url ? <div key={i._id}
  57. onMouseEnter={() => this.moveOnDivArray(images.length, index)}
  58. onMouseLeave={this.downOnDivArray}>
  59. <button onClick={() => this.handlePrev()}
  60. className={`Post__prev Post__btn ${this.state.movePrev ? '--active' : ''}`}><LeftCircleOutlined /></button>
  61. <button onClick={() => this.handleNext()}
  62. className={`Post__next Post__btn ${this.state.moveNext ? '--active' : ''}`}><RightCircleOutlined /></button>
  63. <img src={backURL + '/' + i.url} />
  64. </div> :
  65. <Empty key={i._id} image={nodata} description={false} />) :
  66. <Empty image={nodata} description={false} />
  67. }
  68. </Carousel >
  69. );
  70. }
  71. }
  72. const HeartLike = ({ styleFontSize, likeStatus, changeLike }) =>
  73. <Button
  74. onClick={() => changeLike()}
  75. type="none"
  76. shape="circle"
  77. icon={
  78. likeStatus ?
  79. <HeartFilled style={{ color: '#ff6969', fontSize: `${styleFontSize}` }} /> :
  80. <HeartOutlined style={{ color: '#1890ff', fontSize: `${styleFontSize}` }} />}
  81. />
  82. const PostUserPanel = ({ myID, postId, likes = [], addLikePost, removeLikePost }) => {
  83. let likeStatus
  84. let likeId
  85. likes.find(l => {
  86. if (l.owner._id === myID) {
  87. likeStatus = true
  88. likeId = l._id
  89. } else {
  90. likeStatus = false
  91. }
  92. })
  93. const changeLike = () => likeStatus ? removeLikePost(likeId, postId) : addLikePost(postId)
  94. const styleFontSize = '1.7em'
  95. return (
  96. <>
  97. <Row className="Post__panel-btn">
  98. <Col className='Post__heart'>
  99. <HeartLike
  100. changeLike={changeLike}
  101. likeStatus={likeStatus}
  102. styleFontSize={styleFontSize} />
  103. </Col>
  104. <Col>
  105. </Col>
  106. </Row>
  107. {!!likes.length && <strong>Likes: {likes.length}</strong>}
  108. </>
  109. )
  110. }
  111. const CPostUserPanel = connect(state => ({
  112. myID: state.auth.payload.sub.id || '',
  113. myLikes: state?.promise?.myLikes?.payload || [],
  114. }), {
  115. addLikePost: actionLikePost,
  116. removeLikePost: actionDelLikePost,
  117. })(PostUserPanel)
  118. const PostDescription = ({ title, description, date }) =>
  119. <>
  120. <Row justify='space-between'>
  121. <Col >
  122. {!!title && <Text level={3} strong>{title}</Text>}
  123. </Col>
  124. <Col >
  125. <Text type='secondary'>{date}</Text>
  126. </Col>
  127. </Row>
  128. <Paragraph ellipsis={true ? { rows: 1, expandable: true, symbol: 'more' } : false}>
  129. {description}
  130. </Paragraph>
  131. </>
  132. const Comments = ({ comments }) =>
  133. <>
  134. {comments && comments.length > 2 &&
  135. <Link to={`/#`}>
  136. <Text type={'secondary'} level={3}>{`Посмотреть все ${comments.length} комментария`}</Text>
  137. </Link>}
  138. {comments && <div>
  139. <div className='Post__comments'>
  140. <Link to={`/#`}>{comments[comments?.length - 2]?.owner?.nick || comments[comments?.length - 2]?.owner?.login}: </Link>
  141. <span>{comments[comments?.length - 2]?.text}</span>
  142. </div>
  143. <div className='Post__comments'>
  144. <Link to={`/#`}>{comments[comments?.length - 1]?.owner?.login || comments[comments?.length - 1]?.owner?.login}: </Link>
  145. <span>{comments[comments?.length - 1]?.text}</span>
  146. </div>
  147. </div>}
  148. </>
  149. const FieldCommentSend = ({ postId, sentComment }) => {
  150. const [commentValue, setCommentValue] = useState('')
  151. const [error, setError] = useState(false)
  152. const changeComentTextarea = (e) => {
  153. setCommentValue(e.currentTarget.value)
  154. setError(false)
  155. }
  156. const sendCommentValid = (value) => {
  157. if (value.trim() !== '') {
  158. sentComment(postId, value.trim())
  159. setCommentValue('')
  160. } else {
  161. setError(true)
  162. }
  163. }
  164. return (
  165. <>
  166. {error && <Text type='danger'>Field is required</Text>}
  167. <Row align='middle' className='Post__send-comment'>
  168. <Col flex='auto' offset={1}>
  169. <TextArea value={commentValue}
  170. placeholder="Add a comment ..."
  171. autoSize={{ minRows: 1, maxRows: 2 }}
  172. onChange={changeComentTextarea}
  173. bordered={false}
  174. />
  175. </Col>
  176. <Col span={2}>
  177. <Button
  178. onClick={() => sendCommentValid(commentValue)}
  179. icon={< SendOutlined
  180. style={{ fontSize: '1.2em', opacity: .6 }} />} />
  181. </Col>
  182. </Row>
  183. </>
  184. )
  185. }
  186. const CFieldCommentSend = connect(null, { sentComment: actionFullAddComment })(FieldCommentSend)
  187. const Post = ({ postData: { _id, text, title, owner, images, createdAt = '', comments, likes } }) => {
  188. const date = new Date(createdAt * 1)
  189. const resultDate = new Intl.DateTimeFormat('default').format(date)
  190. return (
  191. <div className='Post'>
  192. <Card
  193. title={<PostTitle owner={owner} />}
  194. cover={<PostImage images={images} />}
  195. actions={[<CFieldCommentSend postId={_id} />]}
  196. >
  197. <CPostUserPanel postId={_id} likes={likes} />
  198. <PostDescription title={title} description={text} date={resultDate} />
  199. <Comments comments={comments} />
  200. </Card>
  201. </div>
  202. )
  203. }
  204. const MainPostsFeed = ({ posts, count, postsFollowing, postsFollowingRemove, following }) => {
  205. const [checkScroll, setCheckScroll] = useState(true)
  206. useEffect(() => {
  207. if (checkScroll && following.length !== 0) {
  208. postsFollowing(following)
  209. setCheckScroll(false)
  210. }
  211. }, [checkScroll, following])
  212. useEffect(() => {
  213. document.addEventListener('scroll', scrollHandler)
  214. return () => {
  215. document.removeEventListener('scroll', scrollHandler)
  216. postsFollowingRemove()
  217. console.log(posts.length);
  218. }
  219. }, [])
  220. const scrollHandler = (e) => {
  221. if (e.target.documentElement.scrollHeight - (e.target.documentElement.scrollTop + window.innerHeight) < 500) {
  222. setCheckScroll(true)
  223. }
  224. }
  225. return (
  226. <>
  227. {posts.map(p => <Post key={p._id} postData={p} />)}
  228. </>
  229. )
  230. }
  231. export const CMainPostsFeed = connect(state => ({
  232. posts: state?.postsFeed?.posts || [],
  233. following: state?.myData.following || []
  234. }), {
  235. postsFollowing: actionPostsFeed,
  236. postsFollowingRemove: actionRemovePostsFeedAC,
  237. })(MainPostsFeed)