Setting the file. One moment.
Subchapter 149.8
examples/event-handling.mdMarkdown14 KBView on GitHub
// Register event listener
client.on('event-name', (payload) => {
// Handle event
});
// Remove event listener
client.off('event-name', handlerFunction);These events are essential for any Video SDK application.
// Connection state changes - REQUIRED
client.on('connection-change', (payload) => {
const { state, reason } = payload;
switch (state) {
case 'Connected':
console.log('Successfully connected to session');
break;
case 'Reconnecting':
console.log('Connection lost, attempting to reconnect...', reason);
showReconnectingUI();
break;
case 'Closed':
console.log('Disconnected from session', reason);
handleDisconnect(reason);
break;
case 'Fail':
console.error('Connection failed', reason);
break;
}
});
function handleDisconnect(reason) {
// Reasons: 'ended by host', 'kicked by host', 'session ended', etc.
cleanup();
showDisconnectMessage(reason);
}// New participant joined
client.on('user-added', (payload) => {
// payload is array of participants
payload.forEach(user => {
console.log('Joined:', user.displayName, 'ID:', user.userId);
createParticipantUI(user);
});
});
// Participant left
client.on('user-removed', (payload) => {
payload.forEach(user => {
console.log('Left:', user.displayName);
removeParticipantUI(user.userId);
// Clean up their video
stream.detachVideo(user.userId).catch(() => {});
});
});
// Participant updated (name, mute status, video status, etc.)
client.on('user-updated', (payload) => {
payload.forEach(user => {
console.log('Updated:', user);
updateParticipantUI(user);
});
});// Remote participant video state changed - CRITICAL
client.on('peer-video-state-change', async (payload) => {
const { action, userId } = payload;
if (action === 'Start') {
// User started video - render it
const element = await stream.attachVideo(userId, VideoQuality.Video_360P);
document.getElementById(`video-${userId}`)?.appendChild(element);
} else if (action === 'Stop') {
// User stopped video - clean up
await stream.detachVideo(userId);
const container = document.getElementById(`video-${userId}`);
if (container) container.innerHTML = '';
}
});
// Local video capture state
client.on('video-capturing-change', (payload) => {
const { state } = payload;
// state: 'Started' | 'Stopped' | 'Failed'
console.log('Video capture:', state);
});
// Video dimension changed
client.on('video-dimension-change', (payload) => {
const { width, height, type } = payload;
console.log('Video dimension:', width, 'x', height, type);
});// Current user audio state changed
client.on('current-audio-change', (payload) => {
const { action, type, source } = payload;
switch (action) {
case 'join':
console.log('Joined audio via:', type); // 'computer' or 'phone'
break;
case 'leave':
console.log('Left audio');
break;
case 'muted':
console.log('Audio muted', source); // 'active', 'passive(mute all)', etc.
updateMuteUI(true);
break;
case 'unmuted':
console.log('Audio unmuted');
updateMuteUI(false);
break;
}
});
// Host asks you to unmute
client.on('host-ask-unmute-audio', (payload) => {
console.log('Host requested unmute:', payload.reason);
showUnmutePrompt();
});
// Active speaker detection
client.on('active-speaker', (payload) => {
// Array sorted by volume (loudest first)
if (payload.length > 0) {
const activeSpeaker = payload[0];
highlightActiveSpeaker(activeSpeaker.userId);
}
});
// Auto-play blocked by browser
client.on('auto-play-audio-failed', () => {
// Show button for user to click to enable audio
showAudioEnableButton();
});// Someone started/stopped sharing
client.on('active-share-change', async (payload) => {
const { state, userId } = payload;
if (state === 'Active') {
console.log('User', userId, 'started sharing');
const element = await stream.attachShareView(userId);
document.getElementById('share-container').appendChild(element);
showShareView();
} else {
console.log('Sharing stopped');
await stream.detachShareView(userId);
hideShareView();
}
});
// You were forced to stop sharing
client.on('passively-stop-share', (payload) => {
console.log('Forced to stop sharing:', payload);
// Reasons: 'PrivilegeChange', 'AnotherShareStarted', etc.
});
// Share privilege changed
client.on('share-privilege-change', (payload) => {
console.log('Share privilege:', payload.privilege);
});// Received a chat message
client.on('chat-on-message', (payload) => {
const { sender, receiver, message, timestamp, id } = payload;
console.log(`[${sender.name}]: ${message}`);
addChatMessage(payload);
});
// Chat privilege changed by host
client.on('chat-privilege-change', (payload) => {
const { chatPrivilege } = payload;
// ChatPrivilege.All, ChatPrivilege.NoOne, ChatPrivilege.EveryonePublicly
updateChatUI(chatPrivilege);
});// Cloud recording state changed
client.on('recording-change', (payload) => {
const { state } = payload;
// state: 'Recording', 'Paused', 'Stopped', etc.
if (state === 'Recording') {
showRecordingIndicator();
} else {
hideRecordingIndicator();
}
});
// Individual recording consent
client.on('individual-recording-change', (payload) => {
const { state, userId } = payload;
if (state === 'Ask') {
// Host is asking for recording consent
showRecordingConsentDialog();
}
});// Transcription status changed
client.on('caption-status', (payload) => {
const { autoCaption, language } = payload;
console.log('Caption enabled:', autoCaption, 'Language:', language);
});
// Received transcription text
client.on('caption-message', (payload) => {
const { text, displayName, done, userId } = payload;
// done=false means partial/interim result
// done=true means final result
updateTranscription(payload);
});
// Captions enabled/disabled
client.on('caption-enable', (isEnabled) => {
console.log('Captions:', isEnabled ? 'enabled' : 'disabled');
});// Device plugged in or removed
client.on('device-change', () => {
console.log('Device list changed');
// Refresh device lists
updateCameraList(stream.getCameraList());
updateMicList(stream.getMicList());
updateSpeakerList(stream.getSpeakerList());
});
// Device permission changed
client.on('device-permission-change', (payload) => {
const { name, state } = payload;
// name: 'microphone' | 'camera'
// state: 'granted' | 'denied' | 'prompt'
console.log(`${name} permission: ${state}`);
});// Network quality changed
client.on('network-quality-change', (payload) => {
const { userId, type, level } = payload;
// type: 'uplink' | 'downlink'
// level: 0-5 (0=unknown, 1=bad, 5=excellent)
if (level <= 2) {
showNetworkWarning(userId, type);
}
});// Video statistics
client.on('video-statistic-data-change', (payload) => {
const { data, type } = payload;
// data.encoding: true=send, false=receive
console.log('Video stats:', {
fps: data.fps,
resolution: `${data.width}x${data.height}`,
bitrate: data.bitrate,
packetLoss: data.avg_loss,
});
});
// Audio statistics
client.on('audio-statistic-data-change', (payload) => {
const { data, type } = payload;
console.log('Audio stats:', {
bitrate: data.bitrate,
packetLoss: data.avg_loss,
jitter: data.jitter,
});
});class ZoomEventHandler {
constructor(client, stream) {
this.client = client;
this.stream = stream;
this.handlers = new Map();
}
init() {
this.setupConnectionEvents();
this.setupParticipantEvents();
this.setupVideoEvents();
| Event | Category | When Triggered |
|---|---|---|
connection-change | Session | Connection state changes |
user-added | Participant | New participant joins |
user-removed | Participant | Participant leaves |
user-updated | Participant | Participant properties change |
peer-video-state-change | Video | Remote video starts/stops |
video-capturing-change | Video | Local capture state changes |
video-dimension-change | Video | Video resolution changes |
current-audio-change | Audio | Local audio state changes |
host-ask-unmute-audio | Audio | Host requests unmute |
active-speaker | Audio | Active speaker changes |
auto-play-audio-failed | Audio | Browser blocked autoplay |
active-share-change | Share | Someone starts/stops sharing |
passively-stop-share | Share | Forced to stop sharing |
share-privilege-change | Share | Share settings changed |
chat-on-message | Chat | Message received |
recording-change | Recording | Recording state changes |
caption-message | Transcription | Transcription text received |
device-change | Device | Device added/removed |
network-quality-change | Network | Network quality changes |
connection-change - Essential for detecting disconnectspeer-video-state-change is for remote video - Not your own videouser-removed - Detach their videoauto-play-audio-failed - Browser may block audio autoplayoff() to clean up - Prevent memory leaks