mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
using configured ice server
This commit is contained in:
parent
dd34a17268
commit
f8dd0ea5e8
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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}>
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
@ -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) => {
|
||||||
|
@ -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>
|
||||||
|
@ -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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -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>
|
||||||
|
{ enableIce && (
|
||||||
<TouchableOpacity style={styles.option} onPress={call}>
|
<TouchableOpacity style={styles.option} onPress={call}>
|
||||||
<MatIcons name={'phone-outline'} size={20} color={Colors.primary} />
|
<MatIcons name={'phone-outline'} size={20} color={Colors.primary} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{ item.status === 'connected' && item.offsync === 1 && (
|
{ item.status === 'connected' && item.offsync === 1 && (
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user