using configured ice server

This commit is contained in:
Roland Osborne 2023-04-13 15:11:33 -07:00
parent dd34a17268
commit f8dd0ea5e8
10 changed files with 94 additions and 40 deletions

View File

@ -181,8 +181,8 @@ export function useAppContext() {
card.actions.setRevision(cardRev); card.actions.setRevision(cardRev);
} }
if (activity.ring) { if (activity.ring) {
const { cardId, callId, calleeToken } = activity.ring; const { cardId, callId, calleeToken, iceUrl, iceUsername, icePassword } = activity.ring;
ring.actions.ring(cardId, callId, calleeToken); ring.actions.ring(cardId, callId, calleeToken, iceUrl, iceUsername, icePassword);
} }
} }
catch (err) { catch (err) {

View File

@ -49,14 +49,6 @@ export function useRingContext() {
const connected = useRef(false); const connected = useRef(false);
const candidates = useRef([]); const candidates = useRef([]);
const iceServers = [
{
urls: 'turn:44.238.207.157:3478?transport=udp',
username: 'user',
credential: 'pass'
},
];
const constraints = { const constraints = {
mandatory: { mandatory: {
OfferToReceiveAudio: true, OfferToReceiveAudio: true,
@ -168,9 +160,9 @@ export function useRingContext() {
processing.current = false; processing.current = false;
} }
const transmit = async (policy) => { const transmit = async (policy, ice) => {
pc.current = new RTCPeerConnection({ iceServers }); pc.current = new RTCPeerConnection({ iceServers: ice });
pc.current.addEventListener( 'connectionstatechange', event => { pc.current.addEventListener( 'connectionstatechange', event => {
console.log("CONNECTION STATE", event); console.log("CONNECTION STATE", event);
} ); } );
@ -234,7 +226,7 @@ export function useRingContext() {
} }
} }
const connect = async (policy, node, token, clearRing, clearAlive) => { const connect = async (policy, node, token, clearRing, clearAlive, ice) => {
// connect signal socket // connect signal socket
connected.current = false; connected.current = false;
@ -256,7 +248,7 @@ export function useRingContext() {
if (policy === 'polite') { if (policy === 'polite') {
connected.current = true; connected.current = true;
InCallManager.start({media: 'audio'}); InCallManager.start({media: 'audio'});
transmit('polite'); transmit('polite', ice);
polite(); polite();
} }
} }
@ -311,7 +303,7 @@ export function useRingContext() {
if (policy === 'impolite') { if (policy === 'impolite') {
connected.current = true; connected.current = true;
InCallManager.start({media: 'audio'}); InCallManager.start({media: 'audio'});
transmit('impolite'); transmit('impolite', ice);
impolite(); impolite();
} }
} }
@ -335,9 +327,9 @@ export function useRingContext() {
clearSession: () => { clearSession: () => {
access.current = null; access.current = null;
}, },
ring: (cardId, callId, calleeToken) => { ring: (cardId, callId, calleeToken, iceUrl, iceUsername, icePassword) => {
const key = `${cardId}:${callId}` const key = `${cardId}:${callId}`
const call = ringing.current.get(key) || { cardId, calleeToken, callId } const call = ringing.current.get(key) || { cardId, calleeToken, callId, iceUrl, iceUsername, icePassword }
call.expires = Date.now() + EXPIRE; call.expires = Date.now() + EXPIRE;
ringing.current.set(key, call); ringing.current.set(key, call);
updateState({ ringing: ringing.current }); updateState({ ringing: ringing.current });
@ -369,7 +361,7 @@ export function useRingContext() {
} }
} }
}, },
accept: async (cardId, callId, contactNode, contactToken, calleeToken) => { accept: async (cardId, callId, contactNode, contactToken, calleeToken, iceUrl, iceUsername, icePassword) => {
if (calling.current) { if (calling.current) {
throw new Error("active session"); throw new Error("active session");
} }
@ -382,7 +374,8 @@ export function useRingContext() {
updateState({ ringing: ringing.current, callStatus: "connecting", cardId }); updateState({ ringing: ringing.current, callStatus: "connecting", cardId });
calling.current = { callId, contactNode, contactToken, host: false }; calling.current = { callId, contactNode, contactToken, host: false };
await connect('impolite', contactNode, calleeToken, () => {}, () => {}); const ice = [{ urls: iceUrl, username: iceUsername, credential: icePassword }];
await connect('impolite', contactNode, calleeToken, () => {}, () => {}, ice);
} }
}, },
end: async () => { end: async () => {
@ -412,9 +405,9 @@ export function useRingContext() {
// create call // create call
const { server, token } = access.current; const { server, token } = access.current;
const call = await addCall(server, token, cardId); const call = await addCall(server, token, cardId);
const { id, keepAlive, callerToken, calleeToken } = call; const { id, keepAlive, callerToken, calleeToken, iceUrl, iceUsername, icePassword } = call;
try { try {
await addContactRing(contactNode, contactToken, { index, callId: id, calleeToken }); await addContactRing(contactNode, contactToken, { index, callId: id, calleeToken, iceUrl, iceUsername, icePassword });
} }
catch (err) { catch (err) {
console.log(err); console.log(err);
@ -436,7 +429,7 @@ export function useRingContext() {
} }
} }
else { else {
await addContactRing(contactNode, contactToken, { index, callId: id, calleeToken }); await addContactRing(contactNode, contactToken, { index, callId: id, calleeToken, iceUrl, iceUsername, icePassword });
index += 1; index += 1;
} }
} }
@ -447,7 +440,8 @@ export function useRingContext() {
updateState({ callStatus: "ringing", cardId }); updateState({ callStatus: "ringing", cardId });
calling.current = { callId: id, host: true }; calling.current = { callId: id, host: true };
await connect('polite', server, callerToken, () => clearInterval(ringInterval), () => clearInterval(aliveInterval)); const ice = [{ urls: iceUrl, username: iceUsername, credential: icePassword }];
await connect('polite', server, callerToken, () => clearInterval(ringInterval), () => clearInterval(aliveInterval), ice);
}, },
enableVideo: async () => { enableVideo: async () => {
if (!videoTrack.current) { if (!videoTrack.current) {

View File

@ -191,6 +191,8 @@ export function Dashboard(props) {
onValueChange={actions.setPushSupported} trackColor={styles.track}/> onValueChange={actions.setPushSupported} trackColor={styles.track}/>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.label}></View>
<TouchableOpacity style={styles.media} activeOpacity={1} <TouchableOpacity style={styles.media} activeOpacity={1}
onPress={() => actions.setEnableImage(!state.enableImage)}> onPress={() => actions.setEnableImage(!state.enableImage)}>
<Text style={styles.modalLabel}>Enable Image Queue: </Text> <Text style={styles.modalLabel}>Enable Image Queue: </Text>
@ -209,6 +211,21 @@ export function Dashboard(props) {
<Switch style={styles.switch} value={state.enableVideo} <Switch style={styles.switch} value={state.enableVideo}
onValueChange={actions.setEnableVideo} trackColor={styles.track}/> onValueChange={actions.setEnableVideo} trackColor={styles.track}/>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.label}></View>
<TouchableOpacity style={styles.ice} activeOpacity={1}
onPress={() => actions.setEnableIce(!state.enableIce)}>
<Text style={styles.modalLabel}>Enable WebRTC Calls: </Text>
<Switch style={styles.switch} value={state.enableIce}
onValueChange={actions.setEnableIce} trackColor={styles.track}/>
</TouchableOpacity>
<TextInput style={styles.input} value={state.iceUrl} onChangeText={actions.setIceUrl}
editable={state.enableIce} autoCorrect={false} autoCapitalize="none" placeholder="Relay URL" />
<TextInput style={styles.input} value={state.iceUsername} onChangeText={actions.setIceUsername}
editable={state.enableIce} autoCorrect={false} autoCapitalize="none" placeholder="Relay Username" />
<TextInput style={styles.input} value={state.icePassword} onChangeText={actions.setIcePassword}
editable={state.enableIce} autoCorrect={false} autoCapitalize="none" placeholder="Relay Password" />
</View> </View>
<View style={styles.modalControls}> <View style={styles.modalControls}>
<TouchableOpacity style={styles.cancel} onPress={actions.hideEditConfig}> <TouchableOpacity style={styles.cancel} onPress={actions.hideEditConfig}>

View File

@ -194,7 +194,7 @@ export const styles = StyleSheet.create({
padding: 4, padding: 4,
borderRadius: 4, borderRadius: 4,
marginBottom: 16, marginBottom: 16,
fontSize: 16, fontSize: 12,
color: Colors.text, color: Colors.text,
}, },
switch: { switch: {
@ -224,4 +224,20 @@ export const styles = StyleSheet.create({
borderRadius: 4, borderRadius: 4,
borderColor: Colors.divider, borderColor: Colors.divider,
}, },
label: {
borderTopWidth: 1,
borderColor: Colors.divider,
marginTop: 12,
},
labelText: {
fontSize: 14,
color: Colors.text,
},
ice: {
display: 'flex',
flexDirection: 'row',
marginTop: 8,
alignItems: 'center',
paddingBottom: 8,
}
}); });

View File

@ -28,6 +28,10 @@ export function useDashboard(config, server, token) {
enableAudio: true, enableAudio: true,
enableVideo: true, enableVideo: true,
createToken: null, createToken: null,
enableIce: false,
iceUrl: null,
iceUsername: null,
icePassword: null,
}); });
const navigate = useNavigate(); const navigate = useNavigate();
@ -55,8 +59,8 @@ export function useDashboard(config, server, token) {
}; };
useEffect(() => { useEffect(() => {
const { keyType, accountStorage, domain, enableImage, enableAudio, enableVideo, pushSupported } = config; const { keyType, accountStorage, domain, enableImage, enableAudio, enableVideo, pushSupported, enableIce, iceUrl, iceUsername, icePassword } = config;
updateState({ keyType, storage: accountStorage.toString(), domain, enableImage, enableAudio, enableVideo, pushSupported }); updateState({ keyType, storage: accountStorage.toString(), domain, enableImage, enableAudio, enableVideo, pushSupported, enableIce, iceUrl, iceUsername, icePassword });
}, [config]); }, [config]);
useEffect(() => { useEffect(() => {
@ -111,9 +115,21 @@ export function useDashboard(config, server, token) {
setKeyType: (keyType) => { setKeyType: (keyType) => {
updateState({ keyType }); updateState({ keyType });
}, },
setEnableIce: (enableIce) => {
updateState({ enableIce });
},
setIceUrl: (iceUrl) => {
updateState({ iceUrl });
},
setIceUsername: (iceUsername) => {
updateState({ iceUsername });
},
setIcePassword: (icePassword) => {
updateState({ icePassword });
},
saveConfig: async () => { saveConfig: async () => {
const { storage, domain, keyType, enableImage, enableAudio, enableVideo } = state; const { storage, domain, keyType, enableImage, enableAudio, enableVideo, enableIce, iceUrl, iceUsername, icePassword } = state;
const config = { accountStorage: Number(storage), domain, keyType, enableImage, enableAudio, enableVideo }; const config = { accountStorage: Number(storage), domain, keyType, enableImage, enableAudio, enableVideo, enableIce, iceUrl, iceUsername, icePassword };
await setNodeConfig(server, token, config); await setNodeConfig(server, token, config);
}, },
enableUser: async (accountId, enabled) => { enableUser: async (accountId, enabled) => {

View File

@ -329,7 +329,8 @@ export function Session() {
useEffect(() => { useEffect(() => {
let incoming = []; let incoming = [];
for (let i = 0; i < state.ringing.length; i++) { for (let i = 0; i < state.ringing.length; i++) {
const { img, name, handle, callId, cardId, contactNode, contactToken, calleeToken } = state.ringing[i]; const call = state.ringing[i];
const { img, cardId, callId, name, handle, contactNode } = call || {};
const label = name ? name : `${handle}@${contactNode}`; const label = name ? name : `${handle}@${contactNode}`;
const key = `${cardId}:${callId}` const key = `${cardId}:${callId}`
incoming.push( incoming.push(
@ -339,10 +340,10 @@ export function Session() {
<TouchableOpacity style={styles.ringIgnore} onPress={() => actions.ignore({ cardId, callId })}> <TouchableOpacity style={styles.ringIgnore} onPress={() => actions.ignore({ cardId, callId })}>
<MatIcons name={'eye-off-outline'} size={20} color={Colors.text} /> <MatIcons name={'eye-off-outline'} size={20} color={Colors.text} />
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.ringDecline} onPress={() => actions.decline({ cardId, contactNode, contactToken, callId })}> <TouchableOpacity style={styles.ringDecline} onPress={() => actions.decline(call)}>
<MatIcons name={'phone-hangup'} size={20} color={Colors.alert} /> <MatIcons name={'phone-hangup'} size={20} color={Colors.alert} />
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.ringAccept} onPress={() => actions.accept({ cardId, callId, contactNode, contactToken, calleeToken })}> <TouchableOpacity style={styles.ringAccept} onPress={() => actions.accept(call)}>
<MatIcons name={'phone'} size={20} color={Colors.primary} /> <MatIcons name={'phone'} size={20} color={Colors.primary} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>

View File

@ -66,7 +66,7 @@ export function CardsBody({ filter, sort, openContact, addChannel }) {
data={state.cards} data={state.cards}
initialNumToRender={25} initialNumToRender={25}
renderItem={({ item }) => <CardItem item={item} openContact={openContact} renderItem={({ item }) => <CardItem item={item} openContact={openContact}
call={() => call(item)} message={() => addChannel(item.cardId)} />} enableIce={state.enableIce} call={() => call(item)} message={() => addChannel(item.cardId)} />}
keyExtractor={item => item.cardId} keyExtractor={item => item.cardId}
/> />
)} )}

View File

@ -4,7 +4,7 @@ import { styles } from './CardItem.styled';
import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MatIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Colors from 'constants/Colors'; import Colors from 'constants/Colors';
export function CardItem({ item, openContact, call, message }) { export function CardItem({ item, openContact, enableIce, call, message }) {
const select = () => { const select = () => {
const { guid, name, handle, node, location, description, imageSet } = item; const { guid, name, handle, node, location, description, imageSet } = item;
@ -26,9 +26,11 @@ export function CardItem({ item, openContact, call, message }) {
<TouchableOpacity style={styles.option} onPress={message}> <TouchableOpacity style={styles.option} onPress={message}>
<MatIcons name={'message-outline'} size={20} color={Colors.primary} /> <MatIcons name={'message-outline'} size={20} color={Colors.primary} />
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.option} onPress={call}> { enableIce && (
<MatIcons name={'phone-outline'} size={20} color={Colors.primary} /> <TouchableOpacity style={styles.option} onPress={call}>
</TouchableOpacity> <MatIcons name={'phone-outline'} size={20} color={Colors.primary} />
</TouchableOpacity>
)}
</View> </View>
)} )}
{ item.status === 'connected' && item.offsync === 1 && ( { item.status === 'connected' && item.offsync === 1 && (

View File

@ -1,13 +1,16 @@
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 { RingContext } from 'context/RingContext';
import { AccountContext } from 'context/AccountContext';
export function useCards(filter, sort) { export function useCards(filter, sort) {
const [state, setState] = useState({ const [state, setState] = useState({
cards: [], cards: [],
enableIce: false,
}); });
const account = useContext(AccountContext);
const card = useContext(CardContext); const card = useContext(CardContext);
const ring = useContext(RingContext); const ring = useContext(RingContext);
@ -15,6 +18,11 @@ export function useCards(filter, sort) {
setState((s) => ({ ...s, ...value })); setState((s) => ({ ...s, ...value }));
} }
useEffect(() => {
const { enableIce } = account.state.status || {};
updateState({ enableIce });
}, [account.state]);
const setCardItem = (item) => { const setCardItem = (item) => {
const { profile, detail, cardId } = item.card || { profile: {}, detail: {} } const { profile, detail, cardId } = item.card || { profile: {}, detail: {} }
const { name, handle, node, guid, location, description, imageSet } = profile; const { name, handle, node, guid, location, description, imageSet } = profile;

View File

@ -44,14 +44,14 @@ export function useSession() {
const expired = Date.now(); const expired = Date.now();
ring.state.ringing.forEach(call => { ring.state.ringing.forEach(call => {
if (call.expires > expired && !call.status) { if (call.expires > expired && !call.status) {
const { callId, cardId, calleeToken } = call; const { callId, cardId, calleeToken, iceUrl, iceUsername, icePassword } = call;
const contact = card.state.cards.get(cardId); const contact = card.state.cards.get(cardId);
if (contact) { if (contact) {
const { imageSet, name, handle, node, guid } = contact.card?.profile || {}; const { imageSet, name, handle, node, guid } = contact.card?.profile || {};
const { token } = contact.card?.detail || {}; const { token } = contact.card?.detail || {};
const contactToken = `${guid}.${token}`; const contactToken = `${guid}.${token}`;
const img = imageSet ? card.actions.getCardImageUrl(cardId) : null; const img = imageSet ? card.actions.getCardImageUrl(cardId) : null;
ringing.push({ cardId, img, name, handle, contactNode: node, callId, contactToken, calleeToken }); ringing.push({ cardId, img, name, handle, contactNode: node, callId, contactToken, calleeToken, iceUrl, iceUsername, icePassword });
} }
} }
}); });
@ -106,8 +106,8 @@ export function useSession() {
await ring.actions.decline(cardId, contactNode, contactToken, callId); await ring.actions.decline(cardId, contactNode, contactToken, callId);
}, },
accept: async (call) => { accept: async (call) => {
const { cardId, callId, contactNode, contactToken, calleeToken } = call; const { cardId, callId, contactNode, contactToken, calleeToken, iceUrl, iceUsername, icePassword } = call;
await ring.actions.accept(cardId, callId, contactNode, contactToken, calleeToken); await ring.actions.accept(cardId, callId, contactNode, contactToken, calleeToken, iceUrl, iceUsername, icePassword);
}, },
end: async () => { end: async () => {
await ring.actions.end(); await ring.actions.end();