index.tsx 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import { makeStyles,Typography } from '@material-ui/core'
  2. import { useState } from 'react';
  3. import { styled } from '@mui/material/styles';
  4. import Menu from '@mui/material/Menu';
  5. import MenuItem from '@mui/material/MenuItem';
  6. import NotificationsNoneIcon from '@mui/icons-material/NotificationsNone';
  7. import VolumeOffIcon from '@mui/icons-material/VolumeOff';
  8. import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
  9. import ListItemButton from '@mui/material/ListItemButton';
  10. import Avatar from '@mui/material/Avatar';
  11. import ListItemText from '@mui/material/ListItemText';
  12. import ListItemIcon from '@mui/material/ListItemIcon';
  13. import Badge from '@mui/material/Badge';
  14. import DoneAllIcon from '@mui/icons-material/DoneAll';
  15. import PushPinIcon from '@mui/icons-material/PushPin';
  16. import CloseIcon from '@mui/icons-material/Close';
  17. import { muteChat,pinChat } from '../../../../../api-data';
  18. import { TChat } from '../../../../../typescript/redux/chats/types';
  19. import { firstLetter, slicedWord, timeStampEU,prodAwsS3 } from '../../../../../helpers';
  20. import DeleteModal from './DeleteModal';
  21. const StyledMenu = styled((props:any) => (
  22. <Menu
  23. elevation={0}
  24. anchorOrigin={{
  25. vertical: 'top',
  26. horizontal: 'right',
  27. }}
  28. transformOrigin={{
  29. vertical: 'bottom',
  30. horizontal: 'right',
  31. }}
  32. {...props}
  33. />
  34. ))(({ theme }:any) => ({
  35. '& .MuiPaper-root': {
  36. borderRadius: 10,
  37. marginTop: theme.spacing(0),
  38. minWidth: 220,
  39. color:
  40. theme.palette.mode === 'light' ? 'rgb(55, 65, 81)' : theme.palette.grey[500],
  41. boxShadow:
  42. 'rgb(255, 255, 255) 0px 0px 0px 0px, rgba(0, 0, 0, 0.05) 0px 0px 0px 1px, rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px',
  43. '& .MuiMenu-list': {
  44. padding: '8px 8px',
  45. },
  46. '& .MuiMenuItem-root': {
  47. marginBottom: theme.spacing(1),
  48. '& .MuiSvgIcon-root': {
  49. fontSize: 21,
  50. color: theme.palette.text.secondary,
  51. marginRight: theme.spacing(4),
  52. }
  53. },
  54. },
  55. }));
  56. const StyledBadge = styled(Badge)(({ theme }) => ({
  57. '& .MuiBadge-badge': {
  58. backgroundColor: '#44b700',
  59. color: '#44b700',
  60. boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
  61. '&::after': {
  62. position: 'absolute',
  63. top: 0,
  64. left: 0,
  65. width: '100%',
  66. height: '100%',
  67. borderRadius: '50%',
  68. animation: 'ripple 1.2s infinite ease-in-out',
  69. border: '1px solid currentColor',
  70. content: '""',
  71. },
  72. },
  73. '@keyframes ripple': {
  74. '0%': {
  75. transform: 'scale(.8)',
  76. opacity: 1,
  77. },
  78. '100%': {
  79. transform: 'scale(2.4)',
  80. opacity: 0,
  81. },
  82. },
  83. }));
  84. const useStyles = makeStyles({
  85. listItemInnerText: {
  86. display: 'flex',
  87. alignContent: 'center',
  88. alignItems: 'center',
  89. flexWrap: 'nowrap',
  90. },
  91. listItemInnerText__icon: {
  92. marginLeft: 5,
  93. },
  94. listItem_iconAvatar: {
  95. marginRight:10
  96. },
  97. listItem_iconRight: {
  98. marginRight: 10,
  99. display: 'flex',
  100. alignItems: 'center',
  101. justifyContent: 'center',
  102. alignContent: 'center',
  103. flexDirection: 'column'
  104. },
  105. listItem_iconTimeChecked: {
  106. display: 'flex',
  107. flexWrap: 'nowrap',
  108. alignItems: 'center',
  109. justifyContent: 'center',
  110. alignContent: 'center',
  111. marginBottom:2
  112. },
  113. listItem_iconRightBtn: {
  114. cursor: 'pointer',
  115. background: '#0ac40a',
  116. borderRadius: '50%',
  117. color: '#ffffff',
  118. border: 'none',
  119. height: 24,
  120. width: 24,
  121. textAlign: 'center',
  122. display: 'flex',
  123. alignItems: 'center',
  124. justifyContent: 'center',
  125. alignContent: 'center',
  126. fontSize: 12,
  127. marginLeft: 10,
  128. '&:hover': {
  129. outline: 'solid 3px #3ee415',
  130. }
  131. },
  132. listItem_iconRightBtnMute: {
  133. cursor: 'pointer',
  134. background: '#a7aaa7',
  135. borderRadius: '50%',
  136. color: '#ffffff',
  137. border: 'none',
  138. height: 24,
  139. width: 24,
  140. textAlign: 'center',
  141. display: 'flex',
  142. alignItems: 'center',
  143. justifyContent: 'center',
  144. alignContent: 'center',
  145. fontSize: 12,
  146. marginLeft: 10,
  147. '&:hover': {
  148. outline: 'solid 3px #cccbcb',
  149. }
  150. },
  151. pinnedIcon: {
  152. transform: 'rotate(45deg)',
  153. },
  154. listIconsRightContainer: {
  155. marginLeft: 'auto',
  156. display: 'flex',
  157. alignItems: 'center',
  158. justifyContent: 'center',
  159. alignContent: 'center',
  160. },
  161. listItem_icon_time: {
  162. fontSize: 12,
  163. marginLeft: 5,
  164. },
  165. listItem_typing: {
  166. animation: 'ripple 4s infinite ease-in-out',
  167. },
  168. listItem_dots: {
  169. fontWeight: 'bold',
  170. display:'inline-block',
  171. fontFamily: 'monospace',
  172. clipPath: 'inset(0 3ch 0 0)',
  173. animation: `$run 2s steps(5) infinite`,
  174. },
  175. '@keyframes run': {
  176. to: {
  177. clipPath: 'inset(0 -1ch 0 0)'
  178. },
  179. },
  180. iconClose: {
  181. '&:hover': {
  182. transform: 'rotate(180deg)',
  183. transition: 'all 250ms ease-out ',
  184. }
  185. },
  186. })
  187. interface IChatItem {
  188. chat: TChat,
  189. handleListItemClick: (companionId: string) => void,
  190. handleNewMsgs: (e: any, companionId: string) => void,
  191. id: string,
  192. pinned: boolean,
  193. selectedCompanionId: string,
  194. }
  195. const ChatItem = ({chat,handleListItemClick,handleNewMsgs,id,pinned,selectedCompanionId}:IChatItem) => {
  196. const classes = useStyles()
  197. const [anchorEl, setAnchorEl] = useState<any>(null);
  198. const [selected, setSelected] = useState<boolean>(false);
  199. const [modal, setModal] = useState<boolean>(false);
  200. const open = Boolean(anchorEl);
  201. const { name, lastName, avatarUrl, color, companionId, mute, seen, total, watched,
  202. typing, online, lastMessage, lastMessageCreatedAt, createdAt } = chat
  203. const openedChat = companionId === selectedCompanionId
  204. const handlePin = (id: string, pinned:boolean) => {
  205. pinChat(id,!pinned)
  206. handleClose(undefined)
  207. }
  208. const handleClose = (type: string | undefined): void => {
  209. if (type === 'mute') muteChat(companionId)
  210. if (type === 'delete') setModal(true)
  211. setAnchorEl(null)
  212. setSelected(false)
  213. }
  214. const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>):void => {
  215. e.preventDefault()
  216. setAnchorEl(e.currentTarget)
  217. setSelected(true)
  218. }
  219. return (
  220. <>
  221. {modal&&<DeleteModal setModal={setModal} chat={chat}/>}
  222. <ListItemButton
  223. sx={{backgroundColor: openedChat ? '#26afee' : selected ? 'rgba(0, 0, 0, 0.1)' : 'transparent',
  224. '&:hover':{backgroundColor:openedChat ? '#26afee':'rgba(0, 0, 0, 0.1)'}}}
  225. onClick={() => handleListItemClick(companionId)}
  226. onContextMenu={(e) => handleContextMenu(e)}>
  227. <ListItemIcon className={classes.listItem_iconAvatar}>
  228. <StyledBadge overlap="circular" variant={online === 'true'?'dot':'standard'}
  229. anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}>
  230. <Avatar alt={name} src={avatarUrl?`${prodAwsS3}/${avatarUrl}`:undefined}
  231. sx={{ background: color, width: 54, height: 54 }}>
  232. {!avatarUrl&&`${firstLetter(name)}${firstLetter(lastName)}`}
  233. </Avatar>
  234. </StyledBadge>
  235. </ListItemIcon>
  236. <ListItemText primaryTypographyProps={{color:openedChat?'#ffffff':'#000000'}} primary={<div className={classes.listItemInnerText}>
  237. <span>{`${firstLetter(name)}${slicedWord(name, 15, 1)}
  238. ${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`}</span>
  239. {mute && <VolumeOffIcon style={{color:openedChat?'#ffffff':'#959595'}}
  240. className={classes.listItemInnerText__icon} fontSize='small' />}</div>}
  241. secondary={typing ? <span style={{color:openedChat?'#ffffff':'#4d4d4d'}}
  242. className={classes.listItem_typing}>
  243. typing<span style={{color:openedChat?'#ffffff':'#1b1b1b'}}
  244. className={classes.listItem_dots}>...</span></span> :
  245. lastMessage ? slicedWord(lastMessage, 35) :
  246. `${firstLetter(name)}${slicedWord(name, 8, 1)} joined Telegram`}
  247. secondaryTypographyProps={{color:openedChat?'#ffffff':'#000000'}}/>
  248. <ListItemIcon className={classes.listItem_iconRight}>
  249. <div className={classes.listItem_iconTimeChecked}>
  250. {watched&& <DoneAllIcon style={{ color: openedChat ? '#ffffff' :'#18bd03' }} fontSize='small' />}
  251. <Typography className={classes.listItem_icon_time} style={{ color: openedChat ? '#ffffff' : '#1b1b1b' }}
  252. variant="h6" color="initial">
  253. {timeStampEU(lastMessageCreatedAt?lastMessageCreatedAt:createdAt)}
  254. </Typography>
  255. </div>
  256. <div className={classes.listIconsRightContainer}>
  257. {pinned && <PushPinIcon className={classes.pinnedIcon} fontSize='small'
  258. style={{color:openedChat?'#ffffff':'#959595'}}/>}
  259. {lastMessage && total > seen &&
  260. <button onClick={(e) => handleNewMsgs(e, companionId)}
  261. style={{outline:openedChat?'none':undefined,color:openedChat&&!mute?'#26afee':openedChat?'#ffffff':undefined,backgroundColor:openedChat&&!mute?'#ffffff':openedChat?'#a7aaa7':undefined}}
  262. className={mute ? classes.listItem_iconRightBtnMute : classes.listItem_iconRightBtn}>
  263. {total - seen}
  264. </button>}
  265. </div>
  266. </ListItemIcon>
  267. </ListItemButton>
  268. <StyledMenu
  269. id="demo-positioned-menu"
  270. aria-labelledby="demo-positioned-button"
  271. anchorEl={anchorEl}
  272. open={open}
  273. onClose={handleClose}
  274. >
  275. <MenuItem onClick={() => handleClose('mute')}>
  276. {mute ? <NotificationsNoneIcon /> : <VolumeOffIcon />}
  277. {mute ? 'Unmute chat':'Mute chat'}
  278. </MenuItem>
  279. <MenuItem onClick={() => handlePin(id,pinned)}>
  280. {pinned ?
  281. <CloseIcon className={classes.iconClose} /> :
  282. <PushPinIcon />}
  283. {pinned?'Unpin chat':'Pin chat'}
  284. </MenuItem>
  285. <MenuItem style={{color:'#f02a2a'}} onClick={() => handleClose('delete')}>
  286. <DeleteOutlineIcon style={{color:'#f02a2a'}}/>
  287. Delete chat
  288. </MenuItem>
  289. </StyledMenu>
  290. </>
  291. );
  292. }
  293. export default ChatItem