index.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. import { makeStyles } from "@material-ui/core/styles";
  2. import { useState, useEffect, useCallback, useMemo } from "react";
  3. import { useSelector,useDispatch } from "react-redux";
  4. import ArrowBack from "./ArrowBack";
  5. import SendMessage from "./SendMessage";
  6. import UnpinBar from "./UnpinBar";
  7. import MessageLeftText from './Messages/MessageLeftText'
  8. import MessageReplyLeft from "./Messages/MessageReplyLeft";
  9. import MessageLeftImage from './Messages/MessageLeftImage'
  10. import MessageLeftAudio from './Messages/MessageLeftAudio'
  11. import MessageLeftVideo from './Messages/MessageLeftVideo'
  12. import MessageLeftFile from "./Messages/MessageLeftFile";
  13. import MessageRightText from './Messages/MessageRightText'
  14. import MessageReplyRight from "./Messages/MessageReplyRight";
  15. import MessageRightImage from './Messages/MessageRightImage'
  16. import MessageRightAudio from './Messages/MessageRightAudio'
  17. import MessageRightVideo from './Messages/MessageRightVideo'
  18. import MessageRightFile from "./Messages/MessageRightFile";
  19. import MessageTime from "./Messages/MessageTime";
  20. import AlertInfo from "../../../reusableComponents/AlertInfo";
  21. import { getMessagesMemo } from '../../../../redux/messages/selector'
  22. import { getAuthorizationState } from '../../../../redux/authorization/selector'
  23. import { getChat } from '../../../../redux/chat/selector'
  24. import { getScrollChat } from '../../../../redux/control/selector'
  25. import { actionScrollChat } from '../../../../redux/control/action'
  26. import { asyncGetMessagesById } from '../../../../redux/messages/operations'
  27. import { asyncGetChatById } from "../../../../redux/chat/operations";
  28. import { seenChat } from "../../../../api-data";
  29. import { TPinnedMessages } from "../../../../typescript/redux/pinnedMessages/types";
  30. import { TMessage } from "../../../../typescript/redux/allMessages/types";
  31. import { timeStampFilter,prodAwsS3,refreshAppTime } from "../../../../helpers";
  32. const debounce = require('lodash.debounce');
  33. const useStyles = makeStyles({
  34. container: {
  35. height: '93vh',
  36. width: "100%",
  37. display: "flex",
  38. alignItems: "center",
  39. alignContent:"center",
  40. flexDirection: "column",
  41. position: "relative",
  42. },
  43. messagesScroll: {
  44. paddingTop: 30,
  45. overflowY: "scroll",
  46. maxHeight: '83vh',
  47. width: "100%",
  48. display: "flex",
  49. justifyContent: 'center',
  50. '&::-webkit-scrollbar': {
  51. width: '0.4em'
  52. },
  53. '&::-webkit-scrollbar-track': {
  54. boxShadow: 'inset 0 0 6px rgba(0,0,0,0.00)',
  55. webkitBoxShadow: 'inset 0 0 6px rgba(0,0,0,0.00)',
  56. backgroundColor: '#eceeec',
  57. },
  58. '&::-webkit-scrollbar-thumb': {
  59. backgroundColor: '#ccc8c8',
  60. },
  61. "&::-webkit-scrollbar-thumb:focus": {
  62. backgroundColor: "#959595",
  63. },
  64. "&::-webkit-scrollbar-thumb:active": {
  65. backgroundColor: "#959595",
  66. },
  67. },
  68. messagesEmpty: {
  69. overflowY: "hidden",
  70. width: "100%",
  71. display: "flex",
  72. justifyContent: 'center',
  73. paddingTop: 30,
  74. },
  75. messagesBody: {
  76. width: "60%",
  77. },
  78. });
  79. interface IChatBar {
  80. chatDivRef: any | null,
  81. selectedArr: string[] | [],
  82. setSelectedArr: React.Dispatch<React.SetStateAction<string[] | []>>,
  83. isSomeSelected: boolean,
  84. setIsSomeSelected: React.Dispatch<React.SetStateAction<boolean>>,
  85. openPinned: boolean,
  86. pinnedMessagesMemo: TPinnedMessages,
  87. handleUnpinAll: () => void
  88. }
  89. const ChatBar = ({chatDivRef,selectedArr,setSelectedArr,isSomeSelected,setIsSomeSelected,openPinned,pinnedMessagesMemo,handleUnpinAll}:IChatBar) => {
  90. const classes = useStyles();
  91. const dispatch = useDispatch()
  92. const messagesMemo = useSelector(getMessagesMemo)
  93. const { number:userNumber,nightMode,silentMode} = useSelector(getAuthorizationState)
  94. const { companionId,total,seen,mute } = useSelector(getChat)
  95. const scrollChat = useSelector(getScrollChat)
  96. const [isReply, setIsReply] = useState<TMessage | undefined>(undefined)
  97. const [isArrow, setIsArrow] = useState<boolean>(false)
  98. const [isNew, setIsNew] = useState<{new:number,mute:boolean}>({new:0,mute:false})
  99. let time: any
  100. const isSelected = (_id: string) => selectedArr.some((el: string) => el === _id)
  101. const handleSelected = (_id: string) => {
  102. !isSomeSelected&&setIsSomeSelected(true)
  103. if (selectedArr.some((el: string) => el === _id))
  104. setSelectedArr(selectedArr.filter((el:string) => el !== _id))
  105. else setSelectedArr([...selectedArr,_id])
  106. }
  107. const handleScrollTo = useCallback(() => {
  108. chatDivRef.current&&chatDivRef.current.scrollTo({
  109. top: chatDivRef.current.scrollHeight,
  110. behavior: 'smooth'
  111. })
  112. },[chatDivRef])
  113. const handleScroll = useCallback(({ target:{scrollHeight,scrollTop,clientHeight}}: any) => {
  114. const different = scrollHeight - Math.floor(scrollTop)
  115. const reached = different - clientHeight
  116. if (total !== seen&&reached < 10 && !openPinned) seenChat(companionId)
  117. setIsArrow(different === clientHeight ? false : true)
  118. }, [total,seen, companionId,openPinned])
  119. const debouncedHandleScroll = debounce(handleScroll, 300)
  120. const renderArr = useMemo(() => {
  121. return !openPinned ? messagesMemo : pinnedMessagesMemo
  122. }, [messagesMemo, pinnedMessagesMemo, openPinned])
  123. const handleReply = (_id: string) => {
  124. setIsReply(renderArr.find((el) => el._id ===_id))
  125. }
  126. const handleScrollToTheMessage = (_id:string) => {
  127. const childNodes = chatDivRef.current.childNodes[0].childNodes
  128. let toScrollNode = [...childNodes].find((el: any) => el.id === _id)
  129. if (toScrollNode) {
  130. toScrollNode = [...toScrollNode.childNodes].slice(-1)[0]
  131. toScrollNode.style.boxShadow = '0px 0px 6px 0px #ffffff'
  132. toScrollNode.scrollIntoView({ behavior: 'smooth' })
  133. setTimeout(() => {
  134. toScrollNode.style.boxShadow = 'unset'
  135. }, 2000)
  136. }
  137. }
  138. useEffect(() => {
  139. if (scrollChat) {
  140. dispatch(asyncGetMessagesById(companionId, handleScrollTo))
  141. dispatch(actionScrollChat(false))
  142. }
  143. }, [dispatch,handleScrollTo, scrollChat, companionId])
  144. useEffect(() => {
  145. const handleReset = () => {
  146. dispatch(asyncGetChatById(companionId))
  147. dispatch(asyncGetMessagesById(companionId, null))
  148. }
  149. handleReset()
  150. const idInterval = setInterval(handleReset, refreshAppTime);
  151. return () => clearInterval(idInterval);
  152. }, [dispatch, companionId]);
  153. useEffect(() => {
  154. setIsNew({ new:total-seen,mute})
  155. }, [total, seen, mute]);
  156. useEffect(() => {
  157. if (chatDivRef.current&&openPinned) {
  158. const { scrollHeight, clientHeight } = chatDivRef.current
  159. if (scrollHeight === clientHeight && isArrow) setIsArrow(false)
  160. }
  161. }, [chatDivRef,openPinned,pinnedMessagesMemo.length, isArrow]);
  162. useEffect(() => {
  163. const handleReset = () => {
  164. if (chatDivRef.current&&!openPinned) {
  165. const { scrollHeight, clientHeight } = chatDivRef.current
  166. if (total !== seen && scrollHeight === clientHeight) seenChat(companionId)
  167. }
  168. }
  169. const idInterval = setInterval(handleReset, refreshAppTime);
  170. return () => clearInterval(idInterval);
  171. }, [total, seen, chatDivRef, companionId,openPinned]);
  172. return (
  173. <div className={classes.container} >
  174. <ArrowBack isArrow={isArrow} isNew={isNew} handleScrollTo={handleScrollTo} openPinned={openPinned}/>
  175. <div id={companionId} ref={chatDivRef} onScroll={debouncedHandleScroll}
  176. className={messagesMemo.length > 0 ? classes.messagesScroll : classes.messagesEmpty}>
  177. <div className={classes.messagesBody}>
  178. {messagesMemo.length > 0 ? renderArr.map(({ replyMessage,message, name, lastName, color,pinned,
  179. createdAt,number, type,fullType,replyName,replyLastName,caption,emoji,emojiCompanion,_id,oldId,}) => {
  180. let isTime
  181. if (!time) {
  182. isTime = true
  183. time = createdAt
  184. } else if (timeStampFilter(time) !== timeStampFilter(createdAt)) {
  185. time = createdAt
  186. isTime = true
  187. }
  188. const url = `${prodAwsS3}/${message}`
  189. const urlReply = `${prodAwsS3}/${replyMessage}`
  190. if (number !== userNumber) {
  191. if (type === 'text' && !oldId) return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  192. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  193. <MessageLeftText
  194. message={message}
  195. createdAt={createdAt}
  196. name={name}
  197. lastName={lastName}
  198. caption={caption}
  199. emoji={emoji}
  200. emojiCompanion={emojiCompanion}
  201. pinned={pinned}
  202. isSomeSelected={isSomeSelected}
  203. isSelected={isSelected}
  204. handleSelected={handleSelected}
  205. _id={_id}
  206. nightMode={nightMode}
  207. handleReply={handleReply}
  208. /></div>)
  209. if (type === 'text' && oldId) return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  210. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  211. <MessageReplyLeft
  212. url={urlReply}
  213. replyMessage={replyMessage}
  214. message={message}
  215. createdAt={createdAt}
  216. replyName={replyName}
  217. replyLastName={replyLastName}
  218. caption={caption}
  219. emoji={emoji}
  220. emojiCompanion={emojiCompanion}
  221. pinned={pinned}
  222. isSomeSelected={isSomeSelected}
  223. isSelected={isSelected}
  224. handleSelected={handleSelected}
  225. _id={_id}
  226. nightMode={nightMode}
  227. handleReply={handleReply}
  228. fullType={fullType}
  229. handleScrollToTheMessage={handleScrollToTheMessage}
  230. oldId={oldId}
  231. /></div>)
  232. if (type === 'image') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  233. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  234. <MessageLeftImage
  235. url={url}
  236. createdAt={createdAt}
  237. color={color}
  238. fullType={fullType}
  239. caption={caption}
  240. emoji={emoji}
  241. emojiCompanion={emojiCompanion}
  242. pinned={pinned}
  243. isSomeSelected={isSomeSelected}
  244. isSelected={isSelected}
  245. handleSelected={handleSelected}
  246. _id={_id}
  247. name={name}
  248. lastName={lastName}
  249. nightMode={nightMode}
  250. handleReply={handleReply}
  251. /></div>)
  252. if (type === 'audio') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  253. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  254. <MessageLeftAudio
  255. url={url}
  256. createdAt={createdAt}
  257. fullType={fullType}
  258. caption={caption}
  259. emoji={emoji}
  260. emojiCompanion={emojiCompanion}
  261. pinned={pinned}
  262. isSomeSelected={isSomeSelected}
  263. isSelected={isSelected}
  264. handleSelected={handleSelected}
  265. _id={_id}
  266. nightMode={nightMode}
  267. handleReply={handleReply}
  268. /></div>)
  269. if (type === 'video') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  270. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  271. <MessageLeftVideo
  272. url={url}
  273. createdAt={createdAt}
  274. fullType={fullType}
  275. caption={caption}
  276. emoji={emoji}
  277. emojiCompanion={emojiCompanion}
  278. pinned={pinned}
  279. isSomeSelected={isSomeSelected}
  280. isSelected={isSelected}
  281. handleSelected={handleSelected}
  282. _id={_id}
  283. nightMode={nightMode}
  284. handleReply={handleReply}
  285. /></div>)
  286. if (type) return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  287. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  288. <MessageLeftFile
  289. url={url}
  290. createdAt={createdAt}
  291. type={type}
  292. caption={caption}
  293. emoji={emoji}
  294. emojiCompanion={emojiCompanion}
  295. pinned={pinned}
  296. isSomeSelected={isSomeSelected}
  297. isSelected={isSelected}
  298. handleSelected={handleSelected}
  299. _id={_id}
  300. nightMode={nightMode}
  301. handleReply={handleReply}
  302. /></div>)
  303. } else {
  304. if (type === 'text' && !oldId) return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  305. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  306. <MessageRightText
  307. message={message}
  308. createdAt={createdAt}
  309. name={name}
  310. lastName={lastName}
  311. caption={caption}
  312. emoji={emoji}
  313. emojiCompanion={emojiCompanion}
  314. pinned={pinned}
  315. isSomeSelected={isSomeSelected}
  316. isSelected={isSelected}
  317. handleSelected={handleSelected}
  318. _id={_id}
  319. nightMode={nightMode}
  320. handleReply={handleReply}
  321. /></div>)
  322. if (type === 'text' && oldId) return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  323. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  324. <MessageReplyRight
  325. url={urlReply}
  326. replyMessage={replyMessage}
  327. message={message}
  328. createdAt={createdAt}
  329. replyName={replyName}
  330. replyLastName={replyLastName}
  331. caption={caption}
  332. emoji={emoji}
  333. emojiCompanion={emojiCompanion}
  334. pinned={pinned}
  335. isSomeSelected={isSomeSelected}
  336. isSelected={isSelected}
  337. handleSelected={handleSelected}
  338. _id={_id}
  339. nightMode={nightMode}
  340. handleReply={handleReply}
  341. fullType={fullType}
  342. handleScrollToTheMessage={handleScrollToTheMessage}
  343. oldId={oldId}
  344. /></div>)
  345. if (type === 'image') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  346. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  347. <MessageRightImage
  348. url={url}
  349. createdAt={createdAt}
  350. color={color}
  351. fullType={fullType}
  352. caption={caption}
  353. emoji={emoji}
  354. emojiCompanion={emojiCompanion}
  355. pinned={pinned}
  356. isSomeSelected={isSomeSelected}
  357. isSelected={isSelected}
  358. handleSelected={handleSelected}
  359. _id={_id}
  360. name={name}
  361. lastName={lastName}
  362. nightMode={nightMode}
  363. handleReply={handleReply}
  364. /></div>)
  365. if (type === 'audio') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  366. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  367. <MessageRightAudio
  368. url={url}
  369. createdAt={createdAt}
  370. fullType={fullType}
  371. caption={caption}
  372. emoji={emoji}
  373. emojiCompanion={emojiCompanion}
  374. pinned={pinned}
  375. isSomeSelected={isSomeSelected}
  376. isSelected={isSelected}
  377. handleSelected={handleSelected}
  378. _id={_id}
  379. nightMode={nightMode}
  380. handleReply={handleReply}
  381. /></div>)
  382. if (type === 'video') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  383. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  384. <MessageRightVideo
  385. url={url}
  386. createdAt={createdAt}
  387. fullType={fullType}
  388. caption={caption}
  389. emoji={emoji}
  390. emojiCompanion={emojiCompanion}
  391. pinned={pinned}
  392. isSomeSelected={isSomeSelected}
  393. isSelected={isSelected}
  394. handleSelected={handleSelected}
  395. _id={_id}
  396. nightMode={nightMode}
  397. handleReply={handleReply}
  398. /></div>)
  399. if (type) return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  400. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  401. <MessageRightFile
  402. url={url}
  403. createdAt={createdAt}
  404. type={type}
  405. caption={caption}
  406. emoji={emoji}
  407. emojiCompanion={emojiCompanion}
  408. pinned={pinned}
  409. isSomeSelected={isSomeSelected}
  410. isSelected={isSelected}
  411. handleSelected={handleSelected}
  412. _id={_id}
  413. nightMode={nightMode}
  414. handleReply={handleReply}
  415. /></div>)
  416. }
  417. }) : <AlertInfo name='You do not have messages yet!' />}
  418. </div>
  419. </div>
  420. {!openPinned&&<SendMessage isArrow={isArrow} silentMode={silentMode} isReply={isReply} setIsReply={setIsReply} handleScrollToTheMessage={handleScrollToTheMessage}/>}
  421. {openPinned&&!isSomeSelected &&<UnpinBar pinnedMessagesMemo={pinnedMessagesMemo} handleUnpinAll={handleUnpinAll} />}
  422. </div>
  423. );
  424. }
  425. export default ChatBar