index.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. import { makeStyles } from "@material-ui/core/styles";
  2. import { styled } from '@mui/material/styles';
  3. import { useState } from "react";
  4. import DownloadForOfflineIcon from '@mui/icons-material/DownloadForOffline';
  5. import { IconButton } from "@material-ui/core";
  6. import ImageIcon from '@mui/icons-material/Image';
  7. import FileDownloadIcon from '@mui/icons-material/FileDownload';
  8. import Button from '@mui/material/Button';
  9. import ContentCopyIcon from '@mui/icons-material/ContentCopy';
  10. import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
  11. import Menu from '@mui/material/Menu';
  12. import MenuItem from '@mui/material/MenuItem';
  13. import Divider from '@mui/material/Divider';
  14. import CheckBoxIcon from '@mui/icons-material/CheckBox';
  15. import Checkbox from '@mui/material/Checkbox';
  16. import PushPinIcon from '@mui/icons-material/PushPin';
  17. import ListItemText from '@mui/material/ListItemText';
  18. import ListItemIcon from '@mui/material/ListItemIcon';
  19. import Avatar from '@mui/material/Avatar';
  20. import ZoomOutIcon from '@mui/icons-material/ZoomOut';
  21. import ZoomInIcon from '@mui/icons-material/ZoomIn';
  22. import CloseIcon from '@mui/icons-material/Close';
  23. import { CopyToClipboard } from 'react-copy-to-clipboard';
  24. import { removeMessageById,updateMessageById,pinMessageById } from "../../../../../../api-data";
  25. import { timeStampMessage, timeStampEU,handleDownload,copied,emojisArr,firstLetter, slicedWord } from '../../../../../../helpers'
  26. const StyledMenu = styled((props:any) => (
  27. <Menu
  28. elevation={0}
  29. anchorOrigin={{
  30. vertical: 'top',
  31. horizontal: 'right',
  32. }}
  33. transformOrigin={{
  34. vertical: 'bottom',
  35. horizontal: 'right',
  36. }}
  37. {...props}
  38. />
  39. ))(({ theme }:any) => ({
  40. '& .MuiPaper-root': {
  41. borderRadius: 10,
  42. marginTop: theme.spacing(0),
  43. minWidth: 220,
  44. color:
  45. theme.palette.mode === 'light' ? 'rgb(55, 65, 81)' : theme.palette.grey[500],
  46. boxShadow:
  47. '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',
  48. '& .MuiMenu-list': {
  49. padding: '4px 4px',
  50. },
  51. '& .MuiMenuItem-root': {
  52. marginBottom: theme.spacing(1),
  53. '& .MuiSvgIcon-root': {
  54. fontSize: 21,
  55. color: theme.palette.text.secondary,
  56. marginRight: theme.spacing(2),
  57. }
  58. },
  59. },
  60. }));
  61. const useStyles = makeStyles({
  62. container: {
  63. display: "flex",
  64. justifyContent: "flex-end",
  65. borderRadius: 7,
  66. },
  67. wrapper: {
  68. width: 400,
  69. position: 'relative',
  70. display: 'flex',
  71. alignItems: 'center',
  72. alignContent: 'center',
  73. justifyContent: 'space-between',
  74. borderRadius: 7,
  75. padding: '12px 5px 18px 5px',
  76. backgroundColor: '#deffa9',
  77. "&:after": {
  78. content: "''",
  79. position: "absolute",
  80. width: "0",
  81. height: "0",
  82. borderBottom: "15px solid #deffa9",
  83. borderLeft: "15px solid transparent",
  84. borderRight: "15px solid transparent",
  85. bottom: "0",
  86. right: "-15px"
  87. },
  88. "&:before": {
  89. content: "''",
  90. position: "absolute",
  91. width: "0",
  92. height: "0",
  93. borderBottom: "17px solid #deffa9",
  94. borderLeft: "16px solid transparent",
  95. borderRight: "16px solid transparent",
  96. bottom: "0px",
  97. right: "-17px"
  98. }
  99. },
  100. wrapperActive: {
  101. width: 400,
  102. position: 'relative',
  103. display: 'flex',
  104. alignItems: 'center',
  105. alignContent: 'center',
  106. justifyContent: 'space-between',
  107. borderRadius: 7,
  108. padding: '12px 5px 18px 5px',
  109. backgroundColor: '#ced8d7',
  110. "&:after": {
  111. content: "''",
  112. position: "absolute",
  113. width: "0",
  114. height: "0",
  115. borderBottom: "15px solid #ced8d7",
  116. borderLeft: "15px solid transparent",
  117. borderRight: "15px solid transparent",
  118. bottom: "0",
  119. right: "-15px"
  120. },
  121. "&:before": {
  122. content: "''",
  123. position: "absolute",
  124. width: "0",
  125. height: "0",
  126. borderBottom: "17px solid #ced8d7",
  127. borderLeft: "16px solid transparent",
  128. borderRight: "16px solid transparent",
  129. bottom: "0px",
  130. right: "-17px"
  131. }
  132. },
  133. image: {
  134. borderRadius: 7,
  135. width: 300,
  136. maxHeight: 400,
  137. cursor:'pointer',
  138. },
  139. time: {
  140. position: "absolute",
  141. fontSize: ".65em",
  142. fontWeight:600,
  143. bottom: 0,
  144. right: 6,
  145. color: '#414141',
  146. padding: 3,
  147. borderRadius: 5,
  148. },
  149. captionWrapper: {
  150. position: "absolute",
  151. fontSize: ".85em",
  152. color: '#626262',
  153. maxWidth:'76%',
  154. fontWeight:600,
  155. bottom: -25,
  156. right: 40,
  157. borderRadius: 5,
  158. wordBreak:'break-word',
  159. textAlign: "left",
  160. font: "400 .9em 'Open Sans', sans-serif",
  161. backgroundColor: '#ffffff',
  162. padding:10,
  163. "&:after": {
  164. content: "''",
  165. position: "absolute",
  166. width: "0",
  167. height: "0",
  168. borderBottom: "15px solid #ffffff",
  169. borderLeft: "15px solid transparent",
  170. borderRight: "15px solid transparent",
  171. bottom: "0",
  172. right: "-15px"
  173. },
  174. "&:before": {
  175. content: "''",
  176. position: "absolute",
  177. width: "0",
  178. height: "0",
  179. borderBottom: "17px solid #ffffff",
  180. borderLeft: "16px solid transparent",
  181. borderRight: "16px solid transparent",
  182. bottom: "-1px",
  183. right: "-17px"
  184. }
  185. },
  186. bntDownload: {
  187. backgroundColor: 'inherit',
  188. color: '#54b0fc',
  189. width: 30,
  190. height:30,
  191. '&:hover': {
  192. backgroundColor: '#54b0fc',
  193. color:'#ffffff'
  194. }
  195. },
  196. overlay: {
  197. position: 'fixed',
  198. top: 0,
  199. left: 0,
  200. width: '100vw',
  201. minHeight: '100vh',
  202. zIndex: 100,
  203. backgroundColor: 'rgba(104, 105, 104, 0.6)',
  204. overflowY: 'hidden',
  205. boxSizing: 'border-box',
  206. display: 'flex',
  207. justifyContent: 'center',
  208. alignContent: 'center',
  209. alignItems: 'center'
  210. },
  211. topBar: {
  212. position: 'fixed',
  213. top: 0,
  214. left: 0,
  215. height: '7vh',
  216. width: '100vw',
  217. display: 'flex',
  218. alignContent: 'center',
  219. alignItems: 'center',
  220. justifyContent: 'space-between',
  221. backgroundColor: 'rgba(65, 65, 65, 0.9)',
  222. zIndex: 10,
  223. padding: '0px 20px'
  224. },
  225. wrapperCredentials: {
  226. display: 'flex',
  227. alignContent: 'center',
  228. alignItems: 'center',
  229. },
  230. wrapperIcons: {
  231. display: 'flex',
  232. alignContent: 'center',
  233. alignItems: 'center',
  234. },
  235. magnifying : {
  236. marginLeft:5,
  237. cursor: 'pointer',
  238. color: '#e9e7e7',
  239. padding: 0,
  240. '&:hover': {
  241. color: '#ffffff',
  242. transform: 'scale(1.1)'
  243. }
  244. },
  245. downloadIcon: {
  246. marginLeft:5,
  247. cursor: 'pointer',
  248. color: '#e9e7e7',
  249. padding: 0,
  250. borderRadius: '50%',
  251. '&:hover': {
  252. backgroundColor: '#ffffff',
  253. color: '#b8b7b7',
  254. }
  255. },
  256. iconCloseOverlay: {
  257. marginLeft:5,
  258. cursor: 'pointer',
  259. color: '#e9e7e7',
  260. padding: 0,
  261. '&:hover': {
  262. color: '#ffffff',
  263. transform: 'rotate(180deg)',
  264. transition: 'all 250ms ease-out ',
  265. }
  266. },
  267. wrapperImage: {
  268. display: 'flex',
  269. overflow: 'auto',
  270. maxWidth: '70%',
  271. maxHeight:'80%',
  272. height: 'auto',
  273. borderRadius:5,
  274. },
  275. modalDelete: {
  276. background: '#ffffff',
  277. position: 'absolute',
  278. content:'',
  279. width: '20%',
  280. height:'auto',
  281. left: '40%',
  282. bottom: '48.5%',
  283. borderRadius: 10,
  284. padding: 10,
  285. display: 'flex',
  286. flexDirection:'column'
  287. },
  288. overlayDelete: {
  289. position: 'fixed',
  290. top: 0,
  291. left: 0,
  292. width: '100vw',
  293. height: '100vh',
  294. zIndex: 100,
  295. backgroundColor: 'rgba(104, 105, 104, 0.6)',
  296. overflowY: 'hidden',
  297. },
  298. emojiTitle: {
  299. position: "absolute",
  300. fontSize: "1.7em",
  301. fontWeight:600,
  302. bottom: 0,
  303. left: -40,
  304. },
  305. emojiCompanionTitle: {
  306. position: "absolute",
  307. fontSize: "1.7em",
  308. fontWeight:600,
  309. bottom: '2rem',
  310. left: -40,
  311. },
  312. emoji: {
  313. cursor: 'pointer',
  314. fontSize: '1.7rem',
  315. transition: 'all 0.3s',
  316. '&:hover': {
  317. transform: 'scale(1.5)'
  318. }
  319. },
  320. emojiActive: {
  321. cursor: 'pointer',
  322. fontSize: '1.2rem',
  323. animation: `$emoji 0.6s ease-out`,
  324. animationDirection: 'forwards',
  325. animationIterationCount: 1,
  326. },
  327. '@keyframes emoji': {
  328. '5%': { transform: 'translateY(1rem)'},
  329. '10%': { transform: 'translateY(0) scale(1)',opacity: 1},
  330. '50%': { transform: 'translateY(-4rem) scale(1.5) rotateY(90deg)'},
  331. '80%': {opacity: 0},
  332. '100%': {transform: 'translateY(-8rem) scale(2) rotateY(180deg)',opacity: 0},
  333. },
  334. iconClose: {
  335. '&:hover': {
  336. transform: 'rotate(180deg)',
  337. transition: 'all 250ms ease-out ',
  338. }
  339. },
  340. });
  341. const label = { inputProps: { 'aria-label': 'Checkbox demo' } };
  342. interface IMessageRightImage {
  343. url:string,
  344. createdAt:string,
  345. color: string,
  346. fullType: string,
  347. caption: string,
  348. emoji: string,
  349. emojiCompanion: string,
  350. pinned: boolean,
  351. isSomeSelected: boolean,
  352. isSelected:(_id:string) => boolean,
  353. handleSelected: (_id:string) => void,
  354. _id: string,
  355. name: string,
  356. lastName: string,
  357. }
  358. const MessageRightImage = ({url,createdAt,color,fullType,caption,emoji,emojiCompanion,pinned,isSomeSelected,isSelected,handleSelected,_id,name,lastName}:IMessageRightImage) => {
  359. const classes = useStyles();
  360. const [watch, setWatch] = useState<boolean>(false)
  361. const [anchorEl, setAnchorEl] = useState<any>(null);
  362. const [selected, setSelected] = useState<boolean>(false);
  363. const [modal, setModal] = useState<boolean>(false)
  364. const [scale, setScale] = useState<number>(1)
  365. const open = Boolean(anchorEl);
  366. const checked = isSelected(_id)
  367. const handleIncreaseScale = () => {
  368. if(scale < 5) return setScale(scale+0.5)
  369. }
  370. const handleDecreaseScale = () => {
  371. if(scale > 1) return setScale(scale-0.5)
  372. }
  373. const handleOpenWatch = () => !watch&&setWatch(true)
  374. const handleCloseWatch = (e: any) => {
  375. const id = e.target.id
  376. if(id === 'overlay') setWatch(false)
  377. if(id === 'close') setWatch(false)
  378. }
  379. const handleClose = (type: string | undefined): void => {
  380. if (type === 'copy') copied('Link')
  381. if (type === 'delete') setModal(true)
  382. setAnchorEl(null)
  383. setSelected(false)
  384. }
  385. const handleDeleteModal = (e: any) => {
  386. const id = e.target.id
  387. if (id === 'overlay' || id === 'cancel') return setModal(false)
  388. if (id === 'delete') {
  389. removeMessageById(_id)
  390. setModal(false)
  391. }
  392. }
  393. const handleContextMenu = (e: React.MouseEvent<HTMLDivElement>):void => {
  394. e.preventDefault()
  395. setAnchorEl(e.currentTarget)
  396. setSelected(true)
  397. }
  398. const handleEmojiMenu = ({ target }: any): void => {
  399. const idEmoji = target.id
  400. if (idEmoji === emoji) {updateMessageById(_id,'')
  401. } else updateMessageById(_id,idEmoji)
  402. }
  403. return (watch ?
  404. <div onClick={handleCloseWatch} id='overlay' className={classes.overlay}>
  405. <div className={classes.topBar}>
  406. <div className={classes.wrapperCredentials}>
  407. <ListItemIcon >
  408. <Avatar alt={name} src={url?url:undefined}
  409. sx={{ background: color, width: 44, height: 44 }}>
  410. {!url&&`${firstLetter(name)}${firstLetter(lastName)}`}
  411. </Avatar>
  412. </ListItemIcon>
  413. <ListItemText primary={`${firstLetter(name)}${slicedWord(name, 15, 1)}
  414. ${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`}
  415. primaryTypographyProps={{ color: '#ffffff' }}
  416. secondary={timeStampEU(createdAt)} secondaryTypographyProps={{ color: '#ffffff' }} />
  417. </div>
  418. <div className={classes.wrapperIcons}>
  419. <ZoomOutIcon onClick={handleDecreaseScale}
  420. className={classes.magnifying} fontSize='large' />
  421. <ZoomInIcon onClick={handleIncreaseScale}
  422. className={classes.magnifying} fontSize='large' />
  423. <DownloadForOfflineIcon onClick={() => handleDownload(url, fullType)}
  424. className={classes.downloadIcon} fontSize='large' />
  425. <CloseIcon id='close' onClick={handleCloseWatch}
  426. className={classes.iconCloseOverlay} fontSize='large' />
  427. </div>
  428. </div>
  429. <div className={classes.wrapperImage}>
  430. <img style={{ transform: `scale(${scale})`, cursor: scale > 1 ? 'grab' : 'auto' }}
  431. width='100%' height='auto' alt='imageItem' src={url} />
  432. </div>
  433. </div> :
  434. <div className={classes.container} style={{ marginBottom: caption ? 40 : 15}}>
  435. <div onContextMenu={(e) => handleContextMenu(e)}
  436. className={selected ? classes.wrapperActive : classes.wrapper}
  437. style={{pointerEvents:isSomeSelected?'none':'auto'}}>
  438. <ImageIcon fontSize='large' style={{ color: '#bd9a00' }} />
  439. <img onClick={handleOpenWatch} className={classes.image} alt='message pic' src={url}
  440. style={{ backgroundColor: url ? '' : color }} width='300' height='400' />
  441. <IconButton onClick={() => handleDownload(url, fullType)} className={classes.bntDownload}>
  442. <FileDownloadIcon fontSize='medium'/>
  443. </IconButton>
  444. <div className={classes.time}>{timeStampMessage(createdAt)}</div>
  445. {emojiCompanion && <div className={classes.emojiCompanionTitle}>{emojisArr[Number(emojiCompanion)]}</div>}
  446. {emoji && <div className={classes.emojiTitle}>{emojisArr[Number(emoji)]}</div>}
  447. {caption&&<div className={classes.captionWrapper}>{caption}</div>}
  448. <StyledMenu id="demo-positioned-menu" aria-labelledby="demo-positioned-button"
  449. anchorEl={anchorEl} open={open} onClose={handleClose}>
  450. <MenuItem onClick={handleEmojiMenu} style={{ cursor: 'none' }} >
  451. {emojisArr.map((el:string, i:number) =>
  452. <div key={el} className={emoji === String(i)?classes.emojiActive:classes.emoji} id={String(i)}>{el}</div>)}
  453. </MenuItem>
  454. <Divider/>
  455. <CopyToClipboard onCopy={() => handleClose('copy')} text={url}>
  456. <MenuItem>
  457. <ContentCopyIcon />
  458. Copy Image link
  459. </MenuItem>
  460. </CopyToClipboard>
  461. <MenuItem onClick={() => {
  462. pinMessageById(_id, !pinned)
  463. handleClose(undefined)
  464. }}>
  465. {pinned ?
  466. <CloseIcon className={classes.iconClose} /> :
  467. <PushPinIcon />}
  468. {pinned?'Unpin message':'Pin message'}
  469. </MenuItem>
  470. <MenuItem onClick={() => {
  471. handleSelected(_id)
  472. handleClose(undefined)
  473. }}>
  474. <CheckBoxIcon />
  475. Select message
  476. </MenuItem>
  477. <MenuItem style={{color:'#f02a2a'}} onClick={() => handleClose('delete')}>
  478. <DeleteOutlineIcon style={{color:'#f02a2a'}}/>
  479. Delete message
  480. </MenuItem>
  481. </StyledMenu>
  482. {modal &&
  483. <div onClick={handleDeleteModal} className={classes.overlayDelete} id='overlay'>
  484. <div className={classes.modalDelete}>
  485. <h3 style={{color: '#2c2c2c'}}>Delete message</h3>
  486. <p style={{ color: '#050505' }}>Are you sure you want to delete message?</p>
  487. <Button id='delete' variant="text" color="error" style={{fontWeight:500,fontSize:22}}>
  488. DELETE MESSAGE
  489. </Button>
  490. <Button id='cancel' variant="text" style={{fontWeight:500,fontSize:22}}>
  491. CANCEL
  492. </Button>
  493. </div>
  494. </div>}
  495. </div>
  496. {isSomeSelected&&<Checkbox {...label} checked={checked} onClick={() => handleSelected(_id)}/>}
  497. </div>
  498. )};
  499. export default MessageRightImage