mirror of
https://github.com/pierre42100/ComunicWeb
synced 2025-07-13 06:28:08 +00:00
Compare commits
47 Commits
22-01-2019
...
25-02-2019
Author | SHA1 | Date | |
---|---|---|---|
650c761c51 | |||
2e07945994 | |||
d9fe86d160 | |||
bbaaf3a5c2 | |||
0f4d3042c3 | |||
be0306b309 | |||
c819aaf716 | |||
b6e818dc00 | |||
a3f8e2f243 | |||
9f3311978d | |||
817ef1069c | |||
faba9b36cb | |||
5276790afe | |||
8d613d4e57 | |||
5a067001e9 | |||
1a3117a603 | |||
cbfe141c32 | |||
35c4597017 | |||
289a66e55b | |||
8c6e04abd0 | |||
79dfa0511b | |||
982c40788c | |||
a46a7154ea | |||
fe2a6c2dfe | |||
c1053b8041 | |||
194b6c60de | |||
0b806d5bb2 | |||
b8865b96f0 | |||
b62fefc258 | |||
590e1d6794 | |||
ece7a97d42 | |||
99fffb839f | |||
154f1b98ef | |||
45a4f552e8 | |||
ca39a08d07 | |||
5eb1c8b274 | |||
29b5499b85 | |||
b08255cd18 | |||
be03249f11 | |||
0a52bd5fe3 | |||
9eab6c7e2e | |||
118cfeee41 | |||
f79ef55e3b | |||
5640580397 | |||
e90c20c262 | |||
cd4e6ddcb1 | |||
feb17e3f13 |
@ -28,4 +28,5 @@ ComunicWeb would not exists without the following technologies developped by the
|
|||||||
- JSZip Utils (https://github.com/Stuk/jszip-utils.git) (MIT License)
|
- JSZip Utils (https://github.com/Stuk/jszip-utils.git) (MIT License)
|
||||||
- SCEditor (BBC WYIWYG editor) (https://github.com/samclarke/SCEditor) (MIT License)
|
- SCEditor (BBC WYIWYG editor) (https://github.com/samclarke/SCEditor) (MIT License)
|
||||||
- JavaScript BBCode Parser (https://github.com/Frug/js-bbcode-parser) (MIT License)
|
- JavaScript BBCode Parser (https://github.com/Frug/js-bbcode-parser) (MIT License)
|
||||||
- Pacman (https://github.com/daleharvey/pacman) (WTFPL License)
|
- Pacman (https://github.com/daleharvey/pacman) (WTFPL License)
|
||||||
|
- SimplePeer (https://github.com/feross/simple-peer) (MIT License)
|
262
assets/3rdparty/SignalExchangerClient/SignalExchangerClient.js
vendored
Normal file
262
assets/3rdparty/SignalExchangerClient/SignalExchangerClient.js
vendored
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
11
assets/3rdparty/simplepeer/simplepeer.min.js
vendored
Normal file
11
assets/3rdparty/simplepeer/simplepeer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
assets/audio/phone_ring.mp3
Normal file
BIN
assets/audio/phone_ring.mp3
Normal file
Binary file not shown.
BIN
assets/audio/phone_ring.ogg
Normal file
BIN
assets/audio/phone_ring.ogg
Normal file
Binary file not shown.
@ -25,6 +25,10 @@ a {
|
|||||||
color: #72afd2;
|
color: #72afd2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sceditor iframe
|
* Sceditor iframe
|
||||||
*/
|
*/
|
||||||
|
158
assets/css/components/calls/callWindow.css
Normal file
158
assets/css/components/calls/callWindow.css
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
assets/css/components/calls/ringScreen.css
Normal file
33
assets/css/components/calls/ringScreen.css
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
@ -4,6 +4,10 @@
|
|||||||
* @author Pierre HUBERT
|
* @author Pierre HUBERT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main definition
|
||||||
|
*/
|
||||||
#conversationsElem .box {
|
#conversationsElem .box {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: 350px;
|
height: 350px;
|
||||||
@ -15,6 +19,10 @@
|
|||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#conversationsElem .chat-window .box-title {
|
||||||
|
font-size: 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conversations create message form
|
* Conversations create message form
|
||||||
*/
|
*/
|
||||||
|
@ -63,6 +63,20 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post .post-youtube.post-youtube-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
background-color: #3c8dbc;
|
||||||
|
color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post .post-youtube.post-youtube-placeholder .title {
|
||||||
|
font-size: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
.post .post-youtube {
|
.post .post-youtube {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 300px;
|
height: 300px;
|
||||||
|
@ -59,8 +59,8 @@ p, h1, h2, h3, h4, h5, h6 {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callouts
|
* Callouts
|
||||||
*/
|
*/
|
||||||
.callout.callout-info {
|
.callout.callout-info {
|
||||||
background-color: var(--black-5) !important;
|
background-color: var(--black-5) !important;
|
||||||
}
|
}
|
||||||
@ -354,11 +354,16 @@ div.sceditor-dropdown input {
|
|||||||
color: var(--black5);
|
color: var(--black5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-dropdown {
|
.select2-dropdown, .select2-selection,
|
||||||
background-color: var(--black5);
|
.select2-search__field {
|
||||||
|
background-color: var(--black5) !important;
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select2-selection__rendered {
|
||||||
|
color: var(--white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||||
background-color: var(--black4);
|
background-color: var(--black4);
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
@ -445,6 +450,10 @@ div.sceditor-dropdown input {
|
|||||||
filter: brightness(100%);
|
filter: brightness(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post-youtube-placeholder {
|
||||||
|
background-color: var(--black6) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.box-comments .username,
|
.box-comments .username,
|
||||||
.box-comments .comment-content {
|
.box-comments .comment-content {
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
@ -571,4 +580,47 @@ img[src$="groups_logo/default.png"] {
|
|||||||
*/
|
*/
|
||||||
.page_account_created .message_container .message {
|
.page_account_created .message_container .message {
|
||||||
background-color: var(--black5);
|
background-color: var(--black5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call system
|
||||||
|
*/
|
||||||
|
.call-window {
|
||||||
|
border: 1px var(--black4) solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ring-call-container {
|
||||||
|
background-color: var(--black5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.call-message-box {
|
||||||
|
background-color: var(--black6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups list page
|
||||||
|
*/
|
||||||
|
.groups-main-page {
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups members page
|
||||||
|
*/
|
||||||
|
.group-members-page {
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick a movie modal
|
||||||
|
*/
|
||||||
|
.pick-movie-modal {
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group settings page
|
||||||
|
*/
|
||||||
|
.group-settings-container label {
|
||||||
|
color: var(--white);
|
||||||
}
|
}
|
35
assets/custom_ts/Utils.d.ts
vendored
Normal file
35
assets/custom_ts/Utils.d.ts
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Typescript typing rules for Utils
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare interface CreateElem2Args {
|
||||||
|
type : string,
|
||||||
|
appendTo ?: HTMLElement,
|
||||||
|
insertBefore ?: HTMLElement,
|
||||||
|
insertAsFirstChild ?: HTMLElement,
|
||||||
|
class ?: string,
|
||||||
|
id ?: string,
|
||||||
|
title ?: string,
|
||||||
|
src ?: string,
|
||||||
|
href ?: string,
|
||||||
|
name ?: string,
|
||||||
|
elemType ?: string,
|
||||||
|
value ?: string,
|
||||||
|
placeholder ?: string,
|
||||||
|
innerHTML ?: string,
|
||||||
|
innerLang ?: string,
|
||||||
|
innerHTMLprefix ?: string,
|
||||||
|
disabled ?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare function createElem(nodeType : string, appendTo : string) : HTMLElement;
|
||||||
|
|
||||||
|
declare function createElem2(infos : CreateElem2Args) : HTMLElement;
|
||||||
|
|
||||||
|
declare function byId(id : string) : HTMLElement;
|
||||||
|
|
||||||
|
declare function emptyElem(target : HTMLElement) : void;
|
||||||
|
|
||||||
|
declare function checkMail(emailAddress : string) : boolean;
|
@ -21,6 +21,11 @@ ComunicWeb.common.date = {
|
|||||||
* @return {String} The generated date
|
* @return {String} The generated date
|
||||||
*/
|
*/
|
||||||
diffToStr: function(difference){
|
diffToStr: function(difference){
|
||||||
|
|
||||||
|
//Check if difference is less than one second
|
||||||
|
if(difference < 0)
|
||||||
|
difference = 0;
|
||||||
|
|
||||||
//Calculate seconds
|
//Calculate seconds
|
||||||
var seconds = difference-Math.floor(difference/60)*60;
|
var seconds = difference-Math.floor(difference/60)*60;
|
||||||
var difference = (difference - seconds)/60;
|
var difference = (difference - seconds)/60;
|
||||||
|
@ -1144,6 +1144,66 @@ 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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Easter egg : pacman
|
* Easter egg : pacman
|
||||||
*/
|
*/
|
||||||
|
51
assets/js/common/songPlayer.js
Normal file
51
assets/js/common/songPlayer.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Song player
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SongPlayer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new SongPlayer instance
|
||||||
|
*
|
||||||
|
* @param {String[]} sources The list of sources to exploit for the song
|
||||||
|
*/
|
||||||
|
constructor(sources){
|
||||||
|
|
||||||
|
this.songElem = document.createElement("audio");
|
||||||
|
|
||||||
|
//Process the list of sources
|
||||||
|
for (var index = 0; index < sources.length; index++) {
|
||||||
|
var url = sources[index];
|
||||||
|
|
||||||
|
var source = document.createElement("source");
|
||||||
|
source.src = url;
|
||||||
|
this.songElem.appendChild(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play audio just once
|
||||||
|
*/
|
||||||
|
playOnce(){
|
||||||
|
this.songElem.loop = false;
|
||||||
|
this.songElem.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play song forever
|
||||||
|
*/
|
||||||
|
playForever(){
|
||||||
|
this.songElem.loop = true;
|
||||||
|
this.songElem.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop song
|
||||||
|
*/
|
||||||
|
stop(){
|
||||||
|
this.songElem.pause();
|
||||||
|
this.songElem.currentTime = 0;
|
||||||
|
}
|
||||||
|
}
|
@ -58,6 +58,11 @@ ComunicWeb.common.system = {
|
|||||||
*/
|
*/
|
||||||
ComunicWeb.components.darkTheme.refresh();
|
ComunicWeb.components.darkTheme.refresh();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize call system
|
||||||
|
*/
|
||||||
|
ComunicWeb.components.calls.controller.init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What to do after login refresh
|
* What to do after login refresh
|
||||||
*/
|
*/
|
||||||
|
@ -700,4 +700,57 @@ function SendEvent(name, details){
|
|||||||
|
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request full screen for an HTML element
|
||||||
|
*
|
||||||
|
* Note : this function must be called inside of an event of
|
||||||
|
* the user like a click, otherwise it will not work
|
||||||
|
*
|
||||||
|
* This function source code is based on this StackOverFlow Answer
|
||||||
|
* https://stackoverflow.com/a/32100295/3781411
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} elem The element for which we want
|
||||||
|
* full screen
|
||||||
|
*/
|
||||||
|
function RequestFullScreen(element){
|
||||||
|
if (
|
||||||
|
document.fullscreenElement ||
|
||||||
|
document.webkitFullscreenElement ||
|
||||||
|
document.mozFullScreenElement ||
|
||||||
|
document.msFullscreenElement
|
||||||
|
) {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else if (document.mozCancelFullScreen) {
|
||||||
|
document.mozCancelFullScreen();
|
||||||
|
} else if (document.webkitExitFullscreen) {
|
||||||
|
document.webkitExitFullscreen();
|
||||||
|
} else if (document.msExitFullscreen) {
|
||||||
|
document.msExitFullscreen();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (element.requestFullscreen) {
|
||||||
|
element.requestFullscreen();
|
||||||
|
} else if (element.mozRequestFullScreen) {
|
||||||
|
element.mozRequestFullScreen();
|
||||||
|
} else if (element.webkitRequestFullscreen) {
|
||||||
|
element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||||
|
} else if (element.msRequestFullscreen) {
|
||||||
|
element.msRequestFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check out whether Comunic has grabbed full screen or not
|
||||||
|
*
|
||||||
|
* @return {Boolean} TRUE if the window is in full screen / FALSE else
|
||||||
|
*/
|
||||||
|
function IsFullScreen(){
|
||||||
|
return document.fullscreenElement ||
|
||||||
|
document.webkitFullscreenElement ||
|
||||||
|
document.mozFullScreenElement ||
|
||||||
|
document.msFullscreenElement;
|
||||||
}
|
}
|
838
assets/js/components/calls/callWindow.js
Normal file
838
assets/js/components/calls/callWindow.js
Normal file
@ -0,0 +1,838 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: "<i class='fa fa-phone'>"
|
||||||
|
});
|
||||||
|
|
||||||
|
//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: "<i class='fa fa-times'></i>"
|
||||||
|
});
|
||||||
|
|
||||||
|
//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: "<i class='fa fa-clock-o'></i>"
|
||||||
|
});
|
||||||
|
|
||||||
|
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: "<i class='fa " + button.icon + "'></i>"
|
||||||
|
});
|
||||||
|
|
||||||
|
//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...");
|
||||||
|
|
||||||
|
ComunicWeb.components.calls.userMedia.get().then(function(stream){
|
||||||
|
|
||||||
|
//Check if connection has already been closed
|
||||||
|
if(!call.open)
|
||||||
|
return;
|
||||||
|
|
||||||
|
call.localStream = stream;
|
||||||
|
|
||||||
|
//Initialize signaling server connection
|
||||||
|
ComunicWeb.components.calls.callWindow.initializeConnectionToSignalingServer(call);
|
||||||
|
|
||||||
|
//Add local stream to the list of visible stream
|
||||||
|
call.localStreamVideo = ComunicWeb.components.calls.callWindow.addVideoStream(call, true, stream);
|
||||||
|
|
||||||
|
//Mark as connecting
|
||||||
|
call.setLoadingMessage("Connecting...");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}).catch(function(e){
|
||||||
|
console.error("Get user media error: ", e);
|
||||||
|
call.setLoadingMessageVisibility(false);
|
||||||
|
return notify("Could not get your microphone and camera!", "danger");
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() || !call.localStream)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
};
|
||||||
|
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("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);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//If this peer does not initialize connection, inform other peer we are ready
|
||||||
|
if(!isInitiator)
|
||||||
|
call.signalClient.sendReadyMessage(member.user_call_id);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
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(){});
|
||||||
|
}
|
||||||
|
}
|
293
assets/js/components/calls/controller.js
Normal file
293
assets/js/components/calls/controller.js
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
open: function(call){
|
||||||
|
|
||||||
|
//Check if the call is already in the list of calls
|
||||||
|
if(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);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
84
assets/js/components/calls/currentList.js
Normal file
84
assets/js/components/calls/currentList.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* 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([]);
|
||||||
|
}
|
||||||
|
}
|
103
assets/js/components/calls/interface.js
Normal file
103
assets/js/components/calls/interface.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
125
assets/js/components/calls/ringScreen.js
Normal file
125
assets/js/components/calls/ringScreen.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/**
|
||||||
|
* 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, "<i>" + title + "</i> 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
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
32
assets/js/components/calls/userMedia.js
Normal file
32
assets/js/components/calls/userMedia.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
44
assets/js/components/calls/utils.js
Normal file
44
assets/js/components/calls/utils.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -56,7 +56,7 @@ ComunicWeb.components.conversations.chatWindows = {
|
|||||||
infosBox.conversationID = infos.conversationID;
|
infosBox.conversationID = infos.conversationID;
|
||||||
|
|
||||||
//Change box root class name
|
//Change box root class name
|
||||||
infosBox.rootElem.className += " direct-chat direct-chat-primary";
|
infosBox.rootElem.className += " chat-window direct-chat direct-chat-primary";
|
||||||
|
|
||||||
//Adapt close button behaviour
|
//Adapt close button behaviour
|
||||||
infosBox.closeFunction = function(){
|
infosBox.closeFunction = function(){
|
||||||
@ -84,18 +84,21 @@ ComunicWeb.components.conversations.chatWindows = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Add button to get conversation members
|
//Add button to get conversation members
|
||||||
infosBox.membersButton = createElem("button");
|
infosBox.membersButton = createElem2({
|
||||||
infosBox.closeButton.parentNode.insertBefore(infosBox.membersButton, infosBox.closeButton);
|
type: "button",
|
||||||
infosBox.membersButton.type = "button";
|
insertBefore: infosBox.closeButton,
|
||||||
infosBox.membersButton.className = "btn btn-box-tool";
|
elemType: "button",
|
||||||
|
class: "btn btn-box-tool",
|
||||||
|
title: "Conversation members"
|
||||||
|
});
|
||||||
infosBox.membersButton.setAttribute("data-toggle", "tooltip");
|
infosBox.membersButton.setAttribute("data-toggle", "tooltip");
|
||||||
infosBox.membersButton.setAttribute("data-widget", "chat-pane-toggle");
|
infosBox.membersButton.setAttribute("data-widget", "chat-pane-toggle");
|
||||||
infosBox.membersButton.title = "Conversation members";
|
|
||||||
|
|
||||||
//Add button icon
|
//Add button icon
|
||||||
var buttonIcon = createElem("i", infosBox.membersButton);
|
var buttonIcon = createElem("i", infosBox.membersButton);
|
||||||
buttonIcon.className = "fa fa-users";
|
buttonIcon.className = "fa fa-users";
|
||||||
|
|
||||||
|
|
||||||
//Add conversation members pane
|
//Add conversation members pane
|
||||||
var membersPane = createElem("div", infosBox.boxBody);
|
var membersPane = createElem("div", infosBox.boxBody);
|
||||||
membersPane.className = "direct-chat-contacts";
|
membersPane.className = "direct-chat-contacts";
|
||||||
@ -319,6 +322,9 @@ ComunicWeb.components.conversations.chatWindows = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Add call button (if possible)
|
||||||
|
ComunicWeb.components.conversations.chatWindows.showCallButton(conversationInfos);
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -563,16 +569,46 @@ ComunicWeb.components.conversations.chatWindows = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a call button to the conversation, if possible
|
||||||
|
*
|
||||||
|
* @param {Object} conversation Information about the conversation
|
||||||
|
*/
|
||||||
|
showCallButton: function(conversation){
|
||||||
|
|
||||||
|
//Check if calls are disabled
|
||||||
|
if(!ComunicWeb.components.calls.controller.isAvailable())
|
||||||
|
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,
|
||||||
|
type: "button",
|
||||||
|
class: "btn btn-box-tool",
|
||||||
|
innerHTML: "<i class='fa fa-phone'></i>"
|
||||||
|
});
|
||||||
|
conversation.box.callButton = button;
|
||||||
|
|
||||||
|
button.addEventListener("click", function(){
|
||||||
|
ComunicWeb.components.calls.controller.call(conversation.infos.ID);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process submited update conversation form
|
* Process submited update conversation form
|
||||||
*
|
*
|
||||||
* @param {Object} conversation Informations about the conversation
|
* @param {Object} conversation Information about the conversation
|
||||||
* @return {Boolean} True for a success
|
* @return {Boolean} True for a success
|
||||||
*/
|
*/
|
||||||
submitUpdateForm: function(conversation){
|
submitUpdateForm: function(conversation){
|
||||||
|
|
||||||
//Then, get informations about the input
|
//Then, get information about the input
|
||||||
var newValues = {
|
var newValues = {
|
||||||
conversationID: conversation.infos.ID,
|
conversationID: conversation.infos.ID,
|
||||||
following: conversation.settingsForm.followConversationInput.checked,
|
following: conversation.settingsForm.followConversationInput.checked,
|
||||||
@ -621,7 +657,7 @@ ComunicWeb.components.conversations.chatWindows = {
|
|||||||
/**
|
/**
|
||||||
* Submit a new message form
|
* Submit a new message form
|
||||||
*
|
*
|
||||||
* @param {Object} convInfos Informations about the conversation
|
* @param {Object} convInfos Information about the conversation
|
||||||
* @return {Boolean} True for a success
|
* @return {Boolean} True for a success
|
||||||
*/
|
*/
|
||||||
submitMessageForm: function(convInfos){
|
submitMessageForm: function(convInfos){
|
||||||
@ -673,7 +709,7 @@ ComunicWeb.components.conversations.chatWindows = {
|
|||||||
/**
|
/**
|
||||||
* Reset a create a message form
|
* Reset a create a message form
|
||||||
*
|
*
|
||||||
* @param {Object} infos Informations about the conversation
|
* @param {Object} infos Information about the conversation
|
||||||
* @return {Boolean} True for a success
|
* @return {Boolean} True for a success
|
||||||
*/
|
*/
|
||||||
resetCreateMessageForm: function(infos){
|
resetCreateMessageForm: function(infos){
|
||||||
|
@ -54,6 +54,27 @@ ComunicWeb.components.conversations.utils = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a conversation ID, get its name
|
||||||
|
*
|
||||||
|
* @param {number} id The ID of the target conversation
|
||||||
|
* @param {function} onGotName Function called once we have got the name of the conversation
|
||||||
|
*/
|
||||||
|
getNameForID: function(id, onGotName){
|
||||||
|
|
||||||
|
ComunicWeb.components.conversations.interface.getInfosOne(id, function(info){
|
||||||
|
|
||||||
|
//Check if an error occurred
|
||||||
|
if(info.error)
|
||||||
|
return onGotName(false);
|
||||||
|
|
||||||
|
//Get and return the name of the conversation
|
||||||
|
ComunicWeb.components.conversations.utils.getName(info, onGotName);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and display a conversation creation / edition form
|
* Create and display a conversation creation / edition form
|
||||||
*
|
*
|
||||||
|
@ -25,14 +25,19 @@ ComunicWeb.components.notifications.interface = {
|
|||||||
/**
|
/**
|
||||||
* Get the number of unread news such as notifications or conversations
|
* Get the number of unread news such as notifications or conversations
|
||||||
*
|
*
|
||||||
|
* @param {boolean} get_calls Get the number of pending calls
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
getAllUnread: function(callback){
|
getAllUnread: function(get_calls, callback){
|
||||||
|
|
||||||
//Perform API request
|
//Perform API request
|
||||||
var apiURI = "notifications/count_all_news";
|
var apiURI = "notifications/count_all_news";
|
||||||
var params = {};
|
var params = {};
|
||||||
|
|
||||||
|
//Check if we have to get the number of pending calls
|
||||||
|
if(get_calls)
|
||||||
|
params.include_calls = true;
|
||||||
|
|
||||||
//Perform the request
|
//Perform the request
|
||||||
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
|
ComunicWeb.common.api.makeAPIrequest(apiURI, params, true, callback);
|
||||||
|
|
||||||
|
@ -35,7 +35,8 @@ ComunicWeb.components.notifications.service = {
|
|||||||
|
|
||||||
|
|
||||||
//Get the number of notifications from the API
|
//Get the number of notifications from the API
|
||||||
ComunicWeb.components.notifications.interface.getAllUnread(function(response){
|
ComunicWeb.components.notifications.interface.getAllUnread(
|
||||||
|
ComunicWeb.components.calls.controller.isAvailable(), function(response){
|
||||||
|
|
||||||
//Continue in case of success
|
//Continue in case of success
|
||||||
if(response.error)
|
if(response.error)
|
||||||
@ -76,6 +77,11 @@ ComunicWeb.components.notifications.service = {
|
|||||||
ComunicWeb.components.notifications.song.play();
|
ComunicWeb.components.notifications.song.play();
|
||||||
|
|
||||||
ComunicWeb.components.notifications.service.last_notifs_number = total_number_notifs;
|
ComunicWeb.components.notifications.service.last_notifs_number = total_number_notifs;
|
||||||
|
|
||||||
|
|
||||||
|
//Process the number of calls if possible
|
||||||
|
if(response.calls && response.calls > 0)
|
||||||
|
ComunicWeb.components.calls.controller.newCallsAvailable(response.calls);
|
||||||
});
|
});
|
||||||
|
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
@ -18,24 +18,13 @@ ComunicWeb.components.notifications.song = {
|
|||||||
|
|
||||||
//Create song element if required
|
//Create song element if required
|
||||||
if(this.songElem == null){
|
if(this.songElem == null){
|
||||||
this.songElem = createElem2({
|
this.songElem = new SongPlayer([
|
||||||
type: "audio"
|
ComunicWeb.__config.assetsURL + "audio/notif_song.mp3",
|
||||||
});
|
ComunicWeb.__config.assetsURL + "audio/notif_song.ogg"
|
||||||
|
]);
|
||||||
createElem2({
|
|
||||||
type: "source",
|
|
||||||
appendTo: this.songElem,
|
|
||||||
src: ComunicWeb.__config.assetsURL + "audio/notif_song.mp3"
|
|
||||||
});
|
|
||||||
|
|
||||||
createElem2({
|
|
||||||
type: "source",
|
|
||||||
appendTo: this.songElem,
|
|
||||||
src: ComunicWeb.__config.assetsURL + "audio/notif_song.ogg"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Play song
|
//Play song
|
||||||
this.songElem.play();
|
this.songElem.playOnce();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -424,17 +424,53 @@ ComunicWeb.components.posts.ui = {
|
|||||||
//In case of YouTube video
|
//In case of YouTube video
|
||||||
else if(info.kind == "youtube"){
|
else if(info.kind == "youtube"){
|
||||||
|
|
||||||
//Create iframe
|
//Create frame placeholder
|
||||||
var youtube_iframe = createElem2({
|
var youtube_placeholder = createElem2({
|
||||||
appendTo: postRoot,
|
appendTo: postRoot,
|
||||||
type: "iframe",
|
type: "div",
|
||||||
class: "post-youtube",
|
class: "post-youtube post-youtube-placeholder"
|
||||||
src: "https://www.youtube-nocookie.com/embed/"+info.file_path+"?rel=0"
|
});
|
||||||
|
|
||||||
|
//Title
|
||||||
|
createElem2({
|
||||||
|
appendTo: youtube_placeholder,
|
||||||
|
type: "div",
|
||||||
|
class: "title",
|
||||||
|
innerHTML: "<i class='fa fa-youtube-play'></i> YouTube Movie"
|
||||||
|
});
|
||||||
|
|
||||||
|
createElem2({
|
||||||
|
appendTo: youtube_placeholder,
|
||||||
|
type: "a",
|
||||||
|
class: "btn btn-default",
|
||||||
|
innerHTML: "Open on YouTube",
|
||||||
|
href: "https://youtube.com/watch?v=" + info.file_path,
|
||||||
|
}).target = "_blank";
|
||||||
|
|
||||||
|
var openHere = createElem2({
|
||||||
|
appendTo: youtube_placeholder,
|
||||||
|
type: "div",
|
||||||
|
class: "cursor-pointer",
|
||||||
|
innerHTML: "Open here"
|
||||||
|
});
|
||||||
|
|
||||||
|
openHere.addEventListener("click", function(){
|
||||||
|
|
||||||
|
//Create iframe
|
||||||
|
var youtube_iframe = createElem2({
|
||||||
|
insertBefore: youtube_placeholder,
|
||||||
|
type: "iframe",
|
||||||
|
class: "post-youtube",
|
||||||
|
src: "https://www.youtube-nocookie.com/embed/"+info.file_path+"?rel=0"
|
||||||
|
});
|
||||||
|
youtube_iframe.setAttribute("frameborder", 0);
|
||||||
|
youtube_iframe.setAttribute("gesture", "media");
|
||||||
|
youtube_iframe.setAttribute("allow", "encrypted-media");
|
||||||
|
youtube_iframe.setAttribute("allowfullscreen", "");
|
||||||
|
|
||||||
|
youtube_placeholder.remove();
|
||||||
|
|
||||||
});
|
});
|
||||||
youtube_iframe.setAttribute("frameborder", 0);
|
|
||||||
youtube_iframe.setAttribute("gesture", "media");
|
|
||||||
youtube_iframe.setAttribute("allow", "encrypted-media");
|
|
||||||
youtube_iframe.setAttribute("allowfullscreen", "");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,12 +77,18 @@ ComunicWeb.user.userLogin = {
|
|||||||
//Perform next action
|
//Perform next action
|
||||||
afterGetCurrentUserID(0);
|
afterGetCurrentUserID(0);
|
||||||
}
|
}
|
||||||
else{
|
else
|
||||||
|
{
|
||||||
//Update user ID
|
//Update user ID
|
||||||
ComunicWeb.user.userLogin.__userID = result.userID;
|
ComunicWeb.user.userLogin.__userID = result.userID;
|
||||||
|
|
||||||
//Perform next action
|
//Perform next action
|
||||||
afterGetCurrentUserID(result.userID)
|
afterGetCurrentUserID(result.userID);
|
||||||
|
|
||||||
|
//Notify about the event
|
||||||
|
SendEvent("got_user_id", {
|
||||||
|
userID: result.userID
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
58
builder
58
builder
@ -63,11 +63,12 @@ function files_to_file(array $files, string $target) : bool {
|
|||||||
/**
|
/**
|
||||||
* Copy an array of files into a specific target file using uglifyJS
|
* Copy an array of files into a specific target file using uglifyJS
|
||||||
*
|
*
|
||||||
|
* @param string $begin_path The begining of each path
|
||||||
* @param array $files The name of the source file
|
* @param array $files The name of the source file
|
||||||
* @param string $target The target file
|
* @param string $target The target file
|
||||||
* @return bool TRUE in case of success / FALSE in case of failure
|
* @return bool TRUE in case of success / FALSE in case of failure
|
||||||
*/
|
*/
|
||||||
function js_files_to_file(array $files, string $target){
|
function js_files_to_file(string $begin_path, array $files, string $target){
|
||||||
|
|
||||||
$source = "";
|
$source = "";
|
||||||
|
|
||||||
@ -77,17 +78,46 @@ function js_files_to_file(array $files, string $target){
|
|||||||
|
|
||||||
foreach($files as $file){
|
foreach($files as $file){
|
||||||
|
|
||||||
//Compress file
|
$uglifyjs = true;
|
||||||
notice("Parsing with UGLIFYJS: ".$file);
|
|
||||||
exec("/usr/bin/uglifyjs '".$file."' -c -o ".TEMP_FILE, $output, $exit_code);
|
|
||||||
|
|
||||||
//Get the content of the file
|
|
||||||
$source .= "\n".file_get_contents(TEMP_FILE);
|
|
||||||
|
|
||||||
if($exit_code != 0){
|
//Check if file entry is an array or a string
|
||||||
notice("An error (".$exit_code.") occured while parsing file ".$file, TRUE);
|
if(is_string($file))
|
||||||
exit(10);
|
$file = $begin_path.$file;
|
||||||
|
|
||||||
|
//It is an array
|
||||||
|
else if(is_array($file)) {
|
||||||
|
|
||||||
|
//Check if we have special information for uglifyjs
|
||||||
|
if(isset($file["uglifyjs"]))
|
||||||
|
$uglifyjs = $file["uglifyjs"];
|
||||||
|
|
||||||
|
$file = $begin_path.$file["path"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Else the kind of entry is not supported
|
||||||
|
else
|
||||||
|
throw new Exception("Excepted string or array, got something else for javascript entry!");
|
||||||
|
|
||||||
|
//Compress file
|
||||||
|
if($uglifyjs){
|
||||||
|
notice("Parsing with UGLIFYJS: ".$file);
|
||||||
|
exec("/usr/bin/uglifyjs '".$file."' -c -o ".TEMP_FILE, $output, $exit_code);
|
||||||
|
|
||||||
|
//Get the content of the file
|
||||||
|
$source .= "\n".file_get_contents(TEMP_FILE);
|
||||||
|
|
||||||
|
if($exit_code != 0){
|
||||||
|
notice("An error (".$exit_code.") occured while parsing file ".$file, TRUE);
|
||||||
|
exit(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Else we take the file as is
|
||||||
|
else
|
||||||
|
$source .= "\n".file_get_contents($file);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete the temp file
|
//Delete the temp file
|
||||||
@ -192,9 +222,8 @@ files_to_file($thirdPartyDebugFiles, $targetThirdPartyCSS);
|
|||||||
|
|
||||||
//3rd party JS
|
//3rd party JS
|
||||||
notice("Third Party JS");
|
notice("Third Party JS");
|
||||||
$thirdPartyDebugFiles = array_put_begining($path_debug_assets, $debug::THIRD_PARTY_JS);
|
|
||||||
$targetThirdPartyJS = $path_release_assets.$release::THIRD_PARTY_JS;
|
$targetThirdPartyJS = $path_release_assets.$release::THIRD_PARTY_JS;
|
||||||
js_files_to_file($thirdPartyDebugFiles, $targetThirdPartyJS);
|
js_files_to_file($path_debug_assets, $debug::THIRD_PARTY_JS, $targetThirdPartyJS);
|
||||||
|
|
||||||
//App CSS
|
//App CSS
|
||||||
notice("App CSS");
|
notice("App CSS");
|
||||||
@ -204,9 +233,8 @@ files_to_file($appDebugFiles, $targetAppCSS);
|
|||||||
|
|
||||||
//App JS
|
//App JS
|
||||||
notice("App JS");
|
notice("App JS");
|
||||||
$appDebugFiles = array_put_begining($path_debug_assets, $debug::APP_JS);
|
|
||||||
$targetAppJS = $path_release_assets.$release::APP_JS;
|
$targetAppJS = $path_release_assets.$release::APP_JS;
|
||||||
js_files_to_file($appDebugFiles, $targetAppJS);
|
js_files_to_file($path_debug_assets, $debug::APP_JS, $targetAppJS);
|
||||||
|
|
||||||
|
|
||||||
//Make some adpations on third party files
|
//Make some adpations on third party files
|
||||||
@ -231,6 +259,8 @@ rcopy($path_debug_assets."3rdparty/adminLTE/plugins/iCheck/flat/icheck-flat-imgs
|
|||||||
rcopy($path_debug_assets."img/", $path_release_assets."img/");
|
rcopy($path_debug_assets."img/", $path_release_assets."img/");
|
||||||
rcopy($path_debug_assets."templates/", $path_release_assets."templates/");
|
rcopy($path_debug_assets."templates/", $path_release_assets."templates/");
|
||||||
|
|
||||||
|
//Copy songs
|
||||||
|
rcopy($path_debug_assets."audio/", $path_release_assets."audio/");
|
||||||
|
|
||||||
//Copy dark theme
|
//Copy dark theme
|
||||||
rcopy($path_debug_assets."css/dark_theme.css", $path_release_assets."css/dark_theme.css");
|
rcopy($path_debug_assets."css/dark_theme.css", $path_release_assets."css/dark_theme.css");
|
||||||
|
@ -159,6 +159,10 @@ class Dev {
|
|||||||
//JS BBCode Parser
|
//JS BBCode Parser
|
||||||
"3rdparty/js-bbcode-parser/bbcode-config.js",
|
"3rdparty/js-bbcode-parser/bbcode-config.js",
|
||||||
"3rdparty/js-bbcode-parser/bbcode-parser.js",
|
"3rdparty/js-bbcode-parser/bbcode-parser.js",
|
||||||
|
|
||||||
|
//Simple peer
|
||||||
|
array("path" => "3rdparty/simplepeer/simplepeer.min.js", "uglifyjs" => false),
|
||||||
|
array("path" => "3rdparty/SignalExchangerClient/SignalExchangerClient.js", "uglifyjs" => false)
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,6 +222,10 @@ class Dev {
|
|||||||
//Incognito mode component
|
//Incognito mode component
|
||||||
"css/components/incognito/ui.css",
|
"css/components/incognito/ui.css",
|
||||||
|
|
||||||
|
//Calls component
|
||||||
|
"css/components/calls/callWindow.css",
|
||||||
|
"css/components/calls/ringScreen.css",
|
||||||
|
|
||||||
//Pacman (easter egg) stylesheet
|
//Pacman (easter egg) stylesheet
|
||||||
"css/components/pacman.css",
|
"css/components/pacman.css",
|
||||||
|
|
||||||
@ -312,6 +320,7 @@ class Dev {
|
|||||||
"js/common/formChecker.js",
|
"js/common/formChecker.js",
|
||||||
"js/common/date.js",
|
"js/common/date.js",
|
||||||
"js/common/system.js",
|
"js/common/system.js",
|
||||||
|
array("path" => "js/common/songPlayer.js", "uglifyjs" => false),
|
||||||
|
|
||||||
//Languages
|
//Languages
|
||||||
"js/langs/en.inc.js",
|
"js/langs/en.inc.js",
|
||||||
@ -437,6 +446,15 @@ class Dev {
|
|||||||
"js/components/incognito/management.js",
|
"js/components/incognito/management.js",
|
||||||
"js/components/incognito/keyboard.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",
|
||||||
|
|
||||||
//Pacman component (easter egg)
|
//Pacman component (easter egg)
|
||||||
"js/components/pacman.js",
|
"js/components/pacman.js",
|
||||||
|
|
||||||
|
@ -134,6 +134,10 @@ function src_inc_list_js(string $assets_url, array $files) : string {
|
|||||||
|
|
||||||
//Process the list of files
|
//Process the list of files
|
||||||
foreach($files as $file){
|
foreach($files as $file){
|
||||||
|
|
||||||
|
if(is_array($file))
|
||||||
|
$file = $file["path"];
|
||||||
|
|
||||||
$source .= src_inc_js($assets_url.$file)."\n\t\t";
|
$source .= src_inc_js($assets_url.$file)."\n\t\t";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user