diff --git a/assets/3rdparty/SignalExchangerClient/SignalExchangerClient.js b/assets/3rdparty/SignalExchangerClient/SignalExchangerClient.js deleted file mode 100644 index 73e43601..00000000 --- a/assets/3rdparty/SignalExchangerClient/SignalExchangerClient.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Signal exchanger web client - * - * @author Pierre HUBERT - */ - -class SignalExchangerClient { - - /** - * Server domain - * - * @type {String} - */ - //domain; - - /** - * Server port - * - * @type {Number} - */ - //port; - - /** - * Current client ID - * - * @type {String} - */ - //clientID; - - /** - * Socket connection to the server - * - * @type {WebSocket} - */ - //socket; - - /** - * Function called in case of error - * - * @type {Function} - */ - //onError = null; - - /** - * Function called when the connection is etablished - * - * @type {Function} - */ - //onConnected = null; - - /** - * Function called when the connection to the socket is closed - * - * @type {Function} - */ - //onClosed = null; - - /** - * Function called when we get a new signal information - * - * @type {Function} - */ - //onSignal = null; - - /** - * Function called when we get a ready message notice - * - * @type {Function} - */ - //onReadyMessage = null; - - /** - * Construct a client instance - * - * @param {String} domain The name of the signal server - * @param {Number} port The port of the server to use - * @param {String} clientID The ID of current client - * @param {Boolean} secure Specify whether connection to the socket should be secure or not - */ - constructor(domain, port, clientID, secure) { - - //Save information - this.domain = domain, - this.port = port; - this.clientID = clientID; - - this.socket = new WebSocket((secure ? "wss" : "ws") + "://" + this.domain + ":" + this.port + "/socket"); - - //Add a few events listeners - this.socket.addEventListener("open", () => { - this.serverConnected(); - - if(this.onConnected != null) - setTimeout(this.onConnected, 10); - }); - - this.socket.addEventListener("message", message => { - - let data; - try { - data = JSON.parse(message.data); - } catch(e){ - console.error("Could not parse message from server!"); - return; - } - - console.log("New message from socket", data); - - this.serverMessage(data); - }); - - this.socket.addEventListener("error", () => { - if(this.onError != null) - setTimeout(this.onError, 0); - }); - - this.socket.addEventListener("close", () => { - if(this.onClosed != null) - setTimeout(this.onClosed, 0); - }); - } - - /** - * Use this method to get the current connection status to the server - * - * @return {Boolean} TRUE if the client is connected to the server / FALSE else - */ - isConnected() { - return this.socket.readyState == WebSocket.OPEN; - } - - /** - * Close the connection to the server (if connected) - */ - close() { - if(this.isConnected()) - this.socket.close(); - } - - /** - * Method called once the client is successfully - * connected to the client - */ - serverConnected(){ - - //Send data to the server to identificate client - this.sendData({ - client_id: this.clientID - }); - - } - - /** - * Send ready message to a peer - * - * @param {String} peerID The ID of the target peer for the message - */ - sendReadyMessage(peerID){ - - this.sendData({ - ready_msg: true, - target_id: peerID - }); - - } - - /** - * Send a signal to the server - * - * @param target_id The ID of the target for the signal - * @param content Signal to send to the target - */ - sendSignal(target_id, content){ - - //Send directly the message to the server - this.sendData({ - signal: content, - target_id: target_id - }); - - //Save the current signal being sent to be able to send - //it again in case of failure - this.pending_signal = content; - this.pending_signal_target = target_id; - } - - /** - * Stop to try to send the current signal message in queue - * - * This does not cancel the sending of messages already sent through - * socket - */ - cancelCurrentSignal() { - this.pending_signal = undefined; - this.pending_signal_target = undefined; - } - - /** - * Send data to the server - * - * @param {Object} data The data to send to the server - */ - sendData(data){ - console.log("Sending data to server", data); - this.socket.send(JSON.stringify(data)); - } - - /** - * This method is called when the server has sent a new message to this client - * - * @param {Object} message The message sent by the server, as a JSON object - */ - serverMessage(message){ - - //Check if it is a callback for a pending message - if(message.signal_sent){ - if(message.number_of_targets < 1 && this.pending_signal && this.pending_signal_target){ - - //We have to send the message again - setTimeout(() => { - this.sendSignal(this.pending_signal, this.pending_signal_target); - }, 1000); - - } - - else { - - //Else we can remove from this class information about the signal being sent - this.cancelCurrentSignal(); - - } - } - - //Check if message is a callback for a ready notice - else if(message.ready_message_sent){ - - if(message.number_of_targets < 1){ - - //Try to send message again - setTimeout(() => { - this.sendReadyMessage(message.target_id); - }, 1000); - - } - - } - - // Check if message is a ready notice - else if(message.ready_msg){ - if(this.onReadyMessage != null) - this.onReadyMessage(message.source_id); - } - - // Check if the message is a signal - else if(message.signal){ - if(this.onSignal != null) - this.onSignal(message.signal, message.source_id); - } - - - } -} \ No newline at end of file diff --git a/assets/css/components/calls/callWindow.css b/assets/css/components/calls/callWindow.css deleted file mode 100644 index 9702660b..00000000 --- a/assets/css/components/calls/callWindow.css +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Calls window stylesheet - * - * @author Pierre HUBERT - */ - -#callsTarget { - position: fixed; - width: 100%; - height: 100%; - top: 0px; - visibility: hidden; - z-index: 1000; -} - - -.call-window { - width: 300px; - min-width: 300px; - min-height: 174px; - position: absolute; - top: 100px; - right: 10px; - z-index: 100; - border: 1px black solid; - display: flex; - flex-direction: column; - background-color: #000000b3; - visibility: visible; -} - -.call-window .call-window-body{ - flex: 1; - display: flex; - max-height: calc(100% - 62px); -} - - -/** - * Toolbar - */ -.call-toolbar { - height: 20px; - background-color: #000000b3; - color: white; - display: flex; - align-items: center; - padding-left: 10px; -} - -.call-toolbar > i:first-child { - margin-right: 6px; -} - -.call-title { - flex: 1; - cursor: move; -} - - -/** - * Loading message - */ -.loading-message-container { - position: absolute; - min-height: 112px; - width: 100%; - display: flex; - background: 1px #0009; - flex-direction: column; - justify-content: space-around; - text-align: center; - font-size: 150%; - color: white; -} - - -/** - * Stream target - */ -.call-window .streams-target { - flex: 1; - display: flex; - flex-wrap: wrap; - flex-direction: row; - max-width: 100%; -} - -.call-window .streams-target video { - flex: 1; - flex-shrink: 1; - max-height: 100%; -} - - -/** - * Footer - */ -.call-window .call-footer { - height: 40px; - display: flex; - justify-content: space-around; - align-items: center; -} - -.call-window .call-footer .call-option-button { - color: #fff6; - flex: 1; - text-align: center; - height: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.call-window .call-footer .call-option-button.selected { - color: white; -} - -.call-window .call-footer .call-option-button:hover { - background-color: #ffffff4d; -} - -.call-window .call-footer .call-option-button:active { - background-color: #fff3; -} - -.call-window .call-footer .call-option-button.hang-up-button { - color: #dd4b39; -} - - -/** - * Responsive mode - */ -@media screen and (max-width: 730px) { - - #callsTarget { - z-index: 1030; - } - - .streams-target { - flex-direction: column !important; - } - - .streams-target video { - max-width: 100%; - } - - .call-window { - position: fixed; - width: 100%; - height: 100%; - top: 0px !important; - left: 0px !important; - } - -} \ No newline at end of file diff --git a/assets/css/components/calls/ringScreen.css b/assets/css/components/calls/ringScreen.css deleted file mode 100644 index 45e3b394..00000000 --- a/assets/css/components/calls/ringScreen.css +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Ring screen stylesheet - * - * @author Pierre HUBERT - */ - -.ring-call-container { - width: 100%; - height: 100%; - position: fixed; - top: 0px; - left: 0px; - background-color: #011932e6; - z-index: 1030; - display: flex; - justify-content: center; - align-items: center; -} - -.call-message-box { - background-color: #fff6; - color: white; - padding: 10px; - border-radius: 5px; - min-width: 200px; - text-align: center; - font-size: 150%; -} - -.ring-call-container .response-buttons { - display: flex; - justify-content: space-around; -} \ No newline at end of file diff --git a/assets/js/common/functionsSchema.js b/assets/js/common/functionsSchema.js index dadbd682..4446d940 100644 --- a/assets/js/common/functionsSchema.js +++ b/assets/js/common/functionsSchema.js @@ -1163,66 +1163,6 @@ var ComunicWeb = { }, - /** - * Calls component - */ - calls: { - - /** - * Calls configuration - */ - __config: undefined, - - /** - * Calls interface - */ - interface: { - //TODO : implement - }, - - /** - * Calls controller - */ - controller: { - //TODO : implement - }, - - /** - * Call window - */ - callWindow: { - //TODO : implement - }, - - /** - * Current calls list - */ - currentList: { - //TODO : implement - }, - - /** - * User media getter - */ - userMedia: { - //TODO : implement - }, - - /** - * Ring screen - */ - ringScreen: { - //TODO : implement - }, - - /** - * Calls utilities - */ - utils: { - //TODO : implement - }, - }, - /** * Web application interface */ diff --git a/assets/js/common/system.js b/assets/js/common/system.js index e66b9597..8e45be76 100644 --- a/assets/js/common/system.js +++ b/assets/js/common/system.js @@ -58,11 +58,6 @@ ComunicWeb.common.system = { */ ComunicWeb.components.darkTheme.refresh(); - /** - * Initialize call system - */ - ComunicWeb.components.calls.controller.init(); - /** * What to do after login refresh */ diff --git a/assets/js/components/calls/callWindow.js b/assets/js/components/calls/callWindow.js deleted file mode 100644 index 0cf60c23..00000000 --- a/assets/js/components/calls/callWindow.js +++ /dev/null @@ -1,889 +0,0 @@ -/** - * Single call window management - * - * @author Pierre HUBERT - */ - -ComunicWeb.components.calls.callWindow = { - - /** - * Initialize a call - * - * @param {Object} info Information about the call to initialize - */ - initCall: function(info){ - - /** - * Initialize call object - */ - var call = { - info: info, - - /** - * @type {String} - */ - localPeerID: undefined, - - /** - * @type {Boolean} - */ - open: true, - - /** - * @type {Boolean} - */ - stopped: false, - - window: {}, - streams: {}, - - /** - * @type {MediaStream} - */ - localStream: undefined, - - /** - * @type {HTMLVideoElement} - */ - localStreamVideo: undefined, - - /** - * @type {SignalExchangerClient} - */ - signalClient: undefined, - - /** - * @type {Boolean} - */ - isRequestingUserMedia: false, - - /** - * @type {() : any} - * - * Request user media again - */ - requestUserMedia: undefined - }; - - - - /** - * Initialize utilities - */ - - - /** - * Get member information based on call ID - * - * @param {String} id The ID of the peer to process - * @return Information about the peer / empty object - * if no peer found - */ - call.getMemberByCallID = function(id){ - var memberInfo = undefined; - call.info.members.forEach(function(member){ - - if(member.user_call_id == id) - memberInfo = member; - - }); - return memberInfo; - } - - /** - * Check out whether local stream audio is enabled or not - * - * @return {Boolean} TRUE if local stream is enabled / FALSE else - */ - call.isLocalAudioEnabled = function(){ - - //Default behaviour : local stream not enabled - if(!call.localStream) - return true; - - return call.localStream.getAudioTracks()[0].enabled; - - } - - /** - * Set audio mute status of local stream - * - * @param {Boolean} enabled New enabled status - */ - call.setLocalAudioEnabled = function(enabled){ - if(call.localStream) - call.localStream.getAudioTracks()[0].enabled = enabled; - } - - - /** - * Check if the local video is enabled or not - * - * @return {Boolean} TRUE to mute video / FALSE else - */ - call.isLocalVideoEnabled = function(){ - - //Default behaviour : video not enabled - if(!call.localStream) - return true; - - return call.localStream.getVideoTracks()[0].enabled; - } - - /** - * Update mute status of local video stream - * - * @param {Boolean} enabled New mute status - */ - call.setLocalVideoEnabled = function(enabled){ - if(call.localStream) - call.localStream.getVideoTracks()[0].enabled = enabled; - - //Request user media - else if(!call.isRequestingUserMedia) - call.requestUserMedia(); - } - - /** - * Set local stream video visibility - * - * @param {Boolean} visible TRUE to make it visible / FALSE else - */ - call.setLocalStreamVisibility = function(visible){ - if(call.localStreamVideo) - call.localStreamVideo.style.display = visible ? "block" : "none"; - - //Request user media - else if(!call.isRequestingUserMedia) - call.requestUserMedia(); - } - - /** - * Get local stream visibility - * - * @return {Boolean} TRUE if local stream is visible / FALSE else - */ - call.isLocalStreamVisible = function(){ - if(!call.localStreamVideo) - return true; - - return call.localStreamVideo.style.display !== "none"; - } - - - /** - * We have to begin to draw conversation UI - */ - var callContainer = createElem2({ - appendTo: byId("callsTarget") ? byId("callsTarget") : byId("wrapper"), //If call target is not found, add call in page wrapper - type: "div", - class: "call-window" - }); - call.window.container = callContainer; - - - //Add toolbar - call.window.toolbar = createElem2({ - appendTo: callContainer, - type: "div", - class: "call-toolbar", - innerHTML: "" - }); - - //Call title - call.window.title = createElem2({ - appendTo: call.window.toolbar, - type: "div", - class: "call-title", - innerHTML: "Loading..." - }); - - /** - * Update the title of the call - */ - call.setTitle = function(title){ - call.window.title.innerHTML = title; - } - - //Add close button - call.window.closeButton = createElem2({ - appendTo: call.window.toolbar, - type: "button", - class: "btn btn-box-tool close-btn", - innerHTML: "" - }); - - //Make close button lives - call.close = function(){ - - //Avoid to call this several times - if(call.stopped) - return; - - call.open = false; - call.stopped = true; - callContainer.remove(); - - //Close sockets connections too - ComunicWeb.components.calls.callWindow.stop(call); - } - - call.window.closeButton.addEventListener("click", function(){ - - //Check if the call is in full screen mode - if(IsFullScreen()) - RequestFullScreen(null) - else - call.close(); - - }); - - - //Get information about related conversation to get the name of the call - ComunicWeb.components.conversations.utils.getNameForID(info.conversation_id, function(name){ - - if(!name) - return notify("Could not get information about related conversation!", "danger"); - - call.setTitle(name); - - }); - - - //Call box body - call.window.body = createElem2({ - appendTo: callContainer, - type: "div", - class: "call-window-body" - }); - - - //Call videos target - call.window.videosTarget = createElem2({ - appendTo: call.window.body, - type: "div", - class: "streams-target" - }); - - - - - - - /** - * Create loading message area - */ - call.window.loadingMessageContainer = createElem2({ - insertBefore: call.window.body.firstChild, - type: "div", - class: "loading-message-container", - innerHTML: "" - }); - - call.window.loadingMessageContent = createElem2({ - appendTo: call.window.loadingMessageContainer, - type: "div", - class: "message", - innerHTML: "Loading..." - }); - - /** - * Set loading message visiblity - * - * @param {Boolean} visible TRUE to make it visible / FALSE else - */ - call.setLoadingMessageVisibility = function(visible){ - call.window.loadingMessageContainer.style.display = visible ? "flex" : "none"; - } - - /** - * Update call loading message - * - * @param {String} message The new message to show to the - * users - */ - call.setLoadingMessage = function(message){ - call.setLoadingMessageVisibility(true); - call.window.loadingMessageContent.innerHTML = message; - } - - - - - //Call footer - call.window.footer = createElem2({ - appendTo: callContainer, - type: "div", - class: "call-footer" - }); - - /** - * This function is used to toggle selection state - * of one of the call toolbar button - * - * @param {HTMLElement} btn Target button - * @param {Boolean} selected Selection state of the button - */ - var togglButtonSelectedStatus = function(btn, selected){ - - if(!selected){ - while(btn.className.includes(" selected")) - btn.className = btn.className.replace(" selected", ""); - } - - else if(selected && !btn.className.includes(" selected")) - btn.className += " selected"; - } - - var buttonsList = [ - - //Show current user camera - { - icon: "fa-eye", - selected: true, - onclick: function(btn){ - call.setLocalStreamVisibility(!call.isLocalStreamVisible()); - togglButtonSelectedStatus(btn, call.isLocalStreamVisible()); - } - }, - - //Mute button - { - icon: "fa-microphone", - selected: true, - onclick: function(btn){ - call.setLocalAudioEnabled(!call.isLocalAudioEnabled()); - togglButtonSelectedStatus(btn, call.isLocalAudioEnabled()); - } - }, - - //Hang up button - { - icon: "fa-phone", - class: "hang-up-button", - selected: false, - onclick: function(){ - call.close(); - } - }, - - //Stop video button - { - icon: "fa-video-camera", - selected: true, - onclick: function(btn){ - call.setLocalVideoEnabled(!call.isLocalVideoEnabled()); - togglButtonSelectedStatus(btn, call.isLocalVideoEnabled()); - console.log(call); - } - }, - - - //Full screen button - { - icon: "fa-expand", - selected: false, - onclick: function(btn){ - RequestFullScreen(callContainer); - setTimeout(function(){ - togglButtonSelectedStatus(btn, IsFullScreen()); - }, 1000); - } - } - ]; - - //Add buttons - buttonsList.forEach(function(button){ - - var buttonEl = createElem2({ - appendTo: call.window.footer, - type: "div", - class: "call-option-button", - innerHTML: "" - }); - - //Add button optionnal class - if(button.class) - buttonEl.className += " " + button.class; - - buttonEl.addEventListener("click", function(){ - button.onclick(buttonEl); - }); - - togglButtonSelectedStatus(buttonEl, button.selected); - - }); - - - - /** - * Make the call window draggable - */ - - function checkWindowMinPosition(){ - - if(window.innerHeight < callContainer.style.top.replace("px", "")) - callContainer.style.top = "0px"; - - if(window.innerWidth < callContainer.style.left.replace("px", "")) - callContainer.style.left = "0px"; - - if(callContainer.style.left.replace("px", "") < 0) - callContainer.style.left = "0px"; - - if(callContainer.style.top.replace("px", "") < 49) - callContainer.style.top = "50px"; - } - - //Enable dragging - { - var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; - - call.window.title.onmousedown = function(e){ - e = e || window.event; - e.preventDefault(); - - //Check if the window is currently in full screen mode - if(IsFullScreen()) - return; - - //get the mouse cursor position at startup - pos3 = e.clientX; - pos4 = e.clientY; - document.onmouseup = closeDragElement; - document.onmousemove = elementDrag; - } - - function elementDrag(e){ - e = e || window.event; - e.preventDefault(); - - //Calculate new cursor position - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - - //Set element new position - callContainer.style.top = (callContainer.offsetTop - pos2) + "px"; - callContainer.style.left = (callContainer.offsetLeft - pos1) + "px"; - - checkWindowMinPosition(); - } - - function closeDragElement(){ - - //Stop moving when mouse button is released - document.onmouseup = null; - document.onmousemove = null; - } - } - - window.addEventListener("resize", function(){ - checkWindowMinPosition(); - }); - - - - - //Load user media - call.setLoadingMessage("Waiting for your microphone and camera..."); - - /** - * Start to automaticaly refresh information about the call - */ - var interval = setInterval(function(){ - - //Check if call is not visible anymore - if(!callContainer.isConnected){ - call.close(); - return; - } - - if(!call.open) - return clearInterval(interval); - - ComunicWeb.components.calls.callWindow.refreshInfo(call); - - }, 4000); - - - //Request user media - call.requestUserMedia = function(){ - - call.isRequestingUserMedia = true; - - ComunicWeb.components.calls.userMedia.get().then(function(stream){ - - call.isRequestingUserMedia = false; - - //Check if connection has already been closed - if(!call.open) - return; - - call.localStream = stream; - - //Add stream to existing peer connections - for (var s in call.streams) { - if (call.streams.hasOwnProperty(s)) { - call.streams[s].peer.addStream(stream); - } - } - - //Add local stream to the list of visible stream - call.localStreamVideo = ComunicWeb.components.calls.callWindow.addVideoStream(call, true, stream); - - //Initialize signaling server connection - ComunicWeb.components.calls.callWindow.initializeConnectionToSignalingServer(call); - - return true; - - }).catch(function(e){ - call.isRequestingUserMedia = false; - console.error("Get user media error: ", e); - call.setLoadingMessageVisibility(false); - return notify("Could not get your microphone and camera!", "danger"); - }); - }; - - call.requestUserMedia(); - }, - - - /** - * Initialize connection to signaling server - * - * @param {Object} call Information about the call - */ - initializeConnectionToSignalingServer: function(call) { - - //Get current user call ID - call.info.members.forEach(function(member){ - - if(member.userID == userID()) - call.localPeerID = member.user_call_id; - }); - - - //Create client instance and connect to server - var config = ComunicWeb.components.calls.controller.getConfig(); - call.signalClient = new SignalExchangerClient( - config.signal_server_name, - config.signal_server_port, - call.localPeerID, - config.is_signal_server_secure - ); - - - /** - * Connection established - */ - call.signalClient.onConnected = function(){ - //Mark as connecting - call.setLoadingMessage("Connecting..."); - } - - /** - * Error when connecting to signaling server - */ - call.signalClient.onError = function(){ - call.setLoadingMessage("Could not connect to signaling server!"); - call.open = false; - }; - - /** - * Connection to signaling server is not supposed to close - */ - call.signalClient.onClosed = function(){ - call.setLoadingMessage("Connection to signaling server closed!"); - call.open = false; - } - - /** - * A remote peer sent a ready notice - */ - call.signalClient.onReadyMessage = function(peerID){ - ComunicWeb.components.calls.callWindow.readyToInitialize(call, peerID); - } - - /** - * A remote peer sent a signal - */ - call.signalClient.onSignal = function(signal, peerID){ - ComunicWeb.components.calls.callWindow.receivedSignal(call, peerID, signal); - } - - }, - - /** - * Refresh at a regular interval information about the call - * - * @param {Object} call Call Root object - */ - refreshInfo: function(call){ - - ComunicWeb.components.calls.interface.getInfo(call.info.id, function(result){ - - if(result.error) - return notify("Could not get information about the call!", "danger"); - - call.info = result; - - ComunicWeb.components.calls.callWindow.gotNewCallInfo(call); - }); - - }, - - /** - * This method get called each time information about the call - * are refreshed on the server - * - * @param {Object} call Information about the call - */ - gotNewCallInfo: function(call) { - - //Check if we are connected to signaling server and we have got local - //streams - if(!call.signalClient || !call.signalClient.isConnected()) - return; - - //Check if all other members rejected call - var allDisconnected = ComunicWeb.components.calls.utils.hasEveryoneLeft(call.info); - - //Check if all call peer rejected the call - if(allDisconnected){ - call.setLoadingMessage("Conversation terminated."); - - setTimeout(function(){ - call.close(); - }, 5000); - - return; - } - - //Process the connection to each accepted member - call.info.members.forEach(function(member){ - - //Ignores all not accepted connection - if(member.userID == userID() || member.status !== "accepted") - return; - - //Check if we have not peer information - if(!call.streams.hasOwnProperty("peer-" + member.userID)){ - - //If the ID of the current user is bigger than the remote - //peer user ID, we wait a ready signal from him - if(member.userID < userID()) - return; - - //Else we have to create peer - ComunicWeb.components.calls.callWindow.createPeerConnection(call, member, false); - } - - //Send ready message if the connection is not established yet - if(!call.streams["peer-" + member.userID].connected) - call.signalClient.sendReadyMessage(member.user_call_id); - - }); - - }, - - /** - * Create a peer connection - * - * @param {Object} call Information about the peer - * @param {Object} member Information about the member - * @param {Boolean} isInitiator Specify whether current user is the - * initiator of the connection or not - */ - createPeerConnection: function(call, member, isInitiator){ - - var peerConnection = { - peer: undefined, - connected: false - }; - call.streams["peer-" + member.userID] = peerConnection; - - //Get call configuration - var config = ComunicWeb.components.calls.controller.getConfig(); - - //Create peer - var peer = new SimplePeer({ - initiator: isInitiator, - stream: call.localStream, - trickle: true, - config: { - 'iceServers': [ - { urls: config.stun_server }, - {"url": config.turn_server, - "credential": config.turn_username, - "username": config.turn_password} - ] - } - }); - peerConnection.peer = peer; - - //Add a function to remove connection - peerConnection.removePeerConnection = function(){ - peer.destroy(); - delete call.streams["peer-" + member.userID]; - - if(peerConnection.video) - peerConnection.video.remove(); - } - - peer.on("connect", function(){ - peerConnection.connected = true; - }); - - - peer.on("error", function(err){ - console.error("Peer error !", err, member); - peerConnection.removePeerConnection(); - }); - - peer.on("signal", function(data){ - console.log('SIGNAL', JSON.stringify(data)); - call.signalClient.sendSignal(member.user_call_id, JSON.stringify(data)); - }); - - peer.on("message", function(message){ - console.log("Message from remote peer: " + message); - }); - - - peer.on("close", function(){ - peerConnection.removePeerConnection(); - }); - - peer.on("stream", function(stream){ - ComunicWeb.components.calls.callWindow.streamAvailable(call, member, stream); - }); - }, - - - /** - * This method is called when a remote peers notify it is ready to - * establish connection - * - * @param {Object} call Information about the call - * @param {String} peerID Remote peer ID - */ - readyToInitialize: function(call, peerID){ - - var member = call.getMemberByCallID(peerID); - if(member == undefined) - return; - - //It the user with the smallest ID who send the ready message - //else it would mess everything up - if(member.userID > userID()) - return; - - //Check if peer connection is already being created - if(call.streams.hasOwnProperty("peer-" + member.userID)) - return; - - this.createPeerConnection(call, member, true); - }, - - /** - * This method is called when we received a remote signal - * - * @param {Object} call Information about the call - * @param {String} peerID Remote peer ID - * @param {String} signal Received signal - */ - receivedSignal: function(call, peerID, signal){ - - console.log("Received signal from " + peerID, signal); - - var member = call.getMemberByCallID(peerID); - if(member == undefined) - return; - - //Check we have got peer information - if(!call.streams.hasOwnProperty("peer-" + member.userID)) - return; - - call.streams["peer-" + member.userID].peer.signal(JSON.parse(signal)); - }, - - /** - * This method is called when a remote stream becomes available - * - * @param {Object} call Information about remote call - * @param {String} member Information about target member - * @param {MediaStream} stream Remote stream available - */ - streamAvailable: function(call, member, stream){ - - call.setLoadingMessageVisibility(false); - - call.streams["peer-" + member.userID].stream = stream; - - call.streams["peer-" + member.userID].video = this.addVideoStream(call, false, stream); - - }, - - /** - * Create and set a video object for a stream - * - * @param {Object} call Target call - * @param {Boolean} muted Specify whether audio should be muted - * or not - * @param {MediaStream} stream Target stream - * @return {HTMLVideoElement} Generated video element - */ - addVideoStream: function(call, muted, stream){ - - /** - * @type {HTMLVideoElement} - */ - var video = createElem2({ - appendTo: call.window.videosTarget, - type: "video" - }); - - video.muted = muted; - - //Set target video object and play it - video.srcObject = stream; - video.play(); - - return video; - }, - - /** - * Stop the ongoing call - * - * @param {Object} call Information about the call - */ - stop: function(call){ - - //Remove the call from the opened list - ComunicWeb.components.calls.currentList.removeCallFromList(call.info.id); - - //Close the connection to the server - if(call.signalClient && call.signalClient.isConnected()) - call.signalClient.close(); - - //Close all socket connections - for (var key in call.streams) { - if (call.streams.hasOwnProperty(key)) { - var element = call.streams[key]; - element.removePeerConnection(); - } - } - - //Close local stream - if(call.localStream){ - call.localStream.getTracks().forEach(function(track){ - track.stop(); - }); - } - - //Notify server - ComunicWeb.components.calls.interface.hangUp(call.info.id, function(){}); - } -} \ No newline at end of file diff --git a/assets/js/components/calls/controller.js b/assets/js/components/calls/controller.js deleted file mode 100644 index c297f277..00000000 --- a/assets/js/components/calls/controller.js +++ /dev/null @@ -1,295 +0,0 @@ -/** - * Calls controller - * - * @author Pierre HUBERT - */ - -ComunicWeb.components.calls.controller = { - - /** - * This variable contains the initialization state - * of the call component - */ - _is_init: false, - - /** - * This variable contains whether the user is being - * notified of a call or not - */ - _is_processing_call: false, - - /** - * Initialize calls component - */ - init: function(){ - - //We init this component just once - if(this._is_init) - return; - - ComunicWeb.debug.logMessage("Initialize calls component"); - - //Initialize call container - var initializeCallContainer = function(){ - - //Signed out users can not make calls - if(!signed_in()){ - ComunicWeb.components.calls.controller.userSignedOut(); - return; - } - - //Need a wrapper to continue - if(!byId("wrapper")) - return; - - //Check if calls target already exists - if(byId("callsTarget")) - return; - - //Call system must be available - if(!ComunicWeb.components.calls.controller.isAvailable()) - return; - - //Create call target - createElem2({ - appendTo: byId("wrapper"), - type: "div", - id: "callsTarget" - }); - - //Now we have to reopen current calls - ComunicWeb.components.calls.controller.reopenCurrentCalls(); - } - - //We wait the user to be connected before trying to get - // call configuration - document.addEventListener("got_user_id", function(){ - - //Check if we have already the call configuration - if(ComunicWeb.components.calls.__config !== undefined) - return; - - ComunicWeb.components.calls.interface.getConfig(function(config){ - - //Check if we could not get calls configuration - if(config.error) - return; - - //Save calls configuration - ComunicWeb.components.calls.__config = config; - - initializeCallContainer(); - }); - - }); - - // Each time a page is opened, wec check if we have to create calls target - document.addEventListener("openPage", function(){ - initializeCallContainer(); - }); - }, - - /** - * Access calls configuration - * - * @return Cached calls configuration - */ - getConfig: function() { - return ComunicWeb.components.calls.__config; - }, - - /** - * Check if the call feature is available or not - */ - isAvailable: function(){ - - //First, check if this browser support webrtc - if(!SimplePeer.WEBRTC_SUPPORT) - return false; - - //If do not have any call configuration, it is not possible to - //make calls - if(this.getConfig() == null) - return false; - - //Read configuration - return this.getConfig().enabled; - }, - - /** - * Initiate a call for a conversation - * - * @param {Number} conversationID The ID of the target conversation - */ - call: function(conversationID){ - - //Create / Get call information for the conversation - ComunicWeb.components.calls.interface.createForConversation(conversationID, function(call){ - - if(call.error) - return notify("Could not get a call for this conversation!", "danger"); - - ComunicWeb.components.calls.controller.open(call); - - }); - - }, - - /** - * Call this method to initialize a call for a call we have information about - * - * @param {Object} call Information about the call - * @param {Boolean} force Specify whether call should be forced to be open, - * even if they are already reported as open - */ - open: function(call, force){ - - //Check if the call is already in the list of calls - if(!force && ComunicWeb.components.calls.currentList.isCallInList(call.id)) - return; - - //Add the call to the list of opened calls - ComunicWeb.components.calls.currentList.addCallToList(call.id); - - //Initialize call - ComunicWeb.components.calls.callWindow.initCall(call); - }, - - /** - * This method is called each time the notification service - * detect that the number of pending calls has increased. It - * must in fact be "thread-safe" to avoid to do twice things - * that should be one only once - * - * @param {number} number The number of pending calls - */ - newCallsAvailable: function(number){ - - //Check if user is already processing a call - if(this._is_processing_call) - return; - this._is_processing_call = true; - - /** - * Switch processing call to false - */ - var undoIsProcessing = function(){ - ComunicWeb.components.calls.controller._is_processing_call = false; - } - - //Get information about the next pending call - ComunicWeb.components.calls.interface.getNextPendingCall(function(call){ - - //Check if there is no pending call - if(call.notice) - return undoIsProcessing(); - - ComunicWeb.components.conversations.utils.getNameForID(call.conversation_id, function(name){ - - //Check for errors - if(!name){ - ComunicWeb.debug.logMessage("Could not get the name of the conversation for a call, cannot process it!"); - undoIsProcessing(); - return; - } - - //Show ring screen - var prompting = true; - var ringScreenInfo = ComunicWeb.components.calls.ringScreen.show(name, 30, function(accept){ - - prompting = false; - - undoIsProcessing(); - - ComunicWeb.components.calls.controller.applyReponseForCall(call, accept); - - }); - - //Regulary check if the call is still valid - var interval = setInterval(function(){ - - if(!prompting) - return clearInterval(interval); - - ComunicWeb.components.calls.interface.getInfo(call.id, function(info){ - - //Check for errors - if(info.error) - return; - - //Refuse the call if everyone has left it - if(ComunicWeb.components.calls.utils.hasEveryoneLeft(info)) - ringScreenInfo.respond(false); - - //Close ring screen if user responded to the call on another Comunic client - if(ComunicWeb.components.calls.utils.getCurrentUserState(info) != "unknown") - ringScreenInfo.close(); - - }); - }, 2000); - - - }); - - }); - }, - - /** - * Apply a response for the call - * - * @param {Object} call Information about the target call - * @param {Boolean} accept TRUE to accept call / FALSE else - */ - applyReponseForCall: function(call, accept){ - - //Send response to server - ComunicWeb.components.calls.interface.respondToCall(call.id, accept, function(r){ - - //Check for error - if(r.error) - return notify("Could not send response to call to server!", "danger"); - - if(!accept) - return; - - //We may start the call now - ComunicWeb.components.calls.controller.open(call); - - }); - - }, - - /** - * Reopen all current calls - */ - reopenCurrentCalls: function(){ - - //Process each call to open it - ComunicWeb.components.calls.currentList.getCurrentCallsList().forEach(function(entry){ - - ComunicWeb.components.calls.interface.getInfo(entry, function(call){ - - if(call.error){ - ComunicWeb.components.calls.currentList.removeCallFromList(entry); - return notify("Could not get information about a call!", "danger"); - } - - ComunicWeb.components.calls.controller.open(call, true); - - }); - - }); - - }, - - /** - * Call this method only if the system is sure that - * nobody is signed in the current tab - */ - userSignedOut: function(){ - - //Remove all the current calls from the list - ComunicWeb.components.calls.currentList.removeAllCalls(); - - } -} \ No newline at end of file diff --git a/assets/js/components/calls/currentList.js b/assets/js/components/calls/currentList.js deleted file mode 100644 index 5bdf7842..00000000 --- a/assets/js/components/calls/currentList.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Currents calls list management - * - * @author Pierre HUBERT - */ - -ComunicWeb.components.calls.currentList = { - - /** - * This variable contains the name of the session storage - * variable that contains active calls - */ - _local_storage_list_calls_name: "current-calls", - - - /** - * Get the list of active calls - * - * @return {number[]} The list of calls - */ - getCurrentCallsList: function(){ - var string = localStorage.getItem(this._local_storage_list_calls_name); - - if(string === null || string == "") - return []; - else - return string.split(","); - }, - - /** - * Check if a call ID is in the list of opened calls - * - * @param {Number} id The ID of the call to check - */ - isCallInList: function(id){ - return this.getCurrentCallsList().includes(""+id); - }, - - /** - * Save a new list of calls - * - * @param {number[]} list The new list of calls to save - */ - saveNewCallsList: function(list){ - localStorage.setItem(this._local_storage_list_calls_name, list.join(",")); - }, - - /** - * Add a call to the list of opened call - * - * @param {number} id The ID of the call to add - */ - addCallToList: function(id){ - var list = this.getCurrentCallsList(); - - if(!list.includes(""+id)) - list.push(id); - - this.saveNewCallsList(list); - }, - - /** - * Remove a call from the list of calls - * - * @param {Number} id The ID of the call to remove - */ - removeCallFromList: function(id){ - - var list = this.getCurrentCallsList(); - - while(list.includes(""+id)) - list.splice(list.indexOf(""+id), 1); - - this.saveNewCallsList(list); - - }, - - /** - * Remove all the calls from the list - */ - removeAllCalls: function(){ - this.saveNewCallsList([]); - } -} \ No newline at end of file diff --git a/assets/js/components/calls/interface.js b/assets/js/components/calls/interface.js deleted file mode 100644 index 80e81278..00000000 --- a/assets/js/components/calls/interface.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Calls interface - * - * @author Pierre HUBERT - */ - -ComunicWeb.components.calls.interface = { - - /** - * Get calls configuration - * - * @param {function} callback Function that will be called - * once the operation has completed - */ - getConfig: function(callback){ - ComunicWeb.common.api.makeAPIrequest("calls/config", {}, true, callback); - }, - - /** - * Create a call for a conversation - * - * @param {Number} convID The ID of the target conversation - * @param {function} callback - */ - createForConversation : function(convID, callback){ - ComunicWeb.common.api.makeAPIrequest( - "calls/createForConversation", - { - conversationID: convID - }, - true, - callback - ); - }, - - /** - * Get information about a single call - * - * @param {Number} callID The ID of the target call - * @param {function} callback Function called on request result - */ - getInfo: function (callID, callback){ - ComunicWeb.common.api.makeAPIrequest( - "calls/getInfo", - { - call_id: callID - }, - true, - callback - ); - }, - - /** - * Get and return the next pending call for the - * user - * - * @param {function} callback - */ - getNextPendingCall: function(callback){ - ComunicWeb.common.api.makeAPIrequest( - "calls/nextPending", - {}, - true, - callback - ); - }, - - /** - * Respond to call - * - * @param {number} call_id The ID of the target call - * @param {boolean} accept TRUE to accept call / FALSE els - * @param {function} callback Function to call once response has been set - */ - respondToCall: function(call_id, accept, callback){ - ComunicWeb.common.api.makeAPIrequest( - "calls/respond", - { - call_id: call_id, - accept: accept - }, - true, - callback - ); - }, - - /** - * Hang up a call - * - * @param {Number} call_id The ID of the target call - * @param {function} callback Function to call on call callback - */ - hangUp: function(call_id, callback){ - ComunicWeb.common.api.makeAPIrequest( - "calls/hangUp", - { - call_id: call_id, - }, - true, - callback - ); - } -} \ No newline at end of file diff --git a/assets/js/components/calls/ringScreen.js b/assets/js/components/calls/ringScreen.js deleted file mode 100644 index 5749e159..00000000 --- a/assets/js/components/calls/ringScreen.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Calls ring screen - * - * Display a popup to ask the user whether he wants - * to respond to a call or not - * - * @author Pierre HUBERT - */ - -ComunicWeb.components.calls.ringScreen = { - - /** - * Song object - * - * @type {SongPlayer} - */ - _song: undefined, - - /** - * Notify user about an incoming call and offer him to respond it - * - * @param {String} title The title of the conversation - * @param {number} timeout Timeout after which the call is automatically - * considered as rejected - * @param {(accept : boolean) => any} callback Callback function - * @return {Object} Information about the window - */ - show: function(title, timeout, callback){ - - //Initialize song first - if(this._song == undefined) - this._song = new SongPlayer([ - ComunicWeb.__config.assetsURL + "audio/phone_ring.mp3", - ComunicWeb.__config.assetsURL + "audio/phone_ring.ogg" - ]); - this._song.playForever(); - - var callContainer = createElem2({ - appendTo: document.body, - type: "div", - class: "ring-call-container" - }); - - var callMessageBox = createElem2({ - appendTo: callContainer, - type: "div", - class: "call-message-box" - }); - - add_p(callMessageBox, "" + title + " is calling you"); - - - //Add buttons to respond to call - var respondButtons = createElem2({ - appendTo: callMessageBox, - type: "div", - class: "response-buttons" - }); - - var rejectButton = createElem2({ - appendTo: respondButtons, - type: "button", - class: "btn btn-danger", - innerHTML: "Reject" - }); - - var acceptButton = createElem2({ - appendTo: respondButtons, - type: "button", - class: "btn btn-success", - innerHTML: "Accept" - }); - - var close = function(){ - - ComunicWeb.components.calls.ringScreen._song.stop(); - - //Remove elem - emptyElem(callContainer); - callContainer.remove(); - - } - - var hasResponded = false; - var respond = function(accept){ - - close(); - - if(hasResponded) - return; - hasResponded = true; - - callback(accept); - } - - rejectButton.addEventListener("click", function() { - respond(false); - }); - - acceptButton.addEventListener("click", function(){ - respond(true); - }); - - //Automatically reject the call after a certain amount of time - setTimeout(function(){ - respond(false); - }, timeout*1000); - - return { - - /** - * A function to close the current ringscreen, without - * calling callback - */ - close: close, - - /** - * A function to programmatically respond to call - */ - respond: respond - - }; - } - -} \ No newline at end of file diff --git a/assets/js/components/calls/userMedia.js b/assets/js/components/calls/userMedia.js deleted file mode 100644 index 2edf7267..00000000 --- a/assets/js/components/calls/userMedia.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * User media getter - * - * @author Pierre HUBERT - */ - -ComunicWeb.components.calls.userMedia = { - - /** - * Get user media - * - * @return {Promise} A promise to get user media - */ - get: function(){ - - //Use latest API - return navigator.mediaDevices - - //Request user media - .getUserMedia({ - audio: true, - video: true - }) - - //Save stream - .then(function(stream){ - ComunicWeb.components.calls.userMedia._currMedia = stream; - return stream; - }); - } - -} \ No newline at end of file diff --git a/assets/js/components/calls/utils.js b/assets/js/components/calls/utils.js deleted file mode 100644 index f38032f9..00000000 --- a/assets/js/components/calls/utils.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Calls utilities - * - * @author Pierre HUBERT - */ - -ComunicWeb.components.calls.utils = { - - /** - * Check out whether all the members of a conversation stop to follow it, - * except the current user - * - * @param {Object} info Information about the conversation to analyze - */ - hasEveryoneLeft: function(info){ - - var allDisconnected = true; - info.members.forEach(function(member){ - if(member.status != "rejected" && member.status != "hang_up" && member.userID != userID()) - allDisconnected = false; - }); - - return allDisconnected; - - }, - - /** - * Get the current user response to a call - * - * @param {Call} call Current call information - * @return The response of the current user to the call - */ - getCurrentUserState: function(call){ - - var userstate = undefined; - call.members.forEach(function(member){ - if(member.userID == userID()) - userstate = member.status - }); - - return userstate; - } - -}; \ No newline at end of file diff --git a/assets/js/components/conversations/chatWindows.js b/assets/js/components/conversations/chatWindows.js index 8a3e7822..c0af34ea 100644 --- a/assets/js/components/conversations/chatWindows.js +++ b/assets/js/components/conversations/chatWindows.js @@ -585,14 +585,9 @@ const ConvChatWindow = { showCallButton: function(conversation){ //Check if calls are disabled - if(!ComunicWeb.components.calls.controller.isAvailable()) + if(/*TODO : implement */false) return; - //Check if it is a conversation with an exceptable number of members - if(conversation.infos.members.length < 2 - || conversation.infos.members.length > ComunicWeb.components.calls.controller.getConfig().maximum_number_members) - return; - //Add the call button var button = createElem2({ insertBefore: conversation.box.boxTools.firstChild, @@ -603,7 +598,7 @@ const ConvChatWindow = { conversation.box.callButton = button; button.addEventListener("click", function(){ - ComunicWeb.components.calls.controller.call(conversation.infos.ID); + // TODO : implement }); }, diff --git a/assets/js/components/notifications/interface.js b/assets/js/components/notifications/interface.js index b6df843a..baaccaac 100644 --- a/assets/js/components/notifications/interface.js +++ b/assets/js/components/notifications/interface.js @@ -25,28 +25,23 @@ ComunicWeb.components.notifications.interface = { /** * Get the number of unread news such as notifications or conversations * - * @param {boolean} get_calls Get the number of pending calls * @param {function} callback */ - getAllUnread: function(get_calls, callback){ + getAllUnread: function(callback){ //Perform API request var apiURI = "notifications/count_all_news"; var params = {}; - //Check if we have to get the number of pending calls - if(get_calls) - params.include_calls = true; - //Perform the request ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback); }, // ASync version of previous request - asyncGetAllUnread: async function(getCalls) { + asyncGetAllUnread: async function() { return await new Promise((res, err) => { - this.getAllUnread(getCalls, (data) => { + this.getAllUnread((data) => { if(data.error) err(data.error); else diff --git a/system/config/dev.config.php b/system/config/dev.config.php index 3d6ce320..313b98fd 100644 --- a/system/config/dev.config.php +++ b/system/config/dev.config.php @@ -162,7 +162,6 @@ class Dev { //Simple peer array("path" => "3rdparty/simplepeer/simplepeer.min.js", "uglifyjs" => false), - array("path" => "3rdparty/SignalExchangerClient/SignalExchangerClient.js", "uglifyjs" => false) ); /** @@ -224,8 +223,7 @@ class Dev { "css/components/incognito/ui.css", //Calls component - "css/components/calls/callWindow.css", - "css/components/calls/ringScreen.css", + //Sidebar component "css/components/sidebar.css", @@ -457,14 +455,8 @@ class Dev { "js/components/incognito/management.js", "js/components/incognito/keyboard.js", - //Calls compontent - "js/components/calls/interface.js", - "js/components/calls/controller.js", - "js/components/calls/callWindow.js", - "js/components/calls/currentList.js", - "js/components/calls/userMedia.js", - "js/components/calls/ringScreen.js", - "js/components/calls/utils.js", + //Calls component + // Web app component "js/components/webApp/interface.js",