From da9b7b03688e40c0ed5b14ab0e33fb77b6d8b931 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 8 Sep 2014 13:54:09 -0700 Subject: Added big massive TODOs on a huge design problem with initial sync --- webclient/components/matrix/event-stream-service.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index 4c0091dedb..1bc850a8fa 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -105,6 +105,8 @@ angular.module('eventStreamService', []) var deferred = $q.defer(); // FIXME: We are discarding all the messages. + // XXX FIXME TODO : The discard works because we are doing this all over + // again on EVERY INSTANTIATION of the recents controller. matrixService.initialSync(1, false).then( function(response) { var rooms = response.data.rooms; -- cgit 1.4.1 From 324020d5fe212badfbd38137adc8dcecfdc15980 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 8 Sep 2014 15:36:52 -0700 Subject: Display the room topic in the room, underneath the name of the room. --- webclient/app.css | 21 +++++++++++++++------ .../components/matrix/event-handler-service.js | 11 +++++++++++ webclient/room/room.html | 9 +++++++-- 3 files changed, 33 insertions(+), 8 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app.css b/webclient/app.css index 19fae632ff..7c367df421 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -220,12 +220,6 @@ a:active { color: #000; } height: 100%; } -#roomName { - float: right; - font-size: 16px; - margin-top: 15px; -} - #roomHeader { margin: auto; padding-left: 20px; @@ -282,6 +276,21 @@ a:active { color: #000; } float: right; } +#roomName { + font-size: 16px; + text-align: right; +} + +#roomTopic { + text-align: right; + font-size: 13px; +} + +.roomHeaderInfo { + float: right; + margin-top: 15px; +} + /*** Participant list ***/ #usersTableWrapper { diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 173055a61b..a14e515999 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -148,6 +148,14 @@ angular.module('eventHandlerService', []) $rootScope.events.rooms[event.room_id][event.type] = event; $rootScope.$broadcast(NAME_EVENT, event, isLiveEvent); }; + + var handleRoomTopic = function(event, isLiveEvent) { + console.log("handleRoomTopic " + isLiveEvent); + + initRoom(event.room_id); + + $rootScope.events.rooms[event.room_id][event.type] = event; + }; var handleCallEvent = function(event, isLiveEvent) { $rootScope.$broadcast(CALL_EVENT, event, isLiveEvent); @@ -204,6 +212,9 @@ angular.module('eventHandlerService', []) case 'm.room.name': handleRoomName(event, isLiveEvent); break; + case 'm.room.topic': + handleRoomTopic(event, isLiveEvent); + break; default: console.log("Unable to handle event type " + event.type); console.log(JSON.stringify(event, undefined, 4)); diff --git a/webclient/room/room.html b/webclient/room/room.html index 5bd2cc92d5..4be2482f96 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -2,8 +2,13 @@
[matrix] -
- {{ room_id | mRoomName }} +
+
+ {{ room_id | mRoomName }} +
+
+ {{ events.rooms[room_id]['m.room.topic'].content.topic }} +
-- cgit 1.4.1 From f64cc237fc819116e78888fb1c542b1c8c08651a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 8 Sep 2014 17:27:51 -0700 Subject: Fixed bug which displayed an older room topic because it was being returned from /initialSync messages key. Check the ts of the event before clobbering state. --- webclient/components/matrix/event-handler-service.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index a14e515999..8232e3b4b0 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -150,10 +150,22 @@ angular.module('eventHandlerService', []) }; var handleRoomTopic = function(event, isLiveEvent) { - console.log("handleRoomTopic " + isLiveEvent); + console.log("handleRoomTopic live="+isLiveEvent); initRoom(event.room_id); + // live events always update, but non-live events only update if the + // ts is later. + if (!isLiveEvent) { + var eventTs = event.ts; + var storedEvent = $rootScope.events.rooms[event.room_id][event.type]; + if (storedEvent) { + if (storedEvent.ts > eventTs) { + // ignore it, we have a newer one already. + return; + } + } + } $rootScope.events.rooms[event.room_id][event.type] = event; }; -- cgit 1.4.1 From 6bdb23449a7b4f5ca0426ec6c942332b245eec30 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 8 Sep 2014 18:40:34 -0700 Subject: Add ability to set topic by double-clicking on the topic text then hitting enter. --- webclient/app.css | 5 +++++ .../components/matrix/event-handler-service.js | 1 + webclient/components/matrix/matrix-service.js | 19 ++++++++++++++++ webclient/room/room-controller.js | 25 ++++++++++++++++++++++ webclient/room/room.html | 10 ++++++++- 5 files changed, 59 insertions(+), 1 deletion(-) (limited to 'webclient/components') diff --git a/webclient/app.css b/webclient/app.css index 2f969641b4..9667f3fd22 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -297,9 +297,14 @@ a:active { color: #000; } font-size: 13px; } +.roomTopicInput { + width: 100%; +} + .roomHeaderInfo { float: right; margin-top: 15px; + width: 50%; } /*** Participant list ***/ diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 8232e3b4b0..5a3e92186e 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -149,6 +149,7 @@ angular.module('eventHandlerService', []) $rootScope.$broadcast(NAME_EVENT, event, isLiveEvent); }; + // TODO: Can this just be a generic "I am a room state event, can haz store?" var handleRoomTopic = function(event, isLiveEvent) { console.log("handleRoomTopic live="+isLiveEvent); diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 6864726ba4..62aff091d4 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -235,6 +235,25 @@ angular.module('matrixService', []) return doRequest("GET", path, undefined, {}); }, + + setTopic: function(room_id, topic) { + var data = { + topic: topic + }; + return this.sendStateEvent(room_id, "m.room.topic", data); + }, + + + sendStateEvent: function(room_id, eventType, content, state_key) { + var path = "/rooms/$room_id/state/"+eventType; + if (state_key !== undefined) { + path += "/" + state_key; + } + room_id = encodeURIComponent(room_id); + path = path.replace("$room_id", room_id); + + return doRequest("PUT", path, undefined, content); + }, sendEvent: function(room_id, eventType, txn_id, content) { // The REST path spec diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index c8ca771b25..10ff12a96b 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -42,6 +42,31 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) $scope.imageURLToSend = ""; $scope.userIDToInvite = ""; + // vars and functions for updating the topic + $scope.topic = { + isEditing: false, + newTopicText: "", + editTopic: function() { + if ($scope.topic.isEditing) { + console.log("Warning: Already editing topic."); + return; + } + $scope.topic.newTopicText = $rootScope.events.rooms[$scope.room_id]['m.room.topic'].content.topic; + $scope.topic.isEditing = true; + }, + updateTopic: function() { + console.log("Updating topic to "+$scope.topic.newTopicText); + matrixService.setTopic($scope.room_id, $scope.topic.newTopicText); + $scope.topic.isEditing = false; + }, + cancelEdit: function() { + $scope.topic.isEditing = false; + } + }; + + + + var scrollToBottom = function(force) { console.log("Scrolling to bottom"); diff --git a/webclient/room/room.html b/webclient/room/room.html index 4be2482f96..0fe45499e0 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -7,7 +7,15 @@ {{ room_id | mRoomName }}
- {{ events.rooms[room_id]['m.room.topic'].content.topic }} +
+ {{ events.rooms[room_id]['m.room.topic'].content.topic | limitTo: 200}} +
+ +
+ +
+
-- cgit 1.4.1 From 16b40cbede292ae0faa073d18b5ff2175a531744 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Sep 2014 11:45:36 +0100 Subject: Show call invites in the message table --- webclient/components/matrix/event-handler-service.js | 3 +++ webclient/room/room.html | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 5a3e92186e..94ac91db5e 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -172,6 +172,9 @@ angular.module('eventHandlerService', []) var handleCallEvent = function(event, isLiveEvent) { $rootScope.$broadcast(CALL_EVENT, event, isLiveEvent); + if (event.type == 'm.call.invite') { + $rootScope.events.rooms[event.room_id].messages.push(event); + } }; return { diff --git a/webclient/room/room.html b/webclient/room/room.html index 01f0c4ee33..5debeaba7c 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -97,6 +97,10 @@ + + Outgoing Call + Incoming Call +
-- cgit 1.4.1 From 472b4fe48cc97656c6ed50b817214ecad61dfcc5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Sep 2014 14:53:47 +0100 Subject: make calls work in Firefox --- webclient/components/matrix/matrix-call.js | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 4eaed89bcf..ae20b7650e 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -35,6 +35,20 @@ var forAllTracksOnStream = function(s, f) { forAllAudioTracksOnStream(s, f); } +navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; +window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection; // but not mozRTCPeerConnection because its interface is not compatible +window.RTCSessionDescription = window.RTCSessionDescription || window.webkitRTCSessionDescription || window.mozRTCSessionDescription; +window.RTCIceCandidate = window.RTCIceCandidate || window.webkitRTCIceCandidate || window.mozRTCIceCandidate; + +var createPeerConnection = function() { + var stunServer = 'stun:stun.l.google.com:19302'; + if (window.mozRTCPeerConnection) { + return new window.mozRTCPeerConnection({'url': stunServer}); + } else { + return new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]}); + } +} + angular.module('MatrixCall', []) .factory('MatrixCall', ['matrixService', 'matrixPhoneService', '$rootScope', function MatrixCallFactory(matrixService, matrixPhoneService, $rootScope) { var MatrixCall = function(room_id) { @@ -44,10 +58,6 @@ angular.module('MatrixCall', []) this.didConnect = false; } - navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; - - window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; - MatrixCall.prototype.placeCall = function() { self = this; matrixPhoneService.callPlaced(this); @@ -58,7 +68,7 @@ angular.module('MatrixCall', []) MatrixCall.prototype.initWithInvite = function(msg) { this.msg = msg; - this.peerConn = new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]}) + this.peerConn = createPeerConnection(); self= this; this.peerConn.oniceconnectionstatechange = function() { self.onIceConnectionStateChanged(); }; this.peerConn.onicecandidate = function(c) { self.gotLocalIceCandidate(c); }; @@ -79,12 +89,12 @@ angular.module('MatrixCall', []) MatrixCall.prototype.stopAllMedia = function() { if (this.localAVStream) { forAllTracksOnStream(this.localAVStream, function(t) { - t.stop(); + if (t.stop) t.stop(); }); } if (this.remoteAVStream) { forAllTracksOnStream(this.remoteAVStream, function(t) { - t.stop(); + if (t.stop) t.stop(); }); } }; @@ -93,6 +103,7 @@ angular.module('MatrixCall', []) console.trace("Ending call "+this.call_id); this.stopAllMedia(); + this.peerConn.close(); var content = { version: 0, @@ -108,7 +119,7 @@ angular.module('MatrixCall', []) for (var i = 0; i < audioTracks.length; i++) { audioTracks[i].enabled = true; } - this.peerConn = new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]}) + this.peerConn = createPeerConnection(); self = this; this.peerConn.oniceconnectionstatechange = function() { self.onIceConnectionStateChanged(); }; this.peerConn.onsignalingstatechange = function() { self.onSignallingStateChanged(); }; @@ -275,6 +286,7 @@ angular.module('MatrixCall', []) $rootScope.$apply(function() { self.state = 'ended'; self.stopAllMedia(); + this.peerConn.close(); self.onHangup(); }); }; @@ -289,6 +301,7 @@ angular.module('MatrixCall', []) MatrixCall.prototype.onHangupReceived = function() { this.state = 'ended'; this.stopAllMedia(); + this.peerConn.close(); this.onHangup(); }; -- cgit 1.4.1 From 253c327252a3455aa5ce3a3147e19406a4d67615 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Sep 2014 17:37:50 +0100 Subject: Don't play an engaged tone if we hang up locally. --- webclient/app-controller.js | 2 +- webclient/components/matrix/matrix-call.js | 11 ++++++++--- webclient/room/room-controller.js | 10 +++++++++- 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 20b5076727..4a57f66ef0 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -118,7 +118,7 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even angular.element('#ringAudio')[0].pause(); angular.element('#ringbackAudio')[0].pause(); angular.element('#callendAudio')[0].play(); - } else if (newVal == 'ended' && oldVal == 'invite_sent') { + } else if (newVal == 'ended' && oldVal == 'invite_sent' && $rootScope.currentCall.hangupParty == 'remote') { angular.element('#ringAudio')[0].pause(); angular.element('#ringbackAudio')[0].pause(); angular.element('#busyAudio')[0].play(); diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index ae20b7650e..aae00a3f77 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -58,12 +58,13 @@ angular.module('MatrixCall', []) this.didConnect = false; } - MatrixCall.prototype.placeCall = function() { + MatrixCall.prototype.placeCall = function(config) { self = this; matrixPhoneService.callPlaced(this); - navigator.getUserMedia({audio: true, video: false}, function(s) { self.gotUserMediaForInvite(s); }, function(e) { self.getUserMediaFailed(e); }); - self.state = 'wait_local_media'; + navigator.getUserMedia({audio: config.audio, video: config.video}, function(s) { self.gotUserMediaForInvite(s); }, function(e) { self.getUserMediaFailed(e); }); + this.state = 'wait_local_media'; this.direction = 'outbound'; + this.config = config; }; MatrixCall.prototype.initWithInvite = function(msg) { @@ -105,6 +106,8 @@ angular.module('MatrixCall', []) this.stopAllMedia(); this.peerConn.close(); + this.hangupParty = 'local'; + var content = { version: 0, call_id: this.call_id, @@ -285,6 +288,7 @@ angular.module('MatrixCall', []) self = this; $rootScope.$apply(function() { self.state = 'ended'; + this.hangupParty = 'remote'; self.stopAllMedia(); this.peerConn.close(); self.onHangup(); @@ -300,6 +304,7 @@ angular.module('MatrixCall', []) MatrixCall.prototype.onHangupReceived = function() { this.state = 'ended'; + this.hangupParty = 'remote'; this.stopAllMedia(); this.peerConn.close(); this.onHangup(); diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 59f64061cd..3d75ef5499 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -756,7 +756,15 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) var call = new MatrixCall($scope.room_id); call.onError = $rootScope.onCallError; call.onHangup = $rootScope.onCallHangup; - call.placeCall(); + call.placeCall({audio: true, video: false}); + $rootScope.currentCall = call; + }; + + $scope.startVideoCall = function() { + var call = new MatrixCall($scope.room_id); + call.onError = $rootScope.onCallError; + call.onHangup = $rootScope.onCallHangup; + call.placeCall({audio: true, video: true}); $rootScope.currentCall = call; }; -- cgit 1.4.1 From 25e96f82db21fe0216f748e53aadb8d9dac3da72 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Sep 2014 17:52:01 +0100 Subject: Don't break if you press the hangup button before allowing media permission. --- webclient/components/matrix/matrix-call.js | 2 +- webclient/index.html | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index aae00a3f77..ef35717da6 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -104,7 +104,7 @@ angular.module('MatrixCall', []) console.trace("Ending call "+this.call_id); this.stopAllMedia(); - this.peerConn.close(); + if (this.peerConn) this.peerConn.close(); this.hangupParty = 'local'; diff --git a/webclient/index.html b/webclient/index.html index 53ac1cb10e..3b531027e1 100644 --- a/webclient/index.html +++ b/webclient/index.html @@ -58,7 +58,8 @@ Calling... Call Connecting... Call Connected - Call Rejected + Call Rejected + Call Canceled Call Ended Call Canceled Call Ended -- cgit 1.4.1 From ccfb42e4ff612cab14318e16436984d2554a13c0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Sep 2014 17:58:26 +0100 Subject: Don't try setting up the call if the user has canceled it before allowing permission. --- webclient/components/matrix/matrix-call.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index ef35717da6..16f22fe364 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -117,6 +117,8 @@ angular.module('MatrixCall', []) }; MatrixCall.prototype.gotUserMediaForInvite = function(stream) { + if (!$rootScope.currentCall || $rootScope.currentCall.state == 'ended') return; + this.localAVStream = stream; var audioTracks = stream.getAudioTracks(); for (var i = 0; i < audioTracks.length; i++) { @@ -140,6 +142,8 @@ angular.module('MatrixCall', []) }; MatrixCall.prototype.gotUserMediaForAnswer = function(stream) { + if (!$rootScope.currentCall || $rootScope.currentCall.state == 'ended') return; + this.localAVStream = stream; var audioTracks = stream.getAudioTracks(); for (var i = 0; i < audioTracks.length; i++) { -- cgit 1.4.1 From f90ce04a8318d00df9db70e1b4cae500785a7d44 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 9 Sep 2014 18:21:03 +0100 Subject: Hangup call if user denies media access. --- webclient/app-controller.js | 8 -------- webclient/components/matrix/matrix-call.js | 2 ++ 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 4a57f66ef0..f28da87ccc 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -142,14 +142,6 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even $scope.hangupCall = function() { $rootScope.currentCall.hangup(); - - $timeout(function() { - var icon = angular.element('#callEndedIcon'); - $animate.addClass(icon, 'callIconRotate'); - $timeout(function(){ - $rootScope.currentCall = undefined; - }, 4070); - }, 100); }; $rootScope.onCallError = function(errStr) { diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 16f22fe364..feb113f60d 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -114,6 +114,7 @@ angular.module('MatrixCall', []) }; matrixService.sendEvent(this.room_id, 'm.call.hangup', undefined, content).then(this.messageSent, this.messageSendFailed); this.state = 'ended'; + self.onHangup(); }; MatrixCall.prototype.gotUserMediaForInvite = function(stream) { @@ -233,6 +234,7 @@ angular.module('MatrixCall', []) MatrixCall.prototype.getUserMediaFailed = function() { this.onError("Couldn't start capturing audio! Is your microphone set up?"); + this.hangup(); }; MatrixCall.prototype.onIceConnectionStateChanged = function() { -- cgit 1.4.1 From b63dd9506ea286f8bdaffd213fa79a382933eb35 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 10 Sep 2014 12:01:00 +0200 Subject: Improved requests: pagination is done from the data received in initialSync --- .../components/matrix/event-handler-service.js | 28 ++++++++++++++++++---- .../components/matrix/event-stream-service.js | 9 ++++++- webclient/room/room-controller.js | 16 ++++++------- 3 files changed, 38 insertions(+), 15 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 94ac91db5e..24d634a28b 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -55,6 +55,11 @@ angular.module('eventHandlerService', []) $rootScope.events.rooms[room_id] = {}; $rootScope.events.rooms[room_id].messages = []; $rootScope.events.rooms[room_id].members = {}; + + // Pagination information + $rootScope.events.rooms[room_id].pagination = { + earliest_token: "END" // how far back we've paginated + } } }; @@ -187,17 +192,21 @@ angular.module('eventHandlerService', []) NAME_EVENT: NAME_EVENT, handleEvent: function(event, isLiveEvent) { - // FIXME: event duplication suppression is all broken as the code currently expect to handles - // events multiple times to get their side-effects... -/* + // Avoid duplicated events + // Needed for rooms where initialSync has not been done. + // In this case, we do not know where to start pagination. So, it starts from the END + // and we can have the same event (ex: joined, invitation) coming from the pagination + // AND from the event stream. + // FIXME: This workaround should be no more required when /initialSync on a particular room + // will be available (as opposite to the global /initialSync done at startup) if (eventMap[event.event_id]) { - console.log("discarding duplicate event: " + JSON.stringify(event)); + console.log("discarding duplicate event: " + JSON.stringify(event, undefined, 4)); return; } else { eventMap[event.event_id] = 1; } -*/ + if (event.type.indexOf('m.call.') === 0) { handleCallEvent(event, isLiveEvent); } @@ -247,6 +256,15 @@ angular.module('eventHandlerService', []) } }, + // Handle messages from /initialSync or /messages + handleRoomMessages: function(room_id, messages, isLiveEvents) { + this.handleEvents(messages.chunk); + + // Store how far back we've paginated + // This assumes the paginations requests are contiguous and in reverse chronological order + $rootScope.events.rooms[room_id].pagination.earliest_token = messages.end; + }, + handleInitialSyncDone: function(initialSyncData) { console.log("# handleInitialSyncDone"); initialSyncDeferred.resolve(initialSyncData); diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index 1bc850a8fa..d7ccc63e89 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -112,9 +112,16 @@ angular.module('eventStreamService', []) var rooms = response.data.rooms; for (var i = 0; i < rooms.length; ++i) { var room = rooms[i]; + // console.log("got room: " + room.room_id); if ("state" in room) { - eventHandlerService.handleEvents(room.state, false); + //eventHandlerService.handleEvents(room.state, false); + } + + if ("messages" in room) { + eventHandlerService.handleRoomMessages(room.room_id, room.messages, false); + + console.log(room.messages.start + " - " + room.messages.end); } } diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 3d75ef5499..9bb0d8e2d4 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -27,8 +27,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) $scope.state = { user_id: matrixService.config().user_id, - events_from: "END", // when to start the event stream from. - earliest_token: "END", // stores how far back we've paginated. first_pagination: true, // this is toggled off when the first pagination is done can_paginate: false, // this is toggled off when we are not ready yet to paginate or when we run out of items paginating: false, // used to avoid concurrent pagination requests pulling in dup contents @@ -159,12 +157,15 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) else { $scope.state.paginating = true; } - // console.log("paginateBackMessages from " + $scope.state.earliest_token + " for " + numItems); + + console.log("paginateBackMessages from " + $rootScope.events.rooms[$scope.room_id].pagination.earliest_token + " for " + numItems); var originalTopRow = $("#messageTable>tbody>tr:first")[0]; - matrixService.paginateBackMessages($scope.room_id, $scope.state.earliest_token, numItems).then( + + // Paginate events from the point in cache + matrixService.paginateBackMessages($scope.room_id, $rootScope.events.rooms[$scope.room_id].pagination.earliest_token, numItems).then( function(response) { - eventHandlerService.handleEvents(response.data.chunk, false); - $scope.state.earliest_token = response.data.end; + + eventHandlerService.handleRoomMessages($scope.room_id, response.data, false); if (response.data.chunk.length < MESSAGES_PER_PAGINATION) { // no more messages to paginate. this currently never gets turned true again, as we never // expire paginated contents in the current implementation. @@ -659,9 +660,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) var onInit3 = function() { console.log("onInit3"); - - // TODO: We should be able to keep them - eventHandlerService.resetRoomMessages($scope.room_id); // Make recents highlight the current room $scope.recentsSelectedRoomID = $scope.room_id; -- cgit 1.4.1 From 55fe0d8adce870cc665f5e68f9cbfaf36ba2617a Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 10 Sep 2014 11:12:02 +0100 Subject: Less buggy rejection of calls when busy --- webclient/app-controller.js | 19 +++++++++++-------- webclient/components/matrix/matrix-call.js | 14 +++++++++----- 2 files changed, 20 insertions(+), 13 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app-controller.js b/webclient/app-controller.js index f8a0d8d35c..55397ed216 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -134,6 +134,7 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even if ($rootScope.currentCall && $rootScope.currentCall.state != 'ended') { console.trace("rejecting call because we're already in a call"); call.hangup(); + return; } call.onError = $scope.onCallError; call.onHangup = $scope.onCallHangup; @@ -152,13 +153,15 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even $scope.feedback = errStr; } - $rootScope.onCallHangup = function() { - $timeout(function() { - var icon = angular.element('#callEndedIcon'); - $animate.addClass(icon, 'callIconRotate'); - $timeout(function(){ - $rootScope.currentCall = undefined; - }, 4070); - }, 100); + $rootScope.onCallHangup = function(call) { + if (call == $rootScope.currentCall) { + $timeout(function() { + var icon = angular.element('#callEndedIcon'); + $animate.addClass(icon, 'callIconRotate'); + $timeout(function(){ + $rootScope.currentCall = undefined; + }, 4070); + }, 100); + } } }]); diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index feb113f60d..68bde78862 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -114,7 +114,7 @@ angular.module('MatrixCall', []) }; matrixService.sendEvent(this.room_id, 'm.call.hangup', undefined, content).then(this.messageSent, this.messageSendFailed); this.state = 'ended'; - self.onHangup(); + if (self.onHangup) self.onHangup(self); }; MatrixCall.prototype.gotUserMediaForInvite = function(stream) { @@ -178,6 +178,10 @@ angular.module('MatrixCall', []) MatrixCall.prototype.gotRemoteIceCandidate = function(cand) { console.trace("Got ICE candidate from remote: "+cand); + if (this.state == 'ended') { + console.trace("Ignoring remote ICE candidate because call has ended"); + return; + } var candidateObject = new RTCIceCandidate({ sdpMLineIndex: cand.label, candidate: cand.candidate @@ -294,10 +298,10 @@ angular.module('MatrixCall', []) self = this; $rootScope.$apply(function() { self.state = 'ended'; - this.hangupParty = 'remote'; + self.hangupParty = 'remote'; self.stopAllMedia(); - this.peerConn.close(); - self.onHangup(); + if (self.peerConn.signalingState != 'closed') self.peerConn.close(); + if (self.onHangup) self.onHangup(self); }); }; @@ -313,7 +317,7 @@ angular.module('MatrixCall', []) this.hangupParty = 'remote'; this.stopAllMedia(); this.peerConn.close(); - this.onHangup(); + if (this.onHangup) this.onHangup(self); }; return MatrixCall; -- cgit 1.4.1 From c2afc6cd0a4f375149956b11b471a8f5d840afde Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 10 Sep 2014 13:48:33 +0200 Subject: Presence events do not have event id. Do not discard them --- webclient/components/matrix/event-handler-service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 24d634a28b..14ac79dd8f 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -199,7 +199,7 @@ angular.module('eventHandlerService', []) // AND from the event stream. // FIXME: This workaround should be no more required when /initialSync on a particular room // will be available (as opposite to the global /initialSync done at startup) - if (eventMap[event.event_id]) { + if (event.event_id && eventMap[event.event_id]) { console.log("discarding duplicate event: " + JSON.stringify(event, undefined, 4)); return; } @@ -258,7 +258,7 @@ angular.module('eventHandlerService', []) // Handle messages from /initialSync or /messages handleRoomMessages: function(room_id, messages, isLiveEvents) { - this.handleEvents(messages.chunk); + this.handleEvents(messages.chunk, isLiveEvents); // Store how far back we've paginated // This assumes the paginations requests are contiguous and in reverse chronological order -- cgit 1.4.1 From b099634ba1dcc7bcc0e591fd511d7045aea4ef91 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 10 Sep 2014 14:36:30 +0200 Subject: Reenabled handle of room states events in initialSync but do not add them to the displayed messages in the room page. Show the m.room.member events only when they come from room.messages (from initialSync of pagination) not from room.state. --- webclient/components/matrix/event-handler-service.js | 15 +++++++++------ webclient/components/matrix/event-stream-service.js | 11 ++++------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 14ac79dd8f..d7705c8e3e 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -105,7 +105,7 @@ angular.module('eventHandlerService', []) $rootScope.$broadcast(MSG_EVENT, event, isLiveEvent); }; - var handleRoomMember = function(event, isLiveEvent) { + var handleRoomMember = function(event, isLiveEvent, isStateEvent) { initRoom(event.room_id); // if the server is stupidly re-relaying a no-op join, discard it. @@ -117,7 +117,10 @@ angular.module('eventHandlerService', []) } // add membership changes as if they were a room message if something interesting changed - if (event.content.prev !== event.content.membership) { + // Exception: Do not do this if the event is a room state event because such events already come + // as room messages events. Moreover, when they come as room messages events, they are relatively ordered + // with other other room messages + if (event.content.prev !== event.content.membership && !isStateEvent) { if (isLiveEvent) { $rootScope.events.rooms[event.room_id].messages.push(event); } @@ -191,7 +194,7 @@ angular.module('eventHandlerService', []) CALL_EVENT: CALL_EVENT, NAME_EVENT: NAME_EVENT, - handleEvent: function(event, isLiveEvent) { + handleEvent: function(event, isLiveEvent, isStateEvent) { // Avoid duplicated events // Needed for rooms where initialSync has not been done. // In this case, we do not know where to start pagination. So, it starts from the END @@ -222,7 +225,7 @@ angular.module('eventHandlerService', []) handleMessage(event, isLiveEvent); break; case "m.room.member": - handleRoomMember(event, isLiveEvent); + handleRoomMember(event, isLiveEvent, isStateEvent); break; case "m.presence": handlePresence(event, isLiveEvent); @@ -250,9 +253,9 @@ angular.module('eventHandlerService', []) // isLiveEvents determines whether notifications should be shown, whether // messages get appended to the start/end of lists, etc. - handleEvents: function(events, isLiveEvents) { + handleEvents: function(events, isLiveEvents, isStateEvents) { for (var i=0; i Date: Wed, 10 Sep 2014 14:45:32 +0200 Subject: dedup events: state events conflict with messages events. Do not consider them in deduplication --- webclient/components/matrix/event-handler-service.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index d7705c8e3e..80a15182ae 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -202,14 +202,16 @@ angular.module('eventHandlerService', []) // AND from the event stream. // FIXME: This workaround should be no more required when /initialSync on a particular room // will be available (as opposite to the global /initialSync done at startup) - if (event.event_id && eventMap[event.event_id]) { - console.log("discarding duplicate event: " + JSON.stringify(event, undefined, 4)); - return; - } - else { - eventMap[event.event_id] = 1; + if (!isStateEvent) { // Do not consider state events + if (event.event_id && eventMap[event.event_id]) { + console.log("discarding duplicate event: " + JSON.stringify(event, undefined, 4)); + return; + } + else { + eventMap[event.event_id] = 1; + } } - + if (event.type.indexOf('m.call.') === 0) { handleCallEvent(event, isLiveEvent); } -- cgit 1.4.1 From da3f842b8cea7d259d4d2d980020625b9703b1ee Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 10 Sep 2014 14:53:03 +0200 Subject: Removed wrong comments about recents-controller.js: it uses $rootScope.rooms not $rootScope.events.rooms managed by event-handler-service.js and used by other controllers --- .../components/matrix/event-stream-service.js | 5 ++--- webclient/recents/recents-controller.js | 22 ---------------------- 2 files changed, 2 insertions(+), 25 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index 249af193df..03b805213d 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -104,9 +104,7 @@ angular.module('eventStreamService', []) settings.isActive = true; var deferred = $q.defer(); - // FIXME: We are discarding all the messages. - // XXX FIXME TODO : The discard works because we are doing this all over - // again on EVERY INSTANTIATION of the recents controller. + // Initial sync: get all information and the last message of all rooms of the user matrixService.initialSync(1, false).then( function(response) { var rooms = response.data.rooms; @@ -128,6 +126,7 @@ angular.module('eventStreamService', []) // Initial sync is done eventHandlerService.handleInitialSyncDone(response); + // Start event streaming from that point settings.from = response.data.end; doEventStream(deferred); }, diff --git a/webclient/recents/recents-controller.js b/webclient/recents/recents-controller.js index 0553eb9be0..aedc7b7a49 100644 --- a/webclient/recents/recents-controller.js +++ b/webclient/recents/recents-controller.js @@ -16,12 +16,6 @@ 'use strict'; -// XXX FIXME TODO -// We should NOT be dumping things into $rootScope!!!! We should NOT be -// making any requests here, and should READ what is already in the -// rootScope from the event handler service!!! -// XXX FIXME TODO - angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHandlerService']) .controller('RecentsController', ['$rootScope', '$scope', 'matrixService', 'eventHandlerService', function($rootScope, $scope, matrixService, eventHandlerService) { @@ -33,11 +27,6 @@ angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHand // $rootScope of the parent where the recents component is included can override this value // in order to highlight a specific room in the list $rootScope.recentsSelectedRoomID; - - // XXX FIXME TODO : We should NOT be doing this here, which could be - // repeated for every controller instance. We should be doing this in - // event handler service instead. In additon, this will break if there - // isn't a recents controller visible when the last message comes in :/ var listenToEventStream = function() { // Refresh the list on matrix invitation and message event @@ -85,23 +74,12 @@ angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHand return; } - // XXX FIXME TODO - // We should NOT be dumping things into $rootScope!!!! We should NOT be - // making any requests here, and should READ what is already in the - // rootScope from the event handler service!!! - // XXX FIXME TODO - $rootScope.rooms = {}; // Use initialSync data to init the recents list eventHandlerService.waitForInitialSyncCompletion().then( function(initialSyncData) { - // XXX FIXME TODO: - // Any assignments to the rootScope here should be done in - // event handler service and not here, because we could have - // many controllers manipulating and clobbering each other, and - // are unecessarily repeating http requests. var rooms = initialSyncData.data.rooms; for (var i=0; i Date: Wed, 10 Sep 2014 16:26:11 +0200 Subject: Member event: store use the the latest one --- webclient/components/matrix/event-handler-service.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 80a15182ae..38b7bd6b6d 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -129,8 +129,13 @@ angular.module('eventHandlerService', []) } } - $rootScope.events.rooms[event.room_id].members[event.state_key] = event; - $rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent); + // Use data from state event or the latest data from the stream. + // Do not care of events that come when paginating back + if (isStateEvent || isLiveEvent) { + $rootScope.events.rooms[event.room_id].members[event.state_key] = event; + } + + $rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent, isStateEvent); }; var handlePresence = function(event, isLiveEvent) { -- cgit 1.4.1 From 5a06f5c5fcac58ecd36c0a3c186ed8639767dbe3 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 10 Sep 2014 18:24:03 +0200 Subject: Reenabled transparent echo message. It turns to opaque without flickering now. --- .../components/matrix/event-handler-service.js | 34 ++++++++++++++++++++-- webclient/room/room-controller.js | 25 +++++----------- 2 files changed, 40 insertions(+), 19 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 38b7bd6b6d..277faa6f77 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -86,8 +86,15 @@ angular.module('eventHandlerService', []) if (isLiveEvent) { if (event.user_id === matrixService.config().user_id && (event.content.msgtype === "m.text" || event.content.msgtype === "m.emote") ) { - // assume we've already echoed it - // FIXME: track events by ID and ungrey the right message to show it's been delivered + // Assume we've already echoed it. So, there is a fake event in the messages list of the room + // Replace this fake event by the true one + var index = getRoomEventIndex(event.room_id, event.event_id); + if (index) { + $rootScope.events.rooms[event.room_id].messages[index] = event; + } + else { + $rootScope.events.rooms[event.room_id].messages.push(event); + } } else { $rootScope.events.rooms[event.room_id].messages.push(event); @@ -190,6 +197,29 @@ angular.module('eventHandlerService', []) } }; + /** + * Get the index of the event in $rootScope.events.rooms[room_id].messages + * @param {type} room_id the room id + * @param {type} event_id the event id to look for + * @returns {Number | undefined} the index. undefined if not found. + */ + var getRoomEventIndex = function(room_id, event_id) { + var index; + + var room = $rootScope.events.rooms[room_id]; + if (room) { + for (var i = 0; i < room.messages.length; i++) { + var message = room.messages[i]; + console.log(message.event_id); + if (event_id === message.event_id) { + index = i; + break; + } + } + } + return index; + } + return { ROOM_CREATE_EVENT: ROOM_CREATE_EVENT, MSG_EVENT: MSG_EVENT, diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 171d4c0d99..0000fcfc61 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -513,8 +513,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) room_id: $scope.room_id, type: "m.room.message", user_id: $scope.state.user_id, - // FIXME: re-enable echo_msg_state when we have a nice way to turn the field off again - // echo_msg_state: "messagePending" // Add custom field to indicate the state of this fake message to HTML + echo_msg_state: "messagePending" // Add custom field to indicate the state of this fake message to HTML }; $scope.textInput = ""; @@ -527,25 +526,17 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) $scope.feedback = ""; promise.then( - function() { + function(response) { console.log("Request successfully sent"); - if (!echo) { - $scope.textInput = ""; - } -/* - if (echoMessage) { - // Remove the fake echo message from the room messages - // It will be replaced by the one acknowledged by the server - // ...except this causes a nasty flicker. So don't swap messages for now. --matthew - // var index = $rootScope.events.rooms[$scope.room_id].messages.indexOf(echoMessage); - // if (index > -1) { - // $rootScope.events.rooms[$scope.room_id].messages.splice(index, 1); - // } + if (echo) { + // Mark this fake message event with its allocated event_id + // When the true message event will come from the events stream (in handleMessage), + // we will be able to replace the fake one by the true one + echoMessage.event_id = response.data.event_id; } else { $scope.textInput = ""; - } -*/ + } }, function(error) { $scope.feedback = "Request failed: " + error.data.error; -- cgit 1.4.1 From 8dcb6f24b518d668290d6a85493ff4994ddce378 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Thu, 11 Sep 2014 09:11:24 +0200 Subject: getRoomEventIndex: improved speed for what it is used --- webclient/components/matrix/event-handler-service.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 277faa6f77..b19ec27a9d 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -205,12 +205,13 @@ angular.module('eventHandlerService', []) */ var getRoomEventIndex = function(room_id, event_id) { var index; - + var room = $rootScope.events.rooms[room_id]; if (room) { - for (var i = 0; i < room.messages.length; i++) { + // Start looking from the tail since the first goal of this function + // is to find a messaged among the latest ones + for (var i = room.messages.length - 1; i > 0; i--) { var message = room.messages[i]; - console.log(message.event_id); if (event_id === message.event_id) { index = i; break; -- cgit 1.4.1 From 7e7eb0efc14733b576942af4590683c8b749e94e Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Thu, 11 Sep 2014 11:31:24 +0200 Subject: Show room topic change in the chat history and in the recents --- .../components/matrix/event-handler-service.js | 25 ++++++++++++++++++---- webclient/recents/recents-controller.js | 5 +++++ webclient/recents/recents.html | 4 ++++ webclient/room/room.html | 5 +++++ 4 files changed, 35 insertions(+), 4 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index b19ec27a9d..002a9fbd5d 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -35,6 +35,7 @@ angular.module('eventHandlerService', []) var POWERLEVEL_EVENT = "POWERLEVEL_EVENT"; var CALL_EVENT = "CALL_EVENT"; var NAME_EVENT = "NAME_EVENT"; + var TOPIC_EVENT = "TOPIC_EVENT"; var initialSyncDeferred = $q.defer(); @@ -170,24 +171,39 @@ angular.module('eventHandlerService', []) }; // TODO: Can this just be a generic "I am a room state event, can haz store?" - var handleRoomTopic = function(event, isLiveEvent) { + var handleRoomTopic = function(event, isLiveEvent, isStateEvent) { console.log("handleRoomTopic live="+isLiveEvent); initRoom(event.room_id); + // Add topic changes as if they were a room message + if (!isStateEvent) { + if (isLiveEvent) { + $rootScope.events.rooms[event.room_id].messages.push(event); + } + else { + $rootScope.events.rooms[event.room_id].messages.unshift(event); + } + } + // live events always update, but non-live events only update if the // ts is later. + var latestData = true; if (!isLiveEvent) { var eventTs = event.ts; var storedEvent = $rootScope.events.rooms[event.room_id][event.type]; if (storedEvent) { if (storedEvent.ts > eventTs) { // ignore it, we have a newer one already. - return; + latestData = false; } } } - $rootScope.events.rooms[event.room_id][event.type] = event; + if (latestData) { + $rootScope.events.rooms[event.room_id][event.type] = event; + } + + $rootScope.$broadcast(TOPIC_EVENT, event, isLiveEvent); }; var handleCallEvent = function(event, isLiveEvent) { @@ -229,6 +245,7 @@ angular.module('eventHandlerService', []) POWERLEVEL_EVENT: POWERLEVEL_EVENT, CALL_EVENT: CALL_EVENT, NAME_EVENT: NAME_EVENT, + TOPIC_EVENT: TOPIC_EVENT, handleEvent: function(event, isLiveEvent, isStateEvent) { // Avoid duplicated events @@ -279,7 +296,7 @@ angular.module('eventHandlerService', []) handleRoomName(event, isLiveEvent); break; case 'm.room.topic': - handleRoomTopic(event, isLiveEvent); + handleRoomTopic(event, isLiveEvent, isStateEvent); break; default: console.log("Unable to handle event type " + event.type); diff --git a/webclient/recents/recents-controller.js b/webclient/recents/recents-controller.js index 5cf74cad4e..8ce0969164 100644 --- a/webclient/recents/recents-controller.js +++ b/webclient/recents/recents-controller.js @@ -68,6 +68,11 @@ angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHand $rootScope.rooms[event.room_id] = event; } }); + $rootScope.$on(eventHandlerService.TOPIC_EVENT, function(ngEvent, event, isLive) { + if (isLive) { + $rootScope.rooms[event.room_id].lastMsg = event; + } + }); }; /** diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html index d6bea52cbe..6976bab879 100644 --- a/webclient/recents/recents.html +++ b/webclient/recents/recents.html @@ -76,6 +76,10 @@
+
+ {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} changed the topic to: {{ room.lastMsg.content.topic }} +
+
Call diff --git a/webclient/room/room.html b/webclient/room/room.html index 054b876f81..dba6586e00 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -113,6 +113,11 @@ ng-click="$parent.fullScreenImageURL = msg.content.url"/>
+ + + {{ members[msg.user_id].displayname || msg.user_id }} changed the topic to: {{ msg.content.topic }} + + -- cgit 1.4.1 From fb082cf50ff1c48f651eb46fdf7398bacb85ad16 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Sep 2014 15:23:06 +0100 Subject: start towards glare support (currently not much better but no worse than before) including fixing a lot of self/var self/this fails that caused chaos when we started to have more than one call in play. --- webclient/app-controller.js | 13 ++- webclient/components/matrix/matrix-call.js | 125 ++++++++++++--------- .../components/matrix/matrix-phone-service.js | 38 ++++++- 3 files changed, 116 insertions(+), 60 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 8383533cfb..945f71ed20 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -135,9 +135,9 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even }); $rootScope.$on(matrixPhoneService.INCOMING_CALL_EVENT, function(ngEvent, call) { - console.trace("incoming call"); + console.log("incoming call"); if ($rootScope.currentCall && $rootScope.currentCall.state != 'ended') { - console.trace("rejecting call because we're already in a call"); + console.log("rejecting call because we're already in a call"); call.hangup(); return; } @@ -146,6 +146,13 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even $rootScope.currentCall = call; }); + $rootScope.$on(matrixPhoneService.REPLACED_CALL_EVENT, function(ngEvent, oldCall, newCall) { + console.log("call ID "+oldCall+" has been replaced by call ID "+newCall+"!"); + newCall.onError = $scope.onCallError; + newCall.onHangup = $scope.onCallHangup; + $rootScope.currentCall = newCall; + }); + $scope.answerCall = function() { $rootScope.currentCall.answer(); }; @@ -161,7 +168,7 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even $rootScope.onCallHangup = function(call) { if (call == $rootScope.currentCall) { $timeout(function(){ - $rootScope.currentCall = undefined; + if (call == $rootScope.currentCall) $rootScope.currentCall = undefined; }, 4070); } } diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 68bde78862..3a692ccf5c 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -40,15 +40,6 @@ window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConne window.RTCSessionDescription = window.RTCSessionDescription || window.webkitRTCSessionDescription || window.mozRTCSessionDescription; window.RTCIceCandidate = window.RTCIceCandidate || window.webkitRTCIceCandidate || window.mozRTCIceCandidate; -var createPeerConnection = function() { - var stunServer = 'stun:stun.l.google.com:19302'; - if (window.mozRTCPeerConnection) { - return new window.mozRTCPeerConnection({'url': stunServer}); - } else { - return new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]}); - } -} - angular.module('MatrixCall', []) .factory('MatrixCall', ['matrixService', 'matrixPhoneService', '$rootScope', function MatrixCallFactory(matrixService, matrixPhoneService, $rootScope) { var MatrixCall = function(room_id) { @@ -58,8 +49,24 @@ angular.module('MatrixCall', []) this.didConnect = false; } + MatrixCall.prototype.createPeerConnection = function() { + var stunServer = 'stun:stun.l.google.com:19302'; + var pc; + if (window.mozRTCPeerConnection) { + pc = window.mozRTCPeerConnection({'url': stunServer}); + } else { + pc = new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]}); + } + var self = this; + pc.oniceconnectionstatechange = function() { self.onIceConnectionStateChanged(); }; + pc.onsignalingstatechange = function() { self.onSignallingStateChanged(); }; + pc.onicecandidate = function(c) { self.gotLocalIceCandidate(c); }; + pc.onaddstream = function(s) { self.onAddStream(s); }; + return pc; + } + MatrixCall.prototype.placeCall = function(config) { - self = this; + var self = this; matrixPhoneService.callPlaced(this); navigator.getUserMedia({audio: config.audio, video: config.video}, function(s) { self.gotUserMediaForInvite(s); }, function(e) { self.getUserMediaFailed(e); }); this.state = 'wait_local_media'; @@ -69,22 +76,23 @@ angular.module('MatrixCall', []) MatrixCall.prototype.initWithInvite = function(msg) { this.msg = msg; - this.peerConn = createPeerConnection(); - self= this; - this.peerConn.oniceconnectionstatechange = function() { self.onIceConnectionStateChanged(); }; - this.peerConn.onicecandidate = function(c) { self.gotLocalIceCandidate(c); }; - this.peerConn.onsignalingstatechange = function() { self.onSignallingStateChanged(); }; - this.peerConn.onaddstream = function(s) { self.onAddStream(s); }; - this.peerConn.setRemoteDescription(new RTCSessionDescription(this.msg.offer), self.onSetRemoteDescriptionSuccess, self.onSetRemoteDescriptionError); + this.peerConn = this.createPeerConnection(); + this.peerConn.setRemoteDescription(new RTCSessionDescription(this.msg.offer), this.onSetRemoteDescriptionSuccess, this.onSetRemoteDescriptionError); this.state = 'ringing'; this.direction = 'inbound'; }; MatrixCall.prototype.answer = function() { - console.trace("Answering call "+this.call_id); - self = this; - navigator.getUserMedia({audio: true, video: false}, function(s) { self.gotUserMediaForAnswer(s); }, function(e) { self.getUserMediaFailed(e); }); - this.state = 'wait_local_media'; + console.log("Answering call "+this.call_id); + var self = this; + if (!this.localAVStream && !this.waitForLocalAVStream) { + navigator.getUserMedia({audio: true, video: false}, function(s) { self.gotUserMediaForAnswer(s); }, function(e) { self.getUserMediaFailed(e); }); + this.state = 'wait_local_media'; + } else if (this.localAVStream) { + this.gotUserMediaForAnswer(this.localAVStream); + } else if (this.waitForLocalAVStream) { + this.state = 'wait_local_media'; + } }; MatrixCall.prototype.stopAllMedia = function() { @@ -100,8 +108,8 @@ angular.module('MatrixCall', []) } }; - MatrixCall.prototype.hangup = function() { - console.trace("Ending call "+this.call_id); + MatrixCall.prototype.hangup = function(suppressEvent) { + console.log("Ending call "+this.call_id); this.stopAllMedia(); if (this.peerConn) this.peerConn.close(); @@ -114,24 +122,23 @@ angular.module('MatrixCall', []) }; matrixService.sendEvent(this.room_id, 'm.call.hangup', undefined, content).then(this.messageSent, this.messageSendFailed); this.state = 'ended'; - if (self.onHangup) self.onHangup(self); + if (this.onHangup && !suppressEvent) this.onHangup(this); }; MatrixCall.prototype.gotUserMediaForInvite = function(stream) { - if (!$rootScope.currentCall || $rootScope.currentCall.state == 'ended') return; + if (this.successor) { + this.successor.gotUserMediaForAnswer(stream); + return; + } + if (this.state == 'ended') return; this.localAVStream = stream; var audioTracks = stream.getAudioTracks(); for (var i = 0; i < audioTracks.length; i++) { audioTracks[i].enabled = true; } - this.peerConn = createPeerConnection(); - self = this; - this.peerConn.oniceconnectionstatechange = function() { self.onIceConnectionStateChanged(); }; - this.peerConn.onsignalingstatechange = function() { self.onSignallingStateChanged(); }; - this.peerConn.onicecandidate = function(c) { self.gotLocalIceCandidate(c); }; - this.peerConn.onaddstream = function(s) { self.onAddStream(s); }; - this.peerConn.addStream(stream); + this.peerConn = this.createPeerConnection(); + var self = this; this.peerConn.createOffer(function(d) { self.gotLocalOffer(d); }, function(e) { @@ -143,7 +150,7 @@ angular.module('MatrixCall', []) }; MatrixCall.prototype.gotUserMediaForAnswer = function(stream) { - if (!$rootScope.currentCall || $rootScope.currentCall.state == 'ended') return; + if (this.state == 'ended') return; this.localAVStream = stream; var audioTracks = stream.getAudioTracks(); @@ -151,7 +158,7 @@ angular.module('MatrixCall', []) audioTracks[i].enabled = true; } this.peerConn.addStream(stream); - self = this; + var self = this; var constraints = { 'mandatory': { 'OfferToReceiveAudio': true, @@ -165,7 +172,7 @@ angular.module('MatrixCall', []) }; MatrixCall.prototype.gotLocalIceCandidate = function(event) { - console.trace(event); + console.log(event); if (event.candidate) { var content = { version: 0, @@ -177,9 +184,9 @@ angular.module('MatrixCall', []) } MatrixCall.prototype.gotRemoteIceCandidate = function(cand) { - console.trace("Got ICE candidate from remote: "+cand); + console.log("Got ICE candidate from remote: "+cand); if (this.state == 'ended') { - console.trace("Ignoring remote ICE candidate because call has ended"); + console.log("Ignoring remote ICE candidate because call has ended"); return; } var candidateObject = new RTCIceCandidate({ @@ -190,12 +197,12 @@ angular.module('MatrixCall', []) }; MatrixCall.prototype.receivedAnswer = function(msg) { - this.peerConn.setRemoteDescription(new RTCSessionDescription(msg.answer), self.onSetRemoteDescriptionSuccess, self.onSetRemoteDescriptionError); + this.peerConn.setRemoteDescription(new RTCSessionDescription(msg.answer), this.onSetRemoteDescriptionSuccess, this.onSetRemoteDescriptionError); this.state = 'connecting'; }; MatrixCall.prototype.gotLocalOffer = function(description) { - console.trace("Created offer: "+description); + console.log("Created offer: "+description); this.peerConn.setLocalDescription(description); var content = { @@ -205,14 +212,14 @@ angular.module('MatrixCall', []) }; matrixService.sendEvent(this.room_id, 'm.call.invite', undefined, content).then(this.messageSent, this.messageSendFailed); - self = this; + var self = this; $rootScope.$apply(function() { self.state = 'invite_sent'; }); }; MatrixCall.prototype.createdAnswer = function(description) { - console.trace("Created answer: "+description); + console.log("Created answer: "+description); this.peerConn.setLocalDescription(description); var content = { version: 0, @@ -220,7 +227,7 @@ angular.module('MatrixCall', []) answer: description }; matrixService.sendEvent(this.room_id, 'm.call.answer', undefined, content).then(this.messageSent, this.messageSendFailed); - self = this; + var self = this; $rootScope.$apply(function() { self.state = 'connecting'; }); @@ -243,10 +250,10 @@ angular.module('MatrixCall', []) MatrixCall.prototype.onIceConnectionStateChanged = function() { if (this.state == 'ended') return; // because ICE can still complete as we're ending the call - console.trace("Ice connection state changed to: "+this.peerConn.iceConnectionState); + console.log("Ice connection state changed to: "+this.peerConn.iceConnectionState); // ideally we'd consider the call to be connected when we get media but chrome doesn't implement nay of the 'onstarted' events yet if (this.peerConn.iceConnectionState == 'completed' || this.peerConn.iceConnectionState == 'connected') { - self = this; + var self = this; $rootScope.$apply(function() { self.state = 'connected'; self.didConnect = true; @@ -255,19 +262,19 @@ angular.module('MatrixCall', []) }; MatrixCall.prototype.onSignallingStateChanged = function() { - console.trace("Signalling state changed to: "+this.peerConn.signalingState); + console.log("call "+this.call_id+": Signalling state changed to: "+this.peerConn.signalingState); }; MatrixCall.prototype.onSetRemoteDescriptionSuccess = function() { - console.trace("Set remote description"); + console.log("Set remote description"); }; MatrixCall.prototype.onSetRemoteDescriptionError = function(e) { - console.trace("Failed to set remote description"+e); + console.log("Failed to set remote description"+e); }; MatrixCall.prototype.onAddStream = function(event) { - console.trace("Stream added"+event); + console.log("Stream added"+event); var s = event.stream; @@ -288,14 +295,15 @@ angular.module('MatrixCall', []) }; MatrixCall.prototype.onRemoteStreamStarted = function(event) { - self = this; + var self = this; $rootScope.$apply(function() { self.state = 'connected'; }); }; MatrixCall.prototype.onRemoteStreamEnded = function(event) { - self = this; + console.log("Remote stream ended"); + var self = this; $rootScope.$apply(function() { self.state = 'ended'; self.hangupParty = 'remote'; @@ -306,18 +314,31 @@ angular.module('MatrixCall', []) }; MatrixCall.prototype.onRemoteStreamTrackStarted = function(event) { - self = this; + var self = this; $rootScope.$apply(function() { self.state = 'connected'; }); }; MatrixCall.prototype.onHangupReceived = function() { + console.log("Hangup received"); this.state = 'ended'; this.hangupParty = 'remote'; this.stopAllMedia(); this.peerConn.close(); - if (this.onHangup) this.onHangup(self); + if (this.onHangup) this.onHangup(this); + }; + + MatrixCall.prototype.replacedBy = function(newCall) { + if (this.state == 'wait_local_media') { + newCall.waitForLocalAVStream = true; + } else if (this.state == 'create_offer') { + newCall.localAVStream = this.localAVStream; + } else if (this.state == 'invite_sent') { + newCall.localAVStream = this.localAVStream; + } + this.successor = newCall; + this.hangup(true); }; return MatrixCall; diff --git a/webclient/components/matrix/matrix-phone-service.js b/webclient/components/matrix/matrix-phone-service.js index ca86b473e7..26be4bf2ad 100644 --- a/webclient/components/matrix/matrix-phone-service.js +++ b/webclient/components/matrix/matrix-phone-service.js @@ -22,6 +22,7 @@ angular.module('matrixPhoneService', []) }; matrixPhoneService.INCOMING_CALL_EVENT = "INCOMING_CALL_EVENT"; + matrixPhoneService.REPLACED_CALL_EVENT = "REPLACED_CALL_EVENT"; matrixPhoneService.allCalls = {}; matrixPhoneService.callPlaced = function(call) { @@ -38,29 +39,56 @@ angular.module('matrixPhoneService', []) call.call_id = msg.call_id; call.initWithInvite(msg); matrixPhoneService.allCalls[call.call_id] = call; - $rootScope.$broadcast(matrixPhoneService.INCOMING_CALL_EVENT, call); + + // Were we trying to call that user (room)? + var existingCall; + var callIds = Object.keys(matrixPhoneService.allCalls); + for (var i = 0; i < callIds.length; ++i) { + var thisCallId = callIds[i]; + var thisCall = matrixPhoneService.allCalls[thisCallId]; + + if (call.room_id == thisCall.room_id && thisCall.direction == 'outbound' + && (thisCall.state == 'wait_local_media' || thisCall.state == 'invite_sent' || thisCall.state == 'create_offer')) { + existingCall = thisCall; + break; + } + } + + if (existingCall) { + if (existingCall.call_id < call.call_id) { + console.log("Glare detected: rejecting incoming call "+call.call_id+" and keeping outgoing call "+existingCall.call_id); + call.hangup(); + } else { + console.log("Glare detected: answering incoming call "+call.call_id+" and canceling outgoing call "+existingCall.call_id); + existingCall.replacedBy(call); + call.answer(); + $rootScope.$broadcast(matrixPhoneService.REPLACED_CALL_EVENT, existingCall, call); + } + } else { + $rootScope.$broadcast(matrixPhoneService.INCOMING_CALL_EVENT, call); + } } else if (event.type == 'm.call.answer') { var call = matrixPhoneService.allCalls[msg.call_id]; if (!call) { - console.trace("Got answer for unknown call ID "+msg.call_id); + console.log("Got answer for unknown call ID "+msg.call_id); return; } call.receivedAnswer(msg); } else if (event.type == 'm.call.candidate') { var call = matrixPhoneService.allCalls[msg.call_id]; if (!call) { - console.trace("Got candidate for unknown call ID "+msg.call_id); + console.log("Got candidate for unknown call ID "+msg.call_id); return; } call.gotRemoteIceCandidate(msg.candidate); } else if (event.type == 'm.call.hangup') { var call = matrixPhoneService.allCalls[msg.call_id]; if (!call) { - console.trace("Got hangup for unknown call ID "+msg.call_id); + console.log("Got hangup for unknown call ID "+msg.call_id); return; } call.onHangupReceived(); - matrixPhoneService.allCalls[msg.call_id] = undefined; + delete(matrixPhoneService.allCalls[msg.call_id]); } }); -- cgit 1.4.1 From 81d061e74e9152c8fb4c340dd5b06329518f3299 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Sep 2014 17:40:38 +0100 Subject: Fix bug where web client wold break trying to add the earliest token without having initialised the room if your first page of history contained only events which didn't call initRoom. Just call initRoom in handleMessages since we use it there rather than leaving it to the individual event handling methods. --- webclient/components/matrix/event-handler-service.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 002a9fbd5d..0c86af1880 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -71,8 +71,6 @@ angular.module('eventHandlerService', []) }; var handleRoomCreate = function(event, isLiveEvent) { - initRoom(event.room_id); - // For now, we do not use the event data. Simply signal it to the app controllers $rootScope.$broadcast(ROOM_CREATE_EVENT, event, isLiveEvent); }; @@ -82,8 +80,6 @@ angular.module('eventHandlerService', []) }; var handleMessage = function(event, isLiveEvent) { - initRoom(event.room_id); - if (isLiveEvent) { if (event.user_id === matrixService.config().user_id && (event.content.msgtype === "m.text" || event.content.msgtype === "m.emote") ) { @@ -114,8 +110,6 @@ angular.module('eventHandlerService', []) }; var handleRoomMember = function(event, isLiveEvent, isStateEvent) { - initRoom(event.room_id); - // if the server is stupidly re-relaying a no-op join, discard it. if (event.prev_content && event.content.membership === "join" && @@ -152,8 +146,6 @@ angular.module('eventHandlerService', []) }; var handlePowerLevels = function(event, isLiveEvent) { - initRoom(event.room_id); - // Keep the latest data. Do not care of events that come when paginating back if (!$rootScope.events.rooms[event.room_id][event.type] || isLiveEvent) { $rootScope.events.rooms[event.room_id][event.type] = event; @@ -164,8 +156,6 @@ angular.module('eventHandlerService', []) var handleRoomName = function(event, isLiveEvent) { console.log("handleRoomName " + isLiveEvent); - initRoom(event.room_id); - $rootScope.events.rooms[event.room_id][event.type] = event; $rootScope.$broadcast(NAME_EVENT, event, isLiveEvent); }; @@ -174,8 +164,6 @@ angular.module('eventHandlerService', []) var handleRoomTopic = function(event, isLiveEvent, isStateEvent) { console.log("handleRoomTopic live="+isLiveEvent); - initRoom(event.room_id); - // Add topic changes as if they were a room message if (!isStateEvent) { if (isLiveEvent) { @@ -316,6 +304,7 @@ angular.module('eventHandlerService', []) // Handle messages from /initialSync or /messages handleRoomMessages: function(room_id, messages, isLiveEvents) { + initRoom(room_id); this.handleEvents(messages.chunk, isLiveEvents); // Store how far back we've paginated -- cgit 1.4.1 From 1e05e30472de1d115e655390ada4fa7a01271b3e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Sep 2014 18:59:22 +0100 Subject: Put back the line that adds the stream to the invite, otherwise caller->callee audio won't work... --- webclient/components/matrix/matrix-call.js | 1 + 1 file changed, 1 insertion(+) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 3a692ccf5c..2191bdab39 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -138,6 +138,7 @@ angular.module('MatrixCall', []) audioTracks[i].enabled = true; } this.peerConn = this.createPeerConnection(); + this.peerConn.addStream(stream); var self = this; this.peerConn.createOffer(function(d) { self.gotLocalOffer(d); -- cgit 1.4.1 From a059ca6915d05f5a5561965d9738cfaf3e20d4a1 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Sep 2014 19:16:57 +0100 Subject: few fixes for errors in glare conditions. still seem to end up with no audio if both calls are placed at the same time. --- webclient/app-controller.js | 2 +- webclient/components/matrix/matrix-call.js | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 945f71ed20..1d38b84e8e 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -147,7 +147,7 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even }); $rootScope.$on(matrixPhoneService.REPLACED_CALL_EVENT, function(ngEvent, oldCall, newCall) { - console.log("call ID "+oldCall+" has been replaced by call ID "+newCall+"!"); + console.log("call ID "+oldCall.call_id+" has been replaced by call ID "+newCall.call_id+"!"); newCall.onError = $scope.onCallError; newCall.onHangup = $scope.onCallHangup; $rootScope.currentCall = newCall; diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 2191bdab39..7407c6fd7c 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -167,9 +167,8 @@ angular.module('MatrixCall', []) }, }; this.peerConn.createAnswer(function(d) { self.createdAnswer(d); }, function(e) {}, constraints); - $rootScope.$apply(function() { - self.state = 'create_answer'; - }); + // This can't be in an apply() because it's called by a predecessor call under glare conditions :( + self.state = 'create_answer'; }; MatrixCall.prototype.gotLocalIceCandidate = function(event) { @@ -326,7 +325,7 @@ angular.module('MatrixCall', []) this.state = 'ended'; this.hangupParty = 'remote'; this.stopAllMedia(); - this.peerConn.close(); + if (this.peerConn.signalingState != 'closed') this.peerConn.close(); if (this.onHangup) this.onHangup(this); }; -- cgit 1.4.1 From 8b3ce851836b8bc2f3b8908b7abe20b7d1a37953 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 12 Sep 2014 08:54:18 +0200 Subject: BF: temp workaround while /initialSync on a particular room is not available initRoom on a new room is not called. Call it for any received events --- webclient/components/matrix/event-handler-service.js | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 0c86af1880..5aabc899b8 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -236,6 +236,11 @@ angular.module('eventHandlerService', []) TOPIC_EVENT: TOPIC_EVENT, handleEvent: function(event, isLiveEvent, isStateEvent) { + + // FIXME: /initialSync on a particular room is not yet available + // So initRoom on a new room is not called. Make sure the room data is initialised here + initRoom(event.room_id); + // Avoid duplicated events // Needed for rooms where initialSync has not been done. // In this case, we do not know where to start pagination. So, it starts from the END -- cgit 1.4.1 From 6ea20f3503d56457d6b027e2404a701377a69e76 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 12 Sep 2014 10:12:56 +0200 Subject: Show room name updates in room history and recents. Update it with the latest value --- .../components/matrix/event-handler-service.js | 74 +++++++++++----------- webclient/components/matrix/matrix-filter.js | 6 +- webclient/recents/recents.html | 4 ++ webclient/room/room.html | 4 ++ 4 files changed, 50 insertions(+), 38 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 5aabc899b8..1f32289bdf 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -60,7 +60,7 @@ angular.module('eventHandlerService', []) // Pagination information $rootScope.events.rooms[room_id].pagination = { earliest_token: "END" // how far back we've paginated - } + }; } }; @@ -70,6 +70,36 @@ angular.module('eventHandlerService', []) } }; + // Generic method to handle events data + var handleRoomDateEvent = function(event, isLiveEvent, addToRoomMessages) { + // Add topic changes as if they were a room message + if (addToRoomMessages) { + if (isLiveEvent) { + $rootScope.events.rooms[event.room_id].messages.push(event); + } + else { + $rootScope.events.rooms[event.room_id].messages.unshift(event); + } + } + + // live events always update, but non-live events only update if the + // ts is later. + var latestData = true; + if (!isLiveEvent) { + var eventTs = event.ts; + var storedEvent = $rootScope.events.rooms[event.room_id][event.type]; + if (storedEvent) { + if (storedEvent.ts > eventTs) { + // ignore it, we have a newer one already. + latestData = false; + } + } + } + if (latestData) { + $rootScope.events.rooms[event.room_id][event.type] = event; + } + }; + var handleRoomCreate = function(event, isLiveEvent) { // For now, we do not use the event data. Simply signal it to the app controllers $rootScope.$broadcast(ROOM_CREATE_EVENT, event, isLiveEvent); @@ -153,44 +183,16 @@ angular.module('eventHandlerService', []) } }; - var handleRoomName = function(event, isLiveEvent) { - console.log("handleRoomName " + isLiveEvent); - - $rootScope.events.rooms[event.room_id][event.type] = event; + var handleRoomName = function(event, isLiveEvent, isStateEvent) { + console.log("handleRoomName room_id: " + event.room_id + " - isLiveEvent: " + isLiveEvent + " - name: " + event.content.name); + handleRoomDateEvent(event, isLiveEvent, !isStateEvent); $rootScope.$broadcast(NAME_EVENT, event, isLiveEvent); }; - // TODO: Can this just be a generic "I am a room state event, can haz store?" - var handleRoomTopic = function(event, isLiveEvent, isStateEvent) { - console.log("handleRoomTopic live="+isLiveEvent); - - // Add topic changes as if they were a room message - if (!isStateEvent) { - if (isLiveEvent) { - $rootScope.events.rooms[event.room_id].messages.push(event); - } - else { - $rootScope.events.rooms[event.room_id].messages.unshift(event); - } - } - - // live events always update, but non-live events only update if the - // ts is later. - var latestData = true; - if (!isLiveEvent) { - var eventTs = event.ts; - var storedEvent = $rootScope.events.rooms[event.room_id][event.type]; - if (storedEvent) { - if (storedEvent.ts > eventTs) { - // ignore it, we have a newer one already. - latestData = false; - } - } - } - if (latestData) { - $rootScope.events.rooms[event.room_id][event.type] = event; - } + var handleRoomTopic = function(event, isLiveEvent, isStateEvent) { + console.log("handleRoomTopic room_id: " + event.room_id + " - isLiveEvent: " + isLiveEvent + " - topic: " + event.content.topic); + handleRoomDateEvent(event, isLiveEvent, !isStateEvent); $rootScope.$broadcast(TOPIC_EVENT, event, isLiveEvent); }; @@ -286,7 +288,7 @@ angular.module('eventHandlerService', []) handlePowerLevels(event, isLiveEvent); break; case 'm.room.name': - handleRoomName(event, isLiveEvent); + handleRoomName(event, isLiveEvent, isStateEvent); break; case 'm.room.topic': handleRoomTopic(event, isLiveEvent, isStateEvent); diff --git a/webclient/components/matrix/matrix-filter.js b/webclient/components/matrix/matrix-filter.js index 260e0827df..015a88bcad 100644 --- a/webclient/components/matrix/matrix-filter.js +++ b/webclient/components/matrix/matrix-filter.js @@ -31,15 +31,17 @@ angular.module('matrixFilter', []) } if (undefined === roomName) { - // Else, build the name from its users + var room = $rootScope.events.rooms[room_id]; if (room) { + // Get name from room state date var room_name_event = room["m.room.name"]; - if (room_name_event) { roomName = room_name_event.content.name; } else if (room.members) { + // Else, build the name from its users + // FIXME: Is it still required? // Limit the room renaming to 1:1 room if (2 === Object.keys(room.members).length) { for (var i in room.members) { diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html index 6976bab879..3fb2b43ee3 100644 --- a/webclient/recents/recents.html +++ b/webclient/recents/recents.html @@ -80,6 +80,10 @@ {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} changed the topic to: {{ room.lastMsg.content.topic }} +
+ {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} changed the room name to: {{ room.lastMsg.content.name }} +
+
Call diff --git a/webclient/room/room.html b/webclient/room/room.html index 25a8e65b97..e8d8c3f46a 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -118,6 +118,10 @@ {{ members[msg.user_id].displayname || msg.user_id }} changed the topic to: {{ msg.content.topic }} + + {{ members[msg.user_id].displayname || msg.user_id }} changed the room name to: {{ msg.content.name }} + +
-- cgit 1.4.1 From d9343289045d4a733710706421ab528626cc02d4 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 12 Sep 2014 10:48:06 +0200 Subject: Added edition of room name --- webclient/app.css | 8 ++--- webclient/components/matrix/matrix-service.js | 7 ++++ webclient/room/room-controller.js | 47 +++++++++++++++++++++++---- webclient/room/room.html | 11 +++++-- 4 files changed, 61 insertions(+), 12 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app.css b/webclient/app.css index 71af550679..1b918f59e2 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -296,20 +296,20 @@ a:active { color: #000; } } #roomTopic { - text-align: right; font-size: 13px; + text-align: right; } -.roomTopicInput { +.roomNameInput, .roomTopicInput { width: 100%; } -.roomTopicSection { +.roomNameSection, .roomTopicSection { float: right; width: 100%; } -.roomTopicSetNew { +.roomNameSetNew, .roomTopicSetNew { float: right; } diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 62aff091d4..68ef16800b 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -236,6 +236,13 @@ angular.module('matrixService', []) return doRequest("GET", path, undefined, {}); }, + setName: function(room_id, name) { + var data = { + name: name + }; + return this.sendStateEvent(room_id, "m.room.name", data); + }, + setTopic: function(room_id, topic) { var data = { topic: topic diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 9f15b61e10..3cc127140b 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -15,8 +15,8 @@ limitations under the License. */ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) -.controller('RoomController', ['$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'eventHandlerService', 'mFileUpload', 'mPresence', 'matrixPhoneService', 'MatrixCall', - function($scope, $timeout, $routeParams, $location, $rootScope, matrixService, eventHandlerService, mFileUpload, mPresence, matrixPhoneService, MatrixCall) { +.controller('RoomController', ['$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'eventHandlerService', 'mFileUpload', 'mPresence', 'matrixPhoneService', 'MatrixCall', + function($filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, eventHandlerService, mFileUpload, mPresence, matrixPhoneService, MatrixCall) { 'use strict'; var MESSAGES_PER_PAGINATION = 30; var THUMBNAIL_SIZE = 320; @@ -42,6 +42,44 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) $scope.imageURLToSend = ""; $scope.userIDToInvite = ""; + + // vars and functions for updating the name + $scope.name = { + isEditing: false, + newNameText: "", + editName: function() { + if ($scope.name.isEditing) { + console.log("Warning: Already editing name."); + return; + }; + + // Use the filter applied in html to set the input value + $scope.name.newNameText = $filter('mRoomName')($scope.room_id); + + // Force focus to the input + $timeout(function() { + angular.element('.roomNameInput').focus(); + }, 0); + + $scope.name.isEditing = true; + }, + updateName: function() { + console.log("Updating name to "+$scope.name.newNameText); + matrixService.setName($scope.room_id, $scope.name.newNameText).then( + function() { + }, + function(error) { + $scope.feedback = "Request failed: " + error.data.error; + } + ); + + $scope.name.isEditing = false; + }, + cancelEdit: function() { + $scope.name.isEditing = false; + } + }; + // vars and functions for updating the topic $scope.topic = { isEditing: false, @@ -81,10 +119,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) cancelEdit: function() { $scope.topic.isEditing = false; } - }; - - - + }; var scrollToBottom = function(force) { console.log("Scrolling to bottom"); diff --git a/webclient/room/room.html b/webclient/room/room.html index e8d8c3f46a..72c8a97adb 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -3,9 +3,16 @@
[matrix]
-
- {{ room_id | mRoomName }} + +
+
+ {{ room_id | mRoomName }} +
+
+ +
+