Appearance
Event Call Page
- File: src/pages/private/events/[eventId]/index.svelte
Overview
The Event Call page manages video conferencing sessions within the application. It handles real-time communication, participant management, device configuration, and call states for virtual meetings.
Core Components
1. View Components
- PrecallView: Pre-call setup and participant waiting room
- CallView: Main video conference interface
- MessageView: Status and error message display
- LoadingView: Loading state interface
2. Page States
ts
export enum Page {
Ringing, // Initial connection state
PreCall, // Pre-meeting setup
Call, // Active call session
Message, // Information display
Loading, // Loading state
}Data Management
1. Event Data Structure
ts
interface Event {
id: string;
title: string;
notes: string;
startDate: Date;
endDate: Date;
sessionId: string;
startTime: Date;
endTime: Date;
participants: {
contactId: string;
contact: {
profileImageUrl: string;
online: boolean;
specialty: string;
phoneNumber: string;
email: string;
firstName: string;
lastName: string;
timeOffset: string;
id: string;
username: string;
};
}[];
}Core Functions
1. Event Data Fetching
svelte
async function getEvent(eventId) {
const {data, error} = await query<{xs}>(`
query {
xs: events(where: { id: { eq: ${eventId}}}) {
items {
id, title, notes,
startDate, endDate,
sessionId, startTime, endTime,
participants { ... }
}
}
}
`);
// Process response and format data
if (error) return { success: false, message: error.message, code: 400 };
const evt = data.xs.items[0];
if (!evt) return { success: false, message: "Event not found", code: 404 };
// Format dates and participant data
evt.startDate = getDateDetail(evt.startDate);
evt.endDate = getDateDetail(evt.endDate);
evt.participants = formatParticipants(evt.participants);
return { success: true, data: evt };
}2. WebSocket Communication
svelte
const url = `${wsServer}/calls/${eventId}`;
let ws = new WebSocket(url);
// WebSocket event handlers
ws.onopen = () => wsOpened = true;
ws.onclose = () => wsOpened = false;
ws.onmessage = (msg) => {
let partitions = JSON.parse(msg.data)
.filter(x => x.id > 0)
.filter(x => x.id !== myId)
.map(x => ({
...x,
initials: getInitials(x.name)
}))
.reduce((s, x) => {
if (x.page === "call") s[1].push(x)
else s[0].push(x)
return s
}, [[], []]);
precallUsers = partitions[0];
incallUsers = partitions[1];
};3. Call State Management
svelte
async function refresh() {
// Wait for user info and connection
if (!$userInfo || !connected()) {
setTimeout(refresh, 100);
return;
}
// Fetch event data
const ret = await getEvent(eventId);
if (!ret.success) {
handleError(ret);
return;
}
// Setup call configuration
const meetingData = await getMeetingDetails(+eventId, null);
if (!meetingData.success) {
setLocalError(meetingData.message);
return;
}
callConfig = meetingData.data;
event = { ...ret.data };
activePage = Page.PreCall;
}Event Handlers
1. Call Control
svelte
function onJoinCall({detail}) {
// Set device configurations
selectedCamera = detail.selectedCamera;
selectedMic = detail.selectedMic;
selectedSpeaker = detail.selectedSpeaker;
micOn = detail.micOn;
cameraOn = detail.cameraOn;
noAudio = detail.noAudio;
noVideo = detail.noVideo;
activePage = Page.Call;
}
function onDisconnected() {
errorMessage = "Call Disconnected";
isError = false;
activePage = Page.Message;
countdown = window.opener ? defaultCcountdown : 0;
}Lifecycle Management
svelte
onMount(async () => {
// Initialize call
await refresh();
// Load peripheral devices
try {
const ret = await loadPeripherals();
if (ret.success) {
peripherals = ret.data;
}
} catch (e) {
showError(e?.message || e);
}
});
onDestroy(() => {
ws.close();
});UI Structure
svelte
<div class="bg-[#2d4e8c] flex-grow grid place-items-center w-screen h-screen">
<div class="w-full h-full p-3 lg:py-6 overflow-hidden">
{#if activePage === Page.PreCall}
<PrecallView {event} on:cancel={onCancel} on:joinCall={onJoinCall}
{precallUsers} {incallUsers}/>
{:else if activePage === Page.Call}
<CallView {event} config={callConfig} {contacts} {selectedCamera}
{selectedMic} {selectedSpeaker} {micOn} {cameraOn}
on:disconnected={onDisconnected} {devices} {noAudio} {noVideo}
{cnt} {peripherals}/>
{:else if activePage === Page.Message}
<MessageView {message} {isError} {countdown}
on:countDownOver={_ => window.close()}/>
{:else if activePage === Page.Loading}
<LoadingView />
{/if}
</div>
</div>