123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- import { makeStyles } from "@material-ui/core/styles";
- import SendIcon from '@mui/icons-material/Send';
- import MicNoneIcon from '@mui/icons-material/MicNone';
- import VideocamIcon from '@mui/icons-material/Videocam';
- import PauseIcon from '@mui/icons-material/Pause';
- import AttachFileIcon from '@mui/icons-material/AttachFile';
- import SentimentSatisfiedAltIcon from '@mui/icons-material/SentimentSatisfiedAlt';
- import CloseIcon from '@mui/icons-material/Close';
- import Avatar from '@mui/material/Avatar';
- import Picker from 'emoji-picker-react';
- import LinearProgress from '@mui/material/LinearProgress';
- import { useReactMediaRecorder } from "react-media-recorder";
- import { useState } from "react";
- import { useSelector } from "react-redux";
- import FilesMenu from "../FilesMenu";
- import {
- sentMessageById, sentImgMessageById, sentAudioMessageById,
- sentVideoMessageById,sentFileMessageById
- } from '../../../../../api-data'
- import { getChat } from '../../../../../redux/chat/selector'
- import { getIsOpen } from '../../../../../redux/control/selector'
- import { playNotification,prodBaseURL } from "../../../../../helpers";
- import { typingChat } from "../../../../../api-data";
- const useStyles = makeStyles({
- container: {
- width: '35vw',
- height:'6vh',
- position: 'fixed',
- bottom: '2vh',
- borderRadius: 8,
- padding: 10,
- display: 'flex',
- flexWrap: 'nowrap',
- alignContent: 'start',
- alignItems: 'start',
- color: '#6b6b6b',
- backgroundColor: '#ffffff',
- },
- textarea: {
- width: '100%',
- height: '100%',
- outline: 'none',
- border:'none',
- padding: '0px 10px',
- marginLeft: 8,
- marginRight: 8,
- overflowY:'auto',
- resize: 'none',
- '&::placeholder': {
- color: 'rgb(82, 82, 82)',
- fontWeight: 600
- }
- },
- attachIcon: {
- transform:'rotate(30deg)',
- },
- borderTop: {
- position: 'absolute',
- left: 0,
- top: '-2vh',
- width: '100%',
- height: 1,
- background:'#ffffff',
- },
- visualizerTop: {
- position: 'absolute',
- left: 0,
- top: '-2vh',
- width: '100%',
- color:'rgb(41, 139, 231)'
- },
- filesMenu: {
- background: '#fdfdfd',
- position: 'absolute',
- width: '15vw',
- maxWidth: '100%',
- left: '61%',
- bottom:'10vh',
- zIndex: 10,
- visibility: 'visible',
- borderRadius: 10,
- padding: '4px 6px',
- },
- emoji: {
- position: 'absolute',
- bottom:'10vh',
- zIndex: 10,
- visibility: 'visible',
- },
- iconCancel: {
- position: 'absolute',
- left: -72,
- bottom:-1,
- display:'flex',
- backgroundColor: '#ffffff',
- color: 'rgb(243, 69, 69)',
- border:'solid 4px rgb(243, 69, 69)',
- borderRadius: '50%',
- '&:hover': {
- backgroundColor: 'rgb(243, 69, 69)',
- color: '#ffffff',
- }
- },
- avatarCamera: {
- position: 'absolute',
- left: -72,
- bottom:-1,
- display: 'flex',
- borderRadius: '50%',
- zIndex: 10,
- border: 'solid 14px #ffffff',
- '&:hover': {
- backgroundColor: 'rgb(41, 139, 231)',
- border:'solid 14px rgb(41, 139, 231)',
- color: '#ffffff',
- }
- },
- avatarRight: {
- position: 'absolute',
- right: -72,
- bottom:-1,
- display: 'flex',
- borderRadius: '50%',
- zIndex: 10,
- border: 'solid 14px #ffffff',
- '&:hover': {
- backgroundColor: 'rgb(41, 139, 231)',
- border:'solid 14px rgb(41, 139, 231)',
- color: '#ffffff'
- }
- },
- pauseLeft: {
- position: 'absolute',
- left: -72,
- bottom:-1,
- zIndex: 10,
- },
- pauseRight: {
- position: 'absolute',
- right: -72,
- bottom:-1,
- zIndex: 10,
- },
- avatarPause: {
- backgroundColor: '#ffffff',
- cursor: 'pointer',
- animation: `$shake 1s`,
- animationIterationCount:'infinite',
- '&:hover': {
- backgroundColor: 'rgb(41, 139, 231)',
- color: '#ffffff',
- }
- },
- overlay: {
- position: 'fixed',
- top: 0,
- left: 0,
- width: '100vw',
- height: '100vh',
- zIndex:100
- },
- ringContainerLeft: {
- position: 'absolute',
- left: -25,
- top: -25,
- zIndex: 10,
- },
- ringContainerRight: {
- position: 'absolute',
- right: -25,
- top: -25,
- zIndex: 10,
- },
- circle: {
- width: 15,
- height: 15,
- backgroundColor: 'rgb(255, 4, 4)',
- borderRadius: '50%',
- position: 'relative'
- },
- ringRing: {
- border: '3px solid rgb(255, 4, 4)',
- borderRadius: '50%',
- height: 25,
- width: 25,
- position: 'absolute',
- right: -5,
- top: -5,
- animation: `$pulsate 1s ease-out`,
- animationIterationCount: 'infinite',
- opacity: 0
- },
- '@keyframes pulsate': {
- '0%': {transform: 'scale(0.1, 0.1)', opacity: 0},
- '50%': { opacity: 1},
- '100%': {transform: 'scale(1.2, 1.2)', opacity: 0},
- },
- '@keyframes shake': {
- '0%': { transform: 'translate(0.5px, 0.5px) rotate(0deg)'},
- '10%': { transform: 'translate(-0.5px, -1px) rotate(-1deg)'},
- '20%': { transform: 'translate(-1.5px, 0px) rotate(1deg)'},
- '30%': { transform: 'translate(1.5px, 1px) rotate(0deg)'},
- '40%': { transform: 'translate(0.5px, -0.5px) rotate(1deg)'},
- '50%': { transform: 'translate(-0.5px, 1px) rotate(-1deg)'},
- '60%': { transform: 'translate(-1.5px, 0.5px) rotate(0deg)'},
- '70%': { transform: 'translate(1.5px, 0.5px) rotate(-1deg)'},
- '80%': { transform: 'translate(-0.5px, -0.5px) rotate(1deg)'},
- '90%': { transform: 'translate(0.5px, 1px) rotate(0deg)'},
- '100%': { transform: 'translate(0.5px, -1px) rotate(-1deg)'},
- },
- });
- interface ISendMessage{
- isArrow: boolean,
- }
- const SendMessage = ({isArrow }:ISendMessage) => {
- const classes = useStyles();
- const { companionId } = useSelector(getChat)
- const isOpen = useSelector(getIsOpen)
- const [value, setValue] = useState<string>('')
- const [file, setFile] = useState<any>(false)
- const [isOpenMenu, setIsOpenMenu] = useState<boolean>(false)
- const [isOpenEmoji, setIsOpenEmoji] = useState<boolean>(false)
- const [isRecording, setIsRecording] = useState<boolean>(false)
- const [isFilming, setIsFilming] = useState<boolean>(false)
- const [type, setType] = useState<string>('')
- const { status, startRecording, stopRecording, mediaBlobUrl, clearBlobUrl } = useReactMediaRecorder({ audio: true });
- const { status: _status, startRecording: _startRecording, stopRecording: _stopRecording,
- mediaBlobUrl: _mediaBlobUrl, clearBlobUrl: _clearBlobUrl } = useReactMediaRecorder({ video: true });
- const onEmojiClick = (_e:any, emojiObject:any) => {
- setValue(prevValue => prevValue + emojiObject.emoji)
- setIsOpenEmoji(false)
- };
- const clearMessage = () => {
- file &&setFile(false)
- isRecording && setIsRecording(false)
- isFilming && setIsFilming(false)
- value && setValue('')
- type && setType('')
- mediaBlobUrl && clearBlobUrl()
- _mediaBlobUrl && _clearBlobUrl()
- isOpenMenu && setIsOpenMenu(false)
- isOpenEmoji && setIsOpenEmoji(false)
-
- }
- const sentMessage = async () => {
- if (value) sentMessageById(companionId, value)
- if (mediaBlobUrl && type === 'recording') {
- const audio = new XMLHttpRequest();
- audio.open('GET', mediaBlobUrl, true);
- audio.responseType = 'blob';
- audio.onload = () => {
- if (audio.status === 200) {
- const blob = audio.response
- const file = new File([blob], 'audio.mp3', {
- type: 'audio/mpeg'
- })
- const formData: any = new FormData()
- formData.append("audio", file)
- sentAudioMessageById(companionId, formData)
- clearBlobUrl()
- }
- }
- audio.send();
- }
- if (_mediaBlobUrl && type === 'filming') {
- const video = new XMLHttpRequest();
- video.open('GET', _mediaBlobUrl, true);
- video.responseType = 'blob';
- video.onload = () => {
- if (video.status === 200) {
- const blob = video.response
- const file = new File([blob], 'video.mp4', {
- type: 'video/mp4'
- })
- const formData: any = new FormData()
- formData.append("video", file)
- sentVideoMessageById(companionId, formData)
- _clearBlobUrl()
- }
- }
- video.send();
- }
- if (file && type) {
- if (file.type.includes('image') && type === 'content') {
- const formData: any = new FormData()
- formData.append("image", file);
- await sentImgMessageById(companionId, formData)
- }
- if (file.type.includes('audio') && type === 'content') {
- const formData: any = new FormData()
- formData.append("audio", file);
- sentAudioMessageById(companionId, formData)
- }
- if (file.type.includes('video') && type === 'content') {
- const formData: any = new FormData()
- formData.append("video", file);
- sentVideoMessageById(companionId, formData)
- }
- if (file.type.includes('application') && type === 'application') {
- const formData: any = new FormData()
- formData.append("file", file);
- sentFileMessageById(companionId, formData)
- }
- }
- clearMessage()
- playNotification(`${prodBaseURL}/send.mp3`)
- }
- const handleTextarea = (e: React.ChangeEvent<HTMLTextAreaElement>) => setValue(e.target.value)
- const handleFocusTextarea = async () => await typingChat(companionId,true)
- const handleBlurTextarea = async () => await typingChat(companionId,false)
- const handleOpenFileMenu = () => !isOpenMenu&&setIsOpenMenu(true)
- const handleCloseFileMenu = (e:any) => e.target.id === 'overlay'&&isOpenMenu&&setIsOpenMenu(false)
- const handleOpenEmoji = () => !isOpenEmoji&&setIsOpenEmoji(true)
- const handleCloseEmoji = (e: any) => e.target.id === 'overlay'&&isOpenEmoji&&setIsOpenEmoji(false)
- const handleRecording = () => {
- if (isRecording) return stopRecording()
- startRecording()
- setType('recording')
- setIsRecording(true)
- }
- const handleFilming = () => {
- if (isFilming) return _stopRecording()
- _startRecording()
- setType('filming')
- setIsFilming(true)
- }
- return (
- <div className={classes.container}>
- {isArrow && <div className={classes.borderTop}></div>}
- {isFilming && _status !== 'stopped' &&
- <>
- <div className={classes.pauseLeft}>
- <Avatar onClick={handleFilming } className={classes.avatarPause}
- sx={{backgroundColor: '#ffffff',color:'#6b6b6b',width: 56, height: 56}}>
- <PauseIcon fontSize="large"/>
- </Avatar>
- </div>
- <div className={classes.ringContainerLeft}>
- <div className={classes.ringRing}></div>
- <div className={classes.circle}></div>
- </div>
- </>}
- {isRecording && status !== 'stopped' &&
- <>
- <div className={classes.pauseRight}>
- <Avatar onClick={handleRecording} className={classes.avatarPause}
- sx={{backgroundColor: '#ffffff',color:'#6b6b6b',width: 56, height: 56}}>
- <PauseIcon fontSize="large"/>
- </Avatar>
- </div>
- <div className={classes.ringContainerRight}>
- <div className={classes.ringRing}></div>
- <div className={classes.circle}></div>
- </div>
- </>}
- <div className={classes.visualizerTop}
- style={{display: value || file || status === 'stopped' || _status === 'stopped' ? 'block':'none' }}>
- <LinearProgress color="inherit" variant="determinate" value={100}/>
- </div>
- <CloseIcon onClick={clearMessage} fontSize="small" className={classes.iconCancel}
- sx={{width: 56, height: 56, display: file || value || status === 'stopped'
- || _status === 'stopped' ? 'inline-block' : 'none'}} />
- <VideocamIcon onClick={handleFilming} className={classes.avatarCamera}
- sx={{backgroundColor: '#ffffff', color: '#6b6b6b', width: 56, height: 56}}
- style={{display: status !== 'idle' || _status === 'stopped' || file || value || isFilming ? 'none' : 'block'}} />
- <SendIcon onClick={sentMessage} className={classes.avatarRight}
- sx={{backgroundColor: '#ffffff',color: 'rgb(41, 139, 231)', width: 56, height: 56}}
- style={{display: value || file || status === 'stopped' || _status === 'stopped' ? 'block':'none' }}/>
- <MicNoneIcon onClick={handleRecording} className={classes.avatarRight}
- sx={{backgroundColor:'#ffffff',color: '#6b6b6b', width: 56, height: 56}}
- style={{display: !value && !file && status !== 'stopped' && _status === 'idle'&&!isRecording ? 'block' : 'none'}}/>
- <SentimentSatisfiedAltIcon onClick={handleOpenEmoji}
- fontSize='medium' sx={{color: isOpenEmoji ? 'rgb(41, 139, 231)' : '#6b6b6b', cursor: 'pointer',
- pointerEvents: file || status !== 'idle' || _status !== 'idle' ? 'none' : "auto",
- '&:hover': { color: 'rgb(41, 139, 231)'}}} />
- <div onClick={handleCloseEmoji} className={classes.overlay} id='overlay'
- style={{ display: isOpenEmoji ? 'block':'none'}}>
- <div className={classes.emoji} style={{left: isOpen&&isOpen !== 'menu'?'32.5vw':'45vw'}}>
- <Picker onEmojiClick={onEmojiClick} />
- </div>
- </div>
- <textarea disabled={file || status !== 'idle' || _status !== 'idle' ? true : false} value={value} onBlur={handleBlurTextarea}
- onFocus={handleFocusTextarea} onChange={handleTextarea} className={classes.textarea}
- placeholder={file ? 'The File is ready to send' : status === 'idle' && _status === 'idle' ? 'Message ' :
- `${status === 'stopped' || _status === 'stopped' ?'Recorded':'Recording in progress'}`} rows={1}>
- </textarea>
- <AttachFileIcon onClick={handleOpenFileMenu} className={classes.attachIcon}
- fontSize='medium' sx={{color: isOpenMenu ? 'rgb(41, 139, 231)' : '#6b6b6b', cursor: 'pointer',
- pointerEvents: value || status !== 'idle' || _status !== 'idle' ? 'none' : "auto",'&:hover': { color: 'rgb(41, 139, 231)'}}} />
- <div onClick={handleCloseFileMenu} className={classes.overlay} id='overlay'
- style={{ display: isOpenMenu ? 'block':'none'}}>
- <div className={classes.filesMenu} style={{left: isOpen&&isOpen !== 'menu'?'52.5vw':'65vw'}}>
- <FilesMenu setFile={setFile} setValue={setValue} setIsOpenMenu={setIsOpenMenu} setType={setType}/>
- </div>
- </div>
- </div>
- )
- }
- export default SendMessage
|