mirror of
https://github.com/balzack/databag.git
synced 2025-04-23 01:55:17 +00:00
adding calling component
This commit is contained in:
parent
37fd22b58b
commit
2eb6722ab1
@ -1,15 +1,31 @@
|
||||
import { useState, useContext, useEffect } from 'react'
|
||||
import { useState, useContext, useEffect, useRef } from 'react'
|
||||
import { DisplayContext } from '../context/DisplayContext';
|
||||
import { AppContext } from '../context/AppContext'
|
||||
import { ContextType } from '../context/ContextType'
|
||||
import { Link } from 'databag-client-sdk';
|
||||
|
||||
import {
|
||||
ScreenCapturePickerView,
|
||||
RTCPeerConnection,
|
||||
RTCIceCandidate,
|
||||
RTCSessionDescription,
|
||||
RTCView,
|
||||
MediaStream,
|
||||
MediaStreamTrack,
|
||||
mediaDevices,
|
||||
registerGlobals
|
||||
} from 'react-native-webrtc';
|
||||
|
||||
export function useCalling() {
|
||||
const app = useContext(AppContext) as ContextType;
|
||||
const display = useContext(DisplayContext) as ContextType;
|
||||
const call = useRef(null as { peerConnection: RTCPeerConnection, signalLink: Link } | null);
|
||||
const [state, setState] = useState({
|
||||
strings: {},
|
||||
ringing: [],
|
||||
calls: [],
|
||||
cards: [],
|
||||
calling: false,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -17,6 +33,13 @@ export function useCalling() {
|
||||
setState((s) => ({ ...s, ...value }))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const calls = state.ringing
|
||||
.map(ring => ({ callId: ring.callId, card: state.cards.find(card => ring.cardId === card.cardId) }) )
|
||||
.filter(ring => (ring.card && !ring.card.blocked));
|
||||
updateState({ calls });
|
||||
}, [state.ringing, state.cards]);
|
||||
|
||||
useEffect(() => {
|
||||
const { strings } = display.state;
|
||||
updateState({ strings });
|
||||
@ -43,8 +66,63 @@ export function useCalling() {
|
||||
|
||||
const actions = {
|
||||
call: async (cardId: string) => {
|
||||
if (call.current) {
|
||||
throw new Error('active call in proegress');
|
||||
}
|
||||
|
||||
const contact = app.state.session.getContact();
|
||||
const link = await contact.callCard(cardId);
|
||||
const ice = link.getIce();
|
||||
|
||||
const peerConnection = new RTCPeerConnection({ iceServers: ice });
|
||||
peerConnection.addEventListener( 'connectionstatechange', event => {
|
||||
console.log("CONNECTION STATE", event);
|
||||
} );
|
||||
peerConnection.addEventListener( 'icecandidate', event => {
|
||||
console.log("ICE CANDIDATE", event);
|
||||
} );
|
||||
peerConnection.addEventListener( 'icecandidateerror', event => {
|
||||
console.log("ICE ERROR");
|
||||
} );
|
||||
peerConnection.addEventListener( 'iceconnectionstatechange', event => {
|
||||
console.log("ICE STATE CHANGE", event);
|
||||
} );
|
||||
peerConnection.addEventListener( 'negotiationneeded', async (ev) => {
|
||||
console.log("ICE NEGOTIATION NEEDEED");
|
||||
} );
|
||||
peerConnection.addEventListener( 'signalingstatechange', event => {
|
||||
console.log("ICE SIGNALING", event);
|
||||
} );
|
||||
peerConnection.addEventListener( 'track', event => {
|
||||
console.log("TRACK EVENT");
|
||||
} );
|
||||
|
||||
link.setStatusListener(async (status: string) => {
|
||||
if (status === 'connected') {
|
||||
try {
|
||||
const stream = await mediaDevices.getUserMedia({
|
||||
audio: true,
|
||||
video: {
|
||||
frameRate: 30,
|
||||
facingMode: 'user'
|
||||
}
|
||||
});
|
||||
for (const track of stream.getTracks()) {
|
||||
if (track.kind === 'audio') {
|
||||
peerConnection.addTrack(track, stream);
|
||||
}
|
||||
if (track.kind === 'video') {
|
||||
track.enabled = false;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
link.setMessageListener(async (message: any) => {
|
||||
// relay ice config
|
||||
});
|
||||
console.log(link);
|
||||
},
|
||||
}
|
||||
|
@ -397,7 +397,7 @@ function ContactsScreen({nav}) {
|
||||
overlayColor: 'rgba(8,8,8,.9)',
|
||||
}}>
|
||||
<ContactsDrawer.Screen name="settings">{({navigation}) => (
|
||||
<SettingsScreen nav={{...nav, textCard, callCard, contacts: navigation}} />
|
||||
<SettingsScreen nav={{...nav, contacts: navigation}} />
|
||||
)}</ContactsDrawer.Screen>
|
||||
</ContactsDrawer.Navigator>
|
||||
);
|
||||
|
@ -17,9 +17,9 @@ export interface Session {
|
||||
}
|
||||
|
||||
export interface Link {
|
||||
setStatusListener(ev: (status: string) => void): void;
|
||||
setStatusListener(ev: (status: string) => Promise<void>): void;
|
||||
clearStatusListener(): void;
|
||||
setMessageListener(ev: (message: any) => void): void;
|
||||
setMessageListener(ev: (message: any) => Promise<void>): void;
|
||||
clearMessageListener(): void;
|
||||
|
||||
getIce(): { urls: string; username: string; credential: string }[];
|
||||
|
@ -854,8 +854,7 @@ export class ContactModule implements Contact {
|
||||
}
|
||||
const { profile, detail } = entry.item;
|
||||
const link = new LinkModule(this.log);
|
||||
console.log("LINK CALL");
|
||||
await link.call(node, secure, token, cardId, profile.node, detail.token);
|
||||
await link.call(node, secure, token, cardId, profile.node, profile.guid, detail.token);
|
||||
return link;
|
||||
}
|
||||
|
||||
|
@ -255,14 +255,17 @@ export type Calling = {
|
||||
callerToken: string;
|
||||
calleeToken: string;
|
||||
keepAlive: number;
|
||||
ice: { urls: string; username: string; credential: string }[];
|
||||
ice: { urls: string[]; username: string; credential: string }[];
|
||||
}
|
||||
|
||||
export type Ringing = {
|
||||
cardId: string;
|
||||
callId: string;
|
||||
calleeToken: string;
|
||||
ice: { urls: string; username: string; credential: string }[];
|
||||
ice: { urls: string[]; username: string; credential: string }[];
|
||||
iceUrl: string;
|
||||
iceUsername: string;
|
||||
icePassword: string;
|
||||
};
|
||||
|
||||
export type Revision = {
|
||||
|
@ -36,6 +36,7 @@ export class LinkModule implements Link {
|
||||
this.status = 'idle';
|
||||
this.error = false;
|
||||
this.closed = false;
|
||||
this.connected = false;
|
||||
this.notifying = false;
|
||||
this.websocket = null;
|
||||
this.staleInterval = null;
|
||||
@ -49,18 +50,15 @@ export class LinkModule implements Link {
|
||||
return this.ice;
|
||||
}
|
||||
|
||||
public async call(node: string, secure: boolean, token: string, cardId: string, contactNode: string, contactToken: string) {
|
||||
console.log('add call');
|
||||
public async call(node: string, secure: boolean, token: string, cardId: string, contactNode: string, contactGuid: string, contactToken: string) {
|
||||
const call = await addCall(node, secure, token, cardId);
|
||||
this.cleanup = () => { removeCall(node, secure, token, call.id) };
|
||||
|
||||
console.log('add ring', contactNode);
|
||||
const { id, keepAlive, calleeToken, callerToken, ice } = call;
|
||||
const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(contactNode);
|
||||
const ring = { index: 0, callId: id, calleeToken, ice };
|
||||
await addContactRing(contactNode, !insecure, contactToken, ring);
|
||||
const ring = { index: 0, callId: id, calleeToken, ice: JSON.parse(JSON.stringify(ice))};
|
||||
await addContactRing(contactNode, !insecure, contactGuid, contactToken, ring);
|
||||
|
||||
console.log('go');
|
||||
this.aliveInterval = setInterval(async () => {
|
||||
try {
|
||||
await keepCall(node, secure, token, id);
|
||||
@ -72,25 +70,25 @@ console.log('go');
|
||||
this.ringInterval = setInterval(async () => {
|
||||
try {
|
||||
ring.index += 1;
|
||||
await addContactRing(contactNode, !insecure, contactToken, ring);
|
||||
await addContactRing(contactNode, !insecure, contactGuid, contactToken, ring);
|
||||
} catch (err) {
|
||||
this.log.error(err);
|
||||
}
|
||||
}, RING_INTERVAL);
|
||||
|
||||
this.ice = ice;
|
||||
connect(callerToken, node, secure);
|
||||
this.connect(callerToken, node, secure);
|
||||
}
|
||||
|
||||
public async join(server: string, access: string, ice: { urls: string; username: string; credential: string }[]) {
|
||||
this.ice = ice;
|
||||
const insecure = /^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|:\d+$|$)){4}$/.test(server);
|
||||
this.cleanup = () => { removeContactCall(server, !insecure, access); }
|
||||
connect(access, server, !insecure);
|
||||
this.connect(access, server, !insecure);
|
||||
}
|
||||
|
||||
private connect(token: string, node: string, secure: boolean) {
|
||||
this.websocket = this.setWebSocket(token, node, secure, ice);
|
||||
this.websocket = this.setWebSocket(token, node, secure);
|
||||
this.staleInterval = setInterval(() => {
|
||||
if (this.websocket?.readyState == 1) {
|
||||
this.websocket.ping?.(); // not defined in browser
|
||||
@ -124,7 +122,7 @@ console.log('go');
|
||||
}
|
||||
}
|
||||
|
||||
public setStatusListener(listener: (status: string) => void) {
|
||||
public setStatusListener(listener: (status: string) => Promise<void>) {
|
||||
this.statusListener = listener;
|
||||
this.notifyStatus(this.status);
|
||||
}
|
||||
@ -132,7 +130,7 @@ console.log('go');
|
||||
this.statusListener = null;
|
||||
}
|
||||
|
||||
public setMessageListener(ev: (message: any) => void) {
|
||||
public setMessageListener(listener: (message: any) => Promise<void>) {
|
||||
this.messageListener = listener;
|
||||
}
|
||||
public clearMessageListener() {
|
||||
@ -153,14 +151,14 @@ console.log('go');
|
||||
while(this.messages.length > 0 && !this.error && !this.closed) {
|
||||
const data = this.messages.shift();
|
||||
try {
|
||||
const message = JSON.parse(daata);
|
||||
const message = JSON.parse(data);
|
||||
if (message.status) {
|
||||
await this.notifyStatus(message.status);
|
||||
} else {
|
||||
await this.notifyMessage(message);
|
||||
}
|
||||
} catch (err) {
|
||||
this.log('failed to process signal message');
|
||||
this.log.error('failed to process signal message');
|
||||
this.notifyStatus('error');
|
||||
}
|
||||
}
|
||||
@ -169,18 +167,21 @@ console.log('go');
|
||||
}
|
||||
|
||||
private async notifyStatus(status: string) {
|
||||
if (status === 'connected' && this.ringInterval) {
|
||||
clearInterval(this.ringInterval);
|
||||
this.ringInterval = null;
|
||||
}
|
||||
|
||||
try {
|
||||
this.status = status;
|
||||
if (this.statusListener) {
|
||||
await this.statusListner(status);
|
||||
await this.statusListener(this.connected && status === 'connected' ? 'reconnected' : status);
|
||||
}
|
||||
} catch (err) {
|
||||
this.log('status notification failed');
|
||||
this.log.error('status notification failed');
|
||||
}
|
||||
|
||||
if (status === 'connected') {
|
||||
this.connected = true;
|
||||
if (this.ringInterval) {
|
||||
clearInterval(this.ringInterval);
|
||||
this.ringInterval = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
import { Ringing } from '../entities';
|
||||
|
||||
export async function addContactRing(server: string, secure: boolean, token: string, ringing: Ringing) {
|
||||
const endpoint = `http${secure ? 's' : '' }://${server}/talk/rings/?contact=${token}`;
|
||||
console.log(endpoint);
|
||||
|
||||
export async function addContactRing(server: string, secure: boolean, guid: string, token: string, ringing: Ringing) {
|
||||
const endpoint = `http${secure ? 's' : '' }://${server}/talk/rings?contact=${guid}.${token}`;
|
||||
const { status } = await fetchWithTimeout(endpoint, { method: 'POST', body: JSON.stringify(ringing) });
|
||||
checkResponse(status);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user