/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable no-undef */
import React, {useEffect, useRef, useState} from 'react';
import {View,Text,SafeAreaView,StyleSheet,LogBox,TouchableOpacity} from 'react-native'
import {NavigationContainer, StackActions} from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import AuthNavigator from './src/assets/navigations/AuthNavigator';
import OnboardingScreen from './src/screens/Onboarding/Onboarding';
import CallEnd from './src/components/CallEnd';
import CallAnswer from './src/components/CallAnswer';
import IconContainer from './src/components/IconContainer';
import CameraSwitch from './src/components/CameraSwitch';
import VideoOff from './src/components/VideoOff';
import VideoOn from './src/components/VideoOn';
import SocketIOClient from 'socket.io-client';
import InCallManager from 'react-native-incall-manager';
import RNRestart from 'react-native-restart';
import {
mediaDevices,
RTCPeerConnection,
RTCView,
RTCIceCandidate,
RTCSessionDescription,
} from 'react-native-webrtc';
import MicOn from './src/components/MicOn';
import MicOff from './src/components/MicOff';
import { HOST } from './src/api/api';
import AsyncStorage from '@react-native-async-storage/async-storage';
LogBox.ignoreAllLogs();
function App() {
const [auth, setAuth] = useState(false);
const [localStream, setlocalStream] = useState(null);
const [localCandidate, setLocalCandidate] = useState([]);
const [remoteStream, setRemoteStream] = useState(null);
const [localMicOn, setlocalMicOn] = useState(true);
const [localWebcamOn, setlocalWebcamOn] = useState(true);
const [type, setType] = useState('JOIN');
const[userId,setUserId]=useState(null);
const[callerName,setCallerName]=useState(null);
const[calleeName,setCalleeName]=useState(null);
let remoteRTCMessage = useRef(null);
let isFront = false;
const [callerId,setCallerId] = useState(
"caller"
);
const otherUserId = useRef(null);
const setOtherUserId=(data)=>{
otherUserId.current=data.userId;
setCalleeName(data.name)
}
const socket = SocketIOClient(HOST.websocket, {
transports: ['websocket'],
query: {
callerId,
},
});
const peerConnection = useRef(
new RTCPeerConnection({
configuration: {
offerToReceiveAudio: true,
offerToReceiveVideo: true
},
iceServers: [
{
urls: 'stun:stun.l.google.com:19302',
},
{ url: 'stun:stun1.l.google.com:19302' },
{ url: 'stun:stun2.l.google.com:19302' },
{ url: 'stun:stun3.l.google.com:19302' },
],
}),
);
useEffect(() => {
socket.on('newCall', data => {
if(userId&&data.calleeId==userId)
{
remoteRTCMessage.current = data.rtcMessage;
otherUserId.current = data.callerId;
setCallerName(data.callerName);
setType('INCOMING_CALL');
}
});
socket.on('newEnd', data => {
if(userId&&(data.user1==userId||data.user2==userId))
{
peerConnection.current.close();
//setType('JOIN');
RNRestart.Restart();
}
});
socket.on('callAnswered', async data => {
if(userId&&(data.callerId==userId) )
{
remoteRTCMessage.current =data.rtcMessage ;
let message= new RTCSessionDescription(remoteRTCMessage.current);
console.log(message);
//peerConnection.current.remoteDescription=message;
await peerConnection.current.setRemoteDescription(message);
console.log(peerConnection);
setType('WEBRTC_ROOM');
}
if(userId&&(data.calleeId==userId) )
{
console.log("Buraya Bak");
await localCandidate.forEach((candidate, index) => {
peerConnection.current
.addIceCandidate(
new RTCIceCandidate({
candidate: candidate.candidate,
sdpMid: candidate.id,
sdpMLineIndex: candidate.label,
}),
)
.then(data => {
console.log('SUCCESS');
})
.catch(err => {
console.log('Error', err);
});
});
setType('WEBRTC_ROOM');
}
});
socket.on('ICEcandidateRoom', async data => {
if(userId&&(data.callerId==userId||data.calleeId==userId))
{
let message = data.rtcMessage;
if(data.callerId==userId && data.type=="answer")
{
if (peerConnection.current) {
await peerConnection.current
.addIceCandidate(
new RTCIceCandidate({
candidate: message.candidate,
sdpMid: message.id,
sdpMLineIndex: message.label,
}),
)
.then(data => {
console.log('SUCCESS');
})
.catch(err => {
console.log('Error', err);
});
}
}
if(data.calleeId==userId && data.type=="offer")
{
localCandidate.push(message);
}
}
});
mediaDevices.enumerateDevices().then(sourceInfos => {
let videoSourceId;
for (let i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if (
sourceInfo.kind == 'videoinput' &&
sourceInfo.facing == (isFront ? 'user' : 'environment')
) {
videoSourceId = sourceInfo.deviceId;
}
}
mediaDevices
.getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 500, // Provide your own width, height and frame rate here
minHeight: 300,
minFrameRate: 30,
},
facingMode: isFront ? 'user' : 'environment',
optional: videoSourceId ? [{sourceId: videoSourceId}] : [],
},
})
.then(stream => {
// Got stream!
setlocalStream(stream);
// setup stream listening
peerConnection.current.addStream(stream);
})
.catch(error => {
// Log error
});
});
peerConnection.current.onaddstream = event => {
setRemoteStream(event.stream);
};
return () => {
socket.off('newCall');
socket.off('callAnswered');
socket.off('ICEcandidate');
};
}
, [userId,otherUserId]);
useEffect(() => {
InCallManager.start();
InCallManager.setKeepScreenOn(true);
InCallManager.setForceSpeakerphoneOn(true);
return () => {
InCallManager.stop();
};
}, []);
function sendICEcandidate(data) {
socket.emit('ICEcandidate', data);
}
async function callUser() {
const offerOptions = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
const sessionDescription = await peerConnection.current.createOffer(offerOptions);
await peerConnection.current.setLocalDescription(sessionDescription);
sendCall({
userId:userId,
calleeId: otherUserId.current,
rtcMessage: sessionDescription,
});
peerConnection.current.addEventListener('icecandidate', event => {
if (event.candidate) {
sendICEcandidate({
callerId: userId,
userId: userId,
calleeId: otherUserId.current,
type:"offer",
rtcMessage: {
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
},
});
}
});
}
async function callEnd(userId,otherUserId) {
sendEnd({
user1:userId,
user2: otherUserId,
});
setType('JOIN');
}
async function callSave(userId) {
sendSave({
userId:userId,
calleeId: null,
rtcMessage: null,
});
}
async function processAccept(otherUserId) {
const offerOptions = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
peerConnection.current.setRemoteDescription(
new RTCSessionDescription(remoteRTCMessage.current),
);
const sessionDescription = await peerConnection.current.createAnswer(offerOptions);
await peerConnection.current.setLocalDescription(sessionDescription);
answerCall({
userId:userId,
callerId: otherUserId.current,
rtcMessage: sessionDescription,
});
peerConnection.current.addEventListener('icecandidate', event => {
if (event.candidate) {
sendICEcandidate({
callerId: otherUserId.current,
userId: userId,
calleeId: userId,
type:"answer",
rtcMessage: {
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
},
});
}
});
}
function answerCall(data) {
socket.emit('answerCall', data);
}
function sendCall(data) {
socket.emit('call', data);
setType('OUTGOING_CALL');
}
function sendEnd(data) {
socket.emit('end', data);
}
function sendSave(data) {
socket.emit('save', data);
}
const OutgoingCallScreen = () => {
return (
<View
style={{
flex: 1,
justifyContent: 'space-around',
backgroundColor: '#050A0E',
}}>
<View
style={{
padding: 35,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 16,
color: '#D0D4DD',
}}>
Calling ...
</Text>
<Text
style={{
fontSize: 36,
marginTop: 12,
color: '#ffff',
letterSpacing: 6,
}}>
{calleeName}
</Text>
</View>
<View
style={{
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity
onPress={() => {
callEnd(userId,otherUserId.current);
}}
style={{
backgroundColor: '#FF5D5D',
borderRadius: 30,
height: 60,
aspectRatio: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<CallEnd width={50} height={12} />
</TouchableOpacity>
</View>
</View>
);
};
const IncomingCallScreen = () => {
return (
<View
style={{
flex: 1,
justifyContent: 'space-around',
backgroundColor: '#050A0E',
}}>
<View
style={{
padding: 35,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 14,
}}>
<Text
style={{
fontSize: 36,
marginTop: 12,
color: '#ffff',
}}>
{callerName}
</Text>
<Text
style={{
fontSize: 16,
color: '#D0D4DD',
}}>
is Calling..
</Text>
</View>
<View
style={{
flexDirection:'row',
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity
onPress={() => {
processAccept(otherUserId);
setType('WEBRTC_ROOM');
}}
style={{
backgroundColor: 'green',
borderRadius: 30,
height: 60,
aspectRatio: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<CallAnswer height={28} fill={'#fff'} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
callEnd(userId,otherUserId.current);
}}
style={{
backgroundColor: '#FF5D5D',
borderRadius: 30,
height: 60,
aspectRatio: 1,
justifyContent: 'center',
alignItems: 'center',
marginLeft:30,
}}>
<CallEnd width={50} height={12} />
</TouchableOpacity>
</View>
</View>
);
};
function switchCamera() {
localStream.getVideoTracks().forEach(track => {
track._switchCamera();
});
}
function toggleCamera() {
localWebcamOn ? setlocalWebcamOn(false) : setlocalWebcamOn(true);
localStream.getVideoTracks().forEach(track => {
localWebcamOn ? (track.enabled = false) : (track.enabled = true);
});
}
function toggleMic() {
localMicOn ? setlocalMicOn(false) : setlocalMicOn(true);
localStream.getAudioTracks().forEach(track => {
localMicOn ? (track.enabled = false) : (track.enabled = true);
});
}
function leave () {
peerConnection.current.close();
setlocalStream(null);
//RNRestart.Restart();
setType('JOIN');
}
const WebrtcRoomScreen = () => {
return (
<View
style={{
flex: 1,
backgroundColor: '#050A0E',
paddingHorizontal: 12,
paddingVertical: 12,
}}>
{localStream ? (
<RTCView
objectFit={'cover'}
style={{flex: 1, backgroundColor: '#050A0E'}}
streamURL={localStream.toURL()}
/>
) : null}
{remoteStream ? (
<RTCView
objectFit={'cover'}
style={{
flex: 1,
backgroundColor: '#050A0E',
marginTop: 8,
}}
streamURL={remoteStream.toURL()}
/>
) : null}
<View
style={{
marginVertical: 12,
flexDirection: 'row',
justifyContent: 'space-evenly',
}}>
<IconContainer
backgroundColor={'red'}
onPress={() => {
leave();
}}
Icon={() => {
return <CallEnd height={26} width={26} fill="#FFF" />;
}}
/>
<IconContainer
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={!localMicOn ? '#fff' : 'transparent'}
onPress={() => {
toggleMic();
}}
Icon={() => {
return localMicOn ? (
<MicOn height={24} width={24} fill="#FFF" />
) : (
<MicOff height={28} width={28} fill="#1D2939" />
);
}}
/>
<IconContainer
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={!localWebcamOn ? '#fff' : 'transparent'}
onPress={() => {
toggleCamera();
}}
Icon={() => {
return localWebcamOn ? (
<VideoOn height={24} width={24} fill="#FFF" />
) : (
<VideoOff height={36} width={36} fill="#1D2939" />
);
}}
/>
<IconContainer
style={{
borderWidth: 1.5,
borderColor: '#2B3034',
}}
backgroundColor={'transparent'}
onPress={() => {
switchCamera();
}}
Icon={() => {
return <CameraSwitch height={24} width={24} fill="#FFF" />;
}}
/>
</View>
</View>
);
};
switch (type) {
case 'JOIN':
return (
<SafeAreaView style={styles.safeArea}>
{auth ? (
<View style={styles.conteiner}>
<OnboardingScreen callUser={callUser} setOtherUserId={setOtherUserId} callSave={callSave} callUserId={setUserId} />
</View>
) : (
<NavigationContainer>
<AuthNavigator callUser={callUser} setOtherUserId={setOtherUserId} callSave={callSave} callUserId={setUserId}/>
</NavigationContainer>
)}
</SafeAreaView>
);
case 'INCOMING_CALL':
return IncomingCallScreen();
case 'OUTGOING_CALL':
return OutgoingCallScreen();
case 'WEBRTC_ROOM':
return WebrtcRoomScreen();
default:
return (
<SafeAreaView style={styles.safeArea}>
{auth ? (
<View style={styles.conteiner}>
<OnboardingScreen callUser={callUser} setOtherUserId={setOtherUserId} callSave={callSave} callUserId={setUserId}/>
</View>
) : (
<NavigationContainer>
<AuthNavigator callUser={callUser} setOtherUserId={setOtherUserId} callSave={callSave} callUserId={setUserId}/>
</NavigationContainer>
)}
</SafeAreaView>
);
}
}
export default App;
const styles = StyleSheet.create({
conteiner: {flex: 1, justifyContent: 'center', alignItems: 'center'},
safeArea: {
flex: 1,
},
});
Candidates created but never join the same room ? What is the problem. “react-native”: “0.71.6”, “react-native-webrtc”: “^111.0.3”,