|
@@ -1,13 +1,13 @@
|
|
|
-import Stack from '@mui/material/Stack';
|
|
|
-import IconButton from '@mui/material/IconButton';
|
|
|
-import SearchIcon from '@mui/icons-material/Search';
|
|
|
-import PhoneIcon from '@mui/icons-material/Phone';
|
|
|
import { makeStyles, Typography } from '@material-ui/core'
|
|
|
import { useState,useEffect,useCallback,useRef } from 'react';
|
|
|
import { useSelector,useDispatch } 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';
|
|
@@ -21,10 +21,13 @@ import CallEndIcon from '@mui/icons-material/CallEnd';
|
|
|
|
|
|
import { getChat } from '../../../redux/chat/selector';
|
|
|
import { getAuthorizationState } from '../../../redux/authorization/selector';
|
|
|
-import { prodAwsS3, firstLetter, slicedWord } from '../../../helpers'
|
|
|
-import { socketIdChat } from '../../../api-data';
|
|
|
+import { prodAwsS3,prodSocketURL, firstLetter, slicedWord } from '../../../helpers'
|
|
|
+import { socketIdChat, mediaControllersChat } from '../../../api-data';
|
|
|
+import { asyncGetChatById } from '../../../redux/chat/operations'
|
|
|
+import { actionRightIsOpen,actionScrollChat,actionOpenPinned } from '../../../redux/control/action'
|
|
|
|
|
|
const Peer = require('simple-peer')
|
|
|
+const socket = io(prodSocketURL)
|
|
|
|
|
|
const useStyles = makeStyles({
|
|
|
container: {
|
|
@@ -42,10 +45,7 @@ const useStyles = makeStyles({
|
|
|
backgroundColor: 'rgba(104, 105, 104, 0.6)',
|
|
|
},
|
|
|
shareScreenActive: {
|
|
|
- width: '90%',
|
|
|
- borderRadius: 7,
|
|
|
- margin: 'auto',
|
|
|
- border:'solid 2px #0084ff',
|
|
|
+ width: '100%',
|
|
|
},
|
|
|
shareScreenDisabled: {
|
|
|
width: 0,
|
|
@@ -59,7 +59,7 @@ const useStyles = makeStyles({
|
|
|
alignItems: 'center',
|
|
|
justifyItems:"center",
|
|
|
width: '34vw',
|
|
|
- height:'50vh',
|
|
|
+ height:'90vh',
|
|
|
borderRadius: 7,
|
|
|
},
|
|
|
rightIcons: {
|
|
@@ -127,6 +127,15 @@ const useStyles = makeStyles({
|
|
|
fontSize: 13,
|
|
|
paddingTop:7
|
|
|
},
|
|
|
+ myVideo: {
|
|
|
+ width: 250,
|
|
|
+ height: 'auto',
|
|
|
+ cursor: 'pointer',
|
|
|
+ position: 'absolute',
|
|
|
+ top: 0,
|
|
|
+ left: 0,
|
|
|
+ zIndex: 150,
|
|
|
+ },
|
|
|
'@keyframes pulsate': {
|
|
|
'0%': {transform: 'scale(1, 1)', opacity: 0},
|
|
|
'50%': { opacity: 1},
|
|
@@ -147,37 +156,54 @@ const useStyles = makeStyles({
|
|
|
})
|
|
|
|
|
|
interface ICallBar {
|
|
|
- callStatus: string,
|
|
|
+ callStatus:any,
|
|
|
setCallStatus: any,
|
|
|
- socket: any,
|
|
|
- myVideoRef :any,
|
|
|
}
|
|
|
|
|
|
-const CallBar = ({callStatus,setCallStatus,socket,myVideoRef}:ICallBar) => {
|
|
|
+const CallBar = ({callStatus,setCallStatus}:ICallBar) => {
|
|
|
const classes = useStyles()
|
|
|
+ const dispatch = useDispatch()
|
|
|
const { _id } = useSelector(getAuthorizationState)
|
|
|
const chat = useSelector(getChat)
|
|
|
- const userVideoRef = useRef<any>(null);
|
|
|
+ const { mutedMyVideo, mutedMyAudio, socketId, companionId } = chat
|
|
|
+ const connectionRef = useRef<any>(null);
|
|
|
+ const myVideoRef = useRef<any>(null);
|
|
|
+ const myAudioRef = useRef<any>(null);
|
|
|
+ const companionVideoRef = useRef<any>(null);
|
|
|
+ const companionAudioRef = useRef<any>(null);
|
|
|
+ const [formChatId, setFormChatId] = useState<string>('')
|
|
|
const [mySocket, setMySocket] = useState<string>('')
|
|
|
+ const [companionSocket, setCompanionSocket] = useState<string>('')
|
|
|
+ const [companionSignal, setCompanionSignal] = useState<string>('')
|
|
|
const [name, setName] = useState<string>('')
|
|
|
const [lastName, setLastName] = useState<string>('')
|
|
|
const [avatarUrl, setAvatarUrl] = useState<string>('')
|
|
|
const [color, setColor] = useState<string>('')
|
|
|
- const [number,setNumber] = useState<string>('')
|
|
|
- const myAudioRef = useRef<any>(null);
|
|
|
- const userAudioRef = useRef<any>(null);
|
|
|
- const connectionRef = useRef<any>(null);
|
|
|
+ const [number, setNumber] = useState<string>('')
|
|
|
+ const handleMuteVideo = () => {
|
|
|
+ if (callStatus === 'conversation') {
|
|
|
+ mediaControllersChat(chat.companionId,!mutedMyVideo,mutedMyAudio)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const handleMuteAudio = () => {
|
|
|
+ mediaControllersChat(chat.companionId,mutedMyVideo,!mutedMyAudio)
|
|
|
+ }
|
|
|
const handleLeaveCall = () => {
|
|
|
+ setCallStatus('hanging up...')
|
|
|
connectionRef.current.destroy();
|
|
|
setCallStatus('')
|
|
|
};
|
|
|
- // requesting, waiting ,ringing, hanging up,line busy
|
|
|
-
|
|
|
const handleStartCall = useCallback(async () => {
|
|
|
- const stream = await navigator.mediaDevices.getUserMedia({
|
|
|
+ setCallStatus('waiting...')
|
|
|
+ const mediaDevices: any = navigator.mediaDevices
|
|
|
+ const stream = await mediaDevices.getDisplayMedia({
|
|
|
video: true,
|
|
|
audio: true
|
|
|
})
|
|
|
+ myVideoRef.current.srcObject = stream;
|
|
|
+ myAudioRef.current.srcObject = stream;
|
|
|
+ companionVideoRef.current.srcObject = stream;
|
|
|
+ companionAudioRef.current.srcObject = stream;
|
|
|
const peer = new Peer({
|
|
|
initiator: true,
|
|
|
trickle: false,
|
|
@@ -185,60 +211,80 @@ const CallBar = ({callStatus,setCallStatus,socket,myVideoRef}:ICallBar) => {
|
|
|
});
|
|
|
peer.on("signal", (data:any) => {
|
|
|
socket.emit("callTo", {
|
|
|
- to: chat.socketId,
|
|
|
+ to: socketId,
|
|
|
signalData: data,
|
|
|
from: mySocket,
|
|
|
userId: _id,
|
|
|
- companionId:chat.companionId
|
|
|
+ companionId
|
|
|
})
|
|
|
- setCallStatus('ringing')
|
|
|
+ setCallStatus('ringing...')
|
|
|
});
|
|
|
- peer.on("stream", (streams:any) => {});
|
|
|
- socket.on("acceptedCall", ({ signal }: any) => {
|
|
|
- peer.signal(signal)
|
|
|
- setCallStatus('accepted')
|
|
|
- console.log(signal,'signal accepted from companion')
|
|
|
+ peer.on("stream", (companionStream: any) => {
|
|
|
+ companionVideoRef.current.srcObject = companionStream;
|
|
|
+ companionAudioRef.current.srcObject = companionStream;
|
|
|
+ });
|
|
|
+ socket.on("acceptedCall", ({ signal }: any) => {
|
|
|
+ setCallStatus('connection')
|
|
|
+ peer.signal(signal)
|
|
|
+ setCallStatus('conversation')
|
|
|
});
|
|
|
connectionRef.current = peer;
|
|
|
- },[chat.socketId,chat.companionId,_id,socket,setCallStatus,mySocket])
|
|
|
+ },[socketId,companionId,_id,mySocket,myVideoRef,setCallStatus])
|
|
|
|
|
|
const handleAnswerCall = useCallback(async () => {
|
|
|
+ setCallStatus('connection')
|
|
|
+ dispatch(actionRightIsOpen(''))
|
|
|
+ dispatch(actionOpenPinned(false))
|
|
|
+ dispatch(asyncGetChatById(formChatId))
|
|
|
+ setTimeout(() => dispatch(actionScrollChat(true)), 500)
|
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
|
video: true,
|
|
|
audio: true
|
|
|
})
|
|
|
+ myVideoRef.current.srcObject = stream;
|
|
|
+ myAudioRef.current.srcObject = stream;
|
|
|
const peer = new Peer({
|
|
|
initiator: false,
|
|
|
trickle: false,
|
|
|
stream,
|
|
|
});
|
|
|
peer.on("signal", (data: any) => {
|
|
|
- socket.emit("answerCall", { signal: data, to: chat.socketId });
|
|
|
- console.log(data,'sent the signal on answer')
|
|
|
+ socket.emit("answerCall", {
|
|
|
+ signal: data,
|
|
|
+ to: companionSocket,
|
|
|
+ });
|
|
|
+ setCallStatus('conversation')
|
|
|
+ });
|
|
|
+ peer.on("stream", (companionStream: any) => {
|
|
|
+ companionVideoRef.current.srcObject = companionStream;
|
|
|
+ companionAudioRef.current.srcObject = companionStream;
|
|
|
});
|
|
|
- peer.on("stream", (streams:any) => {});
|
|
|
- peer.signal();
|
|
|
+ peer.signal(companionSignal);
|
|
|
connectionRef.current = peer;
|
|
|
- },[socket,chat.socketId])
|
|
|
+ },[companionSocket,companionSignal,setCallStatus,formChatId,dispatch])
|
|
|
|
|
|
useEffect(() => {
|
|
|
socket.on("me", (id: string) => {
|
|
|
setMySocket(id)
|
|
|
socketIdChat(id)
|
|
|
})
|
|
|
- socket.on('incomeCall', (data: any) => {
|
|
|
- setCallStatus('is calling you')
|
|
|
- setName(data.name)
|
|
|
- setLastName(data.lastName)
|
|
|
- setAvatarUrl(data.avatarUrl)
|
|
|
- setColor(data.color)
|
|
|
- setNumber(data.number)
|
|
|
- console.log(data,'incomeCall')
|
|
|
+ socket.on('incomeCall', ({ name, lastName, avatarUrl, color, number, from, signal,_companionId }: any) => {
|
|
|
+ if (connectionRef.current === null) {
|
|
|
+ setCallStatus('is calling you')
|
|
|
+ setName(name)
|
|
|
+ setLastName(lastName)
|
|
|
+ setAvatarUrl(avatarUrl)
|
|
|
+ setColor(color)
|
|
|
+ setNumber(number)
|
|
|
+ setCompanionSocket(from)
|
|
|
+ setCompanionSignal(signal)
|
|
|
+ setFormChatId(_companionId)
|
|
|
+ }
|
|
|
})
|
|
|
- },[socket,setCallStatus,setName,setLastName,handleAnswerCall])
|
|
|
+ }, [setCallStatus])
|
|
|
|
|
|
useEffect(() => {
|
|
|
- if(callStatus === 'requesting') handleStartCall()
|
|
|
+ if(callStatus === 'requesting...') handleStartCall()
|
|
|
}, [callStatus, handleStartCall])
|
|
|
|
|
|
useEffect(() => {
|
|
@@ -249,10 +295,22 @@ const CallBar = ({callStatus,setCallStatus,socket,myVideoRef}:ICallBar) => {
|
|
|
setColor(chat.color)
|
|
|
setNumber(chat.number)
|
|
|
}
|
|
|
- }, [callStatus,chat])
|
|
|
+ }, [callStatus, chat])
|
|
|
|
|
|
return (
|
|
|
- <div className={classes.container} style={{top: callStatus?0:'-100%'}}>
|
|
|
+ <div className={classes.container} style={{ top: callStatus ? 0 : '-100%' }}>
|
|
|
+ <video className={classes.myVideo} ref={myVideoRef} playsInline autoPlay/>
|
|
|
+ <Moveable
|
|
|
+ target={myVideoRef.current}
|
|
|
+ draggable={true}
|
|
|
+ throttleDrag={0}
|
|
|
+ hideDefaultLines={true}
|
|
|
+ renderDirections={[]}
|
|
|
+ rotationPosition="none"
|
|
|
+ origin={false}
|
|
|
+ onDrag={({ target, transform }: OnDrag) =>
|
|
|
+ target!.style.transform = transform }
|
|
|
+ />
|
|
|
<div className={classes.modalCall}>
|
|
|
<div className={classes.rightIcons} style={{marginBottom: true?0:40,}}>
|
|
|
<div className={classes.rightIconWrapper}>
|
|
@@ -276,9 +334,11 @@ const CallBar = ({callStatus,setCallStatus,socket,myVideoRef}:ICallBar) => {
|
|
|
${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`}
|
|
|
primaryTypographyProps={{ color: '#dfdfdf', fontSize: 20, fontWeight: 500 }}/>
|
|
|
<ListItemText primary={number} primaryTypographyProps={{ color: '#ffffff', fontSize: 15, fontWeight: 500,textAlign:"center" }}/>
|
|
|
- <ListItemText secondary={callStatus+'...'} secondaryTypographyProps={{ color: "#dfdfdf",textAlign: "center" }} />
|
|
|
+ <ListItemText secondary={callStatus} secondaryTypographyProps={{ color: "#dfdfdf",textAlign: "center" }} />
|
|
|
</div>}
|
|
|
- <video className={false?classes.shareScreenActive:classes.shareScreenDisabled} ref={userVideoRef} playsInline muted autoPlay/>
|
|
|
+ <audio ref={companionAudioRef} playsInline controls autoPlay/>
|
|
|
+ <video className={true ? classes.shareScreenActive : classes.shareScreenDisabled} ref={companionVideoRef} playsInline autoPlay />
|
|
|
+ <audio ref={myAudioRef} playsInline controls autoPlay/>
|
|
|
<div className={classes.bottomWrapper}>
|
|
|
{!true&&<div className={classes.bottomItem}>
|
|
|
<Avatar className={classes.bottomIcon}
|
|
@@ -287,12 +347,12 @@ const CallBar = ({callStatus,setCallStatus,socket,myVideoRef}:ICallBar) => {
|
|
|
</Avatar>
|
|
|
<Typography variant="h6" className={classes.titleIconBottom}>Screencast</Typography>
|
|
|
</div>}
|
|
|
- <div className={classes.bottomItem}>
|
|
|
+ <div className={classes.bottomItem} onClick={handleMuteVideo}>
|
|
|
<Avatar className={classes.bottomIcon}
|
|
|
- sx={{backgroundColor: true?'rgb(88, 88, 88)':'#ffffff',color: true?'#ffffff':'rgb(36, 36, 36)', width: 44, height: 44,zIndex:0}}>
|
|
|
- {true?<VideocamOffIcon fontSize="medium" />:<VideocamIcon fontSize="medium" />}
|
|
|
+ sx={{backgroundColor: mutedMyVideo?'#ffffff':'rgb(88, 88, 88)',color: mutedMyVideo?'rgb(36, 36, 36)':'#ffffff', width: 44, height: 44,zIndex:0}}>
|
|
|
+ {mutedMyVideo?<VideocamIcon fontSize="medium" />:<VideocamOffIcon fontSize="medium" />}
|
|
|
</Avatar>
|
|
|
- <Typography variant="h6" className={classes.titleIconBottom}>{true?'Stop Video':'Start Video'}</Typography>
|
|
|
+ <Typography variant="h6" className={classes.titleIconBottom}>{mutedMyVideo?'Start Video':'Stop Video'}</Typography>
|
|
|
</div>
|
|
|
<div className={classes.bottomItem}>
|
|
|
<Avatar className={classes.bottomIconEndAccept} onClick={handleLeaveCall}
|
|
@@ -315,11 +375,11 @@ const CallBar = ({callStatus,setCallStatus,socket,myVideoRef}:ICallBar) => {
|
|
|
</Typography>
|
|
|
</div>}
|
|
|
<div className={classes.bottomItem}>
|
|
|
- <Avatar className={classes.bottomIcon}
|
|
|
- sx={{backgroundColor: true?'rgb(88, 88, 88)':'#ffffff',color: true?'#ffffff':'rgb(36, 36, 36)', width: 44, height: 44,zIndex:0}}>
|
|
|
- {true?<MicIcon fontSize="medium" />:<MicOffIcon fontSize="medium" />}
|
|
|
+ <Avatar className={classes.bottomIcon} onClick={handleMuteAudio}
|
|
|
+ sx={{backgroundColor: mutedMyAudio?'#ffffff':'rgb(88, 88, 88)',color: mutedMyAudio?'rgb(36, 36, 36)':'#ffffff', width: 44, height: 44,zIndex:0}}>
|
|
|
+ {mutedMyAudio?<MicOffIcon fontSize="medium" />:<MicIcon fontSize="medium" />}
|
|
|
</Avatar>
|
|
|
- <Typography variant="h6" className={classes.titleIconBottom}>{true?'Mute':'Unmute'}</Typography>
|
|
|
+ <Typography variant="h6" className={classes.titleIconBottom}>{mutedMyAudio?'Unmute':'Mute'}</Typography>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|