Overview
Room events handle all room-related operations in Watch N Chill. These events manage room creation, user joining/leaving, and host promotion.
Client Events
Events emitted by the client to the server.
create-room
Creates a new room and automatically joins the creator as the host.
Name of the room host. Must be 2-50 characters and contain only letters, numbers, spaces, and basic punctuation (- _ . ! ?).
TypeScript Interface:
interface CreateRoomData {
hostName: string;
}
Example:
import { useSocket } from '@/hooks/use-socket';
const { socket } = useSocket();
socket.emit('create-room', {
hostName: 'John Doe'
});
Response Events:
room-created - Sent only to the creator with room details and host token
room-joined - Sent only to the creator confirming they joined the room
room-error - Sent if room creation fails
join-room
Joins an existing room as either a guest or the host (if providing valid host token).
6-character room ID (uppercase letters and numbers only).
Name of the user joining. Must be 2-50 characters and contain only letters, numbers, spaces, and basic punctuation (- _ . ! ?).
UUID token required to join as the host. Only the room creator has this token.
TypeScript Interface:
interface JoinRoomData {
roomId: string;
userName: string;
hostToken?: string;
}
Example (Guest):
socket.emit('join-room', {
roomId: 'ABC123',
userName: 'Jane Smith'
});
Example (Host Rejoining):
const hostToken = localStorage.getItem('hostToken');
socket.emit('join-room', {
roomId: 'ABC123',
userName: 'John Doe',
hostToken: hostToken
});
Response Events:
room-joined - Sent to the joining user with full room state
user-joined - Broadcast to other users in the room
sync-update - Sent to newly joined guests if video is currently playing
room-error - Sent if join fails (invalid room, duplicate name, etc.)
leave-room
Leaves the current room. If the last host leaves, the room is closed and all users are disconnected.
6-character room ID to leave.
TypeScript Interface:
interface RoomActionData {
roomId: string;
}
Example:
socket.emit('leave-room', {
roomId: 'ABC123'
});
Response Events:
user-left - Broadcast to remaining users in the room
room-error - Broadcast to all users if the last host leaves (“All hosts have left the room”)
Promotes a guest user to host status. Only current hosts can promote other users.
UUID of the user to promote to host.
TypeScript Interface:
interface PromoteHostData {
roomId: string;
userId: string;
}
Example:
socket.emit('promote-host', {
roomId: 'ABC123',
userId: '550e8400-e29b-41d4-a716-446655440000'
});
Response Events:
user-promoted - Broadcast to all users in the room
error - Sent if promotion fails (not authorized, user not found, etc.)
Server Events
Events emitted by the server to the client.
room-created
Sent only to the room creator after successful room creation.
Complete room object with all details.
UUID token for the host. Store this securely to rejoin as host.
TypeScript Interface:
interface RoomCreatedResponse {
roomId: string;
room: Room;
hostToken: string;
}
interface Room {
id: string;
hostId: string;
hostName: string;
hostToken: string;
videoUrl?: string;
videoType: 'youtube' | null;
videoState: VideoState;
users: User[];
createdAt: Date;
}
interface VideoState {
isPlaying: boolean;
currentTime: number;
duration: number;
lastUpdateTime: number;
}
interface User {
id: string;
name: string;
isHost: boolean;
joinedAt: Date;
}
Example:
socket.on('room-created', ({ roomId, room, hostToken }) => {
console.log('Room created:', roomId);
// Store host token for rejoining
localStorage.setItem('hostToken', hostToken);
// Navigate to room
router.push(`/room/${roomId}`);
});
room-joined
Sent to a user after they successfully join a room.
Complete room object with current state, video info, and all users.
The user object for the joining user.
TypeScript Interface:
interface RoomJoinedResponse {
room: Room;
user: User;
}
Example:
socket.on('room-joined', ({ room, user }) => {
console.log('Joined room:', room.id);
console.log('Current user:', user.name, 'isHost:', user.isHost);
console.log('Users in room:', room.users.length);
if (room.videoUrl) {
console.log('Video loaded:', room.videoUrl);
}
});
user-joined
Broadcast to all existing users in a room when a new user joins.
The user object for the newly joined user.
TypeScript Interface:
interface UserJoinedResponse {
user: User;
}
Example:
socket.on('user-joined', ({ user }) => {
console.log(`${user.name} joined the room`);
toast.success(`${user.name} joined`);
});
user-left
Broadcast to all remaining users when someone leaves the room.
UUID of the user who left.
TypeScript Interface:
interface UserLeftResponse {
userId: string;
}
Example:
socket.on('user-left', ({ userId }) => {
const user = room.users.find(u => u.id === userId);
if (user) {
console.log(`${user.name} left the room`);
toast.info(`${user.name} left`);
}
});
Broadcast to all users when someone is promoted to host.
UUID of the promoted user.
Name of the promoted user.
TypeScript Interface:
interface UserPromotedResponse {
userId: string;
userName: string;
}
Example:
socket.on('user-promoted', ({ userId, userName }) => {
console.log(`${userName} is now a host`);
toast.success(`${userName} promoted to host`);
// Update local state
if (currentUser.id === userId) {
setCurrentUser({ ...currentUser, isHost: true });
}
});
room-error
Sent when a room operation fails or the room is closed.
Error message describing what went wrong.
TypeScript Interface:
interface ErrorResponse {
error: string;
}
Common Error Messages:
"Room not found" - The specified room doesn’t exist
"The name \"X\" is already taken in this room" - Duplicate username
"Invalid host credentials" - Incorrect or missing host token
"All hosts have left the room. Redirecting to home page..." - Room closed
Example:
socket.on('room-error', ({ error }) => {
console.error('Room error:', error);
toast.error(error);
// Handle room closure
if (error.includes('All hosts have left')) {
router.push('/');
}
});
Complete Example
Here’s a complete example showing how to create and manage a room:
import { useEffect, useState } from 'react';
import { useSocket } from '@/hooks/use-socket';
import { Room, User } from '@/types';
import { toast } from 'sonner';
function RoomManager() {
const { socket, isConnected } = useSocket();
const [room, setRoom] = useState<Room | null>(null);
const [currentUser, setCurrentUser] = useState<User | null>(null);
useEffect(() => {
if (!socket || !isConnected) return;
// Listen for room created
socket.on('room-created', ({ roomId, room, hostToken }) => {
localStorage.setItem('hostToken', hostToken);
console.log('Created room:', roomId);
});
// Listen for room joined
socket.on('room-joined', ({ room, user }) => {
setRoom(room);
setCurrentUser(user);
toast.success('Joined room successfully');
});
// Listen for other users joining
socket.on('user-joined', ({ user }) => {
toast.info(`${user.name} joined`);
// Update room state with new user
setRoom(prev => prev ? {
...prev,
users: [...prev.users, user]
} : null);
});
// Listen for users leaving
socket.on('user-left', ({ userId }) => {
setRoom(prev => prev ? {
...prev,
users: prev.users.filter(u => u.id !== userId)
} : null);
});
// Listen for user promotions
socket.on('user-promoted', ({ userId, userName }) => {
toast.success(`${userName} promoted to host`);
setRoom(prev => prev ? {
...prev,
users: prev.users.map(u =>
u.id === userId ? { ...u, isHost: true } : u
)
} : null);
});
// Listen for errors
socket.on('room-error', ({ error }) => {
toast.error(error);
if (error.includes('All hosts have left')) {
setRoom(null);
setCurrentUser(null);
}
});
return () => {
socket.off('room-created');
socket.off('room-joined');
socket.off('user-joined');
socket.off('user-left');
socket.off('user-promoted');
socket.off('room-error');
};
}, [socket, isConnected]);
const createRoom = (hostName: string) => {
socket?.emit('create-room', { hostName });
};
const joinRoom = (roomId: string, userName: string) => {
const hostToken = localStorage.getItem('hostToken');
socket?.emit('join-room', { roomId, userName, hostToken });
};
const leaveRoom = () => {
if (room) {
socket?.emit('leave-room', { roomId: room.id });
setRoom(null);
setCurrentUser(null);
}
};
const promoteUser = (userId: string) => {
if (room && currentUser?.isHost) {
socket?.emit('promote-host', { roomId: room.id, userId });
}
};
return (
// Your UI here
<div>
{/* Room management UI */}
</div>
);
}