index.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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 MessageLeftImage from './Messages/MessageLeftImage'
  9. import MessageLeftAudio from './Messages/MessageLeftAudio'
  10. import MessageLeftVideo from './Messages/MessageLeftVideo'
  11. import MessageLeftFile from "./Messages/MessageLeftFile";
  12. import MessageRightText from './Messages/MessageRightText'
  13. import MessageRightImage from './Messages/MessageRightImage'
  14. import MessageRightAudio from './Messages/MessageRightAudio'
  15. import MessageRightVideo from './Messages/MessageRightVideo'
  16. import MessageRightFile from "./Messages/MessageRightFile";
  17. import MessageTime from "./Messages/MessageTime";
  18. import AlertInfo from "../../../reusableComponents/AlertInfo";
  19. import { getMessagesMemo } from '../../../../redux/messages/selector'
  20. import { getNumber } from '../../../../redux/authorization/selector'
  21. import { getChat } from '../../../../redux/chat/selector'
  22. import { getScrollChat } from '../../../../redux/control/selector'
  23. import { actionScrollChat } from '../../../../redux/control/action'
  24. import { asyncGetMessagesById } from '../../../../redux/messages/operations'
  25. import { asyncGetChatById } from "../../../../redux/chat/operations";
  26. import { seenChat } from "../../../../api-data";
  27. import { TPinnedMessages } from "../../../../typescript/redux/pinnedMessages/types";
  28. import { timeStampFilter,prodAwsS3,refreshAppTime } from "../../../../helpers";
  29. const debounce = require('lodash.debounce');
  30. const useStyles = makeStyles({
  31. container: {
  32. height: '93vh',
  33. width: "100%",
  34. display: "flex",
  35. alignItems: "center",
  36. alignContent:"center",
  37. flexDirection: "column",
  38. position: "relative",
  39. },
  40. messagesScroll: {
  41. paddingTop: 30,
  42. overflowY: "scroll",
  43. maxHeight: '83vh',
  44. width: "100%",
  45. display: "flex",
  46. justifyContent: 'center',
  47. '&::-webkit-scrollbar': {
  48. width: '0.4em'
  49. },
  50. '&::-webkit-scrollbar-track': {
  51. boxShadow: 'inset 0 0 6px rgba(0,0,0,0.00)',
  52. webkitBoxShadow: 'inset 0 0 6px rgba(0,0,0,0.00)',
  53. backgroundColor: '#eceeec',
  54. },
  55. '&::-webkit-scrollbar-thumb': {
  56. backgroundColor: '#ccc8c8',
  57. },
  58. "&::-webkit-scrollbar-thumb:focus": {
  59. backgroundColor: "#959595",
  60. },
  61. "&::-webkit-scrollbar-thumb:active": {
  62. backgroundColor: "#959595",
  63. },
  64. },
  65. messagesEmpty: {
  66. overflowY: "hidden",
  67. width: "100%",
  68. display: "flex",
  69. justifyContent: 'center',
  70. paddingTop: 30,
  71. },
  72. messagesBody: {
  73. width: "60%",
  74. },
  75. });
  76. interface IChatBar {
  77. chatDivRef: any | null,
  78. selectedArr: string[] | [],
  79. setSelectedArr: React.Dispatch<React.SetStateAction<string[] | []>>,
  80. isSomeSelected: boolean,
  81. setIsSomeSelected: React.Dispatch<React.SetStateAction<boolean>>,
  82. openPinned: boolean,
  83. pinnedMessagesMemo: TPinnedMessages,
  84. handleUnpinAll: () => void
  85. }
  86. const ChatBar = ({chatDivRef,selectedArr,setSelectedArr,isSomeSelected,setIsSomeSelected,openPinned,pinnedMessagesMemo,handleUnpinAll}:IChatBar) => {
  87. const classes = useStyles();
  88. const dispatch = useDispatch()
  89. const messagesMemo = useSelector(getMessagesMemo)
  90. const userNumber = useSelector(getNumber)
  91. const { companionId,total,seen,mute } = useSelector(getChat)
  92. const scrollChat = useSelector(getScrollChat)
  93. const [isArrow, setIsArrow] = useState<boolean>(false)
  94. const [isNew, setIsNew] = useState<{new:number,mute:boolean}>({new:0,mute:false})
  95. let time: any
  96. const isSelected = (_id: string) => selectedArr.some((el: string) => el === _id)
  97. const handleSelected = (_id: string) => {
  98. !isSomeSelected&&setIsSomeSelected(true)
  99. if (selectedArr.some((el: string) => el === _id))
  100. setSelectedArr(selectedArr.filter((el:string) => el !== _id))
  101. else setSelectedArr([...selectedArr,_id])
  102. }
  103. const handleScrollTo = useCallback(() => {
  104. chatDivRef.current&&chatDivRef.current.scrollTo({
  105. top: chatDivRef.current.scrollHeight,
  106. behavior: 'smooth'
  107. })
  108. },[chatDivRef])
  109. const handleScroll = useCallback(({ target:{scrollHeight,scrollTop,clientHeight}}: any) => {
  110. const different = scrollHeight - Math.floor(scrollTop)
  111. const reached = different - clientHeight
  112. if (total !== seen&&reached < 10 && !openPinned) seenChat(companionId)
  113. setIsArrow(different === clientHeight ? false : true)
  114. }, [total,seen, companionId,openPinned])
  115. const debouncedHandleScroll = debounce(handleScroll, 300)
  116. useEffect(() => {
  117. if (scrollChat) {
  118. dispatch(asyncGetMessagesById(companionId, handleScrollTo))
  119. dispatch(actionScrollChat(false))
  120. }
  121. }, [dispatch,handleScrollTo, scrollChat, companionId])
  122. useEffect(() => {
  123. const handleReset = () => {
  124. dispatch(asyncGetChatById(companionId))
  125. dispatch(asyncGetMessagesById(companionId, null))
  126. }
  127. handleReset()
  128. const idInterval = setInterval(handleReset, refreshAppTime);
  129. return () => clearInterval(idInterval);
  130. }, [dispatch, companionId]);
  131. useEffect(() => {
  132. setIsNew({ new:total-seen,mute})
  133. }, [total,seen,mute]);
  134. useEffect(() => {
  135. const handleReset = () => {
  136. if (chatDivRef.current&&!openPinned) {
  137. const { scrollHeight, clientHeight } = chatDivRef.current
  138. if (total !== seen && scrollHeight === clientHeight) seenChat(companionId)
  139. }
  140. }
  141. const idInterval = setInterval(handleReset, refreshAppTime);
  142. return () => clearInterval(idInterval);
  143. }, [total, seen, chatDivRef, companionId,openPinned]);
  144. const renderArr = useMemo(() => {
  145. return !openPinned ? messagesMemo : pinnedMessagesMemo
  146. },[messagesMemo,pinnedMessagesMemo,openPinned])
  147. return (
  148. <div className={classes.container} >
  149. <ArrowBack isArrow={isArrow} isNew={isNew} handleScrollTo={handleScrollTo} openPinned={openPinned}/>
  150. <div id={companionId} ref={chatDivRef} onScroll={debouncedHandleScroll}
  151. className={messagesMemo.length > 0 ? classes.messagesScroll : classes.messagesEmpty}>
  152. <div className={classes.messagesBody}>
  153. {messagesMemo.length > 0 ? renderArr.map(({ message, name, lastName, color,pinned,
  154. createdAt,number, type,fullType,caption,emoji,emojiCompanion,_id }) => {
  155. let isTime
  156. if (!time) {
  157. isTime = true
  158. time = createdAt
  159. } else if (timeStampFilter(time) !== timeStampFilter(createdAt)) {
  160. time = createdAt
  161. isTime = true
  162. }
  163. const url = `${prodAwsS3}/${message}`
  164. if (number !== userNumber) {
  165. if (type === 'text') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  166. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  167. <MessageLeftText
  168. message={message}
  169. createdAt={createdAt}
  170. name={name}
  171. lastName={lastName}
  172. caption={caption}
  173. emoji={emoji}
  174. emojiCompanion={emojiCompanion}
  175. pinned={pinned}
  176. isSomeSelected={isSomeSelected}
  177. isSelected={isSelected}
  178. handleSelected={handleSelected}
  179. _id={_id}
  180. /></div>)
  181. if (type === 'image') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  182. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  183. <MessageLeftImage
  184. url={url}
  185. createdAt={createdAt}
  186. color={color}
  187. fullType={fullType}
  188. caption={caption}
  189. emoji={emoji}
  190. emojiCompanion={emojiCompanion}
  191. pinned={pinned}
  192. isSomeSelected={isSomeSelected}
  193. isSelected={isSelected}
  194. handleSelected={handleSelected}
  195. _id={_id}
  196. /></div>)
  197. if (type === 'audio') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  198. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  199. <MessageLeftAudio
  200. url={url}
  201. createdAt={createdAt}
  202. fullType={fullType}
  203. caption={caption}
  204. emoji={emoji}
  205. emojiCompanion={emojiCompanion}
  206. pinned={pinned}
  207. isSomeSelected={isSomeSelected}
  208. isSelected={isSelected}
  209. handleSelected={handleSelected}
  210. _id={_id}
  211. /></div>)
  212. if (type === 'video') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  213. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  214. <MessageLeftVideo
  215. url={url}
  216. createdAt={createdAt}
  217. fullType={fullType}
  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. /></div>)
  227. if (type) return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  228. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  229. <MessageLeftFile
  230. url={url}
  231. createdAt={createdAt}
  232. type={type}
  233. caption={caption}
  234. emoji={emoji}
  235. emojiCompanion={emojiCompanion}
  236. pinned={pinned}
  237. isSomeSelected={isSomeSelected}
  238. isSelected={isSelected}
  239. handleSelected={handleSelected}
  240. _id={_id}
  241. /></div>)
  242. } else {
  243. if (type === 'text') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  244. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  245. <MessageRightText
  246. message={message}
  247. createdAt={createdAt}
  248. name={name}
  249. lastName={lastName}
  250. caption={caption}
  251. emoji={emoji}
  252. emojiCompanion={emojiCompanion}
  253. pinned={pinned}
  254. isSomeSelected={isSomeSelected}
  255. isSelected={isSelected}
  256. handleSelected={handleSelected}
  257. _id={_id}
  258. /></div>)
  259. if (type === 'image') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  260. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  261. <MessageRightImage
  262. url={url}
  263. createdAt={createdAt}
  264. color={color}
  265. fullType={fullType}
  266. caption={caption}
  267. emoji={emoji}
  268. emojiCompanion={emojiCompanion}
  269. pinned={pinned}
  270. isSomeSelected={isSomeSelected}
  271. isSelected={isSelected}
  272. handleSelected={handleSelected}
  273. _id={_id}
  274. /></div>)
  275. if (type === 'audio') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  276. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  277. <MessageRightAudio
  278. url={url}
  279. createdAt={createdAt}
  280. fullType={fullType}
  281. caption={caption}
  282. emoji={emoji}
  283. emojiCompanion={emojiCompanion}
  284. pinned={pinned}
  285. isSomeSelected={isSomeSelected}
  286. isSelected={isSelected}
  287. handleSelected={handleSelected}
  288. _id={_id}
  289. /></div>)
  290. if (type === 'video') return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  291. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  292. <MessageRightVideo
  293. url={url}
  294. createdAt={createdAt}
  295. fullType={fullType}
  296. caption={caption}
  297. emoji={emoji}
  298. emojiCompanion={emojiCompanion}
  299. pinned={pinned}
  300. isSomeSelected={isSomeSelected}
  301. isSelected={isSelected}
  302. handleSelected={handleSelected}
  303. _id={_id}
  304. /></div>)
  305. if (type) return (<div key={createdAt} id={_id} style={{borderRadius: 7}}>
  306. {isTime&&<MessageTime message={timeStampFilter(createdAt)}/>}
  307. <MessageRightFile
  308. url={url}
  309. createdAt={createdAt}
  310. type={type}
  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. /></div>)
  320. }
  321. }) : <AlertInfo name='You do not have messages yet!' />}
  322. </div>
  323. </div>
  324. {!openPinned ? <SendMessage isArrow={isArrow} /> :
  325. <UnpinBar pinnedMessagesMemo={pinnedMessagesMemo} handleUnpinAll={handleUnpinAll} />}
  326. </div>
  327. );
  328. }
  329. export default ChatBar