diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/website_jitsi/static/src | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/website_jitsi/static/src')
| -rw-r--r-- | addons/website_jitsi/static/src/css/chat_room.css | 17 | ||||
| -rw-r--r-- | addons/website_jitsi/static/src/js/chat_room.js | 280 | ||||
| -rw-r--r-- | addons/website_jitsi/static/src/xml/chat_room_modal.xml | 20 |
3 files changed, 317 insertions, 0 deletions
diff --git a/addons/website_jitsi/static/src/css/chat_room.css b/addons/website_jitsi/static/src/css/chat_room.css new file mode 100644 index 00000000..a51f80b6 --- /dev/null +++ b/addons/website_jitsi/static/src/css/chat_room.css @@ -0,0 +1,17 @@ +.o_wjitsi_room_modal .modal-dialog { + top: 10%; + max-width: 100vw; + max-height: 100vh; + width: 80%; + margin: auto; +} + +.o_wjitsi_room_modal .modal-body { + height: 70vh; + overflow: hidden; + margin: 0; +} + +.o_wjitsi_chat_room_loading { + top: 40%; +} diff --git a/addons/website_jitsi/static/src/js/chat_room.js b/addons/website_jitsi/static/src/js/chat_room.js new file mode 100644 index 00000000..c704d126 --- /dev/null +++ b/addons/website_jitsi/static/src/js/chat_room.js @@ -0,0 +1,280 @@ +odoo.define('website_jitsi.chat_room', function (require) { +'use strict'; + +const config = require("web.config"); +const core = require('web.core'); +const Dialog = require('web.Dialog'); +const publicWidget = require('web.public.widget'); +const QWeb = core.qweb; +const _t = core._t; + +publicWidget.registry.ChatRoom = publicWidget.Widget.extend({ + selector: '.o_wjitsi_room_widget', + xmlDependencies: ['/website_jitsi/static/src/xml/chat_room_modal.xml'], + events: { + 'click .o_wjitsi_room_link': '_onChatRoomClick', + }, + + /** + * Manage the chat room (Jitsi), update the participant count... + * + * The widget takes some options + * - 'room-name', the name of the Jitsi room + * - 'chat-room-id', the ID of the `chat.room` record + * - 'auto-open', the chat room will be automatically opened when the page is loaded + * - 'check-full', check if the chat room is full before joining + * - 'attach-to', a JQuery selector of the element on which we will add the Jitsi + * iframe. If nothing is specified, it will open a modal instead. + * - 'default-username': the username to use in the chat room + * - 'jitsi-server': the domain name of the Jitsi server to use + */ + start: async function () { + await this._super.apply(this, arguments); + this.roomName = this.$el.data('room-name'); + this.chatRoomId = parseInt(this.$el.data('chat-room-id')); + // automatically open the current room + this.autoOpen = parseInt(this.$el.data('auto-open') || 0); + // before joining, perform a RPC call to verify that the chat room is not full + this.checkFull = parseInt(this.$el.data('check-full') || 0); + // query selector of the element on which we attach the Jitsi iframe + // if not defined, the widget will pop in a modal instead + this.attachTo = this.$el.data('attach-to') || false; + // default username for jitsi + this.defaultUsername = this.$el.data('default-username') || false; + + this.jitsiServer = this.$el.data('jitsi-server') || 'meet.jit.si'; + + this.maxCapacity = parseInt(this.$el.data('max-capacity')) || Infinity; + + if (this.autoOpen) { + await this._onChatRoomClick(); + } + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Click on a chat room to join it. + * + * @private + */ + _onChatRoomClick: async function () { + if (this.checkFull) { + // maybe we didn't refresh the page for a while and so we might join a room + // which is full, so we perform a RPC call to verify that we can really join + let isChatRoomFull = await this._rpc({ + route: '/jitsi/is_full', + params: { + room_name: this.roomName, + }, + }); + + if (isChatRoomFull) { + window.location.reload(); + return; + } + } + + if (await this._openMobileApplication(this.roomName)) { + // we opened the mobile application + return; + } + + await this._loadJisti(); + + if (this.attachTo) { + // attach the Jitsi iframe on the given parent node + let $parentNode = $(this.attachTo); + $parentNode.find("iframe").trigger("empty"); + $parentNode.empty(); + + await this._joinJitsiRoom($parentNode); + } else { + // create a model and append the Jitsi iframe in it + let $jitsiModal = $(QWeb.render('chat_room_modal', {})); + $("body").append($jitsiModal); + $jitsiModal.modal('show'); + + let jitsiRoom = await this._joinJitsiRoom($jitsiModal.find('.modal-body')); + + // close the modal when hanging up + jitsiRoom.addEventListener('videoConferenceLeft', async () => { + $('.o_wjitsi_room_modal').modal('hide'); + }); + + // when the modal is closed, delete the Jitsi room object and clear the DOM + $jitsiModal.on('hidden.bs.modal', async () => { + jitsiRoom.dispose(); + $(".o_wjitsi_room_modal").remove(); + }); + } + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Jitsi do not provide an REST API to get the number of participant in a room. + * The only way to get the number of participant is to be in the room and to use + * the Javascript API. So, to update the participant count on the server side, + * the participant have to send the count in RPC... + * + * When leaving a room, the event "participantLeft" is called for the current user + * once per participant in the room (like if all other participants were leaving the + * room and then the current user himself). + * + * "participantLeft" is called only one time for the other participant who are still + * in the room. + * + * We can not ask the user who is leaving the room to update the participant count + * because user might close their browser tab without hanging up (and so without + * triggering the event "videoConferenceLeft"). So, we wait for a moment (because the + * event "participantLeft" is called many time for the participant who is leaving) + * and the first participant send the new participant count (so we avoid spamming the + * server with HTTP requests). + * + * We use "setTimout" to send maximum one HTTP request per interval, even if multiple + * participants join/leave at the same time in the defined interval. + * + * Update on the 29 June 2020 + * + * @private + * @param {jQuery} $jitsiModal, jQuery modal element in which we add the Jitsi room + * @returns {JitsiRoom} the newly created Jitsi room + */ + _joinJitsiRoom: async function ($parentNode) { + let jitsiRoom = await this._createJitsiRoom(this.roomName, $parentNode); + + if (this.defaultUsername) { + jitsiRoom.executeCommand("displayName", this.defaultUsername); + } + + let timeoutCall = null; + const updateParticipantCount = (joined) => { + this.allParticipantIds = Object.keys(jitsiRoom._participants).sort(); + // if we reached the maximum capacity, update immediately the participant count + const timeoutTime = this.allParticipantIds.length >= this.maxCapacity ? 0 : 2000; + + // we clear the old timeout to be sure to call it only once each 2 seconds + // (so if 2 participants join/leave in this interval, we will perform only + // one HTTP request for both). + clearTimeout(timeoutCall); + timeoutCall = setTimeout(() => { + this.allParticipantIds = Object.keys(jitsiRoom._participants).sort(); + if (this.participantId === this.allParticipantIds[0]) { + // only the first participant of the room send the new participant + // count so we avoid to send to many HTTP requests + this._updateParticipantCount(this.allParticipantIds.length, joined); + } + }, timeoutTime); + }; + + jitsiRoom.addEventListener('participantJoined', () => updateParticipantCount(true)); + jitsiRoom.addEventListener('participantLeft', () => updateParticipantCount(false)); + + // update the participant count when joining the room + jitsiRoom.addEventListener('videoConferenceJoined', async (event) => { + this.participantId = event.id; + updateParticipantCount(true); + $('.o_wjitsi_chat_room_loading').addClass('d-none'); + + // recheck if the room is not full + if (this.checkFull && this.allParticipantIds.length > this.maxCapacity) { + clearTimeout(timeoutCall); + jitsiRoom.executeCommand('hangup'); + window.location.reload(); + } + }); + + // update the participant count when using the "Leave" button + jitsiRoom.addEventListener('videoConferenceLeft', async (event) => { + this.allParticipantIds = Object.keys(jitsiRoom._participants) + if (!this.allParticipantIds.length) { + // bypass the checks and timer of updateParticipantCount + this._updateParticipantCount(this.allParticipantIds.length, false); + } + }); + + return jitsiRoom; + }, + + /** + * Perform an HTTP request to update the participant count on the server side. + * + * @private + * @param {integer} count, current number of participant in the room + * @param {boolean} joined, true if someone joined the room + */ + _updateParticipantCount: async function (count, joined) { + await this._rpc({ + route: '/jitsi/update_status', + params: { + room_name: this.roomName, + participant_count: count, + joined: joined, + }, + }); + }, + + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Redirect on the Jitsi mobile application if we are on mobile. + * + * @private + * @param {string} roomName + * @returns {boolean} true is we were redirected to the mobile application + */ + _openMobileApplication: async function (roomName) { + if (config.device.isMobile) { + // we are on mobile, open the room in the application + window.location = `intent://${this.jitsiServer}/${roomName}#Intent;scheme=org.jitsi.meet;package=org.jitsi.meet;end`; + return true; + } + return false; + }, + + /** + * Create a Jitsi room on the given DOM element. + * + * @private + * @param {string} roomName + * @param {jQuery} $parentNode + * @returns {JitsiRoom} the newly created Jitsi room + */ + _createJitsiRoom: async function (roomName, $parentNode) { + await this._loadJisti(); + const options = { + roomName: roomName, + width: "100%", + height: "100%", + parentNode: $parentNode[0], + configOverwrite: {disableDeepLinking: true}, + }; + return new window.JitsiMeetExternalAPI(this.jitsiServer, options); + }, + + /** + * Load the Jitsi external library if necessary. + * + * @private + */ + _loadJisti: async function () { + if (!window.JitsiMeetExternalAPI) { + await $.ajax({ + url: `https://${this.jitsiServer}/external_api.js`, + dataType: "script", + }); + } + }, +}); + +return publicWidget.registry.ChatRoom; + +}); diff --git a/addons/website_jitsi/static/src/xml/chat_room_modal.xml b/addons/website_jitsi/static/src/xml/chat_room_modal.xml new file mode 100644 index 00000000..d076a317 --- /dev/null +++ b/addons/website_jitsi/static/src/xml/chat_room_modal.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<templates xml:space="preserve"> + <t t-name="chat_room_modal"> + <div class="o_wjitsi_room_modal modal fade" role="dialog"> + <div class="modal-dialog"> + <div class="modal-content border-0"> + <main class="modal-body p-0"> + <div class="o_wjitsi_chat_room_loading position-absolute w-100 text-center text-muted"> + <i class="fa fa-spin fa-spinner mr-3"/> + <span>Loading your room...</span> + </div> + </main> + <footer class="modal-footer"> + <button class="btn btn-primary" data-dismiss="modal" type="button">Close</button> + </footer> + </div> + </div> + </div> + </t> +</templates> |
