Video Chat Web App using 100ms with intuitive user-interface.
Video Chat Features -
-
In-call Messaging
-
Mute/Unmute (Audio/video)
-
Live Notifications
Posted on September 29, 2021
In this blog, I would like to burst out the popular belief, that building your own video chat app is too challenging.
Before we start building, let me explain briefly, what is 100ms
?
100ms is a cloud platform that allows developers to add video and audio conferencing to Web, Android and iOS applications.
The platform provides REST APIs, SDKs, and a dashboard that makes it simple to capture, distribute, record, and render live interactive audio, video.
Check out the 100ms Documentation
here.
hmsStore
- will hold the complete state of the application such as details of all the participants. We can also visualize this state at any time using the devtools extension.hmsActions
- will help us perform actions such as joining the room, mute our audio and send messages.Room
- When we join a conference call, the participants are said to be in a video call room.Peer
- A participant in the video call. You are the local peer while others are remote peers.Track
- Media. There are two types of track a peer can have - audio and video.Our Video Chat app will have the following features:
1. Video/Audio calling
2. Realtime Chat
npx create-react-app my-app
cd my-app
npm start
npm install --save @100mslive/hms-video@latest @100mslive/hms-video-react@latest
src/Index.js
- we're rendering everything to the 'root' element and also wrapping the entire application with <HMSRoomProvider />
component (which will let us use the hooks for state and actions)
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { HMSRoomProvider } from "@100mslive/hms-video-react";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<HMSRoomProvider>
<App />
</HMSRoomProvider>,
rootElement
);
joinForm.js
component, to join a room, and for that in the component, we need to call the join method on hmsActions. And add this component to the App.js
.we will have a form that includes two input fields accepting :
Username
- The name of the user. This is the value that will be set on the peer object and be visible to everyone connected to the room.
authToken
- A client-side token that is used to authenticate the user. You can read about how to generate this token here
import { useState } from "react";
import {
useHMSActions,
useHMSStore,
selectIsLocalAudioEnabled,
selectIsLocalVideoEnabled,
} from "@100mslive/hms-video-react";
const JoinForm = () => {
const hmsActions = useHMSActions();
const [inputValues, setInputValues] = useState({
name: "",
token: "",
});
const handleInputChange = (e) => {
setInputValues((prevValues) => ({
...prevValues,
[e.target.name]: e.target.value,
}));
};
const handleSubmit = (e) => {
e.preventDefault();
hmsActions.join({
userName: inputValues.name,
authToken: inputValues.token,
});
};
return (
<>
<div className="showcase">
<video autoPlay muted loop >
<source src={backgroundVideo} type="video/mp4" />
</video>
<div className="overlay"></div>
<form onSubmit={handleSubmit}>
<h2 className="heading">Join Room</h2>
<div className="input-container">
<input
className="inputfield"
value={inputValues.name}
onChange={handleInputChange}
id="name"
type="text"
name="name"
placeholder="Your name"
/>
</div>
<div className="input-container">
<input
className="inputfield"
value={inputValues.token}
onChange={handleInputChange}
id="token"
type="text"
name="token"
placeholder="Auth token"
/>
</div>
<button className="btn-primary">Join</button>
</form>
</div>
</>
);
};
export default JoinForm;
Note
- This code includes my css styled classes
Room.js
component that will give us the list of peers and we'll render them.
import { useState, useEffect } from "react";
import { selectPeers, useHMSStore } from "@100mslive/hms-video-react";
import Peer from "./Peer";
import Chat from "./chat"; //will be imported
import ButtonBar from "./ButtonBar"; //will be imported
const Room = ({ toggleChat, setToggleChat }) => {
const peers = useHMSStore(selectPeers);
return (
<div className="grid-container" style={{ backgroundColor: "#1B2156" }}>
<div className="peers-container">
{peers.map((peer) => (
<Peer key={peer.id} peer={peer} />
))}
</div>
// {below} - will be added when creating the chat component
<div className="chatbox-container">{toggleChat ? <Chat /> : null}</div>
</div>
);
};
export default Room;
Also, create a Peer.js
component, where we will be rendering video tiles in the peer-container.
import {
selectVideoTrackByPeerID,
useHMSActions,
useHMSStore,
} from "@100mslive/hms-video-react";
import { useRef, useEffect } from "react";
const Peer = ({ peer }) => {
const videoRef = useRef(null);
const hmsActions = useHMSActions();
const videoTrack = useHMSStore(selectVideoTrackByPeerID(peer.id));
useEffect(() => {
if (videoRef.current && videoTrack) {
if (videoTrack.enabled) {
hmsActions.attachVideo(videoTrack.id, videoRef.current);
} else {
hmsActions.detachVideo(videoTrack.id, videoRef.current);
}
}
}, [videoTrack, hmsActions]);
return (
<div className="peer-container">
<video
ref={videoRef}
className={`peer-video ${peer.isLocal ? "local" : ""}`}
autoPlay
muted
/>
<div className="peer-name">
{peer.name} {peer.isLocal ? "(You)" : ""}
</div>
</div>
);
};
export default Peer;
App.js
component, we will also be adding leaving room functionality, which we can do by setting up leave
method on hmsActions.
import JoinForm from "./components/JoinForm";
import "./styles.css";
import Room from "./components/Room";
import Notification from "./components/Notifications";
import { useEffect } from "react";
import {
selectIsConnectedToRoom,
useHMSActions,
useHMSStore,
} from "@100mslive/hms-video-react";
import ButtonBar from "./components/ButtonBar";
const App = () => {
const isConnected = useHMSStore(selectIsConnectedToRoom);
const hmsActions = useHMSActions();
useEffect(() => {
window.onunload = () => {
if (isConnected) {
hmsActions.leave();
}
};
}, [hmsActions, isConnected]);
return (
<div className="App" >
{isConnected ? (
<>
//these components will be added later
<Notification />
<Room />
<ButtonBar />
</>
) : (
<JoinForm />
)}
</div>
);
};
export default App;
import React, { useState, useEffect, useMemo } from "react";
import { BiMicrophone } from "react-icons/bi";
import { BiMicrophoneOff } from "react-icons/bi";
import { BiVideo } from "react-icons/bi";
import { BiVideoOff } from "react-icons/bi";
import { AiOutlineSetting } from "react-icons/ai";
import { FaPhone } from "react-icons/fa";
import { MdScreenShare } from "react-icons/md";
import { MdStopScreenShare } from "react-icons/md";
import {
useHMSActions,
useHMSStore,
selectPeers,
selectLocalPeer,
selectIsLocalAudioEnabled,
selectIsConnectedToRoom,
selectIsLocalVideoEnabled,
} from "@100mslive/hms-video-react";
import Chat from "./chat";
const ButtonBar = ({ peer }) => {
const peers = useHMSStore(selectPeers);
const localPeer = useHMSStore(selectLocalPeer);
const videoEnabled = useHMSStore(selectIsLocalVideoEnabled);
const audioEnabled = useHMSStore(selectIsLocalAudioEnabled);
const hmsActions = useHMSActions();
const isConnected = useHMSStore(selectIsConnectedToRoom);
const [toggleChat, setToggleChat] = useState(false);
const toggleAudio = () => {
hmsActions.setLocalAudioEnabled(!audioEnabled);
};
const toggleVideo = () => {
hmsActions.setLocalVideoEnabled(!videoEnabled);
};
return (
<>
<div className="control-bar">
<button
className="chatbox-btn"
onClick={() => setToggleChat(!toggleChat)}
>
In-call Messages
</button>
{toggleChat ? <Chat /> : null}
<div className="center-buttons">
<nav>
<button className="btn-control" onClick= {toggleAudio}>
{audioEnabled ? (
<BiMicrophone size="1.8em" />
) : (
<BiMicrophoneOff size="1.8em" />
)}
</button>
<button
className="btn-control"
alt="Leave Room"
onClick={toggleVideo}
>
{videoEnabled ? (
<BiVideo size="1.8em" />
) : (
<BiVideoOff size="1.8em" />
)}
</button>
</nav>
{isConnected && (
<FaPhone
size="2em"
id="leave-btn"
className="btn-danger"
onClick={() => hmsActions.leave()}
/>
)}
</div>
<div className="App-name">VidChat</div>
</div>
</>
);
};
export default ButtonBar;
Note
- That we have to create a chat component which will be included in the button bar. Also, I have added React icons for styling.
chat.js
component for In-call messaging, using await hmsActions.sendBroadcastMessage('message')
which will
send a message to every peer in the room and use useHMSStore (selectHMSMessages)
.
import React from "react";
import { useState, useEffect } from "react";
import {
useHMSActions,
useHMSStore,
selectLocalPeer,
selectPeers,
selectHMSMessages,
selectMessagesByRole,
selectMessagesByPeerID,
selectBroadcastMessages,
} from "@100mslive/hms-video-react";
import "dayjs/locale/es";
import * as dayjs from "dayjs";
const relativeTime = require("dayjs/plugin/relativeTime");
const localizedFormat = require("dayjs/plugin/localizedFormat");
dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);
const Chat = () => {
const hmsActions = useHMSActions();
const getMessages = useHMSStore(selectHMSMessages);
const [inputMessage, setInputMessage] = useState("");
const typedMessages = async () => {
await hmsActions.sendBroadcastMessage(inputMessage);
setInputMessage("");
};
const handleOnKeyDownEvent = (e) => {
if (e.keyCode === 13) {
typedMessages(inputMessage);
setInputMessage("");
}
};
function renderMessages(data) {
console.log(data);
return (
<>
<div>
<div className="message-sender" key={data.id}>
{data.senderName}
<div className="date">{dayjs(data.time).format("LT")}</div>
</div>
<div className="message ">{data.message} </div>
</div>
</>
);
}
return (
<>
<div className="chat">
<div className="chat-title">
<h2> Inbox </h2>
</div>
<div className="messages">
<div className="messages-content scrollbar ">
{getMessages.map(renderMessages)}
</div>
</div>
<div className="message-box">
<textarea
type="text"
className="message-input"
placeholder="Type a message . . ."
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
></textarea>
<button className="message-submit" type="button" onClick={typedMessages}>
Send
</button>
</div>
</div>
</>
);
};
export default Chat;
Add this component to the button bar.
Note
- I have used Day.js for getting formatted time in the chatbox
Some other features that can be added are -
Hope this article is useful. Feel free to reach out to me for any queries.
Full Code on Github -
Video Chat Web App using 100ms with intuitive user-interface.
In-call Messaging
Mute/Unmute (Audio/video)
Live Notifications
Posted on September 29, 2021
Sign up to receive the latest update from our blog.
November 29, 2024