refactor component props with improved state & event handling
This commit is contained in:
@@ -1,18 +1,22 @@
|
||||
<!-- @component
|
||||
The Jitsi component is a lightweight wrapper that manages loading the Jitsi Meet External API
|
||||
and joining a Jitsi meeting room. It accepts configuration options and provides a callback
|
||||
when the API is ready for use.
|
||||
and joining a Jitsi meeting room. It accepts configuration options and several callbacks that
|
||||
allow you to respond to basic meeting events such as joining and leaving. For advanced event
|
||||
handling, use the getAPI() method to interact with the Jitsi instance directly.
|
||||
|
||||
WARNING: Assume that ALL properties are NOT reactive. Due to the nature of the Jitsi Meet
|
||||
External API, changing properties after initialization will NOT update the Jitsi instance.
|
||||
Instead, use the getAPI() method to interact with the Jitsi instance directly.
|
||||
WARNING: While properties are reactive, in most cases changing them after the API has
|
||||
initialized will trigger a complete re-render of the component, which will destroy the
|
||||
existing Jitsi instance and interrupt any ongoing meetings. Use the getAPI() method to
|
||||
interact with the Jitsi instance directly without causing a re-render.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import type {
|
||||
JitsiMeetExternalAPIOptions,
|
||||
JitsiMeetExternalAPI
|
||||
JitsiMeetExternalAPI,
|
||||
VideoConferenceJoinedEvent,
|
||||
VideoConferenceLeftEvent
|
||||
} from '$lib/jitsi-iframe-api.d.ts';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
@@ -32,29 +36,59 @@ Instead, use the getAPI() method to interact with the Jitsi instance directly.
|
||||
*/
|
||||
class?: ClassValue;
|
||||
/**
|
||||
* Children are rendered inside the Jitsi container until the API is initialized.
|
||||
* Optional snippet rendered until the API is initialized and the meeting
|
||||
* is joined. Can be used to provide a loading indicator or placeholder content.
|
||||
*/
|
||||
children?: Snippet;
|
||||
loading?: Snippet;
|
||||
/** Whether to hold loading state until the meeting is joined (default: false). */
|
||||
holdLoadingUntilJoined?: boolean;
|
||||
/**
|
||||
* Optional snippet rendered after the API is initialized and the meeting is joined.
|
||||
* Can be used to provide additional UI elements or controls related to the meeting.
|
||||
*/
|
||||
joined?: Snippet<[api: JitsiMeetExternalAPI]>;
|
||||
/**
|
||||
* Optional snippet rendered after the user has left the meeting. Can be used to
|
||||
* provide a farewell message or other post-meeting content.
|
||||
*/
|
||||
left?: Snippet;
|
||||
/**
|
||||
* Callback function that is called when the Jitsi API is loaded. Can
|
||||
* be used to provide a loading indicator until the API is ready.
|
||||
* @param api The Jitsi Meet External API instance.
|
||||
*/
|
||||
onready?: (api: JitsiMeetExternalAPI) => void;
|
||||
onapiready?: (api: JitsiMeetExternalAPI) => void;
|
||||
/** Callback triggered when the local user joins the meeting. */
|
||||
onjoin?: (ev: VideoConferenceJoinedEvent) => void;
|
||||
/** Callback triggered when the local user leaves the meeting. */
|
||||
onleave?: (ev: VideoConferenceLeftEvent) => void;
|
||||
}
|
||||
|
||||
let {
|
||||
domain = 'https://meet.jit.si',
|
||||
options,
|
||||
class: classValue,
|
||||
children,
|
||||
onready
|
||||
loading,
|
||||
holdLoadingUntilJoined = false,
|
||||
joined,
|
||||
left,
|
||||
onapiready,
|
||||
onjoin,
|
||||
onleave
|
||||
}: Props = $props();
|
||||
|
||||
const src = $derived(`${new URL('external_api.js', domain).toString()}`);
|
||||
|
||||
let container: HTMLDivElement;
|
||||
let ready = $state(false);
|
||||
/** tracks whether the Jitsi API is ready */
|
||||
let apiReady = $state(false);
|
||||
/** tracks whether the local user has joined the meeting */
|
||||
let joinedMeeting = $state(false);
|
||||
/** tracks whether the local user has left the meeting */
|
||||
let leftMeeting = $state(false);
|
||||
/** whether to display the loading state */
|
||||
const showLoading = $derived(holdLoadingUntilJoined ? !joinedMeeting : !apiReady);
|
||||
|
||||
let api = $state<JitsiMeetExternalAPI | null>(null);
|
||||
|
||||
onMount(() => {
|
||||
@@ -64,13 +98,14 @@ Instead, use the getAPI() method to interact with the Jitsi instance directly.
|
||||
}
|
||||
});
|
||||
|
||||
const onload = () => {
|
||||
/** handles Jitsi script load event */
|
||||
const handleScriptLoad = () => {
|
||||
if (typeof window.JitsiMeetExternalAPI === 'undefined') {
|
||||
console.error('Jitsi Meet External API is not available on the window object.');
|
||||
return;
|
||||
}
|
||||
|
||||
ready = true;
|
||||
apiReady = true;
|
||||
// Initialize the Jitsi Meet External API
|
||||
api = new window.JitsiMeetExternalAPI(new URL(domain).host, {
|
||||
...options,
|
||||
@@ -80,8 +115,18 @@ Instead, use the getAPI() method to interact with the Jitsi instance directly.
|
||||
console.error('Failed to initialize Jitsi Meet External API.');
|
||||
return;
|
||||
}
|
||||
// Call the onready callback if provided
|
||||
onready?.(api);
|
||||
// Call the onapiready callback if provided
|
||||
onapiready?.(api);
|
||||
|
||||
// Attach event listeners for meeting join and leave events
|
||||
api.addListener('videoConferenceJoined', (ev: VideoConferenceJoinedEvent) => {
|
||||
joinedMeeting = true;
|
||||
onjoin?.(ev);
|
||||
});
|
||||
api.addListener('videoConferenceLeft', (ev: VideoConferenceLeftEvent) => {
|
||||
leftMeeting = true;
|
||||
onleave?.(ev);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -94,11 +139,21 @@ Instead, use the getAPI() method to interact with the Jitsi instance directly.
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<script {src} {onload}></script>
|
||||
<script {src} onload={handleScriptLoad}></script>
|
||||
</svelte:head>
|
||||
|
||||
<div id="jitsi-container" bind:this={container} class={[classValue]}>
|
||||
{#if !ready}
|
||||
{@render children?.()}
|
||||
<div class={['relative', classValue]}>
|
||||
{#if showLoading}
|
||||
<div class="absolute inset-0">{@render loading?.()}</div>
|
||||
{/if}
|
||||
|
||||
{#if joinedMeeting && api && joined}
|
||||
<div class="absolute inset-0 pointer-events-none">{@render joined(api)}</div>
|
||||
{/if}
|
||||
|
||||
{#if leftMeeting}
|
||||
<div class="absolute inset-0">{@render left?.()}</div>
|
||||
{/if}
|
||||
|
||||
<div id="jitsi-container" bind:this={container} class={['w-full h-full min-w-0']}></div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user