feat(idaa): implement jitsi report streaming and conference lifecycle improvements

- Refactor Jitsi reports to use SvelteKit streaming with a skeleton loader.
- Add conference lifecycle event listeners (left, close) to video conference page.
- Implement manual Novi data re-sync and improve initialization robustness.
- Fix skeleton visibility by using standard Tailwind colors.
This commit is contained in:
Scott Idem
2026-01-30 10:16:37 -05:00
parent c626a0f9df
commit 8c99f5abed
3 changed files with 150 additions and 103 deletions

View File

@@ -215,16 +215,28 @@
}); });
// --- Discrete Event Logging --- // --- Discrete Event Logging ---
// NOTE: This does not seem to be triggered.
api.on('raiseHandUpdated', (participant: { id:string; raisesHand: boolean }) => { api.on('raiseHandUpdated', (participant: { id:string; raisesHand: boolean }) => {
if (participant.raisesHand) { if (participant.raisesHand) {
const p_info = meeting_participants.get(participant.id); const p_info = meeting_participants.get(participant.id);
if (p_info) {
console.log('Jitsi Event: raiseHandUpdated', p_info); console.log('Jitsi Event: raiseHandUpdated', p_info);
create_discrete_activity_log('jitsi_meeting_raise_hand', 'raiseHandUpdated', { create_discrete_activity_log('jitsi_meeting_raise_hand', 'raiseHandUpdated', {
attendee_id: p_info.id, attendee_id: p_info.id,
full_name: p_info.displayName, full_name: p_info.displayName,
}); });
} }
}
});
api.on('videoConferenceLeft', () => {
console.log('Jitsi Event: videoConferenceLeft');
if (duration_timer_id) clearInterval(duration_timer_id);
// meeting_duration = '00:00:00';
});
api.on('readyToClose', () => {
console.log('Jitsi Event: readyToClose');
show_jitsi_container = false;
}); });
} }
@@ -277,7 +289,7 @@
settings: { settings: {
startMuted: data.params.start_muted === 'true', startMuted: data.params.start_muted === 'true',
startHidden: data.params.start_hidden === 'true', startHidden: data.params.start_hidden === 'true',
reactionsMuted: data.params.reactions_muted === 'true' reactionsMuted: disable_reaction_sound
}, },
config: { config: {
'prejoinConfig.enabled': false 'prejoinConfig.enabled': false
@@ -301,19 +313,13 @@
} }
} }
onMount(async () => { async function fetch_novi_data() {
if (log_lvl) {
console.log('Jitsi: onMount - fetching user data and initializing Jitsi...');
}
const url_params = data.params; const url_params = data.params;
if (log_lvl > 1) {
console.log('Jitsi: url_params:', url_params);
}
// --- Start with fallback data from URL --- // --- Start with fallback data from URL ---
user_id = url_params.uuid; // Novi Customer GUID user_id = url_params.uuid; // Novi Customer GUID
display_name = url_params.full_name ?? 'Guest'; // May be overridden display_name = url_params.full_name ?? 'Guest'; // May be overridden
email = (url_params.email ?? 'guest@example.com').replace(/\s+/g, '+'); // May be overridden email = (url_params.email ?? 'guest@example.com').replace(/\s+/g, '+'); // May be overridden
is_moderator = url_params.moderator === 'true'; // URL fallback
room_name = url_params.room ?? 'Default-Room'; room_name = url_params.room ?? 'Default-Room';
domain = url_params.domain ?? 'jitsi.dgrzone.com'; domain = url_params.domain ?? 'jitsi.dgrzone.com';
@@ -370,12 +376,26 @@
console.log(`Jitsi: User ${user_id} is not a moderator.`); console.log(`Jitsi: User ${user_id} is not a moderator.`);
} }
} else { } else {
console.warn('Jitsi: Novi API not configured. Skipping user details/moderator check.'); console.warn('Jitsi: Novi API not configured. Using URL fallback for user details/moderator check.');
} }
// Set initial value for the profile editor inputs // Set initial value for the profile editor inputs
name_input = display_name ?? ''; name_input = display_name ?? '';
email_input = email ?? ''; email_input = email ?? '';
}
async function handle_novi_resync() {
console.log('Jitsi: Manually re-syncing Novi Data...');
await fetch_novi_data();
await init_jitsi();
}
onMount(async () => {
if (log_lvl) {
console.log('Jitsi: onMount - fetching user data and initializing Jitsi...');
}
await fetch_novi_data();
// --- All data fetched, now initialize Jitsi --- // --- All data fetched, now initialize Jitsi ---
await init_jitsi(); await init_jitsi();
@@ -538,6 +558,17 @@
jitsi_api = null; jitsi_api = null;
} }
// --- Check for Jitsi API script ---
// @ts-ignore
if (typeof JitsiMeetExternalAPI === 'undefined') {
console.error('Jitsi: JitsiMeetExternalAPI script not loaded yet.');
const container = document.getElementById(jitsi_container_id);
if (container) {
container.innerHTML = '<h1>Jitsi API script not loaded. Please refresh the page.</h1>';
}
return;
}
console.log('Jitsi: Initializing Jitsi meeting interface...'); console.log('Jitsi: Initializing Jitsi meeting interface...');
// These variables are now expected to be set in the component's state // These variables are now expected to be set in the component's state
@@ -869,10 +900,7 @@
<button <button
class="mt-2 px-2 py-1 bg-blue-200 text-white rounded hover:bg-blue-400" class="mt-2 px-2 py-1 bg-blue-200 text-white rounded hover:bg-blue-400"
onclick={() => { onclick={handle_novi_resync}
// Placeholder for function calls to update Novi data
console.log('Re-sync Novi Data button clicked. Implement as needed.');
}}
title="Re-synchronize Novi data" title="Re-synchronize Novi data"
> >
<span class="fas fa-sync" aria-hidden="true"></span> <span class="fas fa-sync" aria-hidden="true"></span>
@@ -907,9 +935,9 @@
} }
.jitsi-tools { .jitsi-tools {
z-index: 1000; z-index: 10;
position: fixed; position: fixed;
bottom: 6em; bottom: 10em;
right: 20px; right: 20px;
/* background-color: rgba(255, 255, 255, 0.8); */ /* background-color: rgba(255, 255, 255, 0.8); */
padding: 10px; padding: 10px;

View File

@@ -20,9 +20,17 @@
<div class="p-4 space-y-4"> <div class="p-4 space-y-4">
<h1 class="h1">Jitsi Meeting Reports</h1> <h1 class="h1">Jitsi Meeting Reports</h1>
{#if data.meetings && data.meetings.length > 0} {#await data.streamed.meetings}
<div class="space-y-4 animate-pulse">
<div class="h-12 w-full bg-gray-200 dark:bg-gray-700 rounded"></div>
<div class="h-12 w-full bg-gray-200 dark:bg-gray-700 rounded"></div>
<div class="h-12 w-full bg-gray-200 dark:bg-gray-700 rounded"></div>
<div class="h-12 w-full bg-gray-200 dark:bg-gray-700 rounded"></div>
</div>
{:then meetings}
{#if meetings && meetings.length > 0}
<div class="space-y-2"> <div class="space-y-2">
{#each data.meetings as meeting (meeting.meeting_id)} {#each meetings as meeting (meeting.meeting_id)}
<div class="card card-hover"> <div class="card card-hover">
<!-- svelte-ignore a11y_click_events_have_key_events --> <!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions --> <!-- svelte-ignore a11y_no_static_element_interactions -->
@@ -102,4 +110,11 @@
<p>There are no Jitsi activity logs to display.</p> <p>There are no Jitsi activity logs to display.</p>
</div> </div>
{/if} {/if}
{:catch error}
<div class="card p-4 bg-red-100 text-red-900 border-l-4 border-red-500">
<h3 class="h3 font-bold">Error Loading Reports</h3>
<p>An error occurred while fetching the meeting reports:</p>
<pre class="mt-2 text-xs overflow-auto">{error.message}</pre>
</div>
{/await}
</div> </div>

View File

@@ -12,17 +12,21 @@ export const load: PageLoad = async ({ fetch }) => {
if (!api_cfg || !account_id) { if (!api_cfg || !account_id) {
console.error('API config or Account ID not available for loading Jitsi reports.'); console.error('API config or Account ID not available for loading Jitsi reports.');
return { return {
meetings: [] streamed: {
meetings: Promise.resolve([])
}
}; };
} }
const meetings = await load_jitsi_report({ const meetings_promise = load_jitsi_report({
api_cfg, api_cfg,
account_id, account_id,
log_lvl: 1 log_lvl: 1
}); });
return { return {
meetings streamed: {
meetings: meetings_promise
}
}; };
}; };