123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- 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 ListItemText from '@mui/material/ListItemText';
- import ListItemAvatar from '@mui/material/ListItemAvatar';
- import Avatar from '@mui/material/Avatar';
- import MinimizeIcon from '@mui/icons-material/Minimize';
- import CropLandscapeIcon from '@mui/icons-material/CropLandscape';
- import CloseIcon from '@mui/icons-material/Close';
- import ScreenShareIcon from '@mui/icons-material/ScreenShare';
- import StopScreenShareIcon from '@mui/icons-material/StopScreenShare';
- 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 { getChat } from '../../../redux/chat/selector';
- import { getAuthorizationState } from '../../../redux/authorization/selector';
- import { prodAwsS3, firstLetter, slicedWord } from '../../../helpers'
- import { socketIdChat } from '../../../api-data';
- const Peer = require('simple-peer')
- 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)',
- },
- shareScreenActive: {
- width: '30%',
- borderRadius: 7,
- margin: 'auto',
- border:'solid 2px #0084ff',
- },
- shareScreenDisabled: {
- width: 0,
- height:0,
- },
- modalCall: {
- background: 'rgb(36, 36, 36)',
- display: 'flex',
- flexDirection: 'column',
- justifyContent: 'start',
- alignItems: 'center',
- justifyItems:"center",
- width: '34vw',
- height:'90vh',
- borderRadius: 7,
- },
- rightIcons: {
- display: 'flex',
- justifyContent: 'end',
- alignContent: 'center',
- alignItems: 'center',
- width:'100%'
- },
- rightIconWrapper: {
- color: '#ffffff',
- cursor: 'pointer',
- padding:'3px 10px 3px 10px',
- '&:hover': {
- backgroundColor:'rgb(80, 80, 80)'
- }
- },
- rightIconWrapperClose: {
- color: '#ffffff',
- cursor: 'pointer',
- padding:'3px 10px 3px 10px',
- borderTopRightRadius:7,
- '&:hover': {
- backgroundColor:'#f02a2a'
- }
- },
- bottomWrapper: {
- display: 'flex',
- justifyContent: 'center',
- padding: 5
- },
- 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
- },
- '@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: string,
- setCallStatus: any,
- socket: any,
- myVideoRef :any,
- }
- const CallBar = ({callStatus,setCallStatus,socket,myVideoRef}:ICallBar) => {
- const classes = useStyles()
- const { _id } = useSelector(getAuthorizationState)
- const chat = useSelector(getChat)
- const myAudioRef = useRef<any>(null);
- const userAudioRef = useRef<any>(null);
- const userVideoRef = useRef<any>(null);
- const connectionRef = useRef<any>(null);
- 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 handleLeaveCall = () => {
- connectionRef.current.destroy();
- setCallStatus('')
- };
- // requesting, waiting ,ringing, hanging up,line busy
- 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("callTo", {
- to: chat.socketId,
- signalData: data,
- from: mySocket,
- userId: _id,
- companionId:chat.companionId
- })
- setCallStatus('ringing')
- });
- peer.on("stream", (stream: any) => {
- console.log(stream,'user stream')
- });
- socket.on("acceptedCall", ({ signal }: any) => {
- peer.signal(signal)
- setCallStatus('accepted')
- console.log(signal,'signal accepted from companion')
- });
- connectionRef.current = peer;
- },[chat.socketId,chat.companionId,_id,socket,setCallStatus,mySocket])
- const handleAnswerCall = useCallback(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("answerCall", { signal: data, to: companionSocket });
- });
- peer.on("stream", (stream: any) => {
- userVideoRef.current.srcObject = stream;
- });
- peer.signal(companionSignal);
- connectionRef.current = peer;
- },[socket,companionSocket,companionSignal])
- 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)
- setCompanionSocket(data.from)
- setCompanionSignal(data.signal)
- })
- },[socket,setCallStatus,setName,setLastName,setCompanionSocket,setCompanionSignal])
- useEffect(() => {
- if(callStatus === 'requesting') handleStartCall()
- }, [callStatus, handleStartCall])
- useEffect(() => {
- if (callStatus === '') {
- setName(chat.name)
- setLastName(chat.lastName)
- setAvatarUrl(chat.avatarUrl)
- setColor(chat.color)
- setNumber(chat.number)
- }
- }, [callStatus,chat])
- return (
- <div className={classes.container} style={{top: callStatus?0:'-100%'}}>
- <div className={classes.modalCall}>
- <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={handleLeaveCall}>
- <CloseIcon fontSize='small' />
- </div>
- </div>
- {<ListItemAvatar style={{marginBottom:5}}>
- <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>}
- {<div style={{marginBottom:'auto'}}>
- <ListItemText primary={`${firstLetter(name)}${slicedWord(name, 15, 1)}
- ${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" }} />
- </div>}
- <video className={true ? classes.shareScreenActive : classes.shareScreenDisabled} ref={userVideoRef} playsInline autoPlay />
- <div className={classes.bottomWrapper}>
- {!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}
- 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}>{true?'Stop Video':'Start Video'}</Typography>
- </div>
- <div className={classes.bottomItem}>
- <Avatar className={classes.bottomIconEndAccept} onClick={handleLeaveCall}
- sx={{backgroundColor: '#f02a2a',color: '#ffffff', width: 44, height: 44,zIndex:0}}>
- <CallEndIcon fontSize="medium" />
- </Avatar>
- <Typography variant="h6" className={classes.titleIconBottom}>
- {callStatus === 'is calling you' ? 'Decline' : 'End Call'}
- </Typography>
- </div>
- {callStatus === 'is calling you' &&
- <div className={classes.bottomItem} style={{position:"relative"}}>
- <div className={classes.ringPulsate}></div>
- <Avatar className={classes.bottomIconEndAccept} onClick={handleAnswerCall}
- sx={{ backgroundColor: '#21f519', color: '#ffffff', width: 44, height: 44, zIndex: 0 }}>
- <PhoneIcon fontSize="medium" />
- </Avatar>
- <Typography variant="h6" className={classes.titleIconBottom}>
- Accept
- </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>
- <Typography variant="h6" className={classes.titleIconBottom}>{true?'Mute':'Unmute'}</Typography>
- </div>
- </div>
- </div>
- </div>
- )
- }
- export default CallBar
|