Demo App
A demo application consuming the ReactNative Huddle Client SDK

GitHub Link:

Codes

App.js
LoginScreen.js
RoomScreen.js
WaitingScreen.js
1
import React from 'react';
2
import {NavigationContainer} from '@react-navigation/native';
3
import {createStackNavigator} from '@react-navigation/stack';
4
import LoginScreen from './screens/LoginScreen';
5
import RoomScreen from './screens/RoomScreen';
6
import WaitingScreen from './screens/WaitingScreen';
7
8
const Stack = createStackNavigator();
9
10
const App = () => {
11
return (
12
<NavigationContainer>
13
<Stack.Navigator>
14
<Stack.Screen
15
name="Login"
16
component={LoginScreen}
17
options={{headerShown: false}}
18
/>
19
<Stack.Screen name="Room" component={RoomScreen} />
20
<Stack.Screen name="Waiting" component={WaitingScreen} />
21
</Stack.Navigator>
22
</NavigationContainer>
23
);
24
};
25
26
export default App;
Copied!
1
import React, {useState} from 'react';
2
import {View, StyleSheet} from 'react-native';
3
import {TextInput} from 'react-native-paper';
4
import AsyncStorage from '@react-native-community/async-storage';
5
import {Button} from 'react-native-paper';
6
7
export default function LoginScreen(props) {
8
const [roomId, setRoomId] = useState('');
9
const [userName, setUserName] = useState('');
10
const [loading, setLoading] = useState(false);
11
12
const onLogin = async () => {
13
setLoading(true);
14
try {
15
await AsyncStorage.setItem('roomId', roomId);
16
await AsyncStorage.setItem('userName', userName);
17
setLoading(false);
18
props.navigation.push('Room', {roomId, userName});
19
} catch (err) {
20
console.log('Error', err);
21
setLoading(false);
22
}
23
};
24
25
return (
26
<View style={styles.root}>
27
<View style={styles.content}>
28
<TextInput
29
label="Room ID"
30
onChangeText={text => setRoomId(text)}
31
mode="outlined"
32
style={styles.input}
33
/>
34
<TextInput
35
label="Your Name"
36
onChangeText={text => setUserName(text)}
37
mode="outlined"
38
style={styles.input}
39
/>
40
41
<Button
42
mode="contained"
43
onPress={onLogin}
44
loading={loading}
45
style={styles.btn}
46
contentStyle={styles.btnContent}
47
disabled={userName.length === 0}>
48
Join
49
</Button>
50
</View>
51
</View>
52
);
53
}
54
55
const styles = StyleSheet.create({
56
root: {
57
backgroundColor: '#fff',
58
flex: 1,
59
justifyContent: 'center',
60
},
61
content: {
62
paddingHorizontal: 20,
63
justifyContent: 'center',
64
},
65
heading: {
66
fontSize: 18,
67
marginBottom: 10,
68
fontWeight: '600',
69
},
70
input: {
71
height: 60,
72
marginBottom: 10,
73
},
74
btn: {
75
height: 60,
76
alignItems: 'stretch',
77
justifyContent: 'center',
78
fontSize: 18,
79
},
80
btnContent: {
81
alignItems: 'center',
82
justifyContent: 'center',
83
height: 60,
84
},
85
});
Copied!
1
import React, {useEffect, useState} from 'react';
2
import { useNavigation } from '@react-navigation/core';
3
import {View, StyleSheet, Button, Alert} from 'react-native';
4
import {Text, IconButton} from 'react-native-paper';
5
import InCallManager from 'react-native-incall-manager';
6
import HuddleClient, { emitter } from 'react-native-huddle-client';
7
import {
8
RTCView,
9
MediaStream,
10
} from 'react-native-webrtc';
11
12
export default function RoomScreen({...props}) {
13
const navigation = useNavigation();
14
const params = props.route.params;
15
16
[huddle, setHuddle] = useState(null);
17
[remoteStream, setRemoteStream] = useState({toURL: () => null});
18
[localStream, setLocalStream] = useState({toURL: () => null});
19
[peer, setPeer] = useState(null);
20
[isMute, setMute] = useState(true);
21
[isCameraOn, setCameraOn] = useState(false);
22
[isOpenWaitingRoom, setOpenWaitingRoom] = useState(false);
23
[lobbyPeers, setLobbyPeers] = useState([]);
24
25
React.useLayoutEffect(() => {
26
navigation.setOptions({
27
headerRight: () => (
28
<Button onPress={() => {
29
navigation.push('Waiting', {huddle, lobbyPeers});
30
}} title="Waiting Room" />
31
),
32
})
33
}, [navigation]);
34
35
useEffect(() => {
36
let roomId = params.roomId;
37
const userName = params.userName;
38
const userId = generateId(8);
39
40
if (!roomId) {
41
roomId = generateId(8);
42
}
43
44
const config = {
45
apiKey: 'abcd',
46
hostname: 'alpha.huddle01.com:4443',
47
roomId: roomId,
48
peerId: userId,
49
displayName: userName,
50
isBot: false,
51
};
52
53
const h = new HuddleClient(config);
54
setHuddle(h);
55
56
navigation.setOptions({ title: roomId });
57
58
InCallManager.start({media: 'audio'});
59
InCallManager.setForceSpeakerphoneOn(true);
60
InCallManager.setSpeakerphoneOn(true);
61
62
return async () => {
63
await leaveRoom();
64
}
65
}, []);
66
67
useEffect(() => {
68
joinRoom();
69
}, [huddle]);
70
71
generateId = (length) => {
72
let result = '';
73
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
74
const charactersLength = characters.length;
75
for ( var i = 0; i < length; i++ ) {
76
result += characters.charAt(Math.floor(Math.random() * charactersLength));
77
}
78
return result;
79
};
80
81
const enableDataProducers = async () => {
82
await huddle.enableChatDataProducer();
83
await huddle.enableBotDataProducer();
84
await huddle.enableMiscDataProducer();
85
await huddle.enableRaiseHandDataProducer();
86
await huddle.enableReactionsDataProducer();
87
await huddle.enableMiroDataProducer();
88
};
89
90
const setupEventListeners = async () => {
91
emitter.on("roomState", async (state) => {
92
console.log('emitter::roomState: ', state);
93
if (state === 'connected') {
94
await enableDataProducers();
95
await huddle.enableMic();
96
await disableMic();
97
}
98
});
99
100
emitter.on("error", (error) => {
101
console.log('emitter::error: ', JSON.stringify(error));
102
});
103
104
emitter.on("addPeer", (peer) => {
105
setPeer(peer);
106
console.log('emitter::addPeer: ', JSON.stringify(peer));
107
});
108
109
emitter.on("addProducer", (producer) => {
110
console.log('emitter::addProducer: ', JSON.stringify(producer));
111
112
if (producer.type === 'camera') {
113
const track = producer.track;
114
const stream = new MediaStream();
115
stream.addTrack(track);
116
setLocalStream(stream);
117
}
118
});
119
120
emitter.on("addConsumer", (consumer) => {
121
console.log('emitter::addConsumer: ', JSON.stringify(consumer));
122
const track = consumer.track;
123
const stream = new MediaStream();
124
stream.addTrack(track);
125
setRemoteStream(stream);
126
});
127
128
emitter.on("removePeer", (peer) => {
129
setPeer(null);
130
console.log('emitter::removePeer: ', JSON.stringify(peer));
131
});
132
133
emitter.on("removeProducer", (producer) => {
134
setLocalStream({toURL: () => null});
135
console.log('emitter::removeProducer: ', JSON.stringify(producer));
136
});
137
138
emitter.on("removeConsumer", (consumer) => {
139
setRemoteStream({toURL: () => null});
140
console.log('emitter::removeConsumer: ', JSON.stringify(consumer));
141
});
142
143
emitter.on("newLobbyPeer", (peers) => {
144
const lobbyPeers = peers.peers;
145
setLobbyPeers(lobbyPeers);
146
});
147
148
emitter.on("updatedPeersArray", (peers) => {
149
const lobbyPeers = peers.peers;
150
setLobbyPeers(lobbyPeers);
151
})
152
};
153
154
const joinRoom = async () => {
155
if (!huddle) return;
156
try {
157
setupEventListeners();
158
await huddle.join();
159
} catch (error) {
160
console.log(error);
161
}
162
};
163
164
const leaveRoom = async () => {
165
if (!huddle) return;
166
try {
167
await huddle.close();
168
// setRoomState(false);
169
} catch (error) {
170
console.log(error);
171
}
172
};
173
174
const enableCamera = async () => {
175
if (!huddle) return;
176
try {
177
await huddle.enableCamera();
178
} catch (error) {
179
console.log(error);
180
}
181
};
182
183
const disableCamera = async () => {
184
if (!huddle) return;
185
try {
186
await huddle.disableCamera();
187
} catch (error) {
188
console.log(error);
189
}
190
};
191
192
const enableMic = async () => {
193
if (!huddle) return;
194
try {
195
// await huddle.enableMic();
196
await huddle.unmuteMic();
197
} catch (error) {
198
console.log(error);
199
}
200
};
201
202
const disableMic = async () => {
203
if (!huddle) return;
204
try {
205
// await huddle.disableMic();
206
await huddle.muteMic();
207
} catch (error) {
208
console.log(error);
209
}
210
};
211
212
const sendMsg = async () => {
213
if (!huddle) return;
214
try {
215
await huddle.sendReaction("😂");
216
} catch (error) {
217
console.log(error);
218
}
219
};
220
221
const changeCamera = async () => {
222
if (!huddle) return;
223
try {
224
await huddle.changeCamera();
225
} catch (error) {
226
console.log(error);
227
}
228
};
229
230
const changeCameraResolution = async () => {
231
if (!huddle) return;
232
try {
233
await huddle.changeCameraResolution();
234
} catch (error) {
235
console.log(error);
236
}
237
};
238
239
const onToggleCamera = () => {
240
if (isCameraOn) {
241
disableCamera();
242
setCameraOn(false);
243
} else {
244
enableCamera();
245
setCameraOn(true);
246
}
247
};
248
249
const onToggleAudio = () => {
250
if (isMute) {
251
enableMic();
252
setMute(false);
253
} else {
254
disableMic();
255
setMute(true);
256
}
257
};
258
259
const onSwitchCamera = () => {
260
changeCamera();
261
};
262
263
const onAction = async () => {
264
if (!huddle) return;
265
try {
266
await huddle.sendReaction("😂");
267
} catch (error) {
268
console.log(error);
269
}
270
}
271
272
const onRaiseHand = async () => {
273
if (!huddle) return;
274
try {
275
await huddle.raiseHand("true");
276
} catch (error) {
277
console.log(error);
278
}
279
}
280
281
useEffect(() => {
282
283
}, []);
284
285
return (
286
<>
287
<View style={styles.root}>
288
<View style={[styles.videoContainer]}>
289
<Text>My Video</Text>
290
<RTCView streamURL={localStream.toURL()} style={styles.localVideo} />
291
<View style={{position: 'absolute', top: 20, right: 5}}>
292
<IconButton
293
style={styles.controlIcon}
294
icon={ isCameraOn ? 'camera' : 'camera-off' }
295
color='#777'
296
size={25}
297
onPress={onToggleCamera} />
298
<IconButton
299
style={styles.controlIcon}
300
icon={isMute ? 'microphone-off' : 'microphone'}
301
color='#777'
302
size={25}
303
onPress={onToggleAudio} />
304
<IconButton
305
style={styles.controlIcon}
306
icon='camera-party-mode'
307
color='#777'
308
size={25}
309
onPress={onSwitchCamera} />
310
<IconButton
311
style={styles.controlIcon}
312
icon='emoticon'
313
color='#777'
314
size={25}
315
onPress={onAction} />
316
<IconButton
317
style={styles.controlIcon}
318
icon='hand'
319
color='#777'
320
size={25}
321
onPress={onRaiseHand} />
322
</View>
323
</View>
324
<View style={[styles.videoContainer]}>
325
<Text>{(peer ?? {}).displayName ?? 'Friends Video'}</Text>
326
<RTCView
327
streamURL={remoteStream.toURL()}
328
style={styles.remoteVideo}
329
/>
330
</View>
331
</View>
332
</>
333
);
334
}
335
336
const styles = StyleSheet.create({
337
root: {
338
backgroundColor: '#fff',
339
flex: 1,
340
paddingHorizontal: 20,
341
},
342
videoContainer: {
343
flex: 1,
344
overflow: 'hidden',
345
borderRadius: 6,
346
marginVertical: 10,
347
},
348
localVideo: {
349
backgroundColor: '#f2f2f2',
350
flex: 1,
351
alignItems: 'flex-end',
352
},
353
remoteVideo: {
354
backgroundColor: '#f2f2f2',
355
flex: 1,
356
},
357
controlIcon: {
358
backgroundColor: '#d0d0d0',
359
marginHorizontal: 10,
360
marginTop: 10,
361
}
362
});
Copied!
1
import React, {useEffect} from 'react';
2
import { useNavigation } from '@react-navigation/core';
3
import {View, StyleSheet, FlatList, Alert, TouchableOpacity} from 'react-native';
4
import {Text} from 'react-native-paper';
5
6
export default function WaitingScreen({...props}) {
7
const navigation = useNavigation();
8
const params = props.route.params;
9
10
onDeny = (peerId) => {
11
const huddleClient = params.huddle;
12
huddleClient.disallowLobbyPeerFromJoiningRoom(peerId);
13
}
14
15
onAllow = (peerId) => {
16
const huddleClient = params.huddle;
17
huddleClient.allowLobbyPeerToJoinRoom(peerId);
18
}
19
20
renderItem =({item, index}) => {
21
return (
22
<View style={{flexDirection: 'row', alignItems: 'center', marginVertical: 8}}>
23
<Text style={{flex: 1, fontSize: 16, fontWeight: '500', marginVertical: 8}}>{item.displayName}</Text>
24
<TouchableOpacity style={{...styles.button, borderColor: 'red'}} onPress={() => onDeny(item.peerId)}>
25
<Text style={{color: 'red'}}>Deny</Text>
26
</TouchableOpacity>
27
<TouchableOpacity style={{...styles.button, borderColor: 'green'}} onPress={() => onAllow(item.peerId)}>
28
<Text style={{color: 'green'}}>Allow</Text>
29
</TouchableOpacity>
30
</View>
31
)
32
}
33
34
return (
35
<View style={styles.root}>
36
<FlatList
37
data={params.lobbyPeers}
38
renderItem={renderItem}
39
keyExtractor={(item, index) => index.toString()}
40
/>
41
</View>
42
);
43
}
44
45
const styles = StyleSheet.create({
46
root: {
47
backgroundColor: '#fff',
48
flex: 1,
49
paddingHorizontal: 20,
50
},
51
button: {
52
height: 30,
53
paddingVertical: 5,
54
paddingHorizontal: 10,
55
margin: 5,
56
borderRadius: 5,
57
borderWidth: 1,
58
justifyContent: 'center',
59
alignItems: 'center'
60
}
61
});
Copied!
For any help, reach out to us on Slack. We are available 24*7 at: Huddle01 Community.
Last modified 1mo ago
Copy link