Demo App
A demo application consuming the ReactNative Huddle Client SDK
GitHub Link:
https://github.com/Huddle-01/react-native-sdk
Codes
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import LoginScreen from './screens/LoginScreen';
import RoomScreen from './screens/RoomScreen';
import WaitingScreen from './screens/WaitingScreen';
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{headerShown: false}}
/>
<Stack.Screen name="Room" component={RoomScreen} />
<Stack.Screen name="Waiting" component={WaitingScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
import React, {useState} from 'react';
import {View, StyleSheet} from 'react-native';
import {TextInput} from 'react-native-paper';
import AsyncStorage from '@react-native-community/async-storage';
import {Button} from 'react-native-paper';
export default function LoginScreen(props) {
const [roomId, setRoomId] = useState('');
const [userName, setUserName] = useState('');
const [loading, setLoading] = useState(false);
const onLogin = async () => {
setLoading(true);
try {
await AsyncStorage.setItem('roomId', roomId);
await AsyncStorage.setItem('userName', userName);
setLoading(false);
props.navigation.push('Room', {roomId, userName});
} catch (err) {
console.log('Error', err);
setLoading(false);
}
};
return (
<View style={styles.root}>
<View style={styles.content}>
<TextInput
label="Room ID"
onChangeText={text => setRoomId(text)}
mode="outlined"
style={styles.input}
/>
<TextInput
label="Your Name"
onChangeText={text => setUserName(text)}
mode="outlined"
style={styles.input}
/>
<Button
mode="contained"
onPress={onLogin}
loading={loading}
style={styles.btn}
contentStyle={styles.btnContent}
disabled={userName.length === 0}>
Join
</Button>
</View>
</View>
);
}
const styles = StyleSheet.create({
root: {
backgroundColor: '#fff',
flex: 1,
justifyContent: 'center',
},
content: {
paddingHorizontal: 20,
justifyContent: 'center',
},
heading: {
fontSize: 18,
marginBottom: 10,
fontWeight: '600',
},
input: {
height: 60,
marginBottom: 10,
},
btn: {
height: 60,
alignItems: 'stretch',
justifyContent: 'center',
fontSize: 18,
},
btnContent: {
alignItems: 'center',
justifyContent: 'center',
height: 60,
},
});
import React, {useEffect, useState} from 'react';
import { useNavigation } from '@react-navigation/core';
import {View, StyleSheet, Button, Alert} from 'react-native';
import {Text, IconButton} from 'react-native-paper';
import InCallManager from 'react-native-incall-manager';
import HuddleClient, { emitter } from 'react-native-huddle-client';
import {
RTCView,
MediaStream,
} from 'react-native-webrtc';
export default function RoomScreen({...props}) {
const navigation = useNavigation();
const params = props.route.params;
[huddle, setHuddle] = useState(null);
[remoteStream, setRemoteStream] = useState({toURL: () => null});
[localStream, setLocalStream] = useState({toURL: () => null});
[peer, setPeer] = useState(null);
[isMute, setMute] = useState(true);
[isCameraOn, setCameraOn] = useState(false);
[isOpenWaitingRoom, setOpenWaitingRoom] = useState(false);
[lobbyPeers, setLobbyPeers] = useState([]);
React.useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<Button onPress={() => {
navigation.push('Waiting', {huddle, lobbyPeers});
}} title="Waiting Room" />
),
})
}, [navigation]);
useEffect(() => {
let roomId = params.roomId;
const userName = params.userName;
const userId = generateId(8);
if (!roomId) {
roomId = generateId(8);
}
const config = {
apiKey: 'abcd',
hostname: 'alpha.huddle01.com:4443',
roomId: roomId,
peerId: userId,
displayName: userName,
isBot: false,
};
const h = new HuddleClient(config);
setHuddle(h);
navigation.setOptions({ title: roomId });
InCallManager.start({media: 'audio'});
InCallManager.setForceSpeakerphoneOn(true);
InCallManager.setSpeakerphoneOn(true);
return async () => {
await leaveRoom();
}
}, []);
useEffect(() => {
joinRoom();
}, [huddle]);
generateId = (length) => {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
};
const enableDataProducers = async () => {
await huddle.enableChatDataProducer();
await huddle.enableBotDataProducer();
await huddle.enableMiscDataProducer();
await huddle.enableRaiseHandDataProducer();
await huddle.enableReactionsDataProducer();
await huddle.enableMiroDataProducer();
};
const setupEventListeners = async () => {
emitter.on("roomState", async (state) => {
console.log('emitter::roomState: ', state);
if (state === 'connected') {
await enableDataProducers();
await huddle.enableMic();
await disableMic();
}
});
emitter.on("error", (error) => {
console.log('emitter::error: ', JSON.stringify(error));
});
emitter.on("addPeer", (peer) => {
setPeer(peer);
console.log('emitter::addPeer: ', JSON.stringify(peer));
});
emitter.on("addProducer", (producer) => {
console.log('emitter::addProducer: ', JSON.stringify(producer));
if (producer.type === 'camera') {
const track = producer.track;
const stream = new MediaStream();
stream.addTrack(track);
setLocalStream(stream);
}
});
emitter.on("addConsumer", (consumer) => {
console.log('emitter::addConsumer: ', JSON.stringify(consumer));
const track = consumer.track;
const stream = new MediaStream();
stream.addTrack(track);
setRemoteStream(stream);
});
emitter.on("removePeer", (peer) => {
setPeer(null);
console.log('emitter::removePeer: ', JSON.stringify(peer));
});
emitter.on("removeProducer", (producer) => {
setLocalStream({toURL: () => null});
console.log('emitter::removeProducer: ', JSON.stringify(producer));
});
emitter.on("removeConsumer", (consumer) => {
setRemoteStream({toURL: () => null});
console.log('emitter::removeConsumer: ', JSON.stringify(consumer));
});
emitter.on("newLobbyPeer", (peers) => {
const lobbyPeers = peers.peers;
setLobbyPeers(lobbyPeers);
});
emitter.on("updatedPeersArray", (peers) => {
const lobbyPeers = peers.peers;
setLobbyPeers(lobbyPeers);
})
};
const joinRoom = async () => {
if (!huddle) return;
try {
setupEventListeners();
await huddle.join();
} catch (error) {
console.log(error);
}
};
const leaveRoom = async () => {
if (!huddle) return;
try {
await huddle.close();
// setRoomState(false);
} catch (error) {
console.log(error);
}
};
const enableCamera = async () => {
if (!huddle) return;
try {
await huddle.enableCamera();
} catch (error) {
console.log(error);
}
};
const disableCamera = async () => {
if (!huddle) return;
try {
await huddle.disableCamera();
} catch (error) {
console.log(error);
}
};
const enableMic = async () => {
if (!huddle) return;
try {
// await huddle.enableMic();
await huddle.unmuteMic();
} catch (error) {
console.log(error);
}
};
const disableMic = async () => {
if (!huddle) return;
try {
// await huddle.disableMic();
await huddle.muteMic();
} catch (error) {
console.log(error);
}
};
const sendMsg = async () => {
if (!huddle) return;
try {
await huddle.sendReaction("😂");
} catch (error) {
console.log(error);
}
};
const changeCamera = async () => {
if (!huddle) return;
try {
await huddle.changeCamera();
} catch (error) {
console.log(error);
}
};
const changeCameraResolution = async () => {
if (!huddle) return;
try {
await huddle.changeCameraResolution();
} catch (error) {
console.log(error);
}
};
const onToggleCamera = () => {
if (isCameraOn) {
disableCamera();
setCameraOn(false);
} else {
enableCamera();
setCameraOn(true);
}
};
const onToggleAudio = () => {
if (isMute) {
enableMic();
setMute(false);
} else {
disableMic();
setMute(true);
}
};
const onSwitchCamera = () => {
changeCamera();
};
const onAction = async () => {
if (!huddle) return;
try {
await huddle.sendReaction("😂");
} catch (error) {
console.log(error);
}
}
const onRaiseHand = async () => {
if (!huddle) return;
try {
await huddle.raiseHand("true");
} catch (error) {
console.log(error);
}
}
useEffect(() => {
}, []);
return (
<>
<View style={styles.root}>
<View style={[styles.videoContainer]}>
<Text>My Video</Text>
<RTCView streamURL={localStream.toURL()} style={styles.localVideo} />
<View style={{position: 'absolute', top: 20, right: 5}}>
<IconButton
style={styles.controlIcon}
icon={ isCameraOn ? 'camera' : 'camera-off' }
color='#777'
size={25}
onPress={onToggleCamera} />
<IconButton
style={styles.controlIcon}
icon={isMute ? 'microphone-off' : 'microphone'}
color='#777'
size={25}
onPress={onToggleAudio} />
<IconButton
style={styles.controlIcon}
icon='camera-party-mode'
color='#777'
size={25}
onPress={onSwitchCamera} />
<IconButton
style={styles.controlIcon}
icon='emoticon'
color='#777'
size={25}
onPress={onAction} />
<IconButton
style={styles.controlIcon}
icon='hand'
color='#777'
size={25}
onPress={onRaiseHand} />
</View>
</View>
<View style={[styles.videoContainer]}>
<Text>{(peer ?? {}).displayName ?? 'Friends Video'}</Text>
<RTCView
streamURL={remoteStream.toURL()}
style={styles.remoteVideo}
/>
</View>
</View>
</>
);
}
const styles = StyleSheet.create({
root: {
backgroundColor: '#fff',
flex: 1,
paddingHorizontal: 20,
},
videoContainer: {
flex: 1,
overflow: 'hidden',
borderRadius: 6,
marginVertical: 10,
},
localVideo: {
backgroundColor: '#f2f2f2',
flex: 1,
alignItems: 'flex-end',
},
remoteVideo: {
backgroundColor: '#f2f2f2',
flex: 1,
},
controlIcon: {
backgroundColor: '#d0d0d0',
marginHorizontal: 10,
marginTop: 10,
}
});
import React, {useEffect} from 'react';
import { useNavigation } from '@react-navigation/core';
import {View, StyleSheet, FlatList, Alert, TouchableOpacity} from 'react-native';
import {Text} from 'react-native-paper';
export default function WaitingScreen({...props}) {
const navigation = useNavigation();
const params = props.route.params;
onDeny = (peerId) => {
const huddleClient = params.huddle;
huddleClient.disallowLobbyPeerFromJoiningRoom(peerId);
}
onAllow = (peerId) => {
const huddleClient = params.huddle;
huddleClient.allowLobbyPeerToJoinRoom(peerId);
}
renderItem =({item, index}) => {
return (
<View style={{flexDirection: 'row', alignItems: 'center', marginVertical: 8}}>
<Text style={{flex: 1, fontSize: 16, fontWeight: '500', marginVertical: 8}}>{item.displayName}</Text>
<TouchableOpacity style={{...styles.button, borderColor: 'red'}} onPress={() => onDeny(item.peerId)}>
<Text style={{color: 'red'}}>Deny</Text>
</TouchableOpacity>
<TouchableOpacity style={{...styles.button, borderColor: 'green'}} onPress={() => onAllow(item.peerId)}>
<Text style={{color: 'green'}}>Allow</Text>
</TouchableOpacity>
</View>
)
}
return (
<View style={styles.root}>
<FlatList
data={params.lobbyPeers}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
const styles = StyleSheet.create({
root: {
backgroundColor: '#fff',
flex: 1,
paddingHorizontal: 20,
},
button: {
height: 30,
paddingVertical: 5,
paddingHorizontal: 10,
margin: 5,
borderRadius: 5,
borderWidth: 1,
justifyContent: 'center',
alignItems: 'center'
}
});
For any help, reach out to us on Slack. We are available 24*7 at: Huddle01 Community.
Last updated
Was this helpful?