Lobby Preview
In this article, we will discuss key considerations for creating an inviting and user-friendly entrance to your application. We will delve into the various elements that can enhance the user experience and ensure a smooth transition from the lobby to the video call itself. By implementing effective lobby page design principles, you can elevate the overall user satisfaction. We consider lobby to be place, where the user acquires information about the call to be joined and can configure own media devices.
The approach to visualise the components will differ from application to application. Therefore, in this guide, we will focus only on the principles of building components and plugging them with right data sources. We will not focus on the styling (CSS) part.
Prerequisites
Before we can join a call, we need to connect to Stream's edge infrastructure. To do that, we follow these steps:
- Register for a Stream account and obtain our API key and secret.
- Install the Stream React Video SDK:
npm install @stream-io/video-react-sdk
yarn add @stream-io/video-react-sdk
- Initialize the SDK by passing in your API key, token and user information:
For the token generation, you can use our Token Generator.
The call data
We would like to show some basic information about the call, when users arrive to the lobby. For example:
- call name
- who is invited
- who already joined
The initial call information can be retrieved by the get
or getOrCreate
method of a Call
instance. Then we can use the following hooks:
useCallSession
useCallMembers
These hooks make sure, that the information about call session or call members is updated in real time. The updates are made automatically in response to Stream's WebSocket events arriving from the backend.
Learn more about setting up the boilerplate of joining a call room in our Joining and Creating Calls guide.
Video input preview
The SDK comes with a pre-build VideoPreview
component that handles video input stream preview.
It also presents various UIs based on video playing state (starting, playing, unavailable video devices).
In the example below, we are assembling a custom video preview using SDK's VideoPreview
component and our custom UI components for each playing state.
import {
useConnectedUser,
DefaultVideoPlaceholder,
StreamVideoParticipant,
VideoPreview,
} from '@stream-io/video-react-sdk';
import { CameraIcon, LoadingIndicator } from '../icons';
export const Lobby = () => {
return (
<div>
{/* ... other components */}
<VideoPreview
DisabledVideoPreview={DisabledVideoPreview}
NoCameraPreview={NoCameraPreview}
StartingCameraPreview={StartingCameraPreview}
/>
{/* ... other components */}
</div>
);
};
export const DisabledVideoPreview = () => {
const connectedUser = useConnectedUser();
if (!connectedUser) return null;
return (
<DefaultVideoPlaceholder
participant={
{
image: connectedUser.image,
name: connectedUser.name,
} as StreamVideoParticipant
}
/>
);
};
const NoCameraPreview = () => (
<div>
<CameraIcon />
</div>
);
const StartingCameraPreview = () => (
<div>
<LoadingIndicator />
</div>
);
Audio input preview
Microphone configuration in the lobby may consist of checking, whether our microphone works and deciding, whether it will be enabled, when we join the call.
Learn how to build a toggle button for call preview pages in Call controls tutorial.
We build our custom sound detector in the dedicated tutorial about Audio Volume Indicator.
Device selection
Switching devices is done through a set of utility methods or hooks. We speak a bit more about the function and the pre-built components in the media devices guide.
Device selector example
The selectors can be thought of as dropdowns in our example.
import { useCallStateHooks } from '@stream-io/video-react-sdk';
type DeviceSelectorProps = {
devices: MediaDeviceInfo[];
selectedDeviceId?: string;
onSelect: (deviceId: string) => void;
};
export const DeviceSelector = ({
devices,
selectedDeviceId,
onSelect,
}: DeviceSelectorProps) => {
return (
<div className="selector">
{devices.map((device) => {
const selected = selectedDeviceId === device.deviceId;
return (
<div
key={device.deviceId}
className={`option ${selected ? 'option--selected' : ''}`}
onClick={() => onSelect(device.deviceId)}
>
{device.label}
</div>
);
})}
</div>
);
};
export const AudioInputDeviceSelector = () => {
const { useMicrophoneState } = useCallStateHooks();
const { microphone, devices, selectedDevice } = useMicrophoneState();
return (
<DeviceSelector
devices={devices || []}
selectedDeviceId={selectedDevice}
onSelect={(deviceId) => microphone.select(deviceId)}
/>
);
};
export const VideoInputDeviceSelector = () => {
const { useCameraState } = useCallStateHooks();
const { camera, devices, selectedDevice } = useCameraState();
return (
<DeviceSelector
devices={devices || []}
selectedDeviceId={selectedDevice}
onSelect={(deviceId) => camera.select(deviceId)}
/>
);
};
export const AudioOutputDeviceSelector = () => {
const { useSpeakerState } = useCallStateHooks();
const { speaker, devices, selectedDevice, isDeviceSelectionSupported } =
useSpeakerState();
if (!isDeviceSelectionSupported) return null;
return (
<DeviceSelector
devices={devices || []}
selectedDeviceId={selectedDevice}
onSelect={(deviceId) => speaker.select(deviceId)}
/>
);
};
Participants in a call
We can retrieve the list of members, that already joined the call (participants), by inspecting the call session object (session.participants
).
The object is provided and maintained up-to-date by useCallSession
hook.
import { Avatar, useCallStateHooks } from '@stream-io/video-react-sdk';
export const ParticipantsPreview = () => {
const { useCallSession } = useCallStateHooks();
const session = useCallSession();
if (session?.participants || session?.participants.length === 0) return null;
return (
<div>
<div>Already in call ({session.participants.length}):</div>
<div style={{ display: 'flex' }}>
{session.participants.map((participant) => (
<div>
<Avatar
name={participant.user.name}
imageSrc={participant.user.image}
/>
{participant.user.name && <div>{participant.user.name}</div>}
</div>
))}
</div>
</div>
);
};
Joining the call button
Lastly, to join a call we simply invoke call.join()
. Learn more about the topic in the dedicated Joining & Creating Calls guide.