Demo App
A demo application consuming the Huddle01 Client SDK
App.tsx
Room.tsx
lib/utils/helpers.tsx
components/PeerViewport.tsx
1
import {
2
BrowserRouter as Router,
3
Switch,
4
Route,
5
Redirect,
6
} from "react-router-dom";
7
import Room from "./containers/Room";
8
9
function App() {
10
return (
11
<Router>
12
<Switch>
13
<Route path="/room" component={Room} />
14
<Route path="/" component={() => <Redirect to="/room" />} />
15
</Switch>
16
</Router>
17
);
18
}
19
20
export default App;
21
Copied!
1
//client sdk import
2
import HuddleClient, { emitter, HuddleTypes } from "huddle01-client";
3
4
//react imports
5
import { useEffect, useState, useRef } from "react";
6
import { useHistory } from "react-router-dom";
7
8
//helper imports
9
import { getTrack } from "../lib/utils/helpers";
10
import { PeerVideo, PeerAudio, PeerScreen } from "../components/PeerViewport";
11
12
// interfaces
13
import { IConsumerStreams } from "../interface/interfaces";
14
15
function Room() {
16
const history = useHistory();
17
//to allow for recordings
18
const isBot = localStorage.getItem("bot_password") === "huddle01";
19
//initialising states
20
const [huddle, setHuddle] = useState<HuddleClient | null>(null);
21
const [roomState, setRoomState] = useState<string>("");
22
const [micState, setMicState] = useState<boolean>(false);
23
const [webcamState, setWebcamState] = useState<boolean>(false);
24
const [screenshareState, setScreenshareState] = useState<boolean>(false);
25
26
const [peers, setPeers] = useState<HuddleTypes.IPeer[]>([]);
27
const [consumerStreams, setConsumerStreams] = useState<IConsumerStreams>({
28
video: [],
29
audio: [],
30
screen: [],
31
});
32
33
const meVideoElem = useRef<any>(null);
34
const meScreenElem = useRef<any>(null);
35
const joinRoomBtn = useRef<any>(null);
36
37
const config: HuddleTypes.HuddleClientConfig = {
38
apiKey: "API-KEY-HERE",
39
roomId: "C132",
40
peerId: "Rick" + Math.floor(Math.random() * 4000),
41
displayName: "Rick Sanchez",
42
window,
43
isBot,
44
};
45
46
//initialize the app
47
useEffect(() => {
48
history.push(`?roomId=${config.roomId}`);
49
50
const myHuddleClient: HuddleClient = new HuddleClient(config);
51
setHuddle(myHuddleClient);
52
}, []);
53
54
//recording config
55
useEffect(() => {
56
//joinRoomBtn here can be whatever button/function used that calls `huddle.join()`
57
huddle && isBot && joinRoomBtn.current.click();
58
}, [huddle, isBot]);
59
60
const setupEventListeners = async () => {
61
emitter.on("roomState", (state: string) => {
62
switch (state) {
63
case "connected":
64
//do whatever
65
break;
66
case "failed":
67
//do whatever
68
break;
69
case "disconnected":
70
//do whatever
71
break;
72
default:
73
setRoomState(state);
74
break;
75
}
76
setRoomState(state);
77
});
78
79
emitter.on("error", (error: any) => {
80
alert(error);
81
//do whatever
82
});
83
84
emitter.on("addPeer", (peer: HuddleTypes.IPeer) => {
85
console.log("new peer =>", peer);
86
setPeers((_peers) => [..._peers, peer]);
87
});
88
89
emitter.on("addProducer", (producer: HuddleTypes.IProducer) => {
90
console.log("new prod", producer);
91
switch (producer.type) {
92
case "webcam":
93
const videoStream: MediaStreamTrack | null = producer.track;
94
if (typeof videoStream == "object") {
95
try {
96
if (videoStream !== null) {
97
meVideoElem.current.srcObject = getTrack(videoStream);
98
}
99
} catch (error: any) {
100
console.error(error);
101
}
102
}
103
break;
104
case "mic":
105
//do whatever
106
break;
107
case "screen":
108
const screenStream: MediaStreamTrack | null = producer.track;
109
if (typeof screenStream == "object") {
110
try {
111
if (screenStream !== null) {
112
meScreenElem.current.srcObject = getTrack(screenStream);
113
}
114
} catch (error: any) {
115
console.error(error);
116
}
117
}
118
break;
119
120
default:
121
break;
122
}
123
});
124
125
emitter.on("removeProducer", (producer: HuddleTypes.IProducer) => {
126
console.log("remove ", producer);
127
switch (producer.type) {
128
case "webcam":
129
try {
130
meVideoElem.current.srcObject = null;
131
} catch (error: any) {
132
console.error(error);
133
}
134
break;
135
case "mic":
136
//do whatever
137
break;
138
case "screen":
139
try {
140
meScreenElem.current.srcObject = null;
141
} catch (error: any) {
142
console.error(error);
143
}
144
break;
145
146
default:
147
break;
148
}
149
});
150
151
emitter.on("addConsumer", (consumer: HuddleTypes.IConsumer) => {
152
switch (consumer.type) {
153
case "webcam": {
154
const videoStream = consumer.track;
155
setConsumerStreams((prevState) => ({
156
...prevState,
157
video: [...prevState.video, videoStream],
158
}));
159
160
break;
161
}
162
163
case "screen": {
164
const screenStream = consumer.track;
165
setConsumerStreams((prevState) => ({
166
...prevState,
167
screen: [...prevState.screen, screenStream],
168
}));
169
break;
170
}
171
172
case "mic": {
173
const audioStream = consumer.track;
174
setConsumerStreams((prevState) => ({
175
...prevState,
176
audio: [...prevState.audio, audioStream],
177
}));
178
179
break;
180
}
181
182
default:
183
break;
184
}
185
});
186
187
emitter.on("removeConsumer", (consumer: any) => {
188
switch (consumer.type) {
189
case "screen":
190
setConsumerStreams((prevState) => {
191
return {
192
...prevState,
193
screen: prevState.screen.filter(
194
(_consumer) => _consumer.id !== consumer._id
195
),
196
};
197
});
198
break;
199
case "webcam":
200
setConsumerStreams((prevState) => {
201
return {
202
...prevState,
203
video: prevState.video.filter(
204
(_consumer) => _consumer.id !== consumer._id
205
),
206
};
207
});
208
break;
209
case "mic":
210
setConsumerStreams((prevState) => {
211
return {
212
...prevState,
213
audio: prevState.audio.filter(
214
(_consumer) => _consumer.id !== consumer._id
215
),
216
};
217
});
218
break;
219
220
default:
221
break;
222
}
223
});
224
};
225
226
const joinRoom = async () => {
227
if (!huddle) return;
228
try {
229
setupEventListeners();
230
await huddle.join();
231
} catch (error: any) {
232
alert(error);
233
}
234
};
235
236
const leaveRoom = async () => {
237
if (!huddle) return;
238
try {
239
await huddle.close();
240
setRoomState("");
241
} catch (error: any) {
242
alert(error);
243
}
244
};
245
246
//TODO: add pauseWebcam() and resumeWebcam()
247
const enableWebcam = async () => {
248
if (!huddle) return;
249
try {
250
await huddle.enableWebcam();
251
setWebcamState(true);
252
} catch (error: any) {
253
setWebcamState(false);
254
alert(error);
255
}
256
};
257
258
const disableWebcam = async () => {
259
if (!huddle) return;
260
try {
261
await huddle.disableWebcam();
262
setWebcamState(false);
263
} catch (error: any) {
264
alert(error);
265
}
266
};
267
268
const startScreenshare = async () => {
269
if (!huddle) return;
270
try {
271
await huddle.enableShare();
272
setScreenshareState(true);
273
} catch (error: any) {
274
alert(error);
275
setScreenshareState(false);
276
}
277
};
278
279
const stopScreenshare = async () => {
280
if (!huddle) return;
281
try {
282
await huddle.disableShare();
283
setScreenshareState(false);
284
} catch (error: any) {
285
alert(error);
286
}
287
};
288
289
//TODO: add muteMic() and unmuteMic()
290
const enableMic = async () => {
291
if (!huddle) return;
292
try {
293
huddle.enableMic();
294
setMicState(true);
295
} catch (error: any) {
296
setMicState(false);
297
alert(error);
298
}
299
};
300
301
const disableMic = async () => {
302
if (!huddle) return;
303
try {
304
huddle.disableMic();
305
setMicState(false);
306
} catch (error: any) {
307
alert(error);
308
setMicState(true);
309
}
310
};
311
312
const startRecording = async () => {
313
if (!huddle) return;
314
try {
315
const status: boolean = await huddle.startRecording();
316
if (status) console.log("recording successfully initiated");
317
} catch (error: any) {
318
console.error(error);
319
}
320
};
321
322
const stopRecorder = async () => {
323
if (!huddle) return;
324
try {
325
const status: boolean = await huddle.stopRecording();
326
if (status) console.log("recording successfully stopped");
327
} catch (error: any) {
328
console.error(error);
329
}
330
};
331
332
return (
333
<div className="App">
334
<div className="me-ports">
335
<video height="400px" width="400px" autoPlay ref={meVideoElem} />
336
<video height="400px" width="400px" autoPlay ref={meScreenElem} />
337
</div>
338
<div className="btn-grp">
339
<button
340
ref={joinRoomBtn}
341
id="join-btn"
342
onClick={roomState === "connected" ? leaveRoom : joinRoom}
343
>
344
{roomState === "connected" ? "Leave Room" : "Join Room"}
345
</button>
346
<button onClick={webcamState ? disableWebcam : enableWebcam}>
347
{webcamState ? "Disable Webcam" : "Enable Webcam"}
348
</button>
349
<button onClick={micState ? disableMic : enableMic}>
350
{micState ? "Disable Mic" : "Enable Mic"}
351
</button>
352
<button onClick={screenshareState ? stopScreenshare : startScreenshare}>
353
{screenshareState ? "Disable Screenshare" : "Enable Screenshare"}
354
</button>
355
{/* <button onClick={toggleWebcam}>Toggle Webcam</button> */}
356
</div>
357
358
<div className="peer-ports">
359
{consumerStreams.video.map((stream, idx) => {
360
return <PeerVideo key={idx} videoTrack={getTrack(stream)} />;
361
})}
362
{consumerStreams.screen.map((stream, idx) => {
363
return <PeerScreen key={idx} screenTrack={getTrack(stream)} />;
364
})}
365
{consumerStreams.audio.map((stream, idx) => {
366
return <PeerAudio key={idx} audioTrack={getTrack(stream)} />;
367
})}
368
</div>
369
</div>
370
);
371
}
372
373
export default Room;
374
Copied!
1
export const getTrack = (track) => {
2
const stream = new MediaStream();
3
stream.addTrack(track);
4
return stream;
5
};
6
Copied!
1
import { useEffect, useRef } from "react";
2
3
export const PeerVideo = ({
4
videoTrack,
5
}: {
6
videoTrack: MediaProvider | null;
7
}) => {
8
const peerVideoTrack = useRef<HTMLVideoElement>(null!);
9
10
useEffect(() => {
11
peerVideoTrack.current.srcObject = videoTrack;
12
}, []);
13
return (
14
<div>
15
<video ref={peerVideoTrack} height="400px" width="400px" autoPlay />
16
</div>
17
);
18
};
19
20
export const PeerScreen = ({
21
screenTrack,
22
}: {
23
screenTrack: MediaProvider | null;
24
}) => {
25
const peerScreenTrack = useRef<HTMLVideoElement>(null!);
26
27
useEffect(() => {
28
peerScreenTrack.current.srcObject = screenTrack;
29
}, []);
30
return (
31
<div>
32
<video ref={peerScreenTrack} height="400px" width="400px" autoPlay />
33
</div>
34
);
35
};
36
37
export const PeerAudio = ({
38
audioTrack,
39
}: {
40
audioTrack: MediaProvider | null;
41
}) => {
42
const peerAudioTrack = useRef<HTMLAudioElement>(null!);
43
44
useEffect(() => {
45
console.log(audioTrack);
46
peerAudioTrack.current.srcObject = audioTrack;
47
}, []);
48
return (
49
<div>
50
<audio ref={peerAudioTrack} autoPlay playsInline controls={false} />
51
</div>
52
);
53
};
54
Copied!
The demo app can also be found on GitHub.
For any help, reach out to us on Slack. We are available 24*7 at: https://bit.ly/3AsIsT7.
Last modified 1mo ago
Copy link