import { MelEnumerable } from '../../../../../mel_metapage/js/lib/classes/enum.js';
import { EMPTY_STRING } from '../../../../../mel_metapage/js/lib/constants/constants.js';
import { BnumEvent } from '../../../../../mel_metapage/js/lib/mel_events.js';
import { Mel_Promise } from '../../../../../mel_metapage/js/lib/mel_promise.js';
export { JitsiAdaptor, ESourceType, EUserRole };
/**
* Contient les classes utiles à la communication avec Jitsi
* @module Visio/Jitsi
* @local JitsiAdaptor
* @local MutedStatus
* @local VideoMuteStatusChangedCallback
* @local VisibilityStatus
* @local VisibilityStatusChangedCallback
* @local MouseEventStructure
* @local EventMouse
* @local ChatUpdated
* @local ChatUpdatedCallback
* @local EnabledStatus
* @local TileViewChangedCallback
* @local VideoConferenceJoined
* @local VideoConferenceJoinedCallback
* @local ESourceType
* @local ScreenSharingDetails
* @local ScreenSharingObject
* @local ScreenSharingStatusChangedCallback
* @local EUserRole
* @local Participant
* @local Room
* @local ResponseRooms
* @local Device
* @local DeviceEx
* @local DevicesObject
* @local CurrentDevicesObject
*/
/**
* @typedef Participant
* @property {string} id
* @property {string} jid
* @property {EUserRole} role
* @property {string} displayName
*/
/**
* @typedef Room
* @property {boolean} isMainRoom,
* @property {string} id
* @property {string} jid
* @property {Participant[]} participants
* @frommoduleproperty Visio/Jitsi participants {@linkto Participant}
*/
/**
* @typedef ResponseRooms
* @property {Room[]} rooms
* @frommoduleproperty Visio/Jitsi rooms {@linkto Room}
*/
/**
* @typedef DeviceEx
* @property {string} deviceId
* @property {string} groupId
* @property {string} kind
* @property {string} label
* @property {boolean} isCurrent
*/
/**
* @typedef Device
* @property {string} deviceId
* @property {string} groupId
* @property {string} kind
* @property {string} label
*/
/**
* @typedef DevicesObject
* @property {Device[]} audioInput
* @property {Device[]} audioOutput
* @property {Device[]} videoInput
*/
/**
* @typedef CurrentDevicesObject
* @property {Device} audioInput
* @property {Device} audioOutput
* @property {Device} videoInput
*/
/**
* @typedef MutedStatus
* @property {boolean} muted
*/
/**
* @typedef VisibilityStatus
* @property {boolean} visible
*/
/**
* @typedef EnabledStatus
* @property {boolean} enabled
*/
/**
* @typedef MouseEventStructure
* @property {number} clientX
* @property {number} clientY
* @property {number} movementX
* @property {number} movementY
* @property {number} offsetX
* @property {number} offsetY
* @property {number} pageX
* @property {number} pageY
* @property {number} x
* @property {number} y
* @property {number} screenX
* @property {number} screenY
*/
/**
* @typedef EventMouse
* @property {MouseEventStructure} event
*/
/**
* @typedef ChatUpdated
* @property {boolean} isOpen Si le tchat est ouvert ou non
* @property {number} unreadCount Si il y a des messages non-lu
*/
/**
* @typedef VideoConferenceJoined
* @property {string} roomName Nom de la salle
* @property {string} id Id du participant
* @property {string} displayName Nom du participant
* @property {string} avatarURL Url de l'avatr du participant
* @property {boolean} breakoutRoom Si la salle est une sous salle ou non
* @property {boolean} visitor Si l'utilisateur est un visiteur ou non
*/
/**
* @typedef RaiseHand
* @property {string} id Id de l'utilisateur qui à lever/baisser la main
* @property {number} handRaised 0 quand la main est baissé, timestamp sinon
*/
/**
* @typedef ScreenSharingDetails
* @property {ESourceType | undefined} sourceType
*/
/**
* @typedef ScreenSharingObject
* @property {boolean} on Si le partage d'écran est actif ou non
* @property {ScreenSharingDetails} details Où le partage à lieu, si c'est connu
*/
/**
* @callback MuteStatusChangedCallback
* @param {MutedStatus} status Nouveau status
* @return {void}
*/
/**
* @callback VisibilityStatusChangedCallback
* @param {VisibilityStatus} status Nouvelle visibilitée
* @return {void}
*/
/**
* @callback MouseMoveCallback
* @param {EventMouse} obj Données de la souris
* @return {void}
*/
/**
* @callback ChatUpdatedCallback
* @param {ChatUpdated} obj Données du tchat
* @return {void}
*/
/**
* @callback TileViewChangedCallback
* @param {EnabledStatus} obj
* @return {void}
*/
/**
* @callback VideoConferenceJoinedCallback
* @param {VideoConferenceJoined} obj
* @return {void}
*/
/**
* @callback RaiseHandUpdatedCallback
* @param {RaiseHand} obj
* @return {void}
*/
/**
* @callback ScreenSharingStatusChangedCallback
* @param {ScreenSharingObject} obj
* @return {void}
*/
/**
* @class
* @classdesc Gère les actions et les listeners avec l'api de Jitsi
*/
class JitsiAdaptor {
/**
* Lie la visio a jitsi
* @param {external:JitsiMeetExternalAPI} jitsi Jitsi Api
*/
constructor(jitsi) {
/**
* Api
* @type {external:JitsiMeetExternalAPI}
* @package
*/
this._jitsi = jitsi;
/**
* Mot de passe de la visio
* @type {?string}
* @package
*/
this._password = null;
/**
* Appelé lorsque l'on quitte
* @type {BnumEvent<function(string:void)>}
* @event
*/
this.on_video_conference_left = new BnumEvent();
/**
* Est appelé lorsque la caméra est activé/désactivé
* @type {BnumEvent<MuteStatusChangedCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto MuteStatusChangedCallback}
*/
this.on_video_mute_status_changed = new BnumEvent();
/**
* Est appelé lorsque le micro est activé/désactivé
* @type {BnumEvent<MuteStatusChangedCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto MuteStatusChangedCallback}
*/
this.on_audio_mute_status_changed = new BnumEvent();
/**
* Est appelé lorsque la liste des utilisateurs est affichée ou non
* @type {BnumEvent<VisibilityStatusChangedCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto VisibilityStatusChangedCallback}
*/
this.on_film_strip_display_changed = new BnumEvent();
/**
* Est appelé lorsque la souris se déplace dans la frame de la visio
* @type {BnumEvent<MouseMoveCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto MouseMoveCallback}
*/
this.on_mouse_move = new BnumEvent();
/**
* Est appelé lorsque le chat est ouvert/fermé et quand il y a des messages non-lu
* @type {BnumEvent<ChatUpdatedCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto ChatUpdatedCallback}
*/
this.on_chat_updated = new BnumEvent();
//this.on_chat_updated = new BnumEvent();
/**
* Est appelé lorsque la mosaïque est activée/désactivée
* @type {BnumEvent<TileViewChangedCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto TileViewChangedCallback}
*/
this.on_tile_view_updated = new BnumEvent();
/**
* Est appelé lorsque la visio est complètement arretée
* @type {BnumEvent<Function>}
* @event
*/
this.on_ready_to_close = new BnumEvent();
/**
* Est appelé lorsque la visio à besoin d'un mot de passe pour commencer
* @type {BnumEvent<Function>}
* @event
*/
this.on_password_required = new BnumEvent();
/**
* Est appelé lorsqu'une personne rentre dans la visio
* @type {BnumEvent<VideoConferenceJoinedCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto VideoConferenceJoinedCallback}
*/
this.on_video_conference_joined = new BnumEvent();
/**
* Est appelé lorsqu'une personne lève ou baisse la main
* @type {BnumEvent<RaiseHandUpdatedCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto RaiseHandUpdatedCallback}
*/
this.on_raise_hand_updated = new BnumEvent();
/**
* Est appelé lorsque l'écran est partagé ou non
* @type {BnumEvent<ScreenSharingStatusChangedCallback>}
* @event
* @frommodule Visio/Jitsi {@linkto ScreenSharingStatusChangedCallback}
*/
this.on_share_screen_status_changed = new BnumEvent();
this._init_listeners();
}
/**
* Si un mot de passe a été défini
* @returns {boolean}
*/
has_password() {
return !!(this._password || false);
}
/**
* Récupère l'id de l'utilisateur en cours
* @returns {Promise<string>}
* @async
*/
async get_user_id() {
await Mel_Promise.wait(
() => !!this._jitsi._myUserID && this._jitsi._myUserID !== EMPTY_STRING,
);
return this._jitsi._myUserID;
}
/**
* Change l'url de l'avatar
* @param {string} url Url de l'avatar ou données de l'image
*/
change_avatar(url) {
this._jitsi.executeCommand('avatarUrl', url);
}
/**
* Envoi un message dans le tchat
* @param {string} text Message à envoyer
*/
send_message(text) {
this._jitsi.executeCommand('sendChatMessage', text, EMPTY_STRING, true);
}
/**
* Récupère le rôle de l'utilisateur
* @returns {Promise<?EUserRole>}
* @async
* @frommodulereturn Visio/Jitsi {@linkto EUserRole}
*/
async get_role() {
var user;
const [id, rooms] = (
await Promise.allSettled([this.get_user_id(), this.get_rooms_info()])
).map((x) => x.value);
for (const room of rooms.rooms) {
for (const participant of room.participants) {
if (participant.id === id) {
user = participant;
break;
}
}
if (user) break;
}
return user?.role;
}
/**
* Si l'utilisateur en cours est modérateur de la room ou non
* @returns {Promise<boolean>}
* @async
*/
async is_moderator() {
return (await this.get_role()) === EUserRole.moderator; //'moderator';
}
/**
* Défini le nouveau mot de passe de la visio
* @param {string} password Mot de passe
* @async
*/
async set_password(password) {
if (await this.is_moderator()) {
this._jitsi.executeCommand('password', password);
if (password === EMPTY_STRING) password = null;
this._password = password;
}
}
/**
* Récupère les informations des salles
* @returns {Promise<ResponseRooms>}
* @async
* @frommodulereturn Visio/Jitsi {@linkto ResponseRooms}
*/
async get_rooms_info() {
return await this._jitsi.getRoomsInfo();
}
/**
* Si la video est désactivée
* @returns {Promise<boolean>}
* @async
*/
async is_video_muted() {
return await this._jitsi.isVideoMuted();
}
/**
* Si le micro est désactivé
* @returns {Promise<boolean>}
* @async
*/
async is_audio_muted() {
return await this._jitsi.isAudioMuted();
}
/**
* Change d'état la caméra
*/
toggle_video() {
this._jitsi.executeCommand('toggleVideo');
}
/**
* Change d'état le micro
*/
toggle_micro() {
this._jitsi.executeCommand('toggleAudio');
}
/**
* Active ou désactive le partage d'écran
*/
share_screen() {
this._jitsi.executeCommand('toggleShareScreen');
}
/**
* Active ou désactive la mosaïque
* @deprecated Utilisez plutôt toggle_tile_view
*/
toggle_film_strip() {
this._jitsi.executeCommand('toggleTileView');
}
/**
* Active ou désactive la mosaïque
*/
toggle_tile_view() {
this._jitsi.executeCommand('toggleTileView');
}
/**
* Lève ou baisse la main
*/
toggle_hand() {
this._jitsi.executeCommand('toggleRaiseHand');
}
/**
* Affiche ou ferme le tchat
*/
toggle_chat() {
this._jitsi.executeCommand('toggleChat');
}
/**
* Ouvre le gestionnaire d'arrière plan
*/
open_virtual_background() {
this._jitsi.executeCommand('toggleVirtualBackgroundDialog');
}
/**
* Commencer un tchat privé
* @param {string} id Id du participant
*/
initiate_private_chat(id) {
this._jitsi.executeCommand('initiatePrivateChat', id);
}
/**
* Check si la fenêtre des participants est ouverte ou non
* @returns {Promise<boolean>}
* @async
*/
async is_participants_pane_open() {
return await this._jitsi.isParticipantsPaneOpen();
}
/**
* Affiche ou ferme la fenêtre des participants
* @returns {Promise<{old_state:boolean, new_state:boolean}>} Ancien et nouvel état
* @async
*/
async toggle_participants_pane() {
const state = await this.is_participants_pane_open();
this.set_participants_pane_state(!state);
return { old_state: state, new_state: !state };
}
/**
* Affiche ou ferme la fenêtre des participants
* @param {boolean} state true = ouvert, false = fermé
*/
set_participants_pane_state(state) {
this._jitsi.executeCommand('toggleParticipantsPane', state);
}
/**
* Récupère les inputs et outputs disponibles
* @returns {Promise<DevicesObject>}
* @frommodulereturn Visio/Jitsi {@linkto DevicesObject}
* @async
*/
async get_available_devices() {
return await this._jitsi.getAvailableDevices();
}
/**
* Récupère les inputs et outputs séléctionnés
* @returns {Promise<CurrentDevicesObject>}
* @async
* @frommodulereturn Visio/Jitsi {@linkto CurrentDevicesObject}
*/
async get_current_devices() {
return await this._jitsi.getCurrentDevices();
}
/**
* Récupère les input et les output audio.
* @returns {Promise<Array<DeviceEx>>}
* @async
* @frommodulereturn Visio/Jitsi {@linkto DeviceEx}
*/
async get_micro_and_audio_devices() {
var [devices, current_devices] = (
await Promise.allSettled([
this.get_available_devices(),
this.get_current_devices(),
])
).map((x) => x.value);
devices = MelEnumerable.from(devices.audioOutput)
.union(devices.audioInput)
.toArray();
for (const key in current_devices) {
if (
key !== 'videoInput' &&
Object.hasOwnProperty.call(current_devices, key)
) {
const device = current_devices[key];
for (const _key in devices) {
if (Object.hasOwnProperty.call(devices, _key)) {
const element = devices[_key];
if (element.deviceId === device.deviceId)
devices[_key].isCurrent = true;
else devices[_key].isCurrent = false;
}
}
}
}
return devices;
}
/**
* Change le micro de la visio
* @param {string} label Nom du périphérique
* @param {string} id id du périphérique
*/
set_micro_device(label, id) {
this._jitsi.setAudioInputDevice(label, id);
}
/**
* Change la sortie audio de la visio
* @param {string} label Nom du périphérique
* @param {string} id id du périphérique
*/
set_audio_device(label, id) {
this._jitsi.setAudioOutputDevice(label, id);
}
/**
* Change la caméra de la visio
* @param {string} label Nom du périphérique
* @param {string} id id du périphérique
*/
set_video_device(label, id) {
this._jitsi.setVideoInputDevice(label, id);
}
/**
* Récupère les données de la visio
* @returns {Promise<Participant>}
* @async
* @deprecated
* @frommodulereturn Visio/Jitsi {@linkto Participant}
*/
async get_room_infos() {
return this._jitsi.getParticipantsInfo();
}
/**
* Récupère les input videos.
* @returns {Promise<Array<DeviceEx>>}
* @async
* @frommodulereturn Visio/Jitsi {@linkto DeviceEx}
*/
async get_video_devices() {
var [devices, current_devices] = (
await Promise.allSettled([
this.get_available_devices(),
this.get_current_devices(),
])
).map((x) => x.value);
devices = devices.videoInput;
if (current_devices.videoInput !== undefined) {
for (const key in devices) {
if (Object.hasOwnProperty.call(devices, key)) {
const element = devices[key];
if (element.deviceId === current_devices.videoInput.deviceId)
devices[key].isCurrent = true;
else devices[key].isCurrent = false;
}
}
}
return devices;
}
/**
* Affiche une notification Jitsi
* @param {string} content Contenu du titre
* @param {Object} [options={}]
* @param {string | undefined} [options.uid=undefined] Id de la notification
* @param {string} [options.type='info'] Type de notification
* @param {string} [options.timeout='short'] Temps d'affichage de la notification
* @see {@link https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe-commands#shownotification}
*
*/
show_notification(
content,
{ uid = undefined, type = 'info', timeout = 'short' } = {},
) {
this._jitsi.executeCommand('showNotification', {
title: content,
content,
uid,
type,
timeout,
});
}
/**
* Redimensionne la liste des participants
* @param {number} new_size Nouvelle taille
*/
resize_film_strip(new_size) {
this._jitsi.executeCommand('resizeFilmStrip', {
width: new_size,
});
}
/**
* Récupère la frame qui contient Jitsi
* @param {Object} [options={}]
* @param {boolean} [options.jquery=true] Si on récupère un objet jQuery ou HTMLIframeElement
* @returns {HTMLIFrameElement | external:jQuery}
*/
get_frame({ jquery = true } = {}) {
let frame = this._jitsi.getIFrame();
return jquery ? $(frame) : frame;
}
/**
* Arrête la visio
*/
hangup() {
this._jitsi.executeCommand('hangup');
}
/**
* Initialise les listeners
* @returns {JitsiAdaptor} Chaînage
* @package
*/
_init_listeners() {
return this._add_listener(
'videoMuteStatusChanged',
'on_video_mute_status_changed',
)
._add_listener('audioMuteStatusChanged', 'on_audio_mute_status_changed')
._add_listener('filmstripDisplayChanged', 'on_film_strip_display_changed')
._add_listener('chatUpdated', 'on_chat_updated')
._add_listener('tileViewChanged', 'on_tile_view_updated')
._add_listener('raiseHandUpdated', 'on_raise_hand_updated')
._add_listener(
'screenSharingStatusChanged',
'on_share_screen_status_changed',
)
._add_listener('mouseMove', 'on_mouse_move')
._add_listener('readyToClose', 'on_ready_to_close')
._add_listener('videoConferenceLeft', 'on_video_conference_left');
}
/**
* Ajoute un listener
* @param {string} name Nom du listener
* @param {string} prop Nom du bnum event qui sera appelé
* @returns {JitsiAdaptor} Chaînage
*/
_add_listener(name, prop) {
this._jitsi.addEventListener(name, this[prop].call.bind(this[prop]));
return this;
}
}
/**
* Type de source d'un partage d'écran
* @enum {string}
* @readonly
*/
const ESourceType = Object.freeze({
/**
* On partage une fenêtre
* @type {string}
* @default 'window'
*/
window: 'window',
/**
* On partage un écran
* @type {string}
* @default 'screen'
*/
screen: 'screen',
/**
* @type {string}
* @default 'proxy'
*/
proxy: 'proxy',
/**
* @type {string}
* @default 'device'
*/
device: 'device',
});
/**
* Rôle possible d'un utilisateur
* @enum {string}
* @constant
*/
const EUserRole = Object.freeze({
user: 'user',
moderator: 'moderator',
});