mirror of
https://github.com/balzack/databag.git
synced 2025-02-15 04:59:16 +00:00
adding webrtc calling from mobile
This commit is contained in:
parent
fcbe56e160
commit
87a6ba4de9
@ -65,6 +65,7 @@ export function useRingContext() {
|
|||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
setSession: (token) => {
|
setSession: (token) => {
|
||||||
|
|
||||||
if (access.current) {
|
if (access.current) {
|
||||||
throw new Error("invalid ring state");
|
throw new Error("invalid ring state");
|
||||||
}
|
}
|
||||||
@ -160,7 +161,7 @@ export function useRingContext() {
|
|||||||
stream.current.addTrack(event.track, stream.current);
|
stream.current.addTrack(event.track, stream.current);
|
||||||
} );
|
} );
|
||||||
|
|
||||||
const processOffers = async () => {
|
const impolite = async () => {
|
||||||
if (processing.current) {
|
if (processing.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -225,11 +226,10 @@ export function useRingContext() {
|
|||||||
}
|
}
|
||||||
else if (signal.description) {
|
else if (signal.description) {
|
||||||
offers.current.push(signal.description);
|
offers.current.push(signal.description);
|
||||||
processOffers();
|
impolite();
|
||||||
}
|
}
|
||||||
else if (signal.candidate) {
|
else if (signal.candidate) {
|
||||||
if (pc.current.remoteDescription == null) {
|
if (pc.current.remoteDescription == null) {
|
||||||
console.log("IGNOREING CANDIDATE");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const candidate = new RTCIceCandidate(signal.candidate);
|
const candidate = new RTCIceCandidate(signal.candidate);
|
||||||
@ -288,7 +288,8 @@ export function useRingContext() {
|
|||||||
try {
|
try {
|
||||||
const { host, callId, contactNode, contactToken } = calling.current;
|
const { host, callId, contactNode, contactToken } = calling.current;
|
||||||
if (host) {
|
if (host) {
|
||||||
await removeCall(access.current, callId);
|
const { server, token } = access.current;
|
||||||
|
await removeCall(server, token, callId);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await removeContactCall(contactNode, contactToken, callId);
|
await removeContactCall(contactNode, contactToken, callId);
|
||||||
@ -308,14 +309,229 @@ export function useRingContext() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
call: async (cardId, contactNode, contactToken) => {
|
call: async (cardId, contactNode, contactToken) => {
|
||||||
|
if (calling.current) {
|
||||||
|
throw new Error("active session");
|
||||||
|
}
|
||||||
|
|
||||||
|
// create call
|
||||||
|
const { server, token } = access.current;
|
||||||
|
const call = await addCall(server, token, cardId);
|
||||||
|
const { id, keepAlive, callerToken, calleeToken } = call;
|
||||||
|
try {
|
||||||
|
await addContactRing(contactNode, contactToken, { index, callId: id, calleeToken });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
const aliveInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
await keepCall(server, token, id);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}, keepAlive * 1000);
|
||||||
|
let index = 0;
|
||||||
|
const ringInterval = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
await addContactRing(contactNode, contactToken, { index, callId: id, calleeToken });
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}, RING);
|
||||||
|
|
||||||
|
calling.current = { state: "connecting", callId: id, host: true };
|
||||||
|
updateState({ callStatus: "connecting", cardId, remoteVideo: false, remoteAudio: false });
|
||||||
|
|
||||||
|
// form peer connection
|
||||||
|
pc.current = new RTCPeerConnection({ iceServers });
|
||||||
|
pc.current.addEventListener( 'connectionstatechange', event => {
|
||||||
|
console.log("CONNECTION STATE", event);
|
||||||
|
} );
|
||||||
|
pc.current.addEventListener( 'icecandidate', event => {
|
||||||
|
ws.current.send(JSON.stringify({ candidate: event.candidate }));
|
||||||
|
} );
|
||||||
|
pc.current.addEventListener( 'icecandidateerror', event => {
|
||||||
|
console.log("ICE ERROR");
|
||||||
|
} );
|
||||||
|
pc.current.addEventListener( 'iceconnectionstatechange', event => {
|
||||||
|
console.log("ICE STATE CHANGE", event);
|
||||||
|
} );
|
||||||
|
pc.current.addEventListener( 'negotiationneeded', event => {
|
||||||
|
console.log("ICE NEGOTIATION", event);
|
||||||
|
} );
|
||||||
|
pc.current.addEventListener( 'signalingstatechange', event => {
|
||||||
|
console.log("ICE SIGNALING", event);
|
||||||
|
} );
|
||||||
|
pc.current.addEventListener( 'track', event => {
|
||||||
|
if (stream.current == null) {
|
||||||
|
stream.current = new MediaStream();
|
||||||
|
updateState({ remoteStream: stream.current });
|
||||||
|
}
|
||||||
|
if (event.track.kind === 'audio') {
|
||||||
|
updateState({ remoteAudio: true });
|
||||||
|
}
|
||||||
|
if (event.track.kind === 'video') {
|
||||||
|
updateState({ remoteVideo: true });
|
||||||
|
}
|
||||||
|
stream.current.addTrack(event.track, stream.current);
|
||||||
|
} );
|
||||||
|
|
||||||
|
videoTrack.current = false;
|
||||||
|
audioTrack.current = false;
|
||||||
|
accessVideo.current = false;
|
||||||
|
try {
|
||||||
|
const stream = await mediaDevices.getUserMedia({
|
||||||
|
video: false,
|
||||||
|
audio: true,
|
||||||
|
});
|
||||||
|
accessAudio.current = true;
|
||||||
|
updateState({ localVideo: false, localAudio: true, localStream: stream });
|
||||||
|
for (const track of stream.getTracks()) {
|
||||||
|
if (track.kind === 'audio') {
|
||||||
|
audioTrack.current = track;
|
||||||
|
}
|
||||||
|
if (track.kind === 'video') {
|
||||||
|
videoTrack.current = track;
|
||||||
|
}
|
||||||
|
pc.current.addTrack(track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const polite = async () => {
|
||||||
|
if (processing.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
processing.current = true;
|
||||||
|
|
||||||
|
while (offers.current.length > 0) {
|
||||||
|
descriptions = offers.current;
|
||||||
|
offers.current = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < descriptions.length; i++) {
|
||||||
|
const description = descriptions[i];
|
||||||
|
stream.current = null;
|
||||||
|
|
||||||
|
if (description.type === 'offer' && pc.current.signalingState !== 'stable') {
|
||||||
|
const rollback = new RTCSessionDescription({ type: "rollback" });
|
||||||
|
await pc.current.setLocalDescription(reollback);
|
||||||
|
}
|
||||||
|
const offer = new RTCSessionDescription(description);
|
||||||
|
await pc.current.setRemoteDescription(offer);
|
||||||
|
if (description.type === 'offer') {
|
||||||
|
const answer = await pc.current.createAnswer();
|
||||||
|
await pc.current.setLocalDescription(answer);
|
||||||
|
ws.current.send(JSON.stringify({ description: answer }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processing.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.current = createWebsocket(`wss://${server}/signal`);
|
||||||
|
ws.current.onmessage = async (ev) => {
|
||||||
|
// handle messages [polite]
|
||||||
|
try {
|
||||||
|
const signal = JSON.parse(ev.data);
|
||||||
|
if (signal.status) {
|
||||||
|
if (calling.current.state !== 'connected' && signal.status === 'connected') {
|
||||||
|
clearInterval(ringInterval);
|
||||||
|
calling.current.state = 'connected';
|
||||||
|
updateState({ callStatus: "connected" });
|
||||||
|
}
|
||||||
|
if (signal.status === 'closed') {
|
||||||
|
ws.current.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (signal.description) {
|
||||||
|
offers.current.push(signal.description);
|
||||||
|
polite();
|
||||||
|
}
|
||||||
|
else if (signal.candidate) {
|
||||||
|
if (pc.current.remoteDescription == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const candidate = new RTCIceCandidate(signal.candidate);
|
||||||
|
await pc.current.addIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ws.current.onclose = (e) => {
|
||||||
|
pc.current.close();
|
||||||
|
clearInterval(ringInterval);
|
||||||
|
clearInterval(aliveInterval);
|
||||||
|
calling.current = null;
|
||||||
|
if (videoTrack.current) {
|
||||||
|
videoTrack.current.stop();
|
||||||
|
videoTrack.current = null;
|
||||||
|
}
|
||||||
|
if (audioTrack.current) {
|
||||||
|
audioTrack.current.stop();
|
||||||
|
audioTrack.current = null;
|
||||||
|
}
|
||||||
|
updateState({ callStatus: null });
|
||||||
|
}
|
||||||
|
ws.current.onopen = () => {
|
||||||
|
calling.current.state = "ringing";
|
||||||
|
updateState({ callStatus: "ringing" });
|
||||||
|
ws.current.send(JSON.stringify({ AppToken: callerToken }))
|
||||||
|
}
|
||||||
|
ws.current.error = (e) => {
|
||||||
|
console.log(e)
|
||||||
|
ws.current.close();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
enableVideo: async () => {
|
enableVideo: async () => {
|
||||||
|
if (!accessVideo.current) {
|
||||||
|
const stream = await mediaDevices.getUserMedia({
|
||||||
|
video: true,
|
||||||
|
audio: true,
|
||||||
|
});
|
||||||
|
accessVideo.current = true;
|
||||||
|
accessAudio.current = true;
|
||||||
|
updateState({ localStream: stream });
|
||||||
|
for (const track of stream.getTracks()) {
|
||||||
|
if (track.kind === 'audio') {
|
||||||
|
audioTrack.current = track;
|
||||||
|
}
|
||||||
|
if (track.kind === 'video') {
|
||||||
|
videoTrack.current = track;
|
||||||
|
}
|
||||||
|
pc.current.addTrack(track);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
videoTrack.current.enabled = true;
|
||||||
|
}
|
||||||
|
updateState({ localVideo: true, localAudio: true });
|
||||||
},
|
},
|
||||||
disableVideo: async () => {
|
disableVideo: async () => {
|
||||||
|
if (videoTrack.current) {
|
||||||
|
videoTrack.current.enabled = false;
|
||||||
|
}
|
||||||
|
updateState({ localVideo: false });
|
||||||
},
|
},
|
||||||
enableAudio: async () => {
|
enableAudio: async () => {
|
||||||
|
if (accessAudio.current) {
|
||||||
|
audioTrack.current.enabled = true;
|
||||||
|
updateState({ localAudio: true });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disableAudio: async () => {
|
disableAudio: async () => {
|
||||||
|
if (accessAudio.current) {
|
||||||
|
audioTrack.current.enabled = false;
|
||||||
|
updateState({ localAudio: false });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,12 +475,12 @@ export function Session() {
|
|||||||
)}
|
)}
|
||||||
{ !state.localAudio && (
|
{ !state.localAudio && (
|
||||||
<TouchableOpacity style={styles.callOption} onPress={actions.enableVideo}>
|
<TouchableOpacity style={styles.callOption} onPress={actions.enableVideo}>
|
||||||
<MatIcons name={'phone'} size={20} color={Colors.white} />
|
<MatIcons name={'microphone'} size={20} color={Colors.white} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
{ state.localAudio && (
|
{ state.localAudio && (
|
||||||
<TouchableOpacity style={styles.callOption} onPress={actions.disableVideo}>
|
<TouchableOpacity style={styles.callOption} onPress={actions.disableVideo}>
|
||||||
<MatIcons name={'phone-off'} size={20} color={Colors.white} />
|
<MatIcons name={'microphone-off'} size={20} color={Colors.white} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
@ -198,6 +198,10 @@ export function ContactBody({ contact }) {
|
|||||||
<TouchableOpacity style={styles.button} onPress={reportContact}>
|
<TouchableOpacity style={styles.button} onPress={reportContact}>
|
||||||
<Text style={styles.buttonText}>Report Contact</Text>
|
<Text style={styles.buttonText}>Report Contact</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity style={styles.button} onPress={actions.ring}>
|
||||||
|
<Text style={styles.buttonText}>Call Contact</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{ state.status === 'connecting' && (
|
{ state.status === 'connecting' && (
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect, useRef, useContext } from 'react';
|
import { useState, useEffect, useRef, useContext } from 'react';
|
||||||
import { CardContext } from 'context/CardContext';
|
import { CardContext } from 'context/CardContext';
|
||||||
|
import { RingContext } from 'context/RingContext';
|
||||||
import { getListingMessage } from 'api/getListingMessage';
|
import { getListingMessage } from 'api/getListingMessage';
|
||||||
import { getListingImageUrl } from 'api/getListingImageUrl';
|
import { getListingImageUrl } from 'api/getListingImageUrl';
|
||||||
import { addFlag } from 'api/addFlag';
|
import { addFlag } from 'api/addFlag';
|
||||||
@ -22,6 +23,7 @@ export function useContact(contact) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const card = useContext(CardContext);
|
const card = useContext(CardContext);
|
||||||
|
const ring = useContext(RingContext);
|
||||||
|
|
||||||
const updateState = (value) => {
|
const updateState = (value) => {
|
||||||
setState((s) => ({ ...s, ...value }));
|
setState((s) => ({ ...s, ...value }));
|
||||||
@ -151,6 +153,13 @@ export function useContact(contact) {
|
|||||||
resync: () => {
|
resync: () => {
|
||||||
card.actions.resync(contact.card);
|
card.actions.resync(contact.card);
|
||||||
},
|
},
|
||||||
|
ring: async () => {
|
||||||
|
console.log("calling!!");
|
||||||
|
const contact = card.state.cards.get(state.cardId);
|
||||||
|
const { node, guid } = contact.card?.profile || {}
|
||||||
|
const { token } = contact.card?.detail || {}
|
||||||
|
await ring.actions.call(state.cardId, node, `${guid}.${token}`);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return { state, actions };
|
return { state, actions };
|
||||||
|
@ -111,46 +111,16 @@ export function useSession() {
|
|||||||
await ring.actions.end();
|
await ring.actions.end();
|
||||||
},
|
},
|
||||||
enableVideo: async () => {
|
enableVideo: async () => {
|
||||||
if (!accessVideo.current) {
|
await ring.actions.enableVideo();
|
||||||
const stream = await mediaDevices.getUserMedia({
|
|
||||||
video: true,
|
|
||||||
audio: true,
|
|
||||||
});
|
|
||||||
accessVideo.current = true;
|
|
||||||
accessAudio.current = true;
|
|
||||||
updateState({ localStream: stream });
|
|
||||||
for (const track of stream.getTracks()) {
|
|
||||||
if (track.kind === 'audio') {
|
|
||||||
audioTrack.current = track;
|
|
||||||
}
|
|
||||||
if (track.kind === 'video') {
|
|
||||||
videoTrack.current = track;
|
|
||||||
}
|
|
||||||
pc.current.addTrack(track);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
videoTrack.current.enabled = true;
|
|
||||||
}
|
|
||||||
updateState({ localVideo: true, localAudio: true });
|
|
||||||
},
|
},
|
||||||
disableVideo: async () => {
|
disableVideo: async () => {
|
||||||
if (videoTrack.current) {
|
await ring.actions.disableVideo();
|
||||||
videoTrack.current.enabled = false;
|
|
||||||
}
|
|
||||||
updateState({ localVideo: false });
|
|
||||||
},
|
},
|
||||||
enableAudio: async () => {
|
enableAudio: async () => {
|
||||||
if (accessAudio.current) {
|
await ring.actions.enableAudio();
|
||||||
audioTrack.current.enabled = true;
|
|
||||||
updateState({ localAudio: true });
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
disableAudio: async () => {
|
disableAudio: async () => {
|
||||||
if (accessAudio.current) {
|
await ring.actions.disableAudio();
|
||||||
audioTrack.current.enabled = false;
|
|
||||||
updateState({ localAudio: false });
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,7 +164,6 @@ export function useContact(guid, listing, close) {
|
|||||||
const { node, guid } = contact.data.cardProfile;
|
const { node, guid } = contact.data.cardProfile;
|
||||||
const { token } = contact.data.cardDetail;
|
const { token } = contact.data.cardDetail;
|
||||||
await ring.actions.call(state.cardId, node, `${guid}.${token}`);
|
await ring.actions.call(state.cardId, node, `${guid}.${token}`);
|
||||||
//await addContactRing(node, `${guid}.${token}`, { index: 0, callId: 'abc', calleeToken: '123' });
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user