summaryrefslogtreecommitdiff
path: root/addons/website_jitsi/static/src
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/website_jitsi/static/src
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/website_jitsi/static/src')
-rw-r--r--addons/website_jitsi/static/src/css/chat_room.css17
-rw-r--r--addons/website_jitsi/static/src/js/chat_room.js280
-rw-r--r--addons/website_jitsi/static/src/xml/chat_room_modal.xml20
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>