import { makeStyles, Typography } from '@material-ui/core' import { useState,useEffect,useCallback,useRef } from 'react'; import { useSelector } from 'react-redux'; import io from 'socket.io-client'; import Moveable from "react-moveable"; import { OnDrag } from "react-moveable"; import ListItemText from '@mui/material/ListItemText'; import ListItemAvatar from '@mui/material/ListItemAvatar'; import Avatar from '@mui/material/Avatar'; import PhoneIcon from '@mui/icons-material/Phone'; import MinimizeIcon from '@mui/icons-material/Minimize'; import CropLandscapeIcon from '@mui/icons-material/CropLandscape'; import CloseIcon from '@mui/icons-material/Close'; import VideocamIcon from '@mui/icons-material/Videocam'; import VideocamOffIcon from '@mui/icons-material/VideocamOff'; import MicIcon from '@mui/icons-material/Mic'; import MicOffIcon from '@mui/icons-material/MicOff'; import CallEndIcon from '@mui/icons-material/CallEnd'; import StopScreenShareIcon from '@mui/icons-material/StopScreenShare'; import Alert from '@mui/material/Alert'; import { getChat } from '../../../redux/chat/selector'; import { getAuthorizationState } from '../../../redux/authorization/selector'; import { prodAwsS3,prodBaseURL,prodSocketURL, firstLetter, slicedWord,getTimeBySeconds,playNotification } from '../../../helpers' import { socketIdChat } from '../../../api-data'; const Peer = require('simple-peer') const socket = io(prodSocketURL) const useStyles = makeStyles({ container: { position: 'absolute', left: 0, top: 0, width: '100vw', height:'100vh', overflow: 'hidden', zIndex:100, display: 'flex', justifyContent: 'center', alignItems: 'center', alignContent: "center", backgroundColor: 'rgba(104, 105, 104, 0.6)', }, modalCall: { background: 'rgb(36, 36, 36)', position:'relative', display: 'flex', flexDirection: 'column', justifyContent: 'start', alignItems: 'center', justifyItems:"center", borderRadius: 7, }, rightIcons: { display: 'flex', justifyContent: 'end', alignContent: 'center', alignItems: 'center', width: '100%', position: 'absolute', left: 0, top: 0, zIndex:1 }, rightIconWrapper: { color: '#ffffff', cursor: 'pointer', padding:'3px 10px 3px 10px', }, rightIconWrapperClose: { color: '#ffffff', cursor: 'pointer', padding: '3px 10px 3px 10px', backgroundColor:'rgb(36, 36, 36)', borderTopRightRadius:7, '&:hover': { backgroundColor:'#f02a2a' } }, bottomWrapper: { display: 'flex', justifyContent: 'center', padding: 5, position: "absolute", left: 0, bottom: 0, width:'100%' }, bottomItem: { display: 'flex', flexDirection: 'column', justifyContent: 'center', alignContent: 'center', alignItems: 'center', cursor:'pointer', width: 80, }, bottomIcon: { cursor:'pointer', }, bottomIconEndAccept: { cursor: 'pointer', '&:hover': { animation: `$shake 1s`, animationIterationCount:'infinite', } }, ringPulsate: { backgroundColor:"rgb(80, 80, 80)", borderRadius: '50%', height: 60, width: 60, position: 'absolute', left: 10, top: -8, animation: `$pulsate 1.5s ease-out`, animationIterationCount: 'infinite', opacity: 0, }, titleIconBottom: { color: '#ffffff', fontSize: 13, paddingTop:7 }, myVideo: { cursor: 'pointer', position: 'absolute', top: 0, left: 0, zIndex: 150, }, '@keyframes pulsate': { '0%': {transform: 'scale(1, 1)', opacity: 0}, '50%': { opacity: 1}, '100%': {transform: 'scale(1.2, 1.2)', opacity: 0}, }, '@keyframes shake': { '0%': { transform: 'rotate(0deg)'}, '11%': { transform: 'rotate(10deg)'}, '22%': { transform: 'rotate(20deg)'}, '33%': { transform: 'rotate(30deg)'}, '44%': { transform: 'rotate(20deg)'}, '55%': { transform: 'rotate(10deg)'}, '66%': { transform: 'rotate(0deg)'}, '77%': { transform: 'rotate(-10deg)'}, '88%': { transform: 'rotate(-20deg)'}, '100%': { transform: 'rotate(-30deg)'}, }, }) interface ICallBar { callStatus:any, setCallStatus: any, } const CallBar = ({callStatus,setCallStatus}:ICallBar) => { const classes = useStyles() const { _id } = useSelector(getAuthorizationState) const { socketId, companionId, name:_name,lastName:_lastName,avatarUrl:_avatarUrl,color:_color,number:_number } = useSelector(getChat) const connectionRef = useRef(null); const idAudioIntervalRef = useRef(null); const myVideoRef = useRef(null); const companionVideoRef = useRef(null); const companionAudioRef = useRef(null); const [mySocket, setMySocket] = useState('') const [mutedMyVideo,setMutedMyVideo] = useState(false) const [mutedMyAudio,setMutedMyAudio] = useState(false) const [companionSocket, setCompanionSocket] = useState('') const [myStream, setMyStream] = useState(null) const [myShareStream, setMyShareStream] = useState(null) const [companionSignal, setCompanionSignal] = useState(null) const [name, setName] = useState('') const [lastName, setLastName] = useState('') const [avatarUrl, setAvatarUrl] = useState('') const [color, setColor] = useState('') const [number, setNumber] = useState('') const [callLast, setCallLast] = useState(0) const [conversationLast, cetConversationLast] = useState('') const [fullScreen, setFullScreen] = useState(false) const [alert, setAlert] = useState('') const [audioHtml, setAudioHtml] = useState(null) const handleLeaveCall = useCallback(() => { setCallStatus('hanging up...') if (connectionRef.current) { connectionRef.current.destroy() connectionRef.current = null } if(idAudioIntervalRef.current) clearInterval(idAudioIntervalRef.current) myVideoRef.current.srcObject = null companionVideoRef.current.srcObject = null companionAudioRef.current.srcObject = null if (audioHtml) { audioHtml.pause() setAudioHtml(null) } if(myStream) myStream.getTracks().forEach((track:any) => track.stop()) setMyShareStream(null) setMutedMyVideo(false) setMutedMyAudio(false) setCompanionSocket('') setMyStream(null) setCompanionSignal(null) setCallLast(0) cetConversationLast('') setFullScreen(false) setAlert('') setTimeout(() => { setCallStatus('') setName('') setLastName('') setAvatarUrl('') setColor('') setNumber('') },500) }, [setCallStatus, audioHtml, myStream]) const handleConversationLast = (e: any) => cetConversationLast(getTimeBySeconds(e.target.currentTime)) const handleMuteShare = async () => { try { if (myStream) { const mediaDevices: any = navigator.mediaDevices; const stream = await mediaDevices.getDisplayMedia({ video: true, audio: true }) setMyShareStream(stream) const shareTrack = stream.getVideoTracks()[0] const videoTrack = myStream.getVideoTracks()[0] const senders = connectionRef.current._pc.getSenders() const replaceStream = (track:any) => senders.forEach((sender: any) => sender.track.kind === "video" && sender.replaceTrack(track)) shareTrack.onended = () => { setMyShareStream(null) replaceStream(videoTrack) } replaceStream(shareTrack) } else { setAlert(`Can not start Share before ringing`) setTimeout(() => setAlert(''),2000) } } catch (e: any) { setAlert(`Permission fro getDisplayMedia is required!`) setTimeout(() => setAlert(''),2000) } } const handleMuteVideo = () => { if (myStream&&myStream.getVideoTracks()[0]) { setMutedMyVideo(!mutedMyVideo) myStream.getVideoTracks()[0].enabled = !myStream.getVideoTracks()[0].enabled } else { setAlert(`Can not ${mutedMyVideo ? 'start' : 'stop'} Video before ringing`) setTimeout(() => setAlert(''),2000) } } const handleMuteAudio = () => { if (myStream&&myStream.getAudioTracks()[0]) { setMutedMyAudio(!mutedMyAudio) myStream.getAudioTracks()[0].enabled = !myStream.getAudioTracks()[0].enabled } else { setAlert(`Can not start ${mutedMyAudio ? 'start' : 'stop'} Audio before ringing`) setTimeout(() => setAlert(''),2000) } } const handleStartCall = useCallback(async () => { try { setCallStatus('waiting...') const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }) setMyStream(stream) myVideoRef.current.srcObject = stream; const peer = new Peer({ initiator: true, trickle: false, stream }); setCallStatus('ringing...') const audioRing = playNotification(`${prodBaseURL}/calling.mp3`) audioRing.loop = true setAudioHtml(audioRing) idAudioIntervalRef.current = setInterval(() => setCallLast(prevState => prevState + 1), 1000) peer.on("signal", (data: any) => { socket.emit("callTo", { to: socketId, signalData: data, from: mySocket, userId: _id, companionId, peer }) }); peer.on("stream", (companionStream: any) => { companionVideoRef.current.srcObject = companionStream; companionAudioRef.current.srcObject = companionStream; }); peer.on('connect', () => { audioRing.pause() setCallStatus('connection') clearInterval(idAudioIntervalRef.current) setTimeout(() => setCallStatus('conversation'),500) }) peer.on("close", handleLeaveCall); peer.on('error', handleLeaveCall) socket.on("acceptedCall", ({ signal }: any) => peer.signal(signal)); connectionRef.current = peer; } catch (e:any) { handleLeaveCall() } },[socketId,companionId,_id,mySocket,myVideoRef,setCallStatus,handleLeaveCall]) const handleAnswerCall = useCallback(async () => { try { audioHtml.pause() setCallStatus('waiting...') // const stream = await navigator.mediaDevices.getUserMedia({ // video: true, // audio: true // }) // setMyStream(stream) // myVideoRef.current.srcObject = stream; const peer = new Peer({ initiator: false, trickle: false, stream: false, }); peer.on("signal", (data: any) => { socket.emit("answerCall", { signal: data, to: companionSocket, }); }); peer.on("stream", (companionStream: any) => { companionVideoRef.current.srcObject = companionStream; companionAudioRef.current.srcObject = companionStream; }); peer.on('connect', () => { setCallStatus('connection') setTimeout(() => setCallStatus('conversation'),500) }) peer.on("close", handleLeaveCall); peer.on('error', handleLeaveCall) peer.signal(companionSignal); connectionRef.current = peer; } catch (e:any) { handleLeaveCall() } }, [companionSocket, companionSignal, setCallStatus,audioHtml,handleLeaveCall]) useEffect(() => { socket.on("me", (id: string) => { setMySocket(id) socketIdChat(id) }) },[]) useEffect(() => { socket.on('incomeCall', ({ name, lastName, avatarUrl, color, number, from, signal }: any) => { if (connectionRef.current === null) { setName(name) setLastName(lastName) setAvatarUrl(avatarUrl) setColor(color) setNumber(number) setCompanionSocket(from) setCompanionSignal(signal) const audioRing = playNotification(`${prodBaseURL}/ringing.mp3`) audioRing.loop = true setAudioHtml(audioRing) setCallStatus('is calling you') } }) }, [setCallStatus,companionSocket]) useEffect(() => { if (callStatus === 'requesting...') { setName(_name) setLastName(_lastName) setAvatarUrl(_avatarUrl) setColor(_color) setNumber(_number) setTimeout(handleStartCall,500) } }, [callStatus,_name,_lastName,_avatarUrl,_color,_number, handleStartCall]) useEffect(() => { if (callLast === 60) handleLeaveCall() }, [callLast, handleLeaveCall]) return (
) } export default CallBar