Your browser is not able to transcode speech to text. Thus, your peers will not recieve a subtitle. Google Chrome is currently the only browser that support speech recognition.
+ );
+ }
+}
+
+presentationBroadcast.contextTypes = {
+ executeAction: React.PropTypes.func.isRequired
+};
+
+export default presentationBroadcast;
diff --git a/configs/routes.js b/configs/routes.js
index ee1fd4117..b22d15cb7 100644
--- a/configs/routes.js
+++ b/configs/routes.js
@@ -469,6 +469,12 @@ export default {
context.executeAction(loadDeckFamily, payload, done);
}
},
+ webrtc: {
+ path: '/presentationbroadcast/:room',
+ method: 'get',
+ page: 'presentationBroadcast',
+ handler: require('../components/webrtc/presentationBroadcast')
+ },
/* This should be the last route in routes.js */
notfound: {
path: '*',
diff --git a/package.json b/package.json
index 757d62501..f1b328917 100644
--- a/package.json
+++ b/package.json
@@ -122,6 +122,7 @@
"semantic-ui-react": "^0.68.5",
"serialize-javascript": "^1.3.0",
"serve-favicon": "^2.4.1",
+ "socket.io": "^2.0.3",
"striptags": "^2.1.1",
"style-loader": "^0.18.2",
"superagent": "^3.5.0",
From 75ca572ea5b2e6c7a7279431dace13c9da8e457f Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Fri, 4 Aug 2017 11:25:01 +0200
Subject: [PATCH 002/111] Improved the code to better match the react lifecycle
---
components/webrtc/presentationBroadcast.js | 259 +++++++--------------
1 file changed, 86 insertions(+), 173 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 3cf7dd0df..3b742655d 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -4,12 +4,12 @@ class presentationBroadcast extends React.Component {
constructor(props) {
super(props);
+ this.texts = {roleText: '', peerCountText: '', peerCount: ''};
this.isInitiator = false;
this.localStream = undefined;
this.myID = undefined;
this.presenterID = undefined;
- this.pcs = {}; // {: {RTCConnection: RPC, dataChannel: dataChannel}, : {RTCConnection: RPC, dataChannel: dataChannel}}
- this.remoteStreams = [];
+ this.pcs = {}; // {: {RTCConnection: RPC, dataChannel: dataChannel}, : {RTCConnection: RPC, dataChannel: dataChannel}}
this.turnReady = undefined;
this.pcConfig = {
@@ -26,17 +26,24 @@ class presentationBroadcast extends React.Component {
this.room = 'foo';//TODO get it from the URL
this.socket = undefined;
+
+ ////////////////////////////////////////////////////// SlideWiki specific stuff
+ this.iframesrc = '/Presentation/3-1/#/slide-36-2-0';//TODO get it from the URL
+ this.lastRemoteSlide = this.iframesrc;
+ this.paused = false; //user has manually paused slide transitions
+ this.currentSlide = this.iframesrc;
}
componentDidMount() {
+ //Remove menus as they shouldn't appear
$('.menu:first').remove();
$('.footer:first').remove();
- let that = this;
+ let that = this;
that.socket = io('http://localhost:8080');
if (that.room !== '') {
that.socket.emit('create or join', that.room);
- console.log('Attempted to create or join that.room', that.room);
+ console.log('Attempted to create or join room', that.room);
}
function setmyID() {
@@ -48,15 +55,12 @@ class presentationBroadcast extends React.Component {
that.socket.on('created', (room, socketID) => { //only initiator recieves this
console.log('Created room ' + that.room);
that.isInitiator = true;
- $('#roleText')
- .text('You are the presenter, other poeple will hear your voice and reflect your presentation progress.');
- $('#peerCounterText')
- .text('Peers currently listening: ');
- $('#peerCounter')
- .text('0');
+ that.texts.roleText = 'You are the presenter, other poeple will hear your voice and reflect your presentation progress. ';
+ that.texts.peerCountText = 'Peers currently listening: ';
+ that.texts.peerCount = 0;
+ that.forceUpdate();
setmyID();
- $('#slidewikiPresentation')
- .on('load', activateIframeListeners);
+ $('#slidewikiPresentation').on('load', activateIframeListeners);
requestStreams({
audio: true,
// video: {
@@ -66,17 +70,16 @@ class presentationBroadcast extends React.Component {
// }
});
swal({
- title: '
that.room ' + that.room + ' successfully created!
',
+ title: '
Room ' + that.room + ' successfully created!
',
html: '
Other people are free to join it. At the bottom of the page is a peer counter. The current limit is 10 people.
',
type: 'info',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Check'
- })
- .then(() => { activateSpeechRecognition(); });
+ }).then(() => { activateSpeechRecognition(); $('body>a#atlwdg-trigger').remove();});
});
- that.socket.on('join', (room, socketID) => { //whole that.room recieves this, except for the peer that tries to join
- // a listener will join the that.room
+ that.socket.on('join', (room, socketID) => { //whole room recieves this, except for the peer that tries to join
+ // a listener will join the room
console.log('Another peer made a request to join room ' + that.room);
if (that.isInitiator) {
console.log('This peer is the initiator of room ' + that.room + '!');
@@ -85,13 +88,12 @@ class presentationBroadcast extends React.Component {
});
that.socket.on('joined', (room) => { //only recieved by peer that tries to join
- // a listener has joined the that.room
+ // a listener has joined the room
console.log('joined: ' + that.room);
setmyID();
- $('#roleText')
- .text('You are now listening to the presenter. The presentation you see will reflect his progress.');
- $('#slidewikiPresentation')
- .on('load', activateIframeListeners);
+ that.texts.roleText = 'You are now listening to the presenter. The presentation you see will reflect his progress.';
+ that.forceUpdate();
+ $('#slidewikiPresentation').on('load', activateIframeListeners);
requestStreams({
audio: false,
video: false
@@ -99,11 +101,11 @@ class presentationBroadcast extends React.Component {
});
that.socket.on('full', (room) => { //only recieved by peer that tries to join
- console.log('that.room ' + that.room + ' is full');
+ console.log('Room ' + that.room + ' is full');
that.socket.close();
swal({
- title: 'that.room full',
- html: 'This that.room is already full - sorry!',
+ title: 'Room full',
+ html: 'This room is already full - sorry!',
type: 'warning',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Okay'
@@ -131,7 +133,7 @@ class presentationBroadcast extends React.Component {
if (receiver) { //send to one peer only
that.pcs[receiver].dataChannel.send(message);
} else { //broadcast from initiator
- for (var i in that.pcs) {
+ for (let i in that.pcs) {
if (that.pcs[i].dataChannel) {
console.log('Sending Message to peer: ', i);
that.pcs[i].dataChannel.send(message);
@@ -143,8 +145,7 @@ class presentationBroadcast extends React.Component {
// This client receives a message
that.socket.on('message', (message) => {
if (message.sender === that.myID) { //Filter for messages from myself
- if (message.cmd === 'peer wants to connect' && Object.keys(that.pcs)
- .length === 0) { //peer triggers itself
+ if (message.cmd === 'peer wants to connect' && Object.keys(that.pcs).length === 0) { //peer triggers itself
start(that.presenterID);
}
} else if (message.receiver === that.myID) { //adressed to me
@@ -158,12 +159,11 @@ class presentationBroadcast extends React.Component {
}
if (message.cmd === 'candidate') {
try { //Catch defective candidates
- var candidate = new RTCIceCandidate({
+ let candidate = new RTCIceCandidate({
sdpMLineIndex: message.data.label,
candidate: message.data.candidate
});
- that.pcs[message.sender].RTCconnection.addIceCandidate(candidate)
- .catch((e) => {}); //Catch defective candidates
+ that.pcs[message.sender].RTCconnection.addIceCandidate(candidate).catch((e) => {}); //Catch defective candidates
} catch (e) {}
}
}
@@ -186,8 +186,7 @@ class presentationBroadcast extends React.Component {
//$('#videos').append('');
//let localVideo = document.querySelector('#localVideo');
//localVideo.srcObject = stream;
- $('#videos')
- .remove();
+ $('#videos').remove();
}
that.localStream = stream;
@@ -241,10 +240,10 @@ class presentationBroadcast extends React.Component {
that.pcs[peerID].RTCconnection.ondatachannel = handleDataChannelEvent.bind(that, peerID);
}
console.log('Created RTCPeerConnnection');
- if (that.isInitiator)
- $('#peerCounter')
- .text(Object.keys(that.pcs)
- .length);
+ if (that.isInitiator){
+ that.texts.peerCount = Object.keys(that.pcs).length;
+ that.forceUpdate();
+ }
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
console.log('Cannot create RTCPeerConnection object.');
@@ -268,8 +267,10 @@ class presentationBroadcast extends React.Component {
confirmButtonColor: '#3085d6',
confirmButtonText: 'Check'
});
- $('#roleText')
- .text('This presentation has ended. Feel free to look at the deck as long as you want.');
+ that.texts.roleText = 'This presentation has ended. Feel free to look at the deck as long as you want.';
+ that.texts.peerCountText = '';
+ that.texts.peerCount = '';
+ that.forceUpdate();
handleRemoteHangup(that.presenterID);
}
}
@@ -286,7 +287,7 @@ class presentationBroadcast extends React.Component {
channel.onopen = function() {
console.log('Data Channel opened');
if (that.isInitiator)
- sendRTCMessage('gotoslide', currentSlide, peerID);
+ sendRTCMessage('gotoslide', that.currentSlide, peerID);
};
channel.onmessage = handleMessage.bind(that, channel);
@@ -325,11 +326,9 @@ class presentationBroadcast extends React.Component {
function handleRemoteStreamAdded(event) {
if (that.isInitiator === false) {
- $('#videos')
- .append('');
+ $('#videos').append('');
let remoteVideos = $('.remoteVideos');
remoteVideos[remoteVideos.length - 1].srcObject = event.stream;
- that.remoteStreams[remoteVideos.length - 1] = event.stream;
}
}
@@ -361,8 +360,8 @@ class presentationBroadcast extends React.Component {
}
function requestTurn(turnURL) {
- var turnExists = false;
- for (var i in that.pcConfig.iceServers) {
+ let turnExists = false;
+ for (let i in that.pcConfig.iceServers) {
if (that.pcConfig.iceServers[i].url.substr(0, 5) === 'turn:') {
turnExists = true;
that.turnReady = true;
@@ -372,10 +371,10 @@ class presentationBroadcast extends React.Component {
if (!turnExists) {
console.log('Getting TURN server from ', turnURL);
// No TURN server. Get one from computeengineondemand.appspot.com:
- var xhr = new XMLHttpRequest();
+ let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
- var turnServer = JSON.parse(xhr.responseText);
+ let turnServer = JSON.parse(xhr.responseText);
console.log('Got TURN server: ', turnServer);
that.pcConfig.iceServers.push({
'url': 'turn:' + turnServer.username + '@' + turnServer.turn,
@@ -401,7 +400,7 @@ class presentationBroadcast extends React.Component {
sendRTCMessage('bye', that.myID, that.presenterID);
stop(that.presenterID);
}
- //NOTE Don't need to close the that.socket, as the browser does this automatically if the window closes
+ //NOTE Don't need to close the socket, as the browser does this automatically if the window closes
}
function handleRemoteHangup(peerID) { //called by initiator
@@ -411,7 +410,7 @@ class presentationBroadcast extends React.Component {
function stop(peerID, presenter = false) {
if (presenter) {
- for (var i in that.pcs) {
+ for (let i in that.pcs) {
that.pcs[i].dataChannel.close();
that.pcs[i].RTCconnection.close();
delete that.pcs[i];
@@ -421,20 +420,20 @@ class presentationBroadcast extends React.Component {
that.pcs[peerID].RTCconnection.close();
delete that.pcs[peerID];
}
- if (that.isInitiator)
- $('#peerCounter')
- .text(Object.keys(that.pcs)
- .length);
+ if (that.isInitiator){
+ that.texts.peerCount = Object.keys(that.pcs).length;
+ that.forceUpdate();
+ }
}
/////////////////////////////////////////// Codec specific stuff
// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
- var sdpLines = sdp.split('\r\n');
- var mLineIndex;
+ let sdpLines = sdp.split('\r\n');
+ let mLineIndex;
// Search for m line.
- for (var i = 0; i < sdpLines.length; i++) {
+ for (let i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('m=audio') !== -1) {
mLineIndex = i;
break;
@@ -445,9 +444,9 @@ class presentationBroadcast extends React.Component {
}
// If Opus is available, set it as the default in m line.
- for (i = 0; i < sdpLines.length; i++) {
+ for (let i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
- var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
+ let opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
if (opusPayload) {
sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex],
opusPayload);
@@ -464,16 +463,16 @@ class presentationBroadcast extends React.Component {
}
function extractSdp(sdpLine, pattern) {
- var result = sdpLine.match(pattern);
+ let result = sdpLine.match(pattern);
return result && result.length === 2 ? result[1] : null;
}
// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
- var elements = mLine.split(' ');
- var newLine = [];
- var index = 0;
- for (var i = 0; i < elements.length; i++) {
+ let elements = mLine.split(' ');
+ let newLine = [];
+ let index = 0;
+ for (let i = 0; i < elements.length; i++) {
if (index === 3) { // Format of media starts from the fourth.
newLine[index++] = payload; // Put target payload to the first.
}
@@ -486,12 +485,12 @@ class presentationBroadcast extends React.Component {
// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
- var mLineElements = sdpLines[mLineIndex].split(' ');
+ let mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
- for (var i = sdpLines.length - 1; i >= 0; i--) {
- var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
+ for (let i = sdpLines.length - 1; i >= 0; i--) {
+ let payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
if (payload) {
- var cnPos = mLineElements.indexOf(payload);
+ let cnPos = mLineElements.indexOf(payload);
if (cnPos !== -1) {
// Remove CN payload from m line.
mLineElements.splice(cnPos, 1);
@@ -508,28 +507,15 @@ class presentationBroadcast extends React.Component {
/////////////////////////////////////////// SlideWiki specific stuff
- let lastRemoteSlide = document.getElementById('slidewikiPresentation')
- .src;
- let paused = false; //user has manually paused slide transitions
- let currentSlide = document.getElementById('slidewikiPresentation')
- .src;
-
- $('#resumeRemoteControl')
- .click(() => {
- resumeRemoteControl();
- });
-
- function resumeRemoteControl() {
- paused = false;
- $('#resumeRemoteControl')
- .hide();
- changeSlide(lastRemoteSlide);
- }
+ $('#resumeRemoteControl').click(() => {
+ that.paused = false;
+ that.forceUpdate();
+ changeSlide(that.lastRemoteSlide);
+ });
function activateIframeListeners() {
console.log('Adding iframe listeners');
- let iframe = $('#slidewikiPresentation')
- .contents();
+ let iframe = $('#slidewikiPresentation').contents();
/* Currently doesn't work - Stackoverflow Question:
* https://stackoverflow.com/questions/45457271/forward-a-keydown-event-from-the-parent-window-to-an-iframe
*/
@@ -544,98 +530,29 @@ class presentationBroadcast extends React.Component {
// });
if (that.isInitiator) {
iframe.on('slidechanged', () => {
- currentSlide = document.getElementById('slidewikiPresentation')
- .contentWindow.location.href;
- sendRTCMessage('gotoslide', currentSlide);
+ that.currentSlide = document.getElementById('slidewikiPresentation').contentWindow.location.href;
+ sendRTCMessage('gotoslide', that.currentSlide);
});
} else {
iframe.on('slidechanged', () => {
- if (document.getElementById('slidewikiPresentation')
- .contentWindow.location.href !== lastRemoteSlide) {
- paused = true;
- $('#resumeRemoteControl')
- .show();
+ if (document.getElementById('slidewikiPresentation').contentWindow.location.href !== that.lastRemoteSlide) {
+ that.paused = true;
+ that.forceUpdate();
}
});
}
}
function changeSlide(slideID) { // called by peers
- lastRemoteSlide = slideID;
- if (!paused) {
+ that.lastRemoteSlide = slideID;
+ if (!that.paused) {
console.log('Changing to slide: ', slideID);
- $('#slidewikiPresentation')
- .attr('src', slideID);
+ that.iframesrc = slideID;
+ that.forceUpdate();
}
}
- function activateSpeechRecognition() {
- var recognition;
-
- // if (window.hasOwnProperty('webkitSpeechRecognition')) {
- // recognition = new webkitSpeechRecognition();
- // } else if (window.hasOwnProperty('SpeechRecognition')) {
- // recognition = new SpeechRecognition();
- // }
- //
- // if (recognition) {
- // $('body')
- // .append('
Alpha Feature: Speech Recognition is enabled. Peers will recieve a transcoded version of your voice as a subtitle
Your browser is not able to transcode speech to text. Thus, your peers will not recieve a subtitle. Google Chrome is currently the only browser that support speech recognition.
Your browser isn\'t able to transcode speech to text. Thus, your peers will not recieve a subtitle. Google Chrome is currently the only browser that support speech recognition.
',
+ type: 'error',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Okay',
+ });
}
}
@@ -654,7 +675,6 @@ class presentationBroadcast extends React.Component {
that.commentList[currentTime].peer = 'Me';
that.commentList[currentTime].message = data.data;
that.forceUpdate();
- //sendRTCMessage('message', 'Blubb', that.presenterID);
}
that.addMessage = addMessage;
From 7bd044872f5c38360b8a9ad5e6c975ba3e4eecf9 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Mon, 7 Aug 2017 15:28:49 +0200
Subject: [PATCH 010/111] Added event propagation to the iframe
---
components/webrtc/presentationBroadcast.js | 32 ++++++++++------------
1 file changed, 14 insertions(+), 18 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index b2435214d..64ac91ddb 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -26,14 +26,14 @@ class presentationBroadcast extends React.Component {
offerToReceiveVideo: true
};
- this.room = this.props.currentRoute.query.room;//TODO Navigate away if not provided
+ this.room = this.props.currentRoute.query.room + '';//TODO Navigate away if not provided
this.socket = undefined;
////////////////////////////////////////////////////// SlideWiki specific stuff
- this.iframesrc = this.props.currentRoute.query.presentation;//TODO Navigate away if not provided
- this.lastRemoteSlide = this.iframesrc;
+ this.iframesrc = this.props.currentRoute.query.presentation + '';//TODO Navigate away if not provided
+ this.lastRemoteSlide = this.iframesrc + '';
this.paused = false; //user has manually paused slide transitions
- this.currentSlide = this.iframesrc;
+ this.currentSlide = this.iframesrc + '';
this.commentList = {};//{timestamp: {peer: username, message: text},timestamp: {peer: username, message: text}}
this.subtitle = '';
@@ -529,27 +529,23 @@ class presentationBroadcast extends React.Component {
/////////////////////////////////////////// SlideWiki specific stuff
- $('#resumeRemoteControl').click(() => {
+ $('#resumeRemoteControl').click(() => {//TODO fkt. nicht richtig
that.paused = false;
- that.forceUpdate();
changeSlide(that.lastRemoteSlide);
});
function activateIframeListeners() {
console.log('Adding iframe listeners');
let iframe = $('#slidewikiPresentation').contents();
- /* Currently doesn't work - Stackoverflow Question:
- * https://stackoverflow.com/questions/45457271/forward-a-keydown-event-from-the-parent-window-to-an-iframe
- */
- // $(document).keydown((event) => {
- // console.log(event, event.keyCode);
- // var newEvent = new KeyboardEvent("keydown", {key: event.originalEvent.key, code: event.originalEvent.code, charCode: event.originalEvent.charCode, keyCode: event.originalEvent.keyCode, which: event.originalEvent.which});
- // //frames['slidewikiPresentation'].document.dispatchEvent(newEvent);
- // document.getElementById("slidewikiPresentation").contentWindow.document.dispatchEvent(newEvent);
- // //elem.dispatchEvent(event);
- // //var e = jQuery.Event( "keydown", { keyCode: event.keyCode } );
- // //$('#slidewikiPresentation')[0].contentWindow.$('body').trigger(e);
- // });
+
+ document.addEventListener('keydown', (e) => {
+ let frame = document.getElementById('slidewikiPresentation').contentDocument;
+ let newEvent = new Event('keydown', {key: e.key, code: e.code, composed: true, charCode: e.charCode, keyCode: e.keyCode, which: e.which, bubbles: true, cancelable: true, which: e.keyCode});
+ newEvent.keyCode = e.keyCode;
+ newEvent.which = e.keyCode;
+ frame.dispatchEvent(newEvent);
+ });
+
if (that.isInitiator) {
iframe.on('slidechanged', () => {
that.currentSlide = document.getElementById('slidewikiPresentation').contentWindow.location.href;
From 33e136fb6609c7d9e32198185be99e87203955ec Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Mon, 7 Aug 2017 16:29:48 +0200
Subject: [PATCH 011/111] Added button to launch the view + todo + improved
swal messages
---
.../ContentActions/ContentActionsFooter.js | 19 +++++++++
components/webrtc/presentationBroadcast.js | 41 +++++++++++++++----
2 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/components/Deck/ContentPanel/ContentActions/ContentActionsFooter.js b/components/Deck/ContentPanel/ContentActions/ContentActionsFooter.js
index c268547fa..dc99e3380 100644
--- a/components/Deck/ContentPanel/ContentActions/ContentActionsFooter.js
+++ b/components/Deck/ContentPanel/ContentActions/ContentActionsFooter.js
@@ -48,6 +48,20 @@ class ContentActionsFooter extends React.Component {
window.open(this.getPresentationHref());
}
}
+ handlePresentationRoomClick(e){
+ if(process.env.BROWSER){
+ e.preventDefault();
+ swal({
+ title: 'Please enter a room name',
+ input: 'text',
+ showCancelButton: true,
+ confirmButtonText: 'Next',
+ allowOutsideClick: false
+ }).then((roomName) => {
+ window.open('/presentationbroadcast?room=' + roomName + '&presentation=' + this.getPresentationHref());
+ }).catch();
+ }
+ }
/*
getPrintHref(){
return '/PresentationPrint/' + this.props.ContentStore.selector.id + '/?print-pdf';
@@ -158,6 +172,11 @@ class ContentActionsFooter extends React.Component {
+
+
+
-
-
-
-
-
+
+
+
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 6391ad5d0..2c2fefb01 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -8,6 +8,7 @@ class presentationBroadcast extends React.Component {
* TODO add share button that copies the URL into the Zwischenablage (down right corner)
* TODO Use Username instead of "Peer X" if available
* TODO Add some explaining texts for peers with swal or ToolTips(like that it's not a chat, ...)
+ * TODO this.props.currentRoute.query.presentation is not filled correctly if a "#" is in the path
*/
constructor(props) {
From de5f3f83102a52802e75ad98ecd58fb7ca204da0 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 10:22:46 +0200
Subject: [PATCH 013/111] Improved code and added some comments
---
components/webrtc/presentationBroadcast.js | 38 +++++++++-------------
1 file changed, 15 insertions(+), 23 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 2c2fefb01..a43ca40ac 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -5,7 +5,7 @@ import { Grid, Message, Comment, Input, Button, Form, Divider } from 'semantic-u
class presentationBroadcast extends React.Component {
/*
- * TODO add share button that copies the URL into the Zwischenablage (down right corner)
+ * TODO add share button that copies the URL into the clipboard (down right corner)
* TODO Use Username instead of "Peer X" if available
* TODO Add some explaining texts for peers with swal or ToolTips(like that it's not a chat, ...)
* TODO this.props.currentRoute.query.presentation is not filled correctly if a "#" is in the path
@@ -23,27 +23,20 @@ class presentationBroadcast extends React.Component {
this.pcConfig = {
'iceServers': [{
- 'urls': 'stun:stun.l.google.com:19302' //TODO host own STUN (and TURN?) Server?
+ 'urls': 'stun:stun.l.google.com:19302' //TODO host own STUN (and TURN?) Server? --> Socket.io is a stun server...What's this needed for?
}]
};
- // Set up audio and video regardless of what devices are present.
- this.sdpConstraints = {
- offerToReceiveAudio: true,
- offerToReceiveVideo: true
- };
-
this.room = this.props.currentRoute.query.room + '';//TODO Navigate away if not provided
this.socket = undefined;
- ////////////////////////////////////////////////////// SlideWiki specific stuff
+ //******** SlideWiki specific variables ********
this.iframesrc = this.props.currentRoute.query.presentation + '';//TODO Navigate away if not provided
this.lastRemoteSlide = this.iframesrc + '';
this.paused = false; //user has manually paused slide transitions
this.currentSlide = this.iframesrc + '';
this.commentList = {};//{timestamp: {peer: username, message: text},timestamp: {peer: username, message: text}}
-
- this.subtitle = '';
+ this.subtitle = '';//used for speech recognition results
}
componentDidMount() {
@@ -52,7 +45,7 @@ class presentationBroadcast extends React.Component {
$('.footer:first').remove();
let that = this;
- that.socket = io('https://stunservice.experimental.slidewiki.org');
+ that.socket = io('https://stunservice.experimental.slidewiki.org');//TODO remove hardcoded URL
if (that.room !== '') {
that.socket.emit('create or join', that.room);
@@ -68,8 +61,8 @@ class presentationBroadcast extends React.Component {
that.socket.on('created', (room, socketID) => { //only initiator recieves this
console.log('Created room ' + that.room);
that.isInitiator = true;
- that.texts.roleText = 'You are the presenter, other poeple will hear your voice and reflect your presentation progress. ';
- that.texts.peerCountText = 'Peers currently listening: ';
+ that.texts.roleText = 'You are the presenter. Other poeple will hear your voice and reflect your presentation progress. ';
+ that.texts.peerCountText = 'People currently listening: ';
that.texts.peerCount = 0;
that.forceUpdate();
setmyID();
@@ -84,7 +77,7 @@ class presentationBroadcast extends React.Component {
});
swal({
title: '
Room ' + that.room + ' successfully created!
',
- html: '
Other people are free to join it. At the bottom of the page is a peer counter. The current limit is 10 people.
',
+ html: '
Other people are free to join the room. Rooms are currently limited to 10 people. See the counter at the bottom of the page for information about currently listening people.
',
type: 'info',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Check',
@@ -94,21 +87,20 @@ class presentationBroadcast extends React.Component {
that.socket.on('join', (room, socketID) => { //whole room recieves this, except for the peer that tries to join
// a listener will join the room
- console.log('Another peer made a request to join room ' + that.room);
+ console.log('Another peer made a request to join room ' + room);
if (that.isInitiator) {
console.log('This peer is the initiator of room ' + that.room + '!');
that.socket.emit('ID of presenter', that.room, that.myID);
}
});
- that.socket.on('joined', (room) => { //only recieved by peer that tries to join
- // a listener has joined the room
+ that.socket.on('joined', (room) => { //only recieved by peer that tries to join - a peer has joined the room
console.log('joined: ' + that.room);
setmyID();
- that.texts.roleText = 'You are now listening to the presenter. The presentation you see will reflect his progress.';
+ that.texts.roleText = 'You are now listening to the presenter and your presentation will reflect his actions.';
that.forceUpdate();
$('#slidewikiPresentation').on('load', activateIframeListeners);
- requestStreams({
+ requestStreams({//TODO Maybe skip requesting streams for the listeners
audio: false,
video: false
});
@@ -118,8 +110,8 @@ class presentationBroadcast extends React.Component {
console.log('Room ' + that.room + ' is full');
that.socket.close();
swal({
- title: 'Room full',
- html: 'This room is already full - sorry!',
+ title: 'Room ' + room + ' is full',
+ html: 'Rooms have limited capacities for people. The room you tried to join is already full.',
type: 'warning',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Okay',
@@ -139,7 +131,7 @@ class presentationBroadcast extends React.Component {
////////////////////////////////////////////////
function sendMessage(cmd, data = undefined, receiver = undefined) {
- console.log('Sending message: ', cmd, data, receiver);
+ console.log('Sending message over socket: ', cmd, data, receiver);
that.socket.emit('message', { 'cmd': cmd, 'data': data, 'sender': that.myID, 'receiver': receiver }, that.room);
}
From 57500ebdd7fd5266ae88b9cab7cad3c308aaadbc Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 10:54:12 +0200
Subject: [PATCH 014/111] Added some more comments, moved and improved some
code
---
components/webrtc/presentationBroadcast.js | 272 ++++++++++-----------
1 file changed, 136 insertions(+), 136 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index a43ca40ac..4bdba7d3f 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -138,17 +138,16 @@ class presentationBroadcast extends React.Component {
function sendRTCMessage(cmd, data = undefined, receiver = undefined) {
let message = JSON.stringify({ 'cmd': cmd, 'data': data, sender: that.myID });
if (receiver) { //send to one peer only
+ console.log('Sending message to peer: ', receiver);
that.pcs[receiver].dataChannel.send(message);
} else { //broadcast from initiator
+ console.log('Broadcasting message to peers');
for (let i in that.pcs) {
- if (that.pcs[i].dataChannel) {
- console.log('Sending Message to peer: ', i);
+ if (that.pcs[i].dataChannel)
that.pcs[i].dataChannel.send(message);
- }
}
}
}
-
that.sendRTCMessage = sendRTCMessage;
// This client receives a message
@@ -172,25 +171,25 @@ class presentationBroadcast extends React.Component {
sdpMLineIndex: message.data.label,
candidate: message.data.candidate
});
- that.pcs[message.sender].RTCconnection.addIceCandidate(candidate).catch((e) => {}); //Catch defective candidates
- } catch (e) {}
+ that.pcs[message.sender].RTCconnection.addIceCandidate(candidate).catch((e) => {}); //Catch defective candidates, TODO add better exception handling
+ } catch (e) {}//TODO add better exception handling
}
}
});
- ////////////////////////////////////////////////////
+ //******** Media specific methods ********
function requestStreams(options) {
navigator.mediaDevices.getUserMedia(options)
.then(gotStream)
- .catch((e) => {
- gotStream('');
- console.log('getUserMedia() error: ' + e.name);
+ .catch((err) => {
+ gotStream('');//TODO This has been implemented for listener peers. Maybe skip requestStreams for listeners completely in order to have a better error handling. See a comment above.
+ console.log('getUserMedia() error: ' + err.name);
});
}
function gotStream(stream) {
- console.log('Adding local stream.');
+ console.log('Adding local stream');
if (that.isInitiator) {
//$('#videos').append('');
//let localVideo = document.querySelector('#localVideo');
@@ -200,11 +199,12 @@ class presentationBroadcast extends React.Component {
that.localStream = stream;
function sendASAP() {
- if (that.presenterID)
+ if (that.presenterID) //wait for presenterID before sending the message
sendMessage('peer wants to connect', undefined, that.presenterID);
else
setTimeout(() => { sendASAP(); }, 10);
}
+
if (!that.isInitiator) {
sendASAP();
}
@@ -220,10 +220,10 @@ class presentationBroadcast extends React.Component {
if (typeof that.localStream !== 'undefined') {
console.log('creating RTCPeerConnnection for', (that.isInitiator) ? 'initiator' : 'peer');
createPeerConnection(peerID);
- if (that.isInitiator)
+ if (that.isInitiator){
that.pcs[peerID].RTCconnection.addStream(that.localStream);
- if (that.isInitiator)
doCall(peerID);
+ }
}
}
@@ -231,7 +231,7 @@ class presentationBroadcast extends React.Component {
hangup();
};
- /////////////////////////////////////////////////////////
+ //******** WebRTC specific methods ********
function createPeerConnection(peerID) {
try {
@@ -241,19 +241,20 @@ class presentationBroadcast extends React.Component {
that.pcs[peerID].RTCconnection.onaddstream = handleRemoteStreamAdded;
that.pcs[peerID].RTCconnection.onremovestream = handleRemoteStreamRemoved;
if (that.isInitiator) {
- that.pcs[peerID].dataChannel = that.pcs[peerID].RTCconnection.createDataChannel('messages', {
- ordered: true
- });
+ that.pcs[peerID].dataChannel = that.pcs[peerID].RTCconnection
+ .createDataChannel('messages', {
+ ordered: true
+ });
onDataChannelCreated(that.pcs[peerID].dataChannel, peerID);
- } else {
+ } else
that.pcs[peerID].RTCconnection.ondatachannel = handleDataChannelEvent.bind(that, peerID);
- }
+
console.log('Created RTCPeerConnnection');
if (that.isInitiator){
that.texts.peerCount = Object.keys(that.pcs).length;
that.forceUpdate();
}
- } catch (e) {
+ } catch (e) {//TODO handle this better - e.g. show a message and close the window
console.log('Failed to create PeerConnection, exception: ' + e.message);
console.log('Cannot create RTCPeerConnection object.');
return;
@@ -270,7 +271,7 @@ class presentationBroadcast extends React.Component {
function handleRPCClose() {
if (!that.isInitiator) {
swal({
- title: '
The presenter closed the session!
',
+ title: '
The presenter closed the session
',
html: '
This presentation has ended. Feel free to look at the deck as long as you want.
',
type: 'warning',
confirmButtonColor: '#3085d6',
@@ -291,7 +292,7 @@ class presentationBroadcast extends React.Component {
* Browsers do currenty not support events that indicate whether ICE exchange has finished or not and the RPC connection has been fully established. Thus, I'm waiting for latest event onDataChannelCreated in order to close the that.socket after some time. This should be relativly safe.
*/
if (!that.isInitiator && that.socket.disconnected === false) {
- setTimeout(() => { that.socket.close(); }, 5000); //close that.socket after 5 secs
+ setTimeout(() => { that.socket.close(); }, 5000); //close that.socket after 5 secs, TODO maybe come up with a better solution
}
channel.onopen = function() {
@@ -303,38 +304,6 @@ class presentationBroadcast extends React.Component {
channel.onmessage = handleMessage.bind(that, channel);
}
- function handleMessage(channel, event) {
- let data = JSON.parse(event.data);
- switch (data.cmd) {
- case 'gotoslide':
- if (!that.isInitiator)
- changeSlide(data.data);
- break;
- case 'message':
- if (that.isInitiator)
- addMessage(data);
- break;
- case 'log':
- console.log('Recieved message from peer: ', data.data);
- break;
- case 'bye':
- handleRemoteHangup(data.data);
- break;
- case 'subtitle':
- handleSubtitle(data.data);
- break;
- default:
-
- }
- }
-
- function handleSubtitle(subtitle) {
- $('#input_subtitle').val(subtitle);
- $('#input_subtitle').animate({
- scrollLeft: $('#input_subtitle')[0].scrollLeft+1000
- }, 1000);
- }
-
function handleIceCandidate(peerID, event) {
if (event && ((event.target && event.target.iceGatheringState !== 'complete') || event.candidate !== null)) {
sendMessage('candidate', {
@@ -350,14 +319,14 @@ class presentationBroadcast extends React.Component {
function handleRemoteStreamAdded(event) {
if (that.isInitiator === false) {
- $('#videos').append('');
+ $('#videos').append('');//TODO Maybe exchange for audio only?!
let remoteVideos = $('.remoteVideos');
remoteVideos[remoteVideos.length - 1].srcObject = event.stream;
}
}
function handleCreateOfferError(event) {
- console.log('createOffer() error: ', event);
+ console.log('createOffer() error: ', event);//TODO add better error handling for this - maybe close the window if this is fatal
}
function doCall(peerID) { //calledy by initiatior
@@ -379,11 +348,79 @@ class presentationBroadcast extends React.Component {
sendMessage(sessionDescription.type, sessionDescription, peerID);
}
- function onCreateSessionDescriptionError(error) {
+ function onCreateSessionDescriptionError(error) {//TODO add better error handling for this - maybe close the window if this is fatal
trace('Failed to create session description: ' + error.toString());
}
- function requestTurn(turnURL) {
+ function handleRemoteStreamRemoved(event) {
+ console.log('Remote stream removed. Event: ', event);
+ }
+
+ function hangup() { //calledy by peer and by initiatior
+ console.log('Hanging up.');
+ if (that.isInitiator) {
+ stop(undefined, true);
+ } else {
+ sendRTCMessage('bye', that.myID, that.presenterID);
+ stop(that.presenterID);
+ }
+ //NOTE Don't need to close the socket, as the browser does this automatically if the window closes
+ }
+
+ function handleRemoteHangup(peerID) { //called by initiator
+ console.log('Terminating session for ', peerID);
+ stop(peerID);
+ }
+
+ function stop(peerID, presenter = false) {
+ try {
+ if (presenter) {
+ for (let i in that.pcs) {
+ that.pcs[i].dataChannel.close();
+ that.pcs[i].RTCconnection.close();
+ delete that.pcs[i];
+ }
+ } else {
+ that.pcs[peerID].dataChannel.close();
+ that.pcs[peerID].RTCconnection.close();
+ delete that.pcs[peerID];
+ }
+ } catch (e) {//TODO
+ console.log('Error when deleteing RTC connections', e);
+ } finally {
+ if (that.isInitiator){
+ that.texts.peerCount = Object.keys(that.pcs).length;
+ that.forceUpdate();
+ }
+ }
+ }
+
+ function handleMessage(channel, event) {
+ let data = JSON.parse(event.data);
+ switch (data.cmd) {
+ case 'gotoslide':
+ if (!that.isInitiator)
+ changeSlide(data.data);
+ break;
+ case 'message':
+ if (that.isInitiator)
+ addMessage(data);
+ break;
+ case 'log':
+ console.log('Recieved log message from peer: ', data.data);
+ break;
+ case 'bye':
+ handleRemoteHangup(data.data);
+ break;
+ case 'subtitle':
+ handleSubtitle(data.data);
+ break;
+ default:
+
+ }
+ }
+
+ function requestTurn(turnURL) {//NOTE currently not used
let turnExists = false;
for (let i in that.pcConfig.iceServers) {
if (that.pcConfig.iceServers[i].url.substr(0, 5) === 'turn:') {
@@ -412,48 +449,9 @@ class presentationBroadcast extends React.Component {
}
}
- function handleRemoteStreamRemoved(event) {
- console.log('Remote stream removed. Event: ', event);
- }
-
- function hangup() { //calledy by peer and by initiatior
- console.log('Hanging up.');
- if (that.isInitiator) {
- stop(undefined, true);
- } else {
- sendRTCMessage('bye', that.myID, that.presenterID);
- stop(that.presenterID);
- }
- //NOTE Don't need to close the socket, as the browser does this automatically if the window closes
- }
-
- function handleRemoteHangup(peerID) { //called by initiator
- console.log('Terminating session for ', peerID);
- stop(peerID);
- }
+ //******** Media Codec specific methods (like Opus) ********
- function stop(peerID, presenter = false) {
- if (presenter) {
- for (let i in that.pcs) {
- that.pcs[i].dataChannel.close();
- that.pcs[i].RTCconnection.close();
- delete that.pcs[i];
- }
- } else {
- that.pcs[peerID].dataChannel.close();
- that.pcs[peerID].RTCconnection.close();
- delete that.pcs[peerID];
- }
- if (that.isInitiator){
- that.texts.peerCount = Object.keys(that.pcs).length;
- that.forceUpdate();
- }
- }
-
- /////////////////////////////////////////// Codec specific stuff
-
- // Set Opus as the default audio codec if it's present.
- function preferOpus(sdp) {
+ function preferOpus(sdp) { // Set Opus as the default audio codec if it's present.
let sdpLines = sdp.split('\r\n');
let mLineIndex;
// Search for m line.
@@ -491,8 +489,7 @@ class presentationBroadcast extends React.Component {
return result && result.length === 2 ? result[1] : null;
}
- // Set the selected codec to the first in m line.
- function setDefaultCodec(mLine, payload) {
+ function setDefaultCodec(mLine, payload) { // Set the selected codec to the first in m line.
let elements = mLine.split(' ');
let newLine = [];
let index = 0;
@@ -507,8 +504,7 @@ class presentationBroadcast extends React.Component {
return newLine.join(' ');
}
- // Strip CN from sdp before CN constraints is ready.
- function removeCN(sdpLines, mLineIndex) {
+ function removeCN(sdpLines, mLineIndex) { // Strip CN from sdp before CN constraints is ready.
let mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
for (let i = sdpLines.length - 1; i >= 0; i--) {
@@ -528,8 +524,7 @@ class presentationBroadcast extends React.Component {
return sdpLines;
}
-
- /////////////////////////////////////////// SlideWiki specific stuff
+ //******** SlideWiki specific methods ********
$('#resumeRemoteControl').click(() => {//TODO does not correctly work
that.paused = false;
@@ -626,7 +621,7 @@ class presentationBroadcast extends React.Component {
recognition.onerror = function (e) {
console.warn('Recognition error:', e);
- recognition.stop();
+ recognition.stop();//TODO Really stop recognition in case of a fatal error. Pay attention to not restarting it via the onend listener
};
recognition.onend = function (e) {
@@ -654,8 +649,8 @@ class presentationBroadcast extends React.Component {
}
}).then(() => {
swal({
- title: 'Invite others!',
- html: '
Copy the following link and send it to other people in order to invite them to this room:
' + window.location.href + '
',
+ title: 'Invite others people',
+ html: '
Copy the following link and send it to other people in order to invite them to this room:
Your browser isn\'t able to transcode speech to text. Thus, your peers will not recieve a subtitle. Google Chrome is currently the only browser that support speech recognition.
',
+ html: '
Your browser isn\'t able to transcode speech to text. Thus, your peers will not recieve a subtitle. Google Chrome is currently the only browser that supports speech recognition.
- {messages}
-
-
-
-
- {/*
- * TODO Add a visible char counter,e .g. 234/300 next to the send button
- * TODO Don't send empty messages or those with too few words (and show notification about it)
- * TODO move the input box to the bottom of the element (so it doesn't move)
- * TODO disable keydown listener if textarea is focused
- */}
-
-
-
- ;
- } else
- messageArea =
+ {messages}
+
+
+
+
+ {/*
+ * TODO Add a visible char counter,e .g. 234/300 next to the send button
+ * TODO Don't send empty messages or those with too few words (and show notification about it)
+ * TODO move the input box to the bottom of the element (so it doesn't move)
+ * TODO disable keydown listener if textarea is focused
+ */}
+
+
+
+
+ ) : (
+
Messages from peers:
{messages}
+ )}
From 2d714f7d71348ab4315142e861b9a850246e5cc4 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 12:22:45 +0200
Subject: [PATCH 015/111] Added error handling for URL query parameters
---
components/webrtc/presentationBroadcast.js | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 4bdba7d3f..52c17314c 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -1,5 +1,6 @@
import React from 'react';
-import { handleRoute } from 'fluxible-router';
+import { handleRoute, navigateAction} from 'fluxible-router';
+import { isEmpty } from '../../common';
import { Grid, Message, Comment, Input, Button, Form, Divider } from 'semantic-ui-react';
class presentationBroadcast extends React.Component {
@@ -27,11 +28,11 @@ class presentationBroadcast extends React.Component {
}]
};
- this.room = this.props.currentRoute.query.room + '';//TODO Navigate away if not provided
+ this.room = this.props.currentRoute.query.room + '';//NOTE Error handling implemented in first lines of componentDidMount
this.socket = undefined;
//******** SlideWiki specific variables ********
- this.iframesrc = this.props.currentRoute.query.presentation + '';//TODO Navigate away if not provided
+ this.iframesrc = this.props.currentRoute.query.presentation + '';//NOTE Error handling implemented in first lines of componentDidMount
this.lastRemoteSlide = this.iframesrc + '';
this.paused = false; //user has manually paused slide transitions
this.currentSlide = this.iframesrc + '';
@@ -40,17 +41,21 @@ class presentationBroadcast extends React.Component {
}
componentDidMount() {
+
+ let that = this;
+ if(isEmpty(that.iframesrc) || that.iframesrc === 'undefined' || isEmpty(that.room) || that.room === 'undefined'){
+ console.log('Navigating away because of missing paramenters in URL');//TODO Maybe notify users in a more friendly way
+ that.context.executeAction(navigateAction, {'url': '/'});
+ return;
+ }
//Remove menus as they shouldn't appear
$('.menu:first').remove();
$('.footer:first').remove();
- let that = this;
that.socket = io('https://stunservice.experimental.slidewiki.org');//TODO remove hardcoded URL
- if (that.room !== '') {
- that.socket.emit('create or join', that.room);
- console.log('Attempted to create or join room', that.room);
- }
+ that.socket.emit('create or join', that.room);
+ console.log('Attempted to create or join room', that.room);
function setmyID() {
if (that.myID === undefined)
From d209e939d23438a15a46168e1ff0c0887e528c64 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 12:32:07 +0200
Subject: [PATCH 016/111] Not requesting streams for listeners anymore + better
error handling
---
components/webrtc/presentationBroadcast.js | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 52c17314c..c0cc481ff 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -105,10 +105,7 @@ class presentationBroadcast extends React.Component {
that.texts.roleText = 'You are now listening to the presenter and your presentation will reflect his actions.';
that.forceUpdate();
$('#slidewikiPresentation').on('load', activateIframeListeners);
- requestStreams({//TODO Maybe skip requesting streams for the listeners
- audio: false,
- video: false
- });
+ gotStream('');//NOTE Skip requesting streams for the listeners, as they do not need them
});
that.socket.on('full', (room) => { //only recieved by peer that tries to join
@@ -188,7 +185,6 @@ class presentationBroadcast extends React.Component {
navigator.mediaDevices.getUserMedia(options)
.then(gotStream)
.catch((err) => {
- gotStream('');//TODO This has been implemented for listener peers. Maybe skip requestStreams for listeners completely in order to have a better error handling. See a comment above.
console.log('getUserMedia() error: ' + err.name);
});
}
From ea0d2fd27c2ae944d3b2141db1f1fff1814c104d Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 13:05:16 +0200
Subject: [PATCH 017/111] Added better error handling for RTC connection,
exchanged video with audio elements, Added a (not working) pause listener
---
components/webrtc/presentationBroadcast.js | 57 ++++++++++++++++++----
1 file changed, 47 insertions(+), 10 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index c0cc481ff..90007a765 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -192,10 +192,10 @@ class presentationBroadcast extends React.Component {
function gotStream(stream) {
console.log('Adding local stream');
if (that.isInitiator) {
- //$('#videos').append('');
+ //$('#media').append('');
//let localVideo = document.querySelector('#localVideo');
//localVideo.srcObject = stream;
- $('#videos').remove();
+ $('#media').remove();
}
that.localStream = stream;
@@ -255,9 +255,20 @@ class presentationBroadcast extends React.Component {
that.texts.peerCount = Object.keys(that.pcs).length;
that.forceUpdate();
}
- } catch (e) {//TODO handle this better - e.g. show a message and close the window
+ } catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
console.log('Cannot create RTCPeerConnection object.');
+ swal({
+ title: 'An error occured',
+ html: 'We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.',
+ type: 'error',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Okay',
+ allowOutsideClick: false
+ }).then(() => {
+ cleanup();
+ that.context.executeAction(navigateAction, {'url': '/'});
+ });
return;
}
}
@@ -320,9 +331,9 @@ class presentationBroadcast extends React.Component {
function handleRemoteStreamAdded(event) {
if (that.isInitiator === false) {
- $('#videos').append('');//TODO Maybe exchange for audio only?!
- let remoteVideos = $('.remoteVideos');
- remoteVideos[remoteVideos.length - 1].srcObject = event.stream;
+ $('#media').append('');
+ let remoteAudios = $('.remoteAudio');
+ remoteAudios[remoteAudios.length - 1].srcObject = event.stream;
}
}
@@ -386,7 +397,7 @@ class presentationBroadcast extends React.Component {
that.pcs[peerID].RTCconnection.close();
delete that.pcs[peerID];
}
- } catch (e) {//TODO
+ } catch (e) {//TODO add better error handling
console.log('Error when deleteing RTC connections', e);
} finally {
if (that.isInitiator){
@@ -396,6 +407,15 @@ class presentationBroadcast extends React.Component {
}
}
+ function cleanup() {
+ try {
+ that.socket.close();
+ } catch (e) {}
+ try {
+ stop(undefined, true);
+ } catch (e) {}
+ }
+
function handleMessage(channel, event) {
let data = JSON.parse(event.data);
switch (data.cmd) {
@@ -403,6 +423,10 @@ class presentationBroadcast extends React.Component {
if (!that.isInitiator)
changeSlide(data.data);
break;
+ case 'toggleblackscreen':
+ if (!that.isInitiator)
+ toggleBlackScreen();
+ break;
case 'message':
if (that.isInitiator)
addMessage(data);
@@ -532,11 +556,19 @@ class presentationBroadcast extends React.Component {
changeSlide(that.lastRemoteSlide);
});
+ function toggleBlackScreen() {//TODO won't unpause the screen - I have no idea why...
+ let frame = document.getElementById('slidewikiPresentation').contentDocument;
+ let newEvent = new Event('keydown', {keyCode: 58});
+ newEvent.keyCode = 58;
+ newEvent.which = 58;
+ // frame.dispatchEvent(newEvent);
+ }
+
function activateIframeListeners() {
console.log('Adding iframe listeners');
let iframe = $('#slidewikiPresentation').contents();
- document.addEventListener('keydown', (e) => {
+ document.addEventListener('keydown', (e) => {//NOTE used for arrow keys
let frame = document.getElementById('slidewikiPresentation').contentDocument;
let newEvent = new Event('keydown', {key: e.key, code: e.code, composed: true, charCode: e.charCode, keyCode: e.keyCode, which: e.which, bubbles: true, cancelable: true, which: e.keyCode});
newEvent.keyCode = e.keyCode;
@@ -549,6 +581,12 @@ class presentationBroadcast extends React.Component {
that.currentSlide = document.getElementById('slidewikiPresentation').contentWindow.location.href;
sendRTCMessage('gotoslide', that.currentSlide);
});
+ iframe.on('paused', () => {
+ sendRTCMessage('toggleblackscreen');
+ });
+ iframe.on('resumed', () => {
+ sendRTCMessage('toggleblackscreen');
+ });
} else {
iframe.on('slidechanged', () => {
if (document.getElementById('slidewikiPresentation').contentWindow.location.href !== that.lastRemoteSlide) {
@@ -557,7 +595,6 @@ class presentationBroadcast extends React.Component {
}
});
}
- //TODO also listen for events like the black out presenation, ....
}
function changeSlide(slideID) { // called by peers
@@ -757,7 +794,7 @@ class presentationBroadcast extends React.Component {
Speech recognition:{/*TODO Find a better heading and add a boarder to the input*/}
From 6af1fd5c906c522cd4aa31aa96d001d77a766343 Mon Sep 17 00:00:00 2001
From: Kurt Junghanns
Date: Tue, 8 Aug 2017 13:37:14 +0200
Subject: [PATCH 018/111] Fixed resume btn
---
components/webrtc/presentationBroadcast.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 4bdba7d3f..bf888c710 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -298,7 +298,7 @@ class presentationBroadcast extends React.Component {
channel.onopen = function() {
console.log('Data Channel opened');
if (that.isInitiator)
- sendRTCMessage('gotoslide', that.currentSlide, peerID);
+ sendRTCMessage('gotoslide', document.getElementById('slidewikiPresentation').contentWindow.location.href, peerID);// using href instead of currentSlide because it could be bad initialized
};
channel.onmessage = handleMessage.bind(that, channel);
@@ -526,7 +526,7 @@ class presentationBroadcast extends React.Component {
//******** SlideWiki specific methods ********
- $('#resumeRemoteControl').click(() => {//TODO does not correctly work
+ $('#resumeRemoteControl').click(() => {
that.paused = false;
changeSlide(that.lastRemoteSlide);
});
@@ -564,6 +564,7 @@ class presentationBroadcast extends React.Component {
if (!that.paused) {
console.log('Changing to slide: ', slideID);
that.iframesrc = slideID;
+ document.getElementById('slidewikiPresentation').contentWindow.location.assign(slideID);
that.forceUpdate();
}
}
From 50efad235740cd40f143f9870d0b08d14cb44834 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 13:46:06 +0200
Subject: [PATCH 019/111] Added a char count to the textarea
---
components/webrtc/presentationBroadcast.js | 31 +++++++++++++++++-----
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 1b4f05414..e34408959 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -1,7 +1,7 @@
import React from 'react';
import { handleRoute, navigateAction} from 'fluxible-router';
import { isEmpty } from '../../common';
-import { Grid, Message, Comment, Input, Button, Form, Divider } from 'semantic-ui-react';
+import { Grid, Message, Comment, Input, Button, Form, Divider, Label } from 'semantic-ui-react';
class presentationBroadcast extends React.Component {
@@ -15,6 +15,7 @@ class presentationBroadcast extends React.Component {
constructor(props) {
super(props);
this.texts = {roleText: '', peerCountText: '', peerCount: ''};
+ this.textInputLength = 300;
this.isInitiator = false;
this.localStream = undefined;
this.myID = undefined;
@@ -659,8 +660,19 @@ class presentationBroadcast extends React.Component {
};
recognition.onerror = function (e) {
- console.warn('Recognition error:', e);
- recognition.stop();//TODO Really stop recognition in case of a fatal error. Pay attention to not restarting it via the onend listener
+ if(e.type === 'error' && e.error !== 'no-speech'){
+ disabled = true;
+ recognition.stop();
+ swal({
+ title: 'Speech recognition disabled',
+ html: 'An error occured and we had to disable speech recognition. We are sorry about it, but speech recognition is a highly experimental feature. Your listeners will not recieve any subtitles anymore.',
+ type: 'error',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Okay',
+ allowOutsideClick: false
+ });
+ } else
+ recognition.stop();
};
recognition.onend = function (e) {
@@ -697,7 +709,6 @@ class presentationBroadcast extends React.Component {
});
});
- //TODO implement proper error handling
} else {
swal({
title: 'Speech recognition disabled',
@@ -739,6 +750,10 @@ class presentationBroadcast extends React.Component {
return false;
}
+ updateCharCount(){
+ $('#textCharCount').text($('#messageToSend').val().length + '/' + this.textInputLength);
+ }
+
render() {
let messages = [];
for(let i in this.commentList) {
@@ -774,14 +789,16 @@ class presentationBroadcast extends React.Component {
+
{/*
- * TODO Add a visible char counter,e .g. 234/300 next to the send button
* TODO Don't send empty messages or those with too few words (and show notification about it)
* TODO move the input box to the bottom of the element (so it doesn't move)
* TODO disable keydown listener if textarea is focused
*/}
-
+
+
+
+
From f371ef452fc234e1fa5f35b8cbba944be910c3af Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 13:49:27 +0200
Subject: [PATCH 020/111] Added a minimum char count requirement for messages
---
components/webrtc/presentationBroadcast.js | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index e34408959..6bbba5f93 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -745,8 +745,20 @@ class presentationBroadcast extends React.Component {
sendMessage(event) {
event.preventDefault();
- this.sendRTCMessage('message', $('#messageToSend:first').val(), this.presenterID);
- this.addMessage({sender: this.myID, data: $('#messageToSend:first').val()}, true);
+ if($('#messageToSend:first').val().length < 15){
+ swal({
+ title: 'Message too short',
+ html: 'The message you tried to send is too short. Please write more than 15 characters.',
+ type: 'warning',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Okay',
+ allowOutsideClick: false
+ });
+ } else {
+ this.sendRTCMessage('message', $('#messageToSend:first').val(), this.presenterID);
+ this.addMessage({sender: this.myID, data: $('#messageToSend:first').val()}, true);
+ $('#messageToSend:first').val('');
+ }
return false;
}
@@ -791,7 +803,6 @@ class presentationBroadcast extends React.Component {
{/*
- * TODO Don't send empty messages or those with too few words (and show notification about it)
* TODO move the input box to the bottom of the element (so it doesn't move)
* TODO disable keydown listener if textarea is focused
*/}
From 71cfa5f50891565673fd5ea9a6b2edc1fb56ce18 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 14:22:25 +0200
Subject: [PATCH 021/111] disabled event forwarding in case textarea is focused
and strechted message area
---
components/webrtc/presentationBroadcast.js | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 6bbba5f93..f60e488ed 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -33,6 +33,7 @@ class presentationBroadcast extends React.Component {
this.socket = undefined;
//******** SlideWiki specific variables ********
+ this.eventForwarding = true;
this.iframesrc = this.props.currentRoute.query.presentation + '';//NOTE Error handling implemented in first lines of componentDidMount
this.lastRemoteSlide = this.iframesrc + '';
this.paused = false; //user has manually paused slide transitions
@@ -574,7 +575,8 @@ class presentationBroadcast extends React.Component {
let newEvent = new Event('keydown', {key: e.key, code: e.code, composed: true, charCode: e.charCode, keyCode: e.keyCode, which: e.which, bubbles: true, cancelable: true, which: e.keyCode});
newEvent.keyCode = e.keyCode;
newEvent.which = e.keyCode;
- frame.dispatchEvent(newEvent);
+ if(that.eventForwarding)
+ frame.dispatchEvent(newEvent);
});
if (that.isInitiator) {
@@ -595,6 +597,13 @@ class presentationBroadcast extends React.Component {
that.forceUpdate();
}
});
+ let textArea = $('#messageToSend');
+ textArea.on('focus', () => {
+ that.eventForwarding = false;
+ });
+ textArea.on('focusout', () => {
+ that.eventForwarding = true;
+ });
}
}
@@ -794,22 +803,20 @@ class presentationBroadcast extends React.Component {
{(!this.isInitiator) ? (
-
+ {/*TODO calculate heights somehow*/}
Your Questions:
{messages}
- {/*
- * TODO move the input box to the bottom of the element (so it doesn't move)
- * TODO disable keydown listener if textarea is focused
- */}
+
+
+
From 105187d5b5309f63d0d6e70c00ba3b8af5d2790b Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 14:41:46 +0200
Subject: [PATCH 022/111] Added a control box for several actions
---
components/webrtc/presentationBroadcast.js | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index f60e488ed..be6edea93 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -813,7 +813,7 @@ class presentationBroadcast extends React.Component {
@@ -838,6 +837,13 @@ class presentationBroadcast extends React.Component {
) : ''}
+
+
+ {/*TODO open up the right functionality*/}
+ {/*TODO open up the right functionality*/}
+
+
+
);
From f506e7b101e231be34f2731a3147f647c7fcec4c Mon Sep 17 00:00:00 2001
From: Kurt Junghanns
Date: Tue, 8 Aug 2017 15:01:05 +0200
Subject: [PATCH 023/111] ENhanced context of component in order to get
userdata
---
components/webrtc/presentationBroadcast.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 1b4f05414..ff05a42d4 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -1,5 +1,6 @@
import React from 'react';
import { handleRoute, navigateAction} from 'fluxible-router';
+import { provideContext } from 'fluxible-addons-react';
import { isEmpty } from '../../common';
import { Grid, Message, Comment, Input, Button, Form, Divider } from 'semantic-ui-react';
@@ -810,7 +811,8 @@ class presentationBroadcast extends React.Component {
}
presentationBroadcast.contextTypes = {
- executeAction: React.PropTypes.func.isRequired
+ executeAction: React.PropTypes.func.isRequired,
+ getUser: React.PropTypes.func
};
presentationBroadcast = handleRoute(presentationBroadcast);
From 93246da9f9f0d65b5c257be7499a2683d9690cb9 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 15:32:44 +0200
Subject: [PATCH 024/111] Improved style of subtitle, replaced some Jquery
calls with react methods
---
components/webrtc/presentationBroadcast.js | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index be6edea93..dd5e1754f 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -553,11 +553,6 @@ class presentationBroadcast extends React.Component {
//******** SlideWiki specific methods ********
- $('#resumeRemoteControl').click(() => {
- that.paused = false;
- changeSlide(that.lastRemoteSlide);
- });
-
function toggleBlackScreen() {//TODO won't unpause the screen - I have no idea why...
let frame = document.getElementById('slidewikiPresentation').contentDocument;
let newEvent = new Event('keydown', {keyCode: 58});
@@ -616,6 +611,7 @@ class presentationBroadcast extends React.Component {
that.forceUpdate();
}
}
+ that.changeSlide = changeSlide;
function activateSpeechRecognition() {
let recognition;
@@ -775,6 +771,11 @@ class presentationBroadcast extends React.Component {
$('#textCharCount').text($('#messageToSend').val().length + '/' + this.textInputLength);
}
+ resumePlayback(){
+ this.paused = false;
+ this.changeSlide(this.lastRemoteSlide);
+ }
+
render() {
let messages = [];
for(let i in this.commentList) {
@@ -832,16 +833,19 @@ class presentationBroadcast extends React.Component {
{(!this.isInitiator) ? (
- Speech recognition:{/*TODO Find a better heading and add a boarder to the input*/}
-
+
+
+
+
+
) : ''}
-
+ {/*TODO open up the right functionality*/}
{/*TODO open up the right functionality*/}
-
+
From 72252000e370e3ef989b6d475569ea6496fb79e3 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 15:52:26 +0200
Subject: [PATCH 025/111] Added info modal for listeners
---
components/webrtc/presentationBroadcast.js | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index dd5e1754f..6eb623753 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -313,6 +313,16 @@ class presentationBroadcast extends React.Component {
console.log('Data Channel opened');
if (that.isInitiator)
sendRTCMessage('gotoslide', document.getElementById('slidewikiPresentation').contentWindow.location.href, peerID);// using href instead of currentSlide because it could be bad initialized
+ else {
+ swal({
+ title: 'You\'ve joined a live presentation',
+ html: 'Nice to see you here! You will hear the presenters voice and your presentation will reflect his progress. Just lean back and keep watching. In case you have any questions to the presenter, please use the "Send Question" functionality.',
+ type: 'info',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Okay',
+ allowOutsideClick: false
+ });
+ }
};
channel.onmessage = handleMessage.bind(that, channel);
@@ -814,7 +824,7 @@ class presentationBroadcast extends React.Component {
-
+
From 52c5a7280b12d4e1753959325cd25de070117032 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 8 Aug 2017 17:02:15 +0200
Subject: [PATCH 026/111] Added copy to clipboard capabilities and a share
button
---
components/webrtc/presentationBroadcast.js | 90 ++++++++++++++++++++--
1 file changed, 84 insertions(+), 6 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 6eb623753..2a737dc30 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -6,7 +6,6 @@ import { Grid, Message, Comment, Input, Button, Form, Divider, Label } from 'sem
class presentationBroadcast extends React.Component {
/*
- * TODO add share button that copies the URL into the clipboard (down right corner)
* TODO Use Username instead of "Peer X" if available
* TODO Add some explaining texts for peers with swal or ToolTips(like that it's not a chat, ...)
* TODO this.props.currentRoute.query.presentation is not filled correctly if a "#" is in the path
@@ -716,12 +715,43 @@ class presentationBroadcast extends React.Component {
}).then(() => {
swal({
title: 'Invite others people',
- html: '
Copy the following link and send it to other people in order to invite them to this room:
' + window.location.href + '
',//TODO add a copy to clipboard button
+ html: '
Copy the following link and send it to other people in order to invite them to this room:
' + window.location.href + '
',
type: 'info',
confirmButtonColor: '#3085d6',
- confirmButtonText: 'Okay',
- allowOutsideClick: false
- });
+ confirmButtonText: 'Copy to Clipboard',
+ showCancelButton: true,
+ cancelButtonColor: '#d33',
+ allowOutsideClick: false,
+ preConfirm: function () {
+ return new Promise((resolve, reject) => {
+ let toCopy = document.createElement('input');
+ toCopy.style.position = 'fixed';
+ toCopy.style.top = 0;
+ toCopy.style.left = 0;
+ toCopy.style.width = '2em';
+ toCopy.style.height = '2em';
+ toCopy.style.padding = 0;
+ toCopy.style.border = 'none';
+ toCopy.style.outline = 'none';
+ toCopy.style.boxShadow = 'none';
+ toCopy.style.background = 'transparent';
+ toCopy.value = window.location.href;
+ document.getElementById('clipboardtarget').appendChild(toCopy);
+ toCopy.value = window.location.href;
+ toCopy.select();
+
+ try {
+ let successful = document.execCommand('copy');
+ if(!successful)
+ throw 'Unable to copy';
+ resolve('Copied to clipboard');
+ } catch (err) {
+ console.log('Oops, unable to copy');
+ reject('Oops, unable to copy');
+ }
+ });
+ }
+ }).then(() => {}, () => {});
});
} else {
@@ -786,6 +816,50 @@ class presentationBroadcast extends React.Component {
this.changeSlide(this.lastRemoteSlide);
}
+ copyURLToClipboard() {
+ let toCopy = document.createElement('input');
+ toCopy.style.position = 'fixed';
+ toCopy.style.top = 0;
+ toCopy.style.left = 0;
+ toCopy.style.width = '2em';
+ toCopy.style.height = '2em';
+ toCopy.style.padding = 0;
+ toCopy.style.border = 'none';
+ toCopy.style.outline = 'none';
+ toCopy.style.boxShadow = 'none';
+ toCopy.style.background = 'transparent';
+ toCopy.value = window.location.href;
+ document.body.appendChild(toCopy);
+ toCopy.value = window.location.href;
+ toCopy.select();
+
+ try {
+ let successful = document.execCommand('copy');
+ if(!successful)
+ throw 'Unable to copy';
+ else{
+ swal({
+ title: 'URL copied to clipboard',
+ type: 'success',
+ showConfirmButton: false,
+ allowOutsideClick: false,
+ timer: 1500
+ }).then(() => {}, () => {});
+ }
+ } catch (err) {
+ console.log('Oops, unable to copy');
+ swal({
+ title: 'Can\'t copy URL to clipboard',
+ text: 'Please select the URL in your browser and share it manually.',
+ type: 'error',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Check',
+ allowOutsideClick: false
+ });
+ }
+ document.body.removeChild(toCopy);
+ }
+
render() {
let messages = [];
for(let i in this.commentList) {
@@ -855,7 +929,11 @@ class presentationBroadcast extends React.Component {
{/*TODO open up the right functionality*/}
{/*TODO open up the right functionality*/}
-
+ {(this.isInitiator) ? (
+
+ ) : (
+
+ )}
From 721f5a00c8269ece9388a564c012ae4be4d20010 Mon Sep 17 00:00:00 2001
From: Kurt Junghanns
Date: Wed, 9 Aug 2017 10:21:49 +0200
Subject: [PATCH 027/111] Added Button for logged in peer to publish their
username. Username is listed in new chat messages and as a hint on the
presenters peer count.
---
components/webrtc/presentationBroadcast.js | 68 ++++++++++++++++++----
1 file changed, 58 insertions(+), 10 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 0da567a21..c7d2f1bf6 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -2,7 +2,7 @@ import React from 'react';
import { handleRoute, navigateAction} from 'fluxible-router';
import { provideContext } from 'fluxible-addons-react';
import { isEmpty } from '../../common';
-import { Grid, Message, Comment, Input, Button, Form, Divider, Label } from 'semantic-ui-react';
+import { Grid, Message, Comment, Input, Button, Form, Divider, Label, Popup } from 'semantic-ui-react';
class presentationBroadcast extends React.Component {
@@ -20,7 +20,7 @@ class presentationBroadcast extends React.Component {
this.localStream = undefined;
this.myID = undefined;
this.presenterID = undefined;
- this.pcs = {}; // {: {RTCConnection: RPC, dataChannel: dataChannel}, : {RTCConnection: RPC, dataChannel: dataChannel}}
+ this.pcs = {}; // {: {RTCConnection: RPC, dataChannel: dataChannel, username: username}, : {RTCConnection: RPC, dataChannel: dataChannel, username: username}}
this.turnReady = undefined;
this.pcConfig = {
@@ -40,6 +40,7 @@ class presentationBroadcast extends React.Component {
this.currentSlide = this.iframesrc + '';
this.commentList = {};//{timestamp: {peer: username, message: text},timestamp: {peer: username, message: text}}
this.subtitle = '';//used for speech recognition results
+ this.isUsernamePublished = true;
}
componentDidMount() {
@@ -108,6 +109,9 @@ class presentationBroadcast extends React.Component {
that.forceUpdate();
$('#slidewikiPresentation').on('load', activateIframeListeners);
gotStream('');//NOTE Skip requesting streams for the listeners, as they do not need them
+
+ that.isUsernamePublished = false;
+ that.forceUpdate();
});
that.socket.on('full', (room) => { //only recieved by peer that tries to join
@@ -325,7 +329,7 @@ class presentationBroadcast extends React.Component {
}
};
- channel.onmessage = handleMessage.bind(that, channel);
+ channel.onmessage = handleMessage.bind(that, channel, peerID);
}
function handleIceCandidate(peerID, event) {
@@ -428,7 +432,10 @@ class presentationBroadcast extends React.Component {
} catch (e) {}
}
- function handleMessage(channel, event) {
+ function handleMessage(channel, peerID, event) {
+ console.log(event.data);
+ if (event.data === undefined)
+ return;
let data = JSON.parse(event.data);
switch (data.cmd) {
case 'gotoslide':
@@ -441,7 +448,7 @@ class presentationBroadcast extends React.Component {
break;
case 'message':
if (that.isInitiator)
- addMessage(data);
+ addMessage(data, false, peerID);
break;
case 'log':
console.log('Recieved log message from peer: ', data.data);
@@ -452,6 +459,9 @@ class presentationBroadcast extends React.Component {
case 'subtitle':
handleSubtitle(data.data);
break;
+ case 'newUsername':
+ handleNewUsername(data.data, peerID);
+ break;
default:
}
@@ -767,11 +777,11 @@ class presentationBroadcast extends React.Component {
}
}
- function addMessage(data, fromMyself = false) {
+ function addMessage(data, fromMyself = false, peerID = null) {
let currentTime = new Date().getTime();
that.commentList[currentTime] = {};
if(!fromMyself)
- that.commentList[currentTime].peer = Object.keys(that.pcs).indexOf(data.sender);
+ that.commentList[currentTime].peer = that.pcs[peerID].username || Object.keys(that.pcs).indexOf(data.sender);
else
that.commentList[currentTime].peer = 'Me';
that.commentList[currentTime].message = data.data;
@@ -785,6 +795,11 @@ class presentationBroadcast extends React.Component {
scrollLeft: $('#input_subtitle')[0].scrollLeft+1000
}, 1000);
}
+
+ function handleNewUsername(username, peerID) {
+ that.pcs[peerID].username = username;
+ that.forceUpdate();
+ }
}
componentDidUpdate() {}
@@ -808,6 +823,20 @@ class presentationBroadcast extends React.Component {
return false;
}
+ sendUsername(event) {
+ event.preventDefault();
+
+ let username = this.context.getUser().username;
+ console.log('sendUsername got called - username: '+username);
+ if (username !== undefined && username !== '' && username !== null) {
+ this.sendRTCMessage('newUsername', username, this.presenterID);
+ }
+ this.isUsernamePublished = true;
+ this.forceUpdate();
+
+ return false;
+ }
+
updateCharCount(){
$('#textCharCount').text($('#messageToSend').val().length + '/' + this.textInputLength);
}
@@ -869,7 +898,7 @@ class presentationBroadcast extends React.Component {
- {this.commentList[i].peer.toString() === 'Me' ? '' : 'Peer'} {this.commentList[i].peer.toString()}, {new Date(parseInt(i)).toLocaleTimeString('en-GB', { hour12: false, hour: 'numeric', minute: 'numeric'})}
+ {this.commentList[i].peer.toString() === 'Me' ? '' : 'Peer - '} {this.commentList[i].peer.toString()}, {new Date(parseInt(i)).toLocaleTimeString('en-GB', { hour12: false, hour: 'numeric', minute: 'numeric'})}
{this.commentList[i].message}
@@ -879,6 +908,14 @@ class presentationBroadcast extends React.Component {
);
}
+ let peernames = new Set();
+ if (this.isInitiator && this.pcs) {
+ for (let k in this.pcs) {
+ if (this.pcs[k].username)
+ peernames.add(this.pcs[k].username);
+ }
+ }
+
return (
@@ -889,7 +926,7 @@ class presentationBroadcast extends React.Component {
{(!this.isInitiator) ? (
- {/*TODO calculate heights somehow*/}
+ {/*TODO calculate heights somehow*/}
Your browser isn\'t able to transcode speech to text. Thus, your peers will not recieve a subtitle. Google Chrome is currently the only browser that supports speech recognition.
Your browser isn\'t able to transcode speech to text. Thus, your peers will not recieve a subtitle. Google Chrome is currently the only browser that supports speech recognition.
Other people are free to join the room. Rooms are currently limited to 10 people. See the counter at the bottom of the page for information about currently listening people.
',
+ html: '
Other people are free to join the room. Rooms are currently limited to '+this.maxPeers+' people. See the counter at the bottom of the page for information about currently listening people.
',
type: 'info',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Check',
@@ -102,9 +102,9 @@ class presentationBroadcast extends React.Component {
let numberOfPeers = Object.keys(this.pcs).length;
console.log(numberOfPeers, this.maxPeers);
if (numberOfPeers >= this.maxPeers)
- that.socket.emit('room is full', that.room, socketID);
+ that.socket.emit('room is full', socketID);
else
- that.socket.emit('ID of presenter', that.room, that.myID);
+ that.socket.emit('ID of presenter', that.myID, socketID);
}
});
@@ -139,20 +139,18 @@ class presentationBroadcast extends React.Component {
that.presenterID = id;
});
- that.socket.on('room is full', (id) => {
- console.log('Received room is full: ', id);
- if (id === this.myID) {
- swal({
- title: '
The presentation room is full.
',
- html: '
The maximium number of listeners is already reached. Please try again later.
The maximium number of listeners is already reached. Please try again later.
',
+ type: 'warning',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Check',
+ allowOutsideClick: false,
+ allowEscapeKey: false
+ });
+ that.socket.close();
});
that.socket.on('log', (array) => {
@@ -167,7 +165,7 @@ class presentationBroadcast extends React.Component {
function sendMessage(cmd, data = undefined, receiver = undefined) {
// console.log('Sending message over socket: ', cmd, data, receiver);
- that.socket.emit('message', { 'cmd': cmd, 'data': data, 'sender': that.myID, 'receiver': receiver }, that.room);
+ that.socket.emit('message', { 'cmd': cmd, 'data': data, 'sender': that.myID, 'receiver': receiver });
}
function sendRTCMessage(cmd, data = undefined, receiver = undefined) {
diff --git a/custom_modules/custom-semantic-ui b/custom_modules/custom-semantic-ui
index 1b510ab3c..5a85ccc11 160000
--- a/custom_modules/custom-semantic-ui
+++ b/custom_modules/custom-semantic-ui
@@ -1 +1 @@
-Subproject commit 1b510ab3c42bc651a1d58fb6119c0d1d0c939d9b
+Subproject commit 5a85ccc1157e1f48e1dd8246dac59f9d14114485
From de1707101080f1d8bdc08bd3523b4edefa6487b2 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Fri, 22 Sep 2017 15:20:20 +0200
Subject: [PATCH 062/111] Resolved deckservice bug
---
configs/microservices.sample.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configs/microservices.sample.js b/configs/microservices.sample.js
index dcb093dfd..614922c96 100644
--- a/configs/microservices.sample.js
+++ b/configs/microservices.sample.js
@@ -35,7 +35,7 @@ STEP 3: Verify that in STEP 2, you have correct operation mentioned. If you see
export default {
Microservices: {
'deck': {
- uri: 'https://signalingservice.experimental.slidewiki.org'
+ uri: 'https://deckservice.experimental.slidewiki.org'
},
'discussion': {
uri: 'https://discussionservice.experimental.slidewiki.org'
From a202dbefae3cc3253d13e0ba3b100db9d61e20a4 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Mon, 9 Oct 2017 15:22:03 +0200
Subject: [PATCH 063/111] Workaround for swal.hideLoading error
---
components/webrtc/presentationBroadcast.js | 52 ++++++++++++----------
1 file changed, 29 insertions(+), 23 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index dab38eba2..68ccb6500 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -301,28 +301,33 @@ class presentationBroadcast extends React.Component {
}
function connectionFailureHandler() {
- let dialog = {
- title: 'An error occured',
- html: 'We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.',
- type: 'error',
- confirmButtonColor: '#3085d6',
- confirmButtonText: 'Okay',
- allowOutsideClick: false,
- allowEscapeKey: false,
- preConfirm: () => {
- return new Promise((resolve) => {
- cleanup();
- that.context.executeAction(navigateAction, {'url': '/'});
- resolve();
- });
- }
- };
- if(swal.isVisible){
- swal.hideLoading();
- swal.insertQueueStep(dialog);
- swal.clickConfirm();
- } else
- swal(dialog);
+ // let dialog = {
+ // title: 'An error occured',
+ // html: 'We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.',
+ // type: 'error',
+ // confirmButtonColor: '#3085d6',
+ // confirmButtonText: 'Okay',
+ // allowOutsideClick: false,
+ // allowEscapeKey: false,
+ // preConfirm: () => {
+ // return new Promise((resolve) => {
+ // cleanup();
+ // that.context.executeAction(navigateAction, {'url': '/'});
+ // resolve();
+ // });
+ // }
+ // };
+ // if(swal.isVisible){
+ // swal.hideLoading();//NOTE is currently not working, contacted developer.
+ // swal.insertQueueStep(dialog);
+ // swal.clickConfirm();
+ // } else
+ // swal(dialog);
+ /*eslint not-alert: false*/
+ let res = window.confirm('We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.');
+ cleanup();
+ that.context.executeAction(navigateAction, {'url': '/'});
+ /*eslint not-alert: true*/
}
function handleICEConnectionStateChange(peerID, event) {
@@ -332,7 +337,8 @@ class presentationBroadcast extends React.Component {
console.log('The connection has been successfully established');
if(!that.isInitiator){
try {
- swal.hideLoading();
+ connectionFailureHandler();
+ swal.hideLoading();//NOTE is currently not working, contacted developer.
} catch (e) {
console.log('Error: swal was not defined', e);
}
From c4cdf10753f4f8950a7178c48ed584d76340b8a5 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Mon, 9 Oct 2017 15:26:55 +0200
Subject: [PATCH 064/111] Resolved linting errors
---
components/webrtc/presentationBroadcast.js | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 68ccb6500..0bf994d9a 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -213,10 +213,10 @@ class presentationBroadcast extends React.Component {
candidate: message.data.candidate
});
that.pcs[message.sender].RTCconnection.addIceCandidate(candidate).catch((e) => {
- console.log('Error: was unable to add Ice candidate:', candidate, 'to sender', message.sender);
+ console.log('Error: was unable to add Ice candidate:', candidate, 'to sender', message.sender);
}); //Catch defective candidates, TODO add better exception handling
} catch (e) {
- console.log('Error: building the candiate failed with', message);
+ console.log('Error: building the candiate failed with', message);
}//TODO add better exception handling
}
}
@@ -323,11 +323,9 @@ class presentationBroadcast extends React.Component {
// swal.clickConfirm();
// } else
// swal(dialog);
- /*eslint not-alert: false*/
- let res = window.confirm('We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.');
+ let res = window.confirm('We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.');// eslint-disable-line no-alert
cleanup();
that.context.executeAction(navigateAction, {'url': '/'});
- /*eslint not-alert: true*/
}
function handleICEConnectionStateChange(peerID, event) {
@@ -337,10 +335,10 @@ class presentationBroadcast extends React.Component {
console.log('The connection has been successfully established');
if(!that.isInitiator){
try {
- connectionFailureHandler();
- swal.hideLoading();//NOTE is currently not working, contacted developer.
+ connectionFailureHandler();
+ swal.hideLoading();//NOTE is currently not working, contacted developer.
} catch (e) {
- console.log('Error: swal was not defined', e);
+ console.log('Error: swal was not defined', e);
}
}
break;
@@ -541,8 +539,8 @@ class presentationBroadcast extends React.Component {
case 'message':
if (that.isInitiator) {
this.lastMessage = {
- data: data,
- peerID: peerID
+ data: data,
+ peerID: peerID
};
this.forceUpdate();
}
From 175ceffb02534854727f5156f63254c399a2105b Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 10 Oct 2017 10:38:11 +0200
Subject: [PATCH 065/111] Fixed Modal order
---
components/webrtc/presentationBroadcast.js | 87 ++++++++++------------
1 file changed, 41 insertions(+), 46 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 0bf994d9a..df19845b1 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -117,6 +117,24 @@ class presentationBroadcast extends React.Component {
gotStream('');//NOTE Skip requesting streams for the listeners, as they do not need them
that.forceUpdate();
+ swal.queue([{
+ title: 'You\'re about to join a live presentation',
+ html: 'Nice to see you here! You will hear the presenters voice in a few moments and your presentation will reflect his progress. Just lean back and keep watching. In case you have any questions to the presenter, please use the "Send Question" functionality.',
+ type: 'info',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Okay',
+ allowOutsideClick: false,
+ allowEscapeKey: false,
+ onOpen: () => {
+ swal.showLoading();
+ },
+ preConfirm: () => {
+ return new Promise((resolve) => {
+ $('body>a#atlwdg-trigger').remove();
+ resolve();
+ });
+ }
+ }]);
});
that.socket.on('full', (room) => { //only recieved by peer that tries to join
@@ -301,31 +319,28 @@ class presentationBroadcast extends React.Component {
}
function connectionFailureHandler() {
- // let dialog = {
- // title: 'An error occured',
- // html: 'We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.',
- // type: 'error',
- // confirmButtonColor: '#3085d6',
- // confirmButtonText: 'Okay',
- // allowOutsideClick: false,
- // allowEscapeKey: false,
- // preConfirm: () => {
- // return new Promise((resolve) => {
- // cleanup();
- // that.context.executeAction(navigateAction, {'url': '/'});
- // resolve();
- // });
- // }
- // };
- // if(swal.isVisible){
- // swal.hideLoading();//NOTE is currently not working, contacted developer.
- // swal.insertQueueStep(dialog);
- // swal.clickConfirm();
- // } else
- // swal(dialog);
- let res = window.confirm('We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.');// eslint-disable-line no-alert
- cleanup();
- that.context.executeAction(navigateAction, {'url': '/'});
+ let dialog = {
+ title: 'An error occured',
+ html: 'We\'re sorry, but we can\'t connect you to the presenter. It seems like there is a problem with your connection or browser. Please update your browser, disable extensions or ask your network operator about it. We\'re using a peer to peer connection technique called WebRTC.',
+ type: 'error',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Okay',
+ allowOutsideClick: false,
+ allowEscapeKey: false,
+ preConfirm: () => {
+ return new Promise((resolve) => {
+ cleanup();
+ that.context.executeAction(navigateAction, {'url': '/'});
+ resolve();
+ });
+ }
+ };
+ if(swal.isVisible){
+ swal.hideLoading();//NOTE is currently not working, contacted developer.
+ swal.insertQueueStep(dialog);
+ swal.clickConfirm();
+ } else
+ swal(dialog);
}
function handleICEConnectionStateChange(peerID, event) {
@@ -335,8 +350,7 @@ class presentationBroadcast extends React.Component {
console.log('The connection has been successfully established');
if(!that.isInitiator){
try {
- connectionFailureHandler();
- swal.hideLoading();//NOTE is currently not working, contacted developer.
+ swal.hideLoading();
} catch (e) {
console.log('Error: swal was not defined', e);
}
@@ -398,25 +412,6 @@ class presentationBroadcast extends React.Component {
sendRTCMessage('gotoslide', document.getElementById('slidewikiPresentation').contentWindow.location.href, peerID);// using href instead of currentSlide because it could be bad initialized
else {
that.sendUsername();
- swal.queue([{
- title: 'You\'re about to join a live presentation',
- html: 'Nice to see you here! You will hear the presenters voice in a few moments and your presentation will reflect his progress. Just lean back and keep watching. In case you have any questions to the presenter, please use the "Send Question" functionality.',
- type: 'info',
- confirmButtonColor: '#3085d6',
- confirmButtonText: 'Okay',
- allowOutsideClick: false,
- allowEscapeKey: false,
- onOpen: () => {
- if(that.pcs[peerID].RTCconnection.iceConnectionState !== 'connected')
- swal.showLoading();
- },
- preConfirm: () => {
- return new Promise((resolve) => {
- $('body>a#atlwdg-trigger').remove();
- resolve();
- });
- }
- }]);
}
};
From 1e420a5f59944bb8d47996f254ece0e32f84a087 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Tue, 10 Oct 2017 11:08:19 +0200
Subject: [PATCH 066/111] Fixed Linting issues
---
components/webrtc/Chat.js | 2 +-
components/webrtc/SpeechRecognition.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/components/webrtc/Chat.js b/components/webrtc/Chat.js
index a047b8ccb..6ab34271a 100644
--- a/components/webrtc/Chat.js
+++ b/components/webrtc/Chat.js
@@ -44,7 +44,7 @@ class Chat extends React.Component {
|| nextProps.lastMessage.data.sender !== currentMessage.data.sender
|| nextProps.lastMessage.data.data !== currentMessage.data.data) {
this.addMessage(nextProps.lastMessage.data, false, nextProps.lastMessage.peerID);
- }
+ }
}
}
diff --git a/components/webrtc/SpeechRecognition.js b/components/webrtc/SpeechRecognition.js
index 09324da94..f72af6666 100644
--- a/components/webrtc/SpeechRecognition.js
+++ b/components/webrtc/SpeechRecognition.js
@@ -22,7 +22,7 @@ class SpeechRecognition extends React.Component {
}
componentDidMount() {
- this.speechRecognitionDisabled = this.props.speechRecognitionDisabled;
+ this.speechRecognitionDisabled = this.props.speechRecognitionDisabled;
}
componentDidUpdate() {
From e82a93b780ae3ef3c00bbaeb677e7571b0ed4470 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Thu, 12 Oct 2017 14:55:03 +0200
Subject: [PATCH 067/111] Added checks for device availability and added error
dialogs
---
components/webrtc/presentationBroadcast.js | 61 ++++++++++++++++++----
1 file changed, 51 insertions(+), 10 deletions(-)
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index df19845b1..c2cc9498d 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -83,15 +83,6 @@ class presentationBroadcast extends React.Component {
// facingMode: "user"
// }
});
- swal({
- title: '
Room ' + that.room + ' successfully created!
',
- html: '
Other people are free to join the room. Rooms are currently limited to '+this.maxPeers+' people. See the counter at the bottom of the page for information about currently listening people.
',
- type: 'info',
- confirmButtonColor: '#3085d6',
- confirmButtonText: 'Check',
- allowOutsideClick: false,
- allowEscapeKey: false
- }).then(() => { this.startSpeechrecognition = true; this.forceUpdate(); $('body>a#atlwdg-trigger').remove();});
});
that.socket.on('join', (room, socketID) => { //whole room recieves this, except for the peer that tries to join
@@ -246,10 +237,51 @@ class presentationBroadcast extends React.Component {
navigator.mediaDevices.getUserMedia(options)
.then(gotStream)
.catch((err) => {
- console.log('getUserMedia() error: ' + err.name);
+ switch (err.name) {
+ case 'NotAllowedError'://The user declined the use of the media device(s)
+ if(that.isInitiator)
+ requestStreamsErrorHandler('No access to microphone', 'Your browser reported that you refused to grant this application access to your microphone. The presention rooms feature is not usable without a microphone. Please grant us access to your microphone (see your URL bar) and click the Okay button. The room will be automatically recreated.', 'warning');
+ else
+ console.log('getUserMedia() error: ' + err.name);
+ break;
+ default:
+ console.log('getUserMedia() error: ' + err.name);
+ if(that.isInitiator)
+ requestStreamsErrorHandler('Device error', 'Your browser reported a problem accessing your microphone. You can\'t use the presention rooms feature without a microphone. Please try to fix your microphone (settings) and open up a new room. You will be redirected to the homepage.', 'error');
+ else
+ requestStreamsErrorHandler('Browser Error', 'Your browser reported a technical problem. You can\'t use the presention rooms feauter with this problem. Please try to fix it by updating your browser or resetting it and rejoin a room. You will be redirected to the homepage.', 'error');
+ }
});
}
+ function requestStreamsErrorHandler(title1, text1, type1) {
+ let dialog = {
+ title: title1,
+ html: text1,
+ type: type1,
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Okay',
+ allowOutsideClick: false,
+ allowEscapeKey: false,
+ preConfirm: () => {
+ return new Promise((resolve) => {
+ cleanup();
+ if(type1 === 'error')
+ that.context.executeAction(navigateAction, {'url': '/'});
+ else
+ location.reload();
+ resolve();
+ });
+ }
+ };
+
+ that.socket.close();
+ if(swal.isVisible())
+ swal.insertQueueStep(dialog);
+ else
+ swal(dialog);
+ }
+
function gotStream(stream) {
console.log('Adding local stream');
if (that.isInitiator) {
@@ -257,6 +289,15 @@ class presentationBroadcast extends React.Component {
//let localVideo = document.querySelector('#localVideo');
//localVideo.srcObject = stream;
$('#media').remove();
+ swal({//NOTE implemented here because this dialog interrupted with error dialogs of requestStreams()
+ title: '
Room ' + that.room + ' successfully created!
',
+ html: '
Other people are free to join the room. Rooms are currently limited to '+that.maxPeers+' people. See the counter at the bottom of the page for information about currently listening people.
',
+ type: 'info',
+ confirmButtonColor: '#3085d6',
+ confirmButtonText: 'Check',
+ allowOutsideClick: false,
+ allowEscapeKey: false
+ }).then(() => { that.startSpeechrecognition = true; that.forceUpdate(); $('body>a#atlwdg-trigger').remove();});
}
that.localStream = stream;
From 9899ca180257e95f158572643699ea84433b5fc0 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Mon, 23 Oct 2017 14:53:46 +0200
Subject: [PATCH 068/111] Showing better usernames in TaskComplete and
Questions feature
---
components/webrtc/Chat.js | 2 +-
components/webrtc/presentationBroadcast.js | 21 +++++++++++++++------
2 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/components/webrtc/Chat.js b/components/webrtc/Chat.js
index 6ab34271a..83d03a3fe 100644
--- a/components/webrtc/Chat.js
+++ b/components/webrtc/Chat.js
@@ -76,7 +76,7 @@ class Chat extends React.Component {
let currentTime = new Date().getTime();
this.commentList[currentTime] = {};
if(!fromMyself)
- this.commentList[currentTime].peer = 'Peer ' + (this.props.pcs[peerID].username || Object.keys(this.props.pcs).indexOf(data.sender));
+ this.commentList[currentTime].peer = this.props.pcs[peerID].username || Object.keys(this.props.pcs).indexOf(data.sender);
else
this.commentList[currentTime].peer = 'Me';
this.commentList[currentTime].message = data.data;
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index c2cc9498d..89d74e821 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -33,6 +33,7 @@ class presentationBroadcast extends React.Component {
this.subtitle = '';//used for speech recognition results
this.speechRecognitionDisabled = false;
this.startSpeechrecognition = false;
+ this.peerNumber = -1;//used for peernames, will be incremented on each new peer
}
componentDidMount() {
@@ -333,6 +334,7 @@ class presentationBroadcast extends React.Component {
function createPeerConnection(peerID) {
try {
that.pcs[peerID] = {};
+ that.pcs[peerID].username = '';
that.pcs[peerID].RTCconnection = new RTCPeerConnection(that.pcConfig);
that.pcs[peerID].RTCconnection.onicecandidate = handleIceCandidate.bind(that, peerID);
that.pcs[peerID].RTCconnection.ontrack = handleRemoteStreamAdded;
@@ -747,10 +749,18 @@ class presentationBroadcast extends React.Component {
function handleNewUsername(username, peerID) {
- that.pcs[peerID].username = username;
+ if(isEmpty(username) || username === 'undefined')
+ that.pcs[peerID].username = 'Peer ' + nextPeerNumber();//TODO implement separate counter, as this will mess up numbers
+ else
+ that.pcs[peerID].username = username;
that.forceUpdate();
}
+ function nextPeerNumber() {
+ that.peerNumber += 1;
+ return that.peerNumber;
+ }
+
function showCompleteTaskModal() {
swal({
title: 'Complete the given Task',
@@ -774,11 +784,11 @@ class presentationBroadcast extends React.Component {
}
}
-
-
sendUsername() {
if (this.context && this.context.getUser() && this.context.getUser().username)
this.sendRTCMessage('newUsername', this.context.getUser().username, this.presenterID);
+ else
+ this.sendRTCMessage('newUsername', 'undefined');
}
audienceCompleteTask (event) {
@@ -801,8 +811,6 @@ class presentationBroadcast extends React.Component {
this.sendRTCMessage('completeTask');
}
-
-
resumePlayback(){
this.paused = false;
this.changeSlide(this.lastRemoteSlide);
@@ -854,7 +862,8 @@ class presentationBroadcast extends React.Component {
render() {
let peernames = new Set(Object.keys(this.pcs).map((key) => {
- return this.pcs[key].username ? this.pcs[key].username : 'Anonymous Rabbits';
+ let tmp = this.pcs[key].username === '' || this.pcs[key].username.startsWith('Peer');
+ return tmp ? 'Anonymous Rabbits' : this.pcs[key].username;
}));
peernames = Array.from(peernames).reduce((a,b) => a+', '+b, '').substring(1);
From 90af35104e42fa685d9c8d5043daf175f24b70d2 Mon Sep 17 00:00:00 2001
From: Roy Meissner
Date: Mon, 23 Oct 2017 16:53:31 +0200
Subject: [PATCH 069/111] Task modal is now dismissable, increased input length
of questions, added Name of peer to questions feed
---
components/webrtc/Chat.js | 4 +--
components/webrtc/presentationBroadcast.js | 34 ++++++++++++++++++++--
package.json | 2 +-
3 files changed, 35 insertions(+), 5 deletions(-)
diff --git a/components/webrtc/Chat.js b/components/webrtc/Chat.js
index 83d03a3fe..1bcbbc37a 100644
--- a/components/webrtc/Chat.js
+++ b/components/webrtc/Chat.js
@@ -17,7 +17,7 @@ class Chat extends React.Component {
constructor(props) {
super(props);
- this.textInputLength = 300;
+ this.textInputLength = 2000;
this.commentList = {};//{timestamp: {peer: username, message: text},timestamp: {peer: username, message: text}}
}
@@ -112,7 +112,7 @@ class Chat extends React.Component {
{(!this.props.isInitiator) ? (
-
Your Questions:
+
Your Questions ({this.props.myName}):
{messages}
diff --git a/components/webrtc/presentationBroadcast.js b/components/webrtc/presentationBroadcast.js
index 89d74e821..0246cf31b 100644
--- a/components/webrtc/presentationBroadcast.js
+++ b/components/webrtc/presentationBroadcast.js
@@ -34,6 +34,8 @@ class presentationBroadcast extends React.Component {
this.speechRecognitionDisabled = false;
this.startSpeechrecognition = false;
this.peerNumber = -1;//used for peernames, will be incremented on each new peer
+ this.showReopenModalButton = false;
+ this.myName = '';
}
componentDidMount() {
@@ -596,6 +598,12 @@ class presentationBroadcast extends React.Component {
case 'newUsername':
handleNewUsername(data.data, peerID);
break;
+ case 'username':
+ if(!that.isInitiator){
+ that.myName = data.data;
+ this.forceUpdate();
+ }
+ break;
case 'completeTask':
showCompleteTaskModal();
break;
@@ -753,6 +761,7 @@ class presentationBroadcast extends React.Component {
that.pcs[peerID].username = 'Peer ' + nextPeerNumber();//TODO implement separate counter, as this will mess up numbers
else
that.pcs[peerID].username = username;
+ sendRTCMessage('username', that.pcs[peerID].username, peerID);
that.forceUpdate();
}
@@ -762,25 +771,41 @@ class presentationBroadcast extends React.Component {
}
function showCompleteTaskModal() {
+ let tmp = that;
+ if(tmp === undefined)
+ tmp = this;
+ tmp.showReopenModalButton = false;
swal({
title: 'Complete the given Task',
text: 'The presenter asked you to complete a task. As soon as you have completed the task, click on "Completed" and wait for the presenter to proceed.',
type: 'info',
confirmButtonColor: '#3085d6',
confirmButtonText: 'Completed',
+ showCancelButton: true,
+ cancelButtonText: 'Dismiss',
allowOutsideClick: false,
- allowEscapeKey: false
+ allowEscapeKey: false,
}).then(() => {
- sendRTCMessage('taskCompleted',undefined, that.presenterID);
+ tmp.sendRTCMessage('taskCompleted',undefined, tmp.presenterID);
+ }).catch((e) => {
+ if(e === 'cancel'){
+ tmp.showReopenModalButton = true;
+ tmp.forceUpdate();
+ }
});
+ tmp.forceUpdate();
}
+ that.showCompleteTaskModal = showCompleteTaskModal;
+
function checkUser(id) {
$('input#'+id).prop('checked', true);
}
function closeModal() {
+ that.showReopenModalButton = false;
swal.closeModal();
+ that.forceUpdate();
}
}
@@ -882,6 +907,7 @@ class presentationBroadcast extends React.Component {
sendRTCMessage={this.sendRTCMessage}
presenterID={this.presenterID}
myID={this.myID}
+ myName={this.myName}
pcs={this.pcs}
lastMessage={this.lastMessage} />
@@ -912,6 +938,10 @@ class presentationBroadcast extends React.Component {
) : (
)}
+ {(this.showReopenModalButton) ? (
+
+ ) : ''}
+
diff --git a/package.json b/package.json
index 06eb1805d..917ab0750 100644
--- a/package.json
+++ b/package.json
@@ -132,7 +132,7 @@
"superagent": "^3.5.0",
"superagent-csrf": "^1.0.0",
"superagent-csrf-middleware": "^0.3.0",
- "sweetalert2": "^6.4.2",
+ "sweetalert2": "^6.11.1",
"url-loader": "^0.5.8",
"uuid": "^3.0.1",
"virtual-dom": "2.1.1",
From 365bae13858f24c8f116e1d6816c670331ad2531 Mon Sep 17 00:00:00 2001
From: kadevgraaf
Date: Tue, 24 Oct 2017 17:37:23 +0200
Subject: [PATCH 070/111] Working - todo instead use Sole's modal for attaching
slides/decks
---
assets/images/templates/1.png | Bin 0 -> 7478 bytes
.../SlideEditPanel/SlideContentEditor.js | 19 ++++++++++++++----
2 files changed, 15 insertions(+), 4 deletions(-)
create mode 100644 assets/images/templates/1.png
diff --git a/assets/images/templates/1.png b/assets/images/templates/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..85de9548b91d3fea7c6daa69cf8a4d2653d09e00
GIT binary patch
literal 7478
zcmeI1X*kqx)W^q!lthV&82yD7Ls^S35|WUe8Dq_E5XNK~TMH$mEJ+NJb;dr1k*zG*
z_pyvY$~ugF=5L*6dilJ0-aYUBFMhx4zV7?F&V7FObJ^9Mh4Mta9Hqha(K4+3n>P;zxd88b$
z4MmBCU&bx^Mei{>2VAqB2z2$O8FET`;u#Ijcau|}O7qUF-M7J#j~6vya&h=Km%|ob
zC~Zm4f@AuW7dlOw$BK(Sw0>?5Y#}H@H#b$;n_d%PpElb4_7SF;n+tQxil4edg6<#e
zeG7U(NBd#!tS*HvZoNnWA3yk-n)Fn9!F>#gX9+
zJe4Onu7123U4PQR>e!hlS0S9!f@5xl&rfs8ox`2G_{J{V<-G7h)7?##svDby8a|uJxZ`b=pY^|IOM!h{L;pWUn5Lf8K@z7QS9va^FCWSJ$m9(Ikta-
z-rO~yf4Ee^|72}%?4g&o?OkQk;kUQ)CVA=Flsi>*S@ul%>yK$1q1|;AkAAFpS^1;Pg%F&-
z1=k1CT3@${Y(3rhW67%%meP^7P`VAjYwaU*S_hlTvFEXCanUgDK6d;0c660=y%hTD
zVOxC+JAvl&_iY+vEZAP}mjkyAj?B%?mVA@0iCFj;
z+-}h1e|tsi$=f6)Ge(W&R`x&1Msp_ae5R_V{DtMin!M4Kd4X97;QoQgNTGLy$?~An
z6dGtfq~cBG=@CHb4}NeIDpk6k@@aW7rr^S>pO50*Ors|dP+^X>sqKXAbh$cSewd8z
z=lxSn4H_FO`!cifRe+RB5s~fU-WU2hk56&j-g+@nL2edFd=Sb#Gt5|_H}1(j3p*TM
znr!jME`THO%BmTS(`x}FbqKQ<;2IasmW2s5e{6r`!_tQnz7MSZ0RTaXzYm+9$(3~g
z;6k6?eND4xY^#(L@61N(+c&0HR8ReA@Od*66_s_?(fXw1#%oW7770N!n=j70*UP1E
zlwvKD<)D`Z45F?Md~?N~ODeol8WSZJ^GYI%>q`uT?}8s(>M*)mJ|6VnMDsBYqs0m
zi_ULmD_4PBd{*C%^FhR%*}1SuL4{++?0jNn&v?ZmAf2qGYg;DeIb328`wdYDATsHU
zSeeQES4Z75kcg&PwFm(4nmEM%Yqx%D{67&aheG~3`F}c;#wWI}&9QPkH3baItEy6f
zKW!B_lC#Fa>jK>=M5K`1lbE(R;Z!eqlK6Tpke#%!`8iElR6W#cYrbb_<9B;QMQ4dV8o{^8EV1QcW&6cmJrh!q46iYMItBJA_c*nhs;
z9o8qnZm1j4q#}Zoe?rbsMJ*5Lg3R^xV|g>*zC9K0uIU^LPf1F0n{9b5q!Lijh|^6F
zJ*wW^^fO44(|&%Om((TOxH38(j@6khMMzlk-8NtYQWcteU-U%tf=XO5XbaL@acSvj
zU-*$nfx@uEt*C-uBXOm@U8LqyDDIV>@)R4+i2$UY)9kad2=*VVSPXvi6D+32q=p
z3Sy7W?7bMAj?X*`SECB{e!gQ$>Z#kEPe*rMYc{dAE^HSK={`Ot8@9oXv#!*sbm`dy
zR}K!EP=XdR)Mr+zpEy(n?|AfNs=;6cZ}YxPHDtR8YLT_0(OvdbuFqn=%YfDs%3BHY
z@TRCvs7Ceh$Vf($0|9JR2H9HZvvhLGQSutgGB38+^(`tY8r+=ga*j;$D&IHEP%+m}
zkxq<5S-Yui_iF`jw4VP!dAZ|S1BgVvYGiBZ9wJzn1_oC9`T2!%udr^l0aGKjxgVY_
zF=v7q6qNJxjp{4+WH+9Xkf8?~?P$+#VCQ$l#%$~IRDGT8vtJz30tQ}P62KFz2)*1q
zmwL7gQt|W#_a&J0&prAowJu0Rz_u~0-phBot!rDHgsl4tWzza+#SZ|p1aBI0En
zm{ea0c*t(~WiI(+Oz?pA`Hp$-)ve29
z>%j6