Appearance
Overview
- File: src/components/app/call/notification.svelte
The Call Notification component handles incoming call notifications in the HVC Web application, providing real-time call alerts with audio feedback and user interaction options.
Type Definitions
Call Data Interface
svelte
interface ICallData {
expires: Date; // Call expiration time
groupId: string; // Unique group identifier
groupName: string; // Name of the group/meeting
callerName: string; // Name of the caller
}Audio Management
Buzz Sound Setup
svelte
let audioInstance;
export let audio = audioInstance;
export function play() {
audioInstance?.play();
}
export function stop() {
audioInstance?.stop();
}
function init() {
if (!window["buzz"]) {
setTimeout(init, 100);
return;
}
audioInstance = new window["buzz"].sound("/assets/media/ringtone", {
formats: ["mp3"],
preload: true,
autoplay: false,
loop: true
});
}Props
Configuration Props
svelte
export let handleLocally = true; // Handle call actions locally
export let expirySeconds = 45; // Call notification expiry time
export let refreshRate = 2000; // Status check intervalState Management
Local State
svelte
let pendingCalls: ICallData[] = []; // Active call notifications
let intervalHandle: number; // Status check intervalReactive Statements
svelte
$: myId = $userInfo?.id;
$: monitorSounds(pendingCalls);Event Handling
Call Actions
svelte
async function accept(evt: ICallData) {
if (handleLocally) {
await acceptCall(evt.groupId);
pendingCalls = pendingCalls.filter(x => x.groupId !== evt.groupId);
const url = `/private/events/${evt.groupId}`;
window.open(url);
} else {
dispatch("accept", evt);
}
}
async function reject(evt: ICallData) {
if (handleLocally) {
pendingCalls = pendingCalls.filter(x => x.groupId !== evt.groupId);
await cancelCall(evt.groupId, "Cancelled by User");
} else {
dispatch("reject", evt);
}
}Event Subscriptions
svelte
// Subscribe to call events
subscribe("onNewCall", onNewCall);
subscribe("onEndCall", onEndCall);
subscribe("onMeetingEvent", onMeetingEvent);
// Cleanup subscriptions
onDestroy(() => {
unsubscribe("onNewCall", onNewCall);
unsubscribe("onEndCall", onEndCall);
unsubscribe("onMeetingEvent", onMeetingEvent);
});Event Handlers
svelte
// New call received
async function onNewCall(data) {
data.expires = dayjs().add(expirySeconds, "s").toDate();
pendingCalls = [...pendingCalls, data];
play();
}
// Call ended
async function onEndCall({groupId, reason}) {
pendingCalls = pendingCalls.filter(x => x.groupId !== groupId);
}
// Meeting events
async function onMeetingEvent(data) {
switch (data?.eventName) {
case "Cancelled":
remove(data.groupId, data);
break;
case "Started":
case "User Joined":
if (data.userId === myId) {
remove(data.groupId, data);
}
break;
}
}User Interactions
Call Actions
svelte
// Accept call
async function accept(evt: ICallData) {
if (handleLocally) {
await acceptCall(evt.groupId);
pendingCalls = pendingCalls.filter(x => x.groupId !== evt.groupId);
window.open(`/private/events/${evt.groupId}`);
} else {
dispatch("accept", evt);
}
}
// Reject call
async function reject(evt: ICallData) {
if (handleLocally) {
pendingCalls = pendingCalls.filter(x => x.groupId !== evt.groupId);
await cancelCall(evt.groupId, "Cancelled by User");
} else {
dispatch("reject", evt);
}
}UI Implementation
Notification Container
svelte
<div aria-live="assertive" class="pointer-events-none fixed inset-0 flex items-end px-4 py-6 sm:items-start sm:p-6 z-[100]">
<div class="flex w-full flex-col items-center space-y-4 sm:items-end">
{#each pendingCalls as call}
<!-- Call notification card -->
{/each}
</div>
</div>Call Card Template
svelte
<div class="pointer-events-auto w-full max-w-sm rounded-lg bg-white shadow-lg">
<div class="p-4">
<div class="flex items-start">
<!-- Call icon -->
<Icon src={PhoneIncoming} size="40" class="animate-bounce text-green-500" />
<!-- Call details -->
<div class="ml-3 w-0 flex-1">
<p class="text-sm font-medium text-gray-900">{call.callerName}</p>
<p class="mt-1 text-sm text-gray-500">{call.groupName}</p>
<!-- Action buttons -->
<div class="mt-4 flex">
<button on:click={_ => accept(call)}>Accept</button>
<button on:click={_ => reject(call)}>Decline</button>
</div>
</div>
</div>
</div>
</div>Lifecycle Management
Component Initialization
svelte
onMount(() => {
// Start status check interval
intervalHandle = setInterval(() => {
if (pendingCalls.find(x => x.expires < new Date())) {
pendingCalls = pendingCalls.filter(x => x.expires > new Date());
}
}, refreshRate);
});Cleanup
svelte
onDestroy(() => {
// Clear interval
clearInterval(intervalHandle);
// Unsubscribe from events
unsubscribe("onNewCall", onNewCall);
unsubscribe("onEndCall", onEndCall);
unsubscribe("onMeetingEvent", onMeetingEvent);
});Usage
Basic Implementation
svelte
<script>
import CallNotification from './components/app/call/notification.svelte';
</script>
<CallNotification />Custom Event Handling
svelte
<CallNotification
handleLocally={false}
on:accept={handleAccept}
on:reject={handleReject}
/>