import { FramesManager } from '../../../../../mel_metapage/js/lib/classes/frame_manager.js';
import {
MelDialog,
RcmailDialogButton,
} from '../../../../../mel_metapage/js/lib/classes/modal.js';
import { Toolbar } from '../../../../../mel_metapage/js/lib/classes/toolbar.js';
import { EMPTY_STRING } from '../../../../../mel_metapage/js/lib/constants/constants.js';
import { MelHtml } from '../../../../../mel_metapage/js/lib/html/JsHtml/MelHtml.js';
import { isNullOrUndefined } from '../../../../../mel_metapage/js/lib/mel.js';
import { MelObject } from '../../../../../mel_metapage/js/lib/mel_object.js';
import { Mel_Promise } from '../../../../../mel_metapage/js/lib/mel_promise.js';
import { VisioHelper } from '../../../helper.js';
import { MelAudioManager, MelAudioTester } from './audioManager.js';
import { ToolbarPopup } from './toolbar_popup.js';
import { MelVideoManager } from './videoManager.js';
export { ToolbarFunctions, VisioToolbar };
/**
* Contient les classes lié à la toolbar de la visio
* @module Visio/Toolbar
* @local ToolbarFunctions
* @local VisioToolbar
*
*/
/**
* @class
* @classdesc Contient les différentes fonctions des différents boutons de la toolbar
* @hideconstructor
*/
class ToolbarFunctions {
constructor() {
throw new Error('Cannot be initialized !');
}
/**
* Arrête la visioconférence
* @param {Visio} visio
* @static
*/
static Hangup(visio) {
if (top.$('#visio-back-button bnum-icon').text() === 'fullscreen')
top.$('#visio-back-button bnum-icon').click();
MelObject.Empty()
.rcmail(true)
.remove_handler_ex('frames.attach.url', 'visio');
MelObject.Empty()
.rcmail(true)
.remove_handler_ex('frames.attach.url.before', 'visio');
MelObject.Empty().rcmail(true).remove_handler_ex('switch_frame', 'visio');
// FramesManager.Instance.detach('url');
// FramesManager.Instance.detach('before_url');
// FramesManager.Instance.detach('switch_frame');
// if (this._audioManager) this._audioManager.dispose();
// if (this._videoManager) this._videoManager.dispose();
// if (this._popup) this._popup.destroy();
//visio.jitsii.
//visio.toolbar.destroy();
top
.$('#visio-back-button bnum-icon')
.text('undo')
.parent()
.attr('title', 'Quitter la visio')
.off('click')
.on('click', () => {
if (FramesManager.Instance.get_window()._history._history.length)
FramesManager.Instance.get_window()._history.back();
else FramesManager.Instance.switch_frame('bureau', {});
});
(top ?? parent ?? window).$('html').removeClass('fullscreen-visio');
FramesManager.Instance.attach('switch_frame', (task, changeframe) => {
if (changeframe) {
top.$('#visio-back-button').remove();
FramesManager.Instance.detach('switch_frame');
if (FramesManager.Instance.get_window().has_frame('webconf'))
FramesManager.Instance.get_window().remove_frame('webconf');
//FramesManager.Instance.start_mode('stop_visio');
VisioHelper.Instance.stopVisio();
FramesManager.Instance.close_except_selected();
}
});
}
/**
* Affiche ou ferme le tchat
* @param {Visio} visio
* @static
*/
static Chat(visio) {
visio.jitsii.toggle_chat();
}
/**
*Change d'état le micro
* @param {Visio} visio
* @static
*/
static Mic(visio) {
visio.jitsii.toggle_micro();
}
/**
* Affiche la popup de visualisation et de séléction des audios
* @param {Visio} visio
* @return {Promise<void>}
* @async
* @static
*/
static async Mic_0(visio) {
if (!this._popup) {
this._popup = new ToolbarPopup(visio.toolbar.toolbar()).hide();
}
if (this._popup.is_show() && this._popup.has_tag('mic')) {
this._popup.hide();
} else {
this._audioManager = this._audioManager || new MelAudioManager();
this.UpdatePopupDevices(
await visio.jitsii.get_micro_and_audio_devices(),
this.Mic_0_Click.bind(this, visio),
);
this._popup.set_tag('mic');
this._popup.show();
}
}
/**
* Action au click d'un bouton de la popup des devices
* @param {Visio} visio
* @param {string} id
* @param {string} type
* @param {string} label
* @return {Promise<void>}
* @async
* @static
*/
static async Mic_0_Click(visio, id, type, label) {
const initial = this._popup.$content
.find('.mel-ui-button.disabled')
.data('deviceid');
let waitState = null;
this._popup.$content
.find('.mel-ui-button')
.addClass('loading')
.attr('disabled');
switch (type) {
case 'audioinput':
visio.jitsii.set_micro_device(label, id);
waitState = await Mel_Promise.wait_async(async () => {
const tmp = await visio.jitsii.get_current_devices();
return tmp['audioInput'].deviceId === id;
});
break;
case 'audiooutput':
visio.jitsii.set_audio_device(label, id);
waitState = await Mel_Promise.wait_async(async () => {
const tmp = await visio.jitsii.get_current_devices();
return tmp['audioOutput'].deviceId === id;
});
break;
case 'videoinput':
visio.jitsii.set_video_device(label, id);
waitState = await Mel_Promise.wait_async(async () => {
const tmp = await visio.jitsii.get_current_devices();
return tmp['videoInput'].deviceId === id;
});
break;
default:
break;
}
this._popup.$content
.find('.mel-ui-button')
.removeClass('disabled')
.removeAttr('disabled')
.removeClass('loading');
if (!waitState?.resolved) {
this._popup.$content
.find(
`.mel-ui-button[data-deviceid="${initial}"][data-devicekind="${type}"]`,
)
.addClass('disabled')
.attr('disabled', 'disabled');
} else {
this._popup.$content
.find(
`.mel-ui-button[data-deviceid="${id}"][data-devicekind="${type}"]`,
)
.addClass('disabled')
.attr('disabled', 'disabled');
}
}
/**
* Met à jour la liste des périphériques afficher sur la popup
* @param {Array<DeviceEx>} devices Liste des devices envoyé par jitsi
* @param {function} click Action à faire lorsque l'on clique sur un device
* @returns {Promise<this>} Chaînage
* @static
* @async
* @frommoduleparam Visio/Jitsi devices {@linkto DeviceEx}
*/
static async UpdatePopupDevices(devices, click) {
const _$ = top.$;
let devices_by_kind = {};
for (let index = 0; index < devices.length; ++index) {
const element = devices[index];
if (devices_by_kind[element.kind] === undefined)
devices_by_kind[element.kind] = [];
devices_by_kind[element.kind].push(element);
}
let $button = null;
let type = '';
let html = this._popup.$content.html(EMPTY_STRING);
for (const key in devices_by_kind) {
if (Object.hasOwnProperty.call(devices_by_kind, key)) {
const array = devices_by_kind[key];
html.append(
`<span class=toolbar-title>${rcmail.gettext(key, 'mel_metapage')}</span><div class="btn-group-vertical" style=width:100% role="group" aria-label="groupe des ${key}">`,
);
for (let index = 0; index < array.length; ++index) {
const element = array[index];
const disabled = element.isCurrent === true ? 'disabled' : '';
type = element.kind === 'videoinput' ? 'div' : 'button';
$button = _$(
`<${type} data-deviceid="${element.deviceId}" data-devicekind="${element.kind}" data-devicelabel="${element.label}" title="${element.label}" class="mel-ui-button btn btn-primary btn-block ${disabled}" ${disabled}></${type}>`,
).click((e) => {
e = $(e.currentTarget);
click(
e.data('deviceid'),
e.data('devicekind'),
e.data('devicelabel'),
);
});
if (type === 'button') $button.html(element.label);
if (element.kind === 'audioinput') {
//Visualiser les micros
$button = (
await this._audioManager.addElement(element, $button)
).$main.parent();
} else if (element.kind === 'audiooutput') {
//Tester l'audio
var $button_div = $('<div></div>').css('position', 'relative');
$button
.on('mouseover', (event) => {
let $e = $(event.currentTarget);
let $parent = $e.parent();
let $tmp = $('<button>Test</button>')
.data('devicelabel', $e.data('devicelabel'))
.addClass(
'mel-button btn btn-secondary no-button-margin mel-test-audio-button',
)
.click(async (testbuttonevent) => {
const data = $(testbuttonevent.currentTarget).data(
'devicelabel',
);
if (this.audio_tester.audios[data])
await this.audio_tester.audios[data].test({
kind: 'audiooutput',
label: data,
});
else {
this.audio_tester.addAudio(
data,
await new MelAudioTester(
this._audioManager.devices,
).test({
kind: 'audiooutput',
label: data,
}),
);
}
})
.on('mouseleave', (levent) => {
if (!$(levent.relatedTarget).hasClass('mel-ui-button')) {
$(levent.currentTarget).remove();
}
});
$parent.append($tmp);
})
.on('mouseleave', (event) => {
console.log('event', event);
if (!$(event.relatedTarget).hasClass('mel-test-audio-button')) {
$(event.currentTarget)
.parent()
.find('.mel-test-audio-button')
.remove();
}
})
.appendTo($button_div);
$button = $button_div;
$button_div = null;
} else {
//Visualiser les caméras
await this._videoManager.addVideo($button, element, false);
}
html.append($button);
}
if (true) html.append('<separate class="device"></separate>');
}
}
html.find('separate').last().remove();
if (
this._popup.has_tag('video') &&
this._videoManager &&
this._videoManager.count() > 0
) {
(
await this._videoManager
.oncreate((video, device) => {
$('<label></label>')
.addClass('video-visio-label')
.html(device.label)
.appendTo($(video).parent().css('position', 'relative'));
})
.create()
).updateSizePerfect('100%', 'unset');
}
return this;
}
/**
* Affiche ou non la caméra
* @param {Visio} visio
* @static
*/
static Camera(visio) {
visio.jitsii.toggle_video();
}
/**
* Affiche ou cache la popup de séléction d'une caméra
* @param {Visio} visio
* @return {Promise<void>}
* @static
* @async
*/
static async Camera_0(visio) {
if (!this._popup) {
this._popup = new ToolbarPopup(visio.toolbar.toolbar()).hide();
}
if (this._popup.is_show() && this._popup.has_tag('video')) {
this._popup.hide();
} else {
this._videoManager = this._videoManager || new MelVideoManager();
this.UpdatePopupDevices(
await visio.jitsii.get_video_devices(),
this.Mic_0_Click.bind(this, visio),
);
this._popup.set_tag('video');
this._popup.show();
}
}
/**
*Lève ou baisse la main
* @param {Visio} visio
* @static
*/
static Handup(visio) {
visio.jitsii.toggle_hand();
}
/**
* Partage l'écran ou stop le partage.
* @param {Visio} visio
* @static
*/
static Share_screen(visio) {
visio.jitsii.share_screen();
}
/**
* Active ou désactive la mosaïque
* @param {Visio} visio
* @static
*/
static Moz(visio) {
visio.jitsii.toggle_tile_view();
}
/**
* Switch de frame sur le documents lié à l'espace de cette visio
* @param {Visio} visio
* @return {Promise<void>}
* @static
* @async
*/
static Documents(visio) {
const url = `/apps/files?dir=/dossiers-${visio.data.wsp}`;
return FramesManager.Instance.switch_frame('stockage', {
changepage: true,
args: {
_params: url,
},
});
}
/**
* Affiche la poupu de plus d'options
* @param {Visio} visio
* @return {Promise<void>}
* @static
* @async
*/
static async More(visio) {
if (!visio.popover.onhide.has('focus')) {
visio.popover.onhide.add('focus', () => {
visio.toolbar.get_button('more').$item.focus();
});
}
visio.popover.toggle();
}
/**
* Copie l'url public de la visio
* @param {Visio} visio
* @static
*/
static Action_Url(visio) {
let config = {
_key: visio.data.room,
};
if (visio.data.wsp) config._wsp = visio.data.wsp;
const url = mel_metapage.Functions.public_url('webconf', config);
MelObject.Empty().copy_to_clipboard(url);
visio.popover.hide();
}
/**
* Copie le numéro de téléphone et le code pin de la visio
* @param {Visio} visio
* @returns {Promise<void>}
* @static
* @async
*/
static async Action_Phone(visio) {
const data = await visio.get_call_data();
const copy_value = `Numéro : ${data.number} - PIN : ${data.pin}`;
MelObject.Empty().copy_to_clipboard(copy_value);
visio.popover.hide();
}
/**
* Affiche les participants
* @param {Visio} visio
* @returns {Promise<void>}
* @static
* @async
*/
static async Action_Users(visio) {
visio.jitsii.toggle_participants_pane();
visio.popover.hide();
}
/**
* Ouvre l'arrière plan virtuel
* @param {Visio} visio
* @returns {Promise<void>}
* @static
* @async
*/
static async Action_Background(visio) {
visio.jitsii.open_virtual_background();
visio.popover.hide();
}
/**
* Ouvre la poupup de mot de passe
* @param {Visio} visio
* @returns {Promise<void>}
* @static
* @async
*/
static async Action_Security(visio) {
visio.popover.hide();
if (await visio.jitsii.is_moderator()) {
//prettier-ignore
const content = MelHtml.start
.div({ id:'popup-security' })
.input_checkbox({
id: 'security-hav-password', onchange: this._Action_Security_OnCheckChanged.bind(this) }).attr(visio.jitsii.has_password() ? 'checked' : 'not-checked', true).removeClass('form-control').css('margin-right', '5px')
.label({ for:'security-hav-password' })
.text('Visio sécurisée ?')
.end()
.input_text_floating('Mot de passe', {
inputs_attribs: { type: 'text', id: 'security-pass', value:visio.jitsii._password },
div_attribs: { style:(visio.jitsii.has_password() ? EMPTY_STRING : 'display:none') }
})
.end();
const save = RcmailDialogButton.ButtonSave({
text: 'Changer de mot de passe',
click: this._Action_Security_On_Save.bind(this, visio, () =>
dialog.destroy(),
),
});
const cancel = RcmailDialogButton.ButtonCancel({
text: 'Annuler',
click: () => dialog.destroy(),
});
let dialog = MelDialog.Create('index', content, {
title: 'Mot de passe de la visioconférence',
buttons: [save, cancel],
options: {
height: 200,
beforeClose: () => {
visio.toolbar.get_button('more').$item.focus();
},
},
});
dialog.show();
} else {
visio.jitsii.show_notification(
'Vous devez être modérateur pour changer le mot de passe !',
{
type: 'warning',
},
);
}
}
/**
* Est appelé lorsque le mot de passe de l'input a changé
* @static
*/
static _Action_Security_OnCheckChanged() {
if ($('#security-hav-password')[0].checked)
$('#security-pass').parent().show();
else $('#security-pass').parent().hide();
}
/**
* Est appelé lorsque l'on sauvegarde le mot de passe
* @param {Visio} visio
* @param {Function} callback
* @returns {Promise<void>}
* @static
* @async
*/
static async _Action_Security_On_Save(visio, callback) {
const val = $('#security-hav-password')[0].checked
? $('#security-pass').val() || null
: null;
if ($('#security-hav-password')[0].checked && !val) {
visio.jitsii.show_notification('Vous devez mettre un mot de passe !', {
type: 'warning',
});
return;
}
if (await visio.jitsii.is_moderator()) {
if (val !== visio.jitsii._password) {
visio.jitsii.set_password(val);
visio.jitsii.show_notification(
val
? `Le mot de passe a été changé en ${val} !`
: 'La visioconférence est désomait ouvert à tous !',
{
type: 'normal',
timeout: 'long',
},
);
if (val) top.mel_metapage.Functions.copy(val);
} else {
visio.jitsii.show_notification(
'Vous devez mettre un mot de passe différent !',
{
type: 'warning',
},
);
return;
}
} else {
visio.jitsii.show_notification(
'Vous devez être modérateur pour changer le mot de passe !',
{
type: 'warning',
},
);
}
callback();
}
}
/**
* @type {ToolbarPopup}
* @static
* @package
*/
ToolbarFunctions._popup = null;
/**
* @class
* @classdesc Toolbar de la visioconférence
* @extends Toolbar
*/
class VisioToolbar extends Toolbar {
/**
*
* @param {string} id Id de la toolbar
*/
constructor(id) {
super(id);
}
/**
* Met à jour l'état de la toolbar
* @param {boolean} state Etat de la toolbar
* @param {string} id id du bouton
* @param {?string} [sub_id=null] Id du bouton enfant
* @returns {VisioToolbar} Chaînage
*/
updateToolbarState(state, id, sub_id = null) {
let button = this.get_button(id);
if (!isNullOrUndefined(sub_id) && button.get) button = button.get(sub_id);
return this.updateToolbarStateFromButton(state, button);
}
/**
* Met à jours l'éat d'un bouton
* @param {boolean} state Nouvel état
* @param {ToolbarItem} button Boutton à modifier
* @returns {VisioToolbar} Chaînage
*/
updateToolbarStateFromButton(state, button) {
switch (state) {
case true:
button.$item.addClass('state-active');
break;
case false:
button.$item.removeClass('state-active');
break;
default:
break;
}
return this;
}
}