|
@@ -1,6 +1,10 @@
|
|
|
+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 } from 'react';
|
|
|
-import { useSelector } from 'react-redux';
|
|
|
+import { useState,useEffect,useCallback,useRef } from 'react';
|
|
|
+import { useSelector,useDispatch } from 'react-redux';
|
|
|
import ListItemText from '@mui/material/ListItemText';
|
|
|
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
|
|
import Avatar from '@mui/material/Avatar';
|
|
@@ -14,21 +18,23 @@ 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 Moveable from "react-moveable";
|
|
|
-import { OnDrag } from "react-moveable";
|
|
|
+
|
|
|
+import { getChat } from '../../../redux/chat/selector';
|
|
|
+import { getAuthorizationState } from '../../../redux/authorization/selector';
|
|
|
+import { prodAwsS3, firstLetter, slicedWord } from '../../../helpers'
|
|
|
+import { socketIdChat } from '../../../api-data';
|
|
|
|
|
|
-import { getChat } from '../../../../../../redux/chat/selector';
|
|
|
-import { prodAwsS3,firstLetter,slicedWord } from '../../../../../../helpers'
|
|
|
+const Peer = require('simple-peer')
|
|
|
|
|
|
const useStyles = makeStyles({
|
|
|
- overlay: {
|
|
|
- position: 'fixed',
|
|
|
- top: 0,
|
|
|
+ container: {
|
|
|
+ position: 'absolute',
|
|
|
left: 0,
|
|
|
+ top: 0,
|
|
|
width: '100vw',
|
|
|
- height: '100vh',
|
|
|
- zIndex: 100,
|
|
|
+ height:'100vh',
|
|
|
overflow: 'hidden',
|
|
|
+ zIndex:100,
|
|
|
display: 'flex',
|
|
|
justifyContent: 'center',
|
|
|
alignItems: 'center',
|
|
@@ -120,114 +126,111 @@ const useStyles = makeStyles({
|
|
|
},
|
|
|
})
|
|
|
|
|
|
-interface ICallModal {
|
|
|
- setModalCall:any,
|
|
|
- shareRef: any,
|
|
|
- videoRef: any,
|
|
|
+interface ICallBar {
|
|
|
+ callStatus: string,
|
|
|
+ setCallStatus: any,
|
|
|
+ socket: any,
|
|
|
+ myVideoRef :any,
|
|
|
}
|
|
|
|
|
|
-const CallModal = ({setModalCall,shareRef,videoRef}:ICallModal) => {
|
|
|
+const CallBar = ({callStatus,setCallStatus,socket,myVideoRef}:ICallBar) => {
|
|
|
const classes = useStyles()
|
|
|
- const { name, lastName, avatarUrl, color } = useSelector(getChat)
|
|
|
- const [audio, setAudio] = useState<boolean>(true)
|
|
|
- const [video, setVideo] = useState<any>(false)
|
|
|
- const [share, setShare] = useState<any>(null)
|
|
|
- const handleShareScreen = async () => {
|
|
|
- const displayMediaStreamConstraints = {
|
|
|
- video: true,
|
|
|
- };
|
|
|
- const navigator:any = window.navigator
|
|
|
- const stream = await navigator.mediaDevices.getDisplayMedia(displayMediaStreamConstraints);
|
|
|
- shareRef.current.srcObject = stream;
|
|
|
- setShare(true)
|
|
|
- stream.getVideoTracks()[0].onended = () => setShare(false)
|
|
|
+ const { _id } = useSelector(getAuthorizationState)
|
|
|
+ const { name, lastName, avatarUrl, color,socketId,companionId } = useSelector(getChat)
|
|
|
+ const userVideoRef = useRef<any>(null);
|
|
|
+ const [mySocket,setMySocket] = useState<string>('')
|
|
|
+ const myAudioRef = useRef<any>(null);
|
|
|
+ const userAudioRef = useRef<any>(null);
|
|
|
+ const connectionRef = useRef<any>(null);
|
|
|
+ const handleLeaveCall = () => {
|
|
|
+ // connectionRef.current.destroy();
|
|
|
};
|
|
|
- const handleAudio = () => setAudio(prevState => !prevState)
|
|
|
- const handleVideo = async () => {
|
|
|
- const displayMediaStreamConstraints = {
|
|
|
- audio: false,
|
|
|
- video: {
|
|
|
- width: {
|
|
|
- min: 1280,
|
|
|
- ideal: 1920,
|
|
|
- max: 2560,
|
|
|
- },
|
|
|
- height: {
|
|
|
- min: 720,
|
|
|
- ideal: 1080,
|
|
|
- max: 1440
|
|
|
- },
|
|
|
- facingMode: 'user'
|
|
|
- },
|
|
|
- };
|
|
|
- const navigator:any = window.navigator
|
|
|
- const stream = await navigator.mediaDevices.getUserMedia(displayMediaStreamConstraints)
|
|
|
-
|
|
|
- if (!video) {
|
|
|
- setVideo(true)
|
|
|
- videoRef.current.srcObject = stream;
|
|
|
- } else {
|
|
|
- videoRef.current.srcObject = null;
|
|
|
- stream.getVideoTracks()[0].stop()
|
|
|
- setVideo(false)
|
|
|
- }
|
|
|
+ const handleCloseCall = () => {
|
|
|
+ setCallStatus(false)
|
|
|
+ handleLeaveCall()
|
|
|
}
|
|
|
- const handleCloseCallModal = () => setModalCall(false)
|
|
|
// requesting, waiting ,ringing, hanging up,line busy
|
|
|
- // useEffect(() => {
|
|
|
- // socket.on('connect', () => {
|
|
|
- // setIsConnected(true);
|
|
|
- // });
|
|
|
|
|
|
- // socket.on('disconnect', () => {
|
|
|
- // setIsConnected(false);
|
|
|
- // });
|
|
|
+ const handleStartCall = useCallback(async () => {
|
|
|
+ const stream = await navigator.mediaDevices.getUserMedia({
|
|
|
+ video: true,
|
|
|
+ audio: true
|
|
|
+ })
|
|
|
+ const peer = new Peer({
|
|
|
+ initiator: true,
|
|
|
+ trickle: false,
|
|
|
+ stream
|
|
|
+ });
|
|
|
+ peer.on("signal", (data:any) => {
|
|
|
+ socket.emit("call", {
|
|
|
+ socketId,
|
|
|
+ signalData: data,
|
|
|
+ from: mySocket,
|
|
|
+ userId: _id,
|
|
|
+ companionId
|
|
|
+ })
|
|
|
+ });
|
|
|
+ peer.on("stream", (streams:any) => {});
|
|
|
+ socket.on("accepted", ({ signal }:any) => peer.signal(signal));
|
|
|
+ connectionRef.current = peer;
|
|
|
+ },[mySocket,companionId,_id,socket])
|
|
|
+
|
|
|
+ const handleAnswerCall = async () => {
|
|
|
+ const stream = await navigator.mediaDevices.getUserMedia({
|
|
|
+ video: true,
|
|
|
+ audio: true
|
|
|
+ })
|
|
|
+ const peer = new Peer({
|
|
|
+ initiator: false,
|
|
|
+ trickle: false,
|
|
|
+ stream,
|
|
|
+ });
|
|
|
+ peer.on("signal", (data:any) => {
|
|
|
+ socket.emit("call", { signal: data, to: 'caller' });
|
|
|
+ });
|
|
|
+ peer.on("stream", (streams:any) => {});
|
|
|
+ peer.signal();
|
|
|
+ connectionRef.current = peer;
|
|
|
+ };
|
|
|
|
|
|
- // socket.on('pong', () => {
|
|
|
- // setLastPong(new Date().toISOString());
|
|
|
- // });
|
|
|
+ useEffect(() => {
|
|
|
+ socket.on("me", (id: string) => {
|
|
|
+ setMySocket(id)
|
|
|
+ socketIdChat(id)
|
|
|
+ })
|
|
|
+ socket.on('call', (data: any) => {
|
|
|
+ console.log(data,'income call')
|
|
|
+ })
|
|
|
+ socket.on('answer', (data: any) => {
|
|
|
+ console.log(data,'answer')
|
|
|
+ })
|
|
|
+ },[socket])
|
|
|
|
|
|
- // return () => {
|
|
|
- // socket.off('connect');
|
|
|
- // socket.off('disconnect');
|
|
|
- // socket.off('pong');
|
|
|
- // };
|
|
|
- // }, []);
|
|
|
+ useEffect(() => {
|
|
|
+ if(callStatus === 'call') handleStartCall()
|
|
|
+ }, [callStatus,handleStartCall])
|
|
|
|
|
|
return (
|
|
|
- <div className={classes.overlay} >
|
|
|
- <video ref={videoRef} style={{width: video ? 250 : 0, height: video ? 'auto' : 0, cursor: 'pointer',
|
|
|
- position: 'absolute', top: 0, left: 0, zIndex: 100}} playsInline autoPlay muted/>
|
|
|
- <Moveable
|
|
|
- target={videoRef.current}
|
|
|
- draggable={true}
|
|
|
- throttleDrag={0}
|
|
|
- hideDefaultLines={true}
|
|
|
- renderDirections={[]}
|
|
|
- rotationPosition="none"
|
|
|
- origin={false}
|
|
|
- onDrag={({ target, transform }: OnDrag) =>
|
|
|
- target!.style.transform = transform }
|
|
|
- />
|
|
|
+ <div className={classes.container} style={{top: callStatus === 'call'?0:'-100%'}}>
|
|
|
<div className={classes.modalCall}>
|
|
|
- <div className={classes.rightIcons} style={{marginBottom: share?0:40,}}>
|
|
|
+ <div className={classes.rightIcons} style={{marginBottom: true?0:40,}}>
|
|
|
<div className={classes.rightIconWrapper}>
|
|
|
<MinimizeIcon fontSize='small' />
|
|
|
</div>
|
|
|
<div className={classes.rightIconWrapper}>
|
|
|
<CropLandscapeIcon fontSize='small' />
|
|
|
</div>
|
|
|
- <div className={classes.rightIconWrapperClose} onClick={handleCloseCallModal}>
|
|
|
+ <div className={classes.rightIconWrapperClose} onClick={handleCloseCall}>
|
|
|
<CloseIcon fontSize='small' />
|
|
|
</div>
|
|
|
</div>
|
|
|
- {!share&&<ListItemAvatar style={{marginBottom:15}}>
|
|
|
+ {!true&&<ListItemAvatar style={{marginBottom:15}}>
|
|
|
<Avatar alt={name} src={avatarUrl?`${prodAwsS3}/${avatarUrl}`:undefined}
|
|
|
sx={{ background: color, width: 120, height: 120, marginRight: 2, fontSize:30,zIndex:0}}>
|
|
|
{`${firstLetter(name)}${firstLetter(lastName)}`}
|
|
|
</Avatar>
|
|
|
</ListItemAvatar>}
|
|
|
- {!share&&<div style={{marginBottom:'auto'}}>
|
|
|
+ {!true&&<div style={{marginBottom:'auto'}}>
|
|
|
<ListItemText primary={`${firstLetter(name)}${slicedWord(name, 15, 1)}
|
|
|
${firstLetter(lastName)}${slicedWord(lastName, 15, 1)}`}
|
|
|
primaryTypographyProps={{ color: '#ffffff', fontSize: 20, fontWeight: 500 }} />
|
|
@@ -236,35 +239,35 @@ const CallModal = ({setModalCall,shareRef,videoRef}:ICallModal) => {
|
|
|
...</span>
|
|
|
</span>} secondaryTypographyProps={{ textAlign: "center" }} />
|
|
|
</div>}
|
|
|
- <video className={share?classes.shareScreenActive:classes.shareScreenDisabled} ref={shareRef} playsInline muted autoPlay/>
|
|
|
+ <video className={true?classes.shareScreenActive:classes.shareScreenDisabled} ref={userVideoRef} playsInline muted autoPlay/>
|
|
|
<div className={classes.bottomWrapper}>
|
|
|
- {!share&&<div className={classes.bottomItem}>
|
|
|
- <Avatar className={classes.bottomIcon} onClick={handleShareScreen}
|
|
|
+ {!true&&<div className={classes.bottomItem}>
|
|
|
+ <Avatar className={classes.bottomIcon}
|
|
|
sx={{backgroundColor: '#ffffff',color: 'rgb(36, 36, 36)', width: 44, height: 44,zIndex:0}}>
|
|
|
<ScreenShareIcon fontSize="medium" />
|
|
|
</Avatar>
|
|
|
<Typography variant="h6" className={classes.titleIconBottom}>Screencast</Typography>
|
|
|
</div>}
|
|
|
<div className={classes.bottomItem}>
|
|
|
- <Avatar className={classes.bottomIcon} onClick={handleVideo}
|
|
|
- sx={{backgroundColor: video?'rgb(88, 88, 88)':'#ffffff',color: video?'#ffffff':'rgb(36, 36, 36)', width: 44, height: 44,zIndex:0}}>
|
|
|
- {video?<VideocamOffIcon fontSize="medium" />:<VideocamIcon fontSize="medium" />}
|
|
|
+ <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" />}
|
|
|
</Avatar>
|
|
|
- <Typography variant="h6" className={classes.titleIconBottom}>{video?'Stop Video':'Start Video'}</Typography>
|
|
|
+ <Typography variant="h6" className={classes.titleIconBottom}>{true?'Stop Video':'Start Video'}</Typography>
|
|
|
</div>
|
|
|
<div className={classes.bottomItem}>
|
|
|
- <Avatar className={classes.bottomIcon}
|
|
|
+ <Avatar className={classes.bottomIcon} onClick={handleLeaveCall}
|
|
|
sx={{backgroundColor: '#f02a2a',color: '#ffffff', width: 44, height: 44,zIndex:0}}>
|
|
|
<CallEndIcon fontSize="medium" />
|
|
|
</Avatar>
|
|
|
<Typography variant="h6" className={classes.titleIconBottom}>End Call</Typography>
|
|
|
</div>
|
|
|
<div className={classes.bottomItem}>
|
|
|
- <Avatar className={classes.bottomIcon} onClick={handleAudio}
|
|
|
- sx={{backgroundColor: audio?'rgb(88, 88, 88)':'#ffffff',color: audio?'#ffffff':'rgb(36, 36, 36)', width: 44, height: 44,zIndex:0}}>
|
|
|
- {audio?<MicIcon fontSize="medium" />:<MicOffIcon fontSize="medium" />}
|
|
|
+ <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>
|
|
|
- <Typography variant="h6" className={classes.titleIconBottom}>{audio?'Mute':'Unmute'}</Typography>
|
|
|
+ <Typography variant="h6" className={classes.titleIconBottom}>{true?'Mute':'Unmute'}</Typography>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -272,4 +275,4 @@ const CallModal = ({setModalCall,shareRef,videoRef}:ICallModal) => {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
-export default CallModal
|
|
|
+export default CallBar
|