index.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { makeStyles } from '@material-ui/core'
  2. import { useSelector } from 'react-redux'
  3. import { useState,useMemo } from 'react'
  4. import List from '@mui/material/List';
  5. import ListItem from '@mui/material/ListItem';
  6. import ListItemText from '@mui/material/ListItemText';
  7. import ListItemAvatar from '@mui/material/ListItemAvatar';
  8. import Avatar from '@mui/material/Avatar';
  9. import Typography from '@mui/material/Typography';
  10. import Divider from '@mui/material/Divider';
  11. import LibraryMusicIcon from '@mui/icons-material/LibraryMusic';
  12. import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
  13. import ImageIcon from '@mui/icons-material/Image';
  14. import ContentCopyIcon from '@mui/icons-material/ContentCopy';
  15. import VideoLibraryIcon from '@mui/icons-material/VideoLibrary';
  16. import Search from './Search'
  17. import AlertInfo from "../../../reusableComponents/AlertInfo";
  18. import { getMessagesMemo } from '../../../../redux/messages/selector'
  19. import { getOpenPinned } from '../../../../redux/control/selector';
  20. import { getPinnedMessagesMemo } from '../../../../redux/pinnedMessages/selector'
  21. import { getChat } from '../../../../redux/chat/selector'
  22. import { timeStampEU, timeStampFilter, firstLetter, slicedWord, handleSort,prodAwsS3 } from '../../../../helpers'
  23. import { TMessages,TMessage } from '../../../../typescript/redux/messages/types';
  24. const useStyles = makeStyles({
  25. container: {
  26. height: '100%',
  27. backgroundColor: '#ffffff'
  28. },
  29. list: {
  30. maxHeight: '93vh',
  31. overflowY: 'scroll',
  32. '&::-webkit-scrollbar': {
  33. width: '0.4em'
  34. },
  35. '&::-webkit-scrollbar-track': {
  36. boxShadow: 'inset 0 0 6px rgba(0,0,0,0.00)',
  37. webkitBoxShadow: 'inset 0 0 6px rgba(0,0,0,0.00)',
  38. backgroundColor: '#eceeec',
  39. },
  40. '&::-webkit-scrollbar-thumb': {
  41. backgroundColor: '#ccc8c8',
  42. },
  43. "&::-webkit-scrollbar-thumb:focus": {
  44. backgroundColor: "#959595",
  45. },
  46. "&::-webkit-scrollbar-thumb:active": {
  47. backgroundColor: "#959595",
  48. },
  49. },
  50. listItem: {
  51. cursor:'pointer',
  52. '&:hover': {
  53. backgroundColor: '#f0f0f0',
  54. }
  55. },
  56. folderIcon: {
  57. margin: 'auto 0px',
  58. color: '#54b0fc',
  59. }
  60. })
  61. interface ISearchList {
  62. chatDivRef: any | null,
  63. }
  64. const SearchList= ({chatDivRef}:ISearchList) => {
  65. const classes = useStyles()
  66. const { sort } = useSelector(getChat)
  67. const openPinned = useSelector(getOpenPinned)
  68. const messagesMemo = useSelector(getMessagesMemo)
  69. const pinnedMessagesMemo = useSelector(getPinnedMessagesMemo)
  70. const [value, setValue] = useState<string>('')
  71. const [date, setDate] = useState<any>('');
  72. const handleSearch = (e: React.ChangeEvent<HTMLInputElement>): void => setValue(e.target.value)
  73. const handleScrollToTheMessage = (_id: string) => {
  74. const childNodes = chatDivRef.current.childNodes[0].childNodes
  75. let toScrollNode = [...childNodes].find((el: any) => el.id === _id)
  76. if (toScrollNode) {
  77. toScrollNode = [...toScrollNode.childNodes].slice(-1)[0]
  78. toScrollNode.style.backgroundColor = 'rgba(70, 70, 70, 0.4)'
  79. toScrollNode.style.boxShadow = '0px 0px 6px 0px #ffffff'
  80. toScrollNode.scrollIntoView({ behavior: 'smooth' })
  81. setTimeout(() => {
  82. toScrollNode.style.backgroundColor = 'unset'
  83. toScrollNode.style.boxShadow = 'unset'
  84. }, 2000)
  85. }
  86. }
  87. const filteredAndSorted: TMessages = useMemo(() => handleSort('createdAt', !openPinned ? messagesMemo : pinnedMessagesMemo, sort)
  88. .filter((el: TMessage) => {
  89. if (!date) {
  90. return el.message.toLowerCase().includes(value.toLowerCase())
  91. } else if (el.message.toLowerCase().includes(value.toLowerCase())
  92. && timeStampFilter(date) === timeStampFilter(el.createdAt)) {
  93. return el
  94. } return undefined
  95. }),[messagesMemo,date,sort,value,openPinned,pinnedMessagesMemo])
  96. return (
  97. <div className={classes.container}>
  98. <Search handleSearch={handleSearch} value={value}
  99. setDate={setDate} date={date} />
  100. <div className={messagesMemo.length > 0 ?classes.list:undefined}>
  101. {messagesMemo.length > 0 ? filteredAndSorted.length > 0 ?
  102. <List sx={{ width: '100%' }}>
  103. {filteredAndSorted.map(({ name, lastName, avatarUrl, color, message, createdAt,_id,fullType,type }) =>
  104. <div key={createdAt}>
  105. <ListItem onClick={() => handleScrollToTheMessage(_id)}
  106. alignItems="flex-start" className={classes.listItem}>
  107. <ListItemAvatar>
  108. <Avatar alt={name} src={avatarUrl?`${prodAwsS3}/${avatarUrl}`:undefined}
  109. sx={{ background: color, width: 40, height: 40, marginRight:2 }}>
  110. {!avatarUrl&&`${firstLetter(name)}${firstLetter(lastName)}`}
  111. </Avatar>
  112. </ListItemAvatar>
  113. <ListItemText
  114. primary={`${firstLetter(name)}${slicedWord(name, 15, 1)}
  115. ${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`}
  116. secondary={<>
  117. <Typography
  118. sx={{ display: 'block',wordBreak:'break-word' }}
  119. component="span"
  120. variant="body2"
  121. color="text.primary"
  122. >
  123. {type === 'text'? message : fullType}
  124. </Typography>
  125. {timeStampEU(createdAt)}
  126. </>}
  127. />
  128. {type === 'text' &&<ContentCopyIcon className={classes.folderIcon} fontSize='large'/>}
  129. {type !== 'text'&&fullType.includes('audio') &&<LibraryMusicIcon className={classes.folderIcon} fontSize='large' />}
  130. {type !== 'text'&&fullType.includes('video') &&<VideoLibraryIcon className={classes.folderIcon} fontSize='large' />}
  131. {type !== 'text'&&fullType.includes('image') &&<ImageIcon className={classes.folderIcon} fontSize='large' />}
  132. {type !== 'text' && fullType.includes('application') && <InsertDriveFileIcon className={classes.folderIcon} fontSize='large' />}
  133. </ListItem>
  134. <Divider variant="inset"/>
  135. </div>)}
  136. </List> :
  137. <AlertInfo name={`Can not find message by request: ${value}`}/>:
  138. <AlertInfo name='You do not have messages yet!' />}
  139. </div>
  140. </div>
  141. )
  142. }
  143. export default SearchList