Things are looking good. Now have a tools and setting pop up thing.
This commit is contained in:
@@ -11,6 +11,12 @@
|
||||
let show_jitsi_tools: boolean = $state(true);
|
||||
let expand_jitsi_tools: boolean = $state(false);
|
||||
|
||||
// Toggles for collapsible sections
|
||||
let show_meeting_details: boolean = $state(false);
|
||||
let show_live_stats: boolean = $state(false);
|
||||
let show_name_changer: boolean = $state(false);
|
||||
let show_sound_settings: boolean = $state(false);
|
||||
|
||||
let user_id: null | string = $state(null);
|
||||
let display_name: null | string = $state(null);
|
||||
let email: null | string = $state(null);
|
||||
@@ -30,6 +36,84 @@
|
||||
|
||||
let name_input: string = $state('');
|
||||
|
||||
// State for Live Meeting Stats
|
||||
let meeting_participants = $state(new Map<string, any>());
|
||||
let meeting_start_time: Date | null = $state(null);
|
||||
let meeting_duration: string = $state('00:00:00');
|
||||
let duration_timer_id: any = $state(null);
|
||||
|
||||
function add_jitsi_event_listeners(api: any) {
|
||||
api.on('videoConferenceJoined', (data: { id: string; displayName: string }) => {
|
||||
console.log('Jitsi Event: videoConferenceJoined', data);
|
||||
meeting_start_time = new Date();
|
||||
|
||||
if (duration_timer_id) clearInterval(duration_timer_id);
|
||||
|
||||
duration_timer_id = setInterval(() => {
|
||||
if (meeting_start_time) {
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - meeting_start_time.getTime();
|
||||
const hours = Math.floor(diff / (1000 * 60 * 60))
|
||||
.toString()
|
||||
.padStart(2, '0');
|
||||
const minutes = Math.floor((diff / (1000 * 60)) % 60)
|
||||
.toString()
|
||||
.padStart(2, '0');
|
||||
const seconds = Math.floor((diff / 1000) % 60)
|
||||
.toString()
|
||||
.padStart(2, '0');
|
||||
meeting_duration = `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
const local_participant = {
|
||||
id: data.id,
|
||||
displayName: data.displayName,
|
||||
role: 'participant'
|
||||
};
|
||||
meeting_participants.set(data.id, local_participant);
|
||||
|
||||
const all_participants = api.getParticipantsInfo();
|
||||
all_participants.forEach((p: { participantId: string; displayName: string }) => {
|
||||
if (!meeting_participants.has(p.participantId)) {
|
||||
meeting_participants.set(p.participantId, {
|
||||
id: p.participantId,
|
||||
displayName: p.displayName,
|
||||
role: 'participant'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
api.on('participantJoined', (participant: { id: string; displayName: string }) => {
|
||||
console.log('Jitsi Event: participantJoined', participant);
|
||||
meeting_participants.set(participant.id, {
|
||||
id: participant.id,
|
||||
displayName: participant.displayName,
|
||||
role: 'participant'
|
||||
});
|
||||
});
|
||||
|
||||
api.on('participantLeft', (participant: { id: string }) => {
|
||||
console.log('Jitsi Event: participantLeft', participant);
|
||||
if (meeting_participants.has(participant.id)) {
|
||||
meeting_participants.delete(participant.id);
|
||||
}
|
||||
});
|
||||
|
||||
api.on(
|
||||
'participantRoleChanged',
|
||||
(participant: { id: string; role: string }) => {
|
||||
console.log('Jitsi Event: participantRoleChanged', participant);
|
||||
if (meeting_participants.has(participant.id)) {
|
||||
const p = meeting_participants.get(participant.id);
|
||||
p.role = participant.role;
|
||||
meeting_participants.set(participant.id, p);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function handle_name_update() {
|
||||
if (name_input && name_input.trim() !== '' && name_input !== display_name) {
|
||||
console.log(`Jitsi: User updating name from "${display_name}" to "${name_input}"`);
|
||||
@@ -167,6 +251,7 @@
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (duration_timer_id) clearInterval(duration_timer_id);
|
||||
if (jitsi_api) {
|
||||
console.log('Jitsi: Disposing of Jitsi API instance on component destroy.');
|
||||
jitsi_api.dispose();
|
||||
@@ -296,6 +381,12 @@
|
||||
* If an existing Jitsi instance is present, it will be disposed of before creating a new one.
|
||||
*/
|
||||
async function init_jitsi() {
|
||||
// Clear stats and timers
|
||||
if (duration_timer_id) clearInterval(duration_timer_id);
|
||||
meeting_participants.clear();
|
||||
meeting_start_time = null;
|
||||
meeting_duration = '00:00:00';
|
||||
|
||||
// Dispose of any existing Jitsi instance to allow for a clean restart
|
||||
if (jitsi_api) {
|
||||
console.log('Jitsi: Disposing of existing Jitsi API instance before re-initialization.');
|
||||
@@ -365,6 +456,7 @@
|
||||
console.log('Jitsi: Initializing JitsiMeetExternalAPI with options:', options);
|
||||
// @ts-ignore
|
||||
jitsi_api = new JitsiMeetExternalAPI(domain, options);
|
||||
add_jitsi_event_listeners(jitsi_api);
|
||||
console.log('Jitsi: JitsiMeetExternalAPI initialized:', jitsi_api);
|
||||
}
|
||||
|
||||
@@ -380,127 +472,198 @@
|
||||
|
||||
|
||||
{#if show_jitsi_tools}
|
||||
<div class="jitsi-tools text-sm max-w-xl outline">
|
||||
<div
|
||||
class:bg-gray-400={expand_jitsi_tools}
|
||||
class="jitsi-tools text-sm max-w-xl">
|
||||
{#if expand_jitsi_tools}
|
||||
<!-- NOTE: This is a <div> instead of <header> to work with IDAA's Novi styles. -->
|
||||
<div class="flex flex-row gap-1 items-center justify-between w-full">
|
||||
<!-- NOTE: This is a <div> instead of <h1> to work with IDAA's Novi styles. -->
|
||||
<div class="text-lg w-fit">IDAA Jitsi</div>
|
||||
<div class="text-base w-fit">
|
||||
IDAA's Jitsi
|
||||
{#if display_name && email}
|
||||
<span class="bg-amber-50 px-1 py-0.5 rounded font-bold">{display_name} ({email})</span>
|
||||
{/if}
|
||||
</div>
|
||||
<button
|
||||
class="px-2 py-1 bg-red-200 text-white rounded hover:bg-red-400"
|
||||
onclick={() => (expand_jitsi_tools = false)}
|
||||
title="Close Jitsi Tools"
|
||||
>
|
||||
<span class="fas fa-times" aria-hidden="true"></span>
|
||||
<span class="sr-only">Close Jitsi Tools</span>
|
||||
</button>
|
||||
</div>
|
||||
<ul>
|
||||
{#if display_name && email}
|
||||
<li class="bg-amber-50">{display_name} ({email})</li>
|
||||
{/if}
|
||||
<li>Room: <span class="text-sm font-mono">{room_name}</span></li>
|
||||
<li>Domain: <span class="text-sm font-mono">{domain}</span></li>
|
||||
<li>User ID: <span class="text-sm font-mono">{user_id}</span></li>
|
||||
<li>Moderator: {is_moderator ? 'Yes' : 'No'}</li>
|
||||
</ul>
|
||||
|
||||
<div class="mt-2 pt-2 border-t-2 border-dashed border-gray-400">
|
||||
<label for="display_name_input" class="block font-bold">Change Name:</label>
|
||||
<span class="w-full flex flex-row items-center justify-between gap-1">
|
||||
<input
|
||||
type="text"
|
||||
id="display_name_input"
|
||||
bind:value={name_input}
|
||||
class="border rounded px-2 py-1 w-full"
|
||||
placeholder="Enter new display name"
|
||||
/>
|
||||
<!-- Meeting Details -->
|
||||
<div class="mt-1">
|
||||
<button
|
||||
onclick={handle_name_update}
|
||||
class="px-2 py-1 bg-green-200 text-white rounded hover:bg-green-400"
|
||||
onclick={() => (show_meeting_details = !show_meeting_details)}
|
||||
class="w-full flex justify-between items-center font-bold text-left"
|
||||
>
|
||||
<span class="fas fa-user-edit" aria-hidden="true"></span>
|
||||
Update
|
||||
<span class="sr-only"> Display Name</span>
|
||||
Meeting Details
|
||||
<span class="fas {show_meeting_details ? 'fa-chevron-up' : 'fa-chevron-down'}"></span>
|
||||
</button>
|
||||
</span>
|
||||
{#if show_meeting_details}
|
||||
<ul class="mt-1 pl-2">
|
||||
<!-- {#if display_name && email}
|
||||
<li class="bg-amber-50">{display_name} ({email})</li>
|
||||
{/if} -->
|
||||
<li>Room: <span class="text-sm font-mono font-bold">{room_name}</span></li>
|
||||
<li>Domain: <span class="text-sm font-mono">{domain}</span></li>
|
||||
<li>User ID: <span class="text-sm font-mono">{user_id}</span></li>
|
||||
<li>Moderator: {is_moderator ? 'Yes' : 'No'}</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- margin-top: 1.5em; border-top: 2px dashed #ccc; padding: 1em; background-color: pink; -->
|
||||
<div
|
||||
class="
|
||||
mt-2 pt-2 border-t-2 border-dashed border-gray-400
|
||||
flex flex-col lg:flex-row gap-05
|
||||
"
|
||||
<!-- Live Meeting Stats -->
|
||||
<div class="mt-2 pt-2 border-t-2 border-dashed border-gray-400">
|
||||
<button
|
||||
onclick={() => (show_live_stats = !show_live_stats)}
|
||||
class="w-full flex justify-between items-center font-bold text-left"
|
||||
>
|
||||
<strong>Jitsi Sound Settings:</strong>
|
||||
Live Meeting Stats
|
||||
<span class="fas {show_live_stats ? 'fa-chevron-up' : 'fa-chevron-down'}"></span>
|
||||
</button>
|
||||
{#if show_live_stats}
|
||||
<div class="mt-1 pl-2">
|
||||
<p>Duration: <span class="font-mono">{meeting_duration}</span></p>
|
||||
<p>Total Participants: {meeting_participants.size}</p>
|
||||
<div class="mt-1">
|
||||
<div class="font-bold">Moderators:</div>
|
||||
<ul class="pl-4 list-disc">
|
||||
{#each Array.from(meeting_participants.values()).filter((p) => p.role === 'moderator') as mod (mod.id)}
|
||||
<li>{mod.displayName}</li>
|
||||
{:else}
|
||||
<li class="text-gray-500 italic">None</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<h4 class="font-semibold">Participants:</h4>
|
||||
<ul class="pl-4 list-disc">
|
||||
{#each Array.from(meeting_participants.values()).filter((p) => p.role !== 'moderator') as person (person.id)}
|
||||
<li>{person.displayName}</li>
|
||||
{:else}
|
||||
<li class="text-gray-500 italic">None</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- WARNING: Fully disables the Play sound on option in the Jitsi > Settings > Notifications tab. -->
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_incoming_msg_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<span>Disable Incoming Message Sound</span>
|
||||
</label>
|
||||
<!-- Change Name -->
|
||||
<div class="mt-2 pt-2 border-t-2 border-dashed border-gray-400">
|
||||
<button
|
||||
onclick={() => (show_name_changer = !show_name_changer)}
|
||||
class="w-full flex justify-between items-center font-bold text-left"
|
||||
>
|
||||
Change Name
|
||||
<span class="fas {show_name_changer ? 'fa-chevron-up' : 'fa-chevron-down'}"></span>
|
||||
</button>
|
||||
{#if show_name_changer}
|
||||
<div class="mt-1 pl-2">
|
||||
<span class="w-full flex flex-row items-center justify-between gap-1">
|
||||
<input
|
||||
type="text"
|
||||
id="display_name_input"
|
||||
bind:value={name_input}
|
||||
class="border rounded px-2 py-1 w-full"
|
||||
placeholder="Enter new display name"
|
||||
/>
|
||||
<button
|
||||
onclick={handle_name_update}
|
||||
class="px-2 py-1 bg-green-200 text-white rounded hover:bg-green-400"
|
||||
title="Update your display name"
|
||||
>
|
||||
<span class="fas fa-user-edit" aria-hidden="true"></span>
|
||||
<span class="sr-only">Update Display Name</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_participant_joined_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<span>Disable Participant Joined Sound</span>
|
||||
</label>
|
||||
<!-- Jitsi Sound Settings -->
|
||||
<div class="mt-2 pt-2 border-t-2 border-dashed border-gray-400">
|
||||
<button
|
||||
onclick={() => (show_sound_settings = !show_sound_settings)}
|
||||
class="w-full flex justify-between items-center font-bold text-left"
|
||||
>
|
||||
Sound Settings
|
||||
<span class="fas {show_sound_settings ? 'fa-chevron-up' : 'fa-chevron-down'}"></span>
|
||||
</button>
|
||||
{#if show_sound_settings}
|
||||
<div class="mt-1 pl-2 flex flex-col">
|
||||
<!-- WARNING: Fully disables the Play sound on option in the Jitsi > Settings > Notifications tab. -->
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_incoming_msg_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<span>Disable Incoming Message Sound</span>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_participant_left_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<span>Disable Participant Left Sound</span>
|
||||
</label>
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_participant_joined_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<span>Disable Participant Joined Sound</span>
|
||||
</label>
|
||||
|
||||
<!-- FUTURE: Under Notifications: Talk while muted -->
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_participant_left_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<span>Disable Participant Left Sound</span>
|
||||
</label>
|
||||
|
||||
<!-- FUTURE: Under Notifications: Participant entered lobby -->
|
||||
<!-- FUTURE: Under Notifications: Talk while muted -->
|
||||
<!-- FUTURE: Under Notifications: Participant entered lobby -->
|
||||
|
||||
<!--
|
||||
FUTURE: Under Moderator:
|
||||
"Everyone starts muted"
|
||||
"Everyone starts hidden"
|
||||
"Everyone follows me"
|
||||
"Recorder follows me"
|
||||
-->
|
||||
<!--
|
||||
FUTURE: Under Moderator:
|
||||
"Everyone starts muted"
|
||||
"Everyone starts hidden"
|
||||
"Everyone follows me"
|
||||
"Recorder follows me"
|
||||
-->
|
||||
|
||||
<!-- WARNING: This does not seem to work. It should be unchecking Moderator options under Jitsi > Settings > Moderator tab. -->
|
||||
<!-- NOTE: This does not seem to work as expected. It does not seem to do anything at all? -->
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_reaction_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<!-- Full text from Jitsi Settings popup: "Mute reaction sounds for everyone" -->
|
||||
<span>Disable Reaction Sound</span>
|
||||
</label>
|
||||
<!-- WARNING: This does not seem to work. It should be unchecking Moderator options under Jitsi > Settings > Moderator tab. -->
|
||||
<!-- NOTE: This does not seem to work as expected. It does not seem to do anything at all? -->
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_reaction_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<!-- Full text from Jitsi Settings popup: "Mute reaction sounds for everyone" -->
|
||||
<span>Disable Reaction Sound</span>
|
||||
</label>
|
||||
|
||||
<!-- WARNING: What does this correspond to in the Jitsi settings? -->
|
||||
<!-- NOTE: This does not seem to work as expected. It does not seem to do anything at all? -->
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_raise_hand_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<span>Disable Raise Hand Sound</span>
|
||||
</label>
|
||||
<!-- WARNING: What does this correspond to in the Jitsi settings? -->
|
||||
<!-- NOTE: This does not seem to work as expected. It does not seem to do anything at all? -->
|
||||
<label class="flex items-center space-x-2 mt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={disable_raise_hand_sound}
|
||||
disabled={!is_moderator}
|
||||
onchange={init_jitsi}
|
||||
/>
|
||||
<span>Disable Raise Hand Sound</span>
|
||||
</label>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="mt-2 pt-2 border-t-2 border-dashed border-gray-400">
|
||||
@@ -508,6 +671,7 @@
|
||||
<button
|
||||
class="mt-2 px-2 py-1 bg-orange-200 text-white rounded hover:bg-orange-400"
|
||||
onclick={() => init_jitsi()}
|
||||
title="Re-initialize Jitsi Meeting"
|
||||
>
|
||||
<span class="fas fa-redo" aria-hidden="true"></span>
|
||||
Re-initialize Jitsi
|
||||
@@ -517,6 +681,7 @@
|
||||
<button
|
||||
onclick={() => jitsi_api && jitsi_api.executeCommand('endConference')}
|
||||
class="mt-2 px-2 py-1 bg-red-200 text-white rounded hover:bg-red-400"
|
||||
title="End meeting for all participants"
|
||||
>
|
||||
<span class="fas fa-phone-slash" aria-hidden="true"></span>
|
||||
End Meeting for *All*
|
||||
@@ -525,6 +690,7 @@
|
||||
<button
|
||||
onclick={() => (show_jitsi_container = !show_jitsi_container)}
|
||||
class="mt-2 px-2 py-1 bg-gray-200 text-white rounded hover:bg-gray-400"
|
||||
title="{show_jitsi_container ? 'Hide' : 'Show'} Jitsi Meeting"
|
||||
>
|
||||
{#if show_jitsi_container}
|
||||
<span class="fas fa-video-slash" aria-hidden="true"></span>
|
||||
@@ -543,6 +709,7 @@
|
||||
// Placeholder for function calls to update Novi data
|
||||
console.log('Re-sync Novi Data button clicked. Implement as needed.');
|
||||
}}
|
||||
title="Re-synchronize Novi data"
|
||||
>
|
||||
<span class="fas fa-sync" aria-hidden="true"></span>
|
||||
Re-sync Novi Data
|
||||
@@ -580,7 +747,7 @@
|
||||
position: fixed;
|
||||
bottom: 6em;
|
||||
right: 20px;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
/* background-color: rgba(255, 255, 255, 0.8); */
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
|
||||
Reference in New Issue
Block a user