index.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import List from '@mui/material/List';
  2. import ListItemButton from '@mui/material/ListItemButton';
  3. import Avatar from '@mui/material/Avatar';
  4. import ListItemText from '@mui/material/ListItemText';
  5. import ListItemIcon from '@mui/material/ListItemIcon';
  6. import { styled } from '@mui/material/styles';
  7. import Badge from '@mui/material/Badge';
  8. import VolumeOffIcon from '@mui/icons-material/VolumeOff';
  9. import { makeStyles, Typography } from '@material-ui/core'
  10. import { useState,useEffect,useRef } from 'react';
  11. import shortid from 'shortid';
  12. import { useSelector, useDispatch } from 'react-redux';
  13. import AlertInfo from '../../../reusableComponents/AlertInfo'
  14. import DoneAllIcon from '@mui/icons-material/DoneAll';
  15. import { firstLetter, slicedWord, timeStamp,notification,playNotificationWithoutPermission } from '../../../../helpers'
  16. import { getState } from '../../../../redux/chats/selector'
  17. import { asyncGetChats } from '../../../../redux/chats/operations'
  18. import { asyncStartChatById } from '../../../../redux/chat/operations'
  19. import { actionScroll } from '../../../../redux/control/action'
  20. const StyledBadge = styled(Badge)(({ theme }) => ({
  21. '& .MuiBadge-badge': {
  22. backgroundColor: '#44b700',
  23. color: '#44b700',
  24. boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
  25. '&::after': {
  26. position: 'absolute',
  27. top: 0,
  28. left: 0,
  29. width: '100%',
  30. height: '100%',
  31. borderRadius: '50%',
  32. animation: 'ripple 1.2s infinite ease-in-out',
  33. border: '1px solid currentColor',
  34. content: '""',
  35. },
  36. },
  37. '@keyframes ripple': {
  38. '0%': {
  39. transform: 'scale(.8)',
  40. opacity: 1,
  41. },
  42. '100%': {
  43. transform: 'scale(2.4)',
  44. opacity: 0,
  45. },
  46. },
  47. }));
  48. const useStyles = makeStyles({
  49. list: {
  50. width: '100%',
  51. maxHeight: '890px',
  52. overflowY: 'scroll',
  53. '&::-webkit-scrollbar': {
  54. width: '0.4em'
  55. },
  56. '&::-webkit-scrollbar-track': {
  57. boxShadow: 'inset 0 0 6px rgba(0,0,0,0.00)',
  58. webkitBoxShadow: 'inset 0 0 6px rgba(0,0,0,0.00)',
  59. backgroundColor: '#eceeec',
  60. },
  61. '&::-webkit-scrollbar-thumb': {
  62. backgroundColor: '#ccc8c8',
  63. },
  64. "&::-webkit-scrollbar-thumb:focus": {
  65. backgroundColor: "#959595",
  66. },
  67. "&::-webkit-scrollbar-thumb:active": {
  68. backgroundColor: "#959595",
  69. },
  70. },
  71. listItemInnerText: {
  72. display: 'flex',
  73. alignContent: 'center',
  74. alignItems: 'center',
  75. flexWrap: 'nowrap',
  76. },
  77. listItemInnerText__icon: {
  78. marginLeft: 5,
  79. color: '#959595',
  80. },
  81. listItem_iconAvatar: {
  82. marginRight:10
  83. },
  84. listItem_iconRight: {
  85. marginRight: 10,
  86. display: 'flex',
  87. alignItems: 'center',
  88. justifyContent: 'center',
  89. alignContent: 'center',
  90. flexDirection: 'column'
  91. },
  92. listItem_iconTimeChecked: {
  93. display: 'flex',
  94. flexWrap: 'nowrap',
  95. alignItems: 'center',
  96. justifyContent: 'center',
  97. alignContent: 'center',
  98. marginBottom:2
  99. },
  100. listItem_iconRightBtn: {
  101. background: '#0ac40a',
  102. borderRadius: '50%',
  103. color: '#ffffff',
  104. border: 'none',
  105. height: 24,
  106. width: 24,
  107. textAlign: 'center',
  108. display: 'flex',
  109. alignItems: 'center',
  110. justifyContent: 'center',
  111. alignContent: 'center',
  112. fontSize: 12,
  113. marginLeft: 'auto',
  114. '&:hover': {
  115. outline: 'solid 3px #3ee415',
  116. }
  117. },
  118. listItem_iconRightBtnMute: {
  119. background: '#a7aaa7',
  120. borderRadius: '50%',
  121. color: '#ffffff',
  122. border: 'none',
  123. height: 24,
  124. width: 24,
  125. textAlign: 'center',
  126. display: 'flex',
  127. alignItems: 'center',
  128. justifyContent: 'center',
  129. alignContent: 'center',
  130. fontSize: 12,
  131. marginLeft: 'auto',
  132. '&:hover': {
  133. outline: 'solid 3px #cccbcb',
  134. }
  135. },
  136. listItem_iconRightBtnHidden: {
  137. background: 'inherit',
  138. borderRadius: '50%',
  139. border: 'none',
  140. height: 24,
  141. width: 24,
  142. textAlign: 'center',
  143. display: 'flex',
  144. alignItems: 'center',
  145. justifyContent: 'center',
  146. alignContent: 'center',
  147. fontSize: 12,
  148. marginLeft: 'auto',
  149. },
  150. listItem_icon_time: {
  151. fontSize: 12,
  152. marginLeft: 5,
  153. color: '#1b1b1b'
  154. },
  155. listItem_typing: {
  156. color: '#4d4d4d',
  157. animation: 'ripple 4s infinite ease-in-out',
  158. },
  159. listItem_dots: {
  160. color: '#1b1b1b',
  161. fontWeight: 'bold',
  162. display:'inline-block',
  163. fontFamily: 'monospace',
  164. clipPath: 'inset(0 3ch 0 0)',
  165. animation: `$run 2s steps(5) infinite`,
  166. },
  167. '@keyframes run': {
  168. to: {
  169. clipPath: 'inset(0 -1ch 0 0)'
  170. },
  171. },
  172. })
  173. const ChatsList = () => {
  174. const classes = useStyles()
  175. const dispatch = useDispatch()
  176. const ref = useRef<any>(null)
  177. const [selectedIndex, setSelectedIndex] = useState<number>(1);
  178. const { total, chats , lastMessages,lastOnline } = useSelector(getState)
  179. const handleListItemClick = (i: number, companionId: string) => {
  180. dispatch(asyncStartChatById(companionId))
  181. dispatch(actionScroll(false))
  182. setSelectedIndex(i);
  183. }
  184. const handleNewMsgs = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>, i: number,companionId: string) => {
  185. e.stopPropagation()
  186. dispatch(asyncStartChatById(companionId))
  187. dispatch(actionScroll(true))
  188. console.log(i,'index','clicked read new messages')
  189. }
  190. useEffect(() => {
  191. const handleReset = () => dispatch(asyncGetChats())
  192. const idInterval = setInterval(handleReset, 1500);
  193. return () => clearInterval(idInterval);
  194. }, [dispatch]);
  195. useEffect(() => {
  196. dispatch(asyncGetChats())
  197. }, [dispatch])
  198. useEffect(() => {
  199. const handleNotification= (companionId: string) => {
  200. dispatch(asyncStartChatById(companionId))
  201. dispatch(actionScroll(true))
  202. }
  203. if (ref.current) {
  204. ref.current.forEach(({total,seen}: any,i:number) => {
  205. const oldDifferent = total - seen
  206. const chat = chats[i]
  207. const newDifferent = chat.total - chat.seen
  208. if (newDifferent > oldDifferent && !chat.mute) {
  209. playNotificationWithoutPermission('http://localhost:3000/recive.mp3')
  210. notification(chat.name,() => handleNotification(chat.companionId))
  211. }
  212. })
  213. }
  214. ref.current = chats
  215. }, [chats,dispatch])
  216. return total !== '0' ? (
  217. <List className={classes.list} component="nav"
  218. aria-label="main mailbox folders">
  219. {chats.map(({name, lastName, avatarUrl, updatedAt, color,companionId,mute,seen,total,watched,typing }, i: number) =>
  220. <ListItemButton
  221. key={shortid.generate()}
  222. selected={selectedIndex === i}
  223. onClick={() => handleListItemClick(i,companionId)}
  224. >
  225. <ListItemIcon className={classes.listItem_iconAvatar}>
  226. <StyledBadge overlap="circular" variant={lastOnline[i].online === 'true'?'dot':'standard'}
  227. anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}>
  228. <Avatar alt={name} src={avatarUrl?`http://localhost:3000/${avatarUrl}`:undefined}
  229. sx={{ background: color, width: 54, height: 54 }}>
  230. {!avatarUrl&&`${firstLetter(name)}${firstLetter(lastName)}`}
  231. </Avatar>
  232. </StyledBadge>
  233. </ListItemIcon>
  234. <ListItemText primary={<div className={classes.listItemInnerText}>
  235. <span>{`${firstLetter(name)}${slicedWord(name, 15, 1)}
  236. ${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`}</span>
  237. {mute&&<VolumeOffIcon className={classes.listItemInnerText__icon} fontSize='small' />}</div>}
  238. secondary={typing ? <span className={classes.listItem_typing}>
  239. typing<span className={classes.listItem_dots}>...</span></span> :
  240. lastMessages[i] ? slicedWord(lastMessages[i].message, 35) :
  241. `${firstLetter(name)}${slicedWord(name, 8, 1)} joined Telegram`}/>
  242. <ListItemIcon className={classes.listItem_iconRight}>
  243. <div className={classes.listItem_iconTimeChecked}>
  244. {watched&& <DoneAllIcon style={{ color: '#18bd03' }} fontSize='small' />}
  245. <Typography className={classes.listItem_icon_time} variant="h6" color="initial">
  246. {lastMessages[i] ? timeStamp(lastMessages[i].updatedAt) :
  247. timeStamp(updatedAt)}</Typography>
  248. </div>
  249. {lastMessages[i] && total > seen ? <button onClick={(e) => handleNewMsgs(e, i,companionId)}
  250. className={mute?classes.listItem_iconRightBtnMute:classes.listItem_iconRightBtn}>{total-seen}</button> :
  251. <button className={classes.listItem_iconRightBtnHidden}/>}
  252. </ListItemIcon>
  253. </ListItemButton>)}
  254. </List>
  255. ):<AlertInfo name='You do not have any chats yet!' />;
  256. }
  257. export default ChatsList