From 842898df159f9e3c59570d9ba7dc127f26b682b8 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 12 Sep 2014 18:16:24 +0100 Subject: Send multiple candidates at once instead of all individually. Changes spec to include multiple candidates in a candidate(s) message. --- webclient/components/matrix/matrix-call.js | 59 +++++++++++++++++++--- .../components/matrix/matrix-phone-service.js | 8 +-- 2 files changed, 58 insertions(+), 9 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 2e3e2b0967..fd21198d24 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -47,6 +47,10 @@ angular.module('MatrixCall', []) this.call_id = "c" + new Date().getTime(); this.state = 'fledgling'; this.didConnect = false; + + // a queue for candidates waiting to go out. We try to amalgamate candidates into a single candidate message where possible + this.candidateSendQueue = []; + this.candidateSendTries = 0; } MatrixCall.prototype.createPeerConnection = function() { @@ -174,12 +178,7 @@ angular.module('MatrixCall', []) MatrixCall.prototype.gotLocalIceCandidate = function(event) { console.log(event); if (event.candidate) { - var content = { - version: 0, - call_id: this.call_id, - candidate: event.candidate - }; - this.sendEventWithRetry('m.call.candidate', content); + this.sendCandidate(event.candidate); } } @@ -370,5 +369,53 @@ angular.module('MatrixCall', []) }, delayMs); }; + // Sends candidates with are sent in a special way because we try to amalgamate them into one message + MatrixCall.prototype.sendCandidate = function(content) { + this.candidateSendQueue.push(content); + var self = this; + if (this.candidateSendTries == 0) $timeout(function() { self.sendCandidateQueue(); }, 100); + }; + + MatrixCall.prototype.sendCandidateQueue = function(content) { + if (this.candidateSendQueue.length == 0) return; + + var cands = this.candidateSendQueue; + this.candidateSendQueue = []; + ++this.candidateSendTries; + var content = { + version: 0, + call_id: this.call_id, + candidates: cands + }; + var self = this; + console.log("Attempting to send "+cands.length+" candidates"); + matrixService.sendEvent(self.room_id, 'm.call.candidates', undefined, content).then(function() { self.candsSent(); }, function(error) { self.candsSendFailed(cands, error); } ); + }; + + MatrixCall.prototype.candsSent = function() { + this.candidateSendTries = 0; + this.sendCandidateQueue(); + }; + + MatrixCall.prototype.candsSendFailed = function(cands, error) { + for (var i = 0; i < cands.length; ++i) { + this.candidateSendQueue.push(cands[i]); + } + + if (this.candidateSendTries > 5) { + console.log("Failed to send candidates on attempt "+ev.tries+". Giving up for now."); + this.candidateSendTries = 0; + return; + } + + var delayMs = 500 * Math.pow(2, this.candidateSendTries); + ++this.candidateSendTries; + console.log("Failed to send candidates. Retrying in "+delayMs+"ms"); + var self = this; + $timeout(function() { + self.sendCandidateQueue(); + }, delayMs); + }; + return MatrixCall; }]); diff --git a/webclient/components/matrix/matrix-phone-service.js b/webclient/components/matrix/matrix-phone-service.js index b0dcf19100..2d0732a8da 100644 --- a/webclient/components/matrix/matrix-phone-service.js +++ b/webclient/components/matrix/matrix-phone-service.js @@ -77,13 +77,15 @@ angular.module('matrixPhoneService', []) return; } call.receivedAnswer(msg); - } else if (event.type == 'm.call.candidate') { + } else if (event.type == 'm.call.candidates') { var call = matrixPhoneService.allCalls[msg.call_id]; if (!call) { - console.log("Got candidate for unknown call ID "+msg.call_id); + console.log("Got candidates for unknown call ID "+msg.call_id); return; } - call.gotRemoteIceCandidate(msg.candidate); + for (var i = 0; i < msg.candidates.length; ++i) { + call.gotRemoteIceCandidate(msg.candidates[i]); + } } else if (event.type == 'm.call.hangup') { var call = matrixPhoneService.allCalls[msg.call_id]; if (!call) { -- cgit 1.4.1 From a9da2ec895ad5caccda16e60e40f21360c6a5a73 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 15 Sep 2014 10:39:30 +0200 Subject: BF: presence and eventMap were not reset at logout. --- .../components/matrix/event-handler-service.js | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 705a5a07f2..4604ff6192 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -38,6 +38,13 @@ angular.module('eventHandlerService', []) var TOPIC_EVENT = "TOPIC_EVENT"; var RESET_EVENT = "RESET_EVENT"; // eventHandlerService has been resetted + // used for dedupping events - could be expanded in future... + // FIXME: means that we leak memory over time (along with lots of the rest + // of the app, given we never try to reap memory yet) + var eventMap = {}; + + $rootScope.presence = {}; + var initialSyncDeferred; var reset = function() { @@ -46,16 +53,13 @@ angular.module('eventHandlerService', []) $rootScope.events = { rooms: {} // will contain roomId: { messages:[], members:{userid1: event} } }; - } - reset(); - // used for dedupping events - could be expanded in future... - // FIXME: means that we leak memory over time (along with lots of the rest - // of the app, given we never try to reap memory yet) - var eventMap = {}; + $rootScope.presence = {}; + + eventMap = {}; + }; + reset(); - $rootScope.presence = {}; - var initRoom = function(room_id) { if (!(room_id in $rootScope.events.rooms)) { console.log("Creating new handler entry for " + room_id); @@ -204,7 +208,7 @@ angular.module('eventHandlerService', []) var handleCallEvent = function(event, isLiveEvent) { $rootScope.$broadcast(CALL_EVENT, event, isLiveEvent); - if (event.type == 'm.call.invite') { + if (event.type === 'm.call.invite') { $rootScope.events.rooms[event.room_id].messages.push(event); } }; @@ -231,7 +235,7 @@ angular.module('eventHandlerService', []) } } return index; - } + }; return { ROOM_CREATE_EVENT: ROOM_CREATE_EVENT, -- cgit 1.4.1 From d821755b49e5387879a63a01e0f8916e360bfc5b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 15 Sep 2014 14:31:53 +0100 Subject: Updated webclient to support the new registration logic. --- webclient/components/matrix/matrix-service.js | 143 +++++++++++++++++++++----- 1 file changed, 120 insertions(+), 23 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 68ef16800b..d7d278a7f6 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -81,38 +81,135 @@ angular.module('matrixService', []) return $http(request); }; + + var doRegisterLogin = function(path, loginType, sessionId, userName, password, threepidCreds) { + var data = {}; + if (loginType === "m.login.recaptcha") { + var challengeToken = Recaptcha.get_challenge(); + var captchaEntry = Recaptcha.get_response(); + data = { + type: "m.login.recaptcha", + challenge: challengeToken, + response: captchaEntry + }; + } + else if (loginType === "m.login.email.identity") { + data = { + threepidCreds: threepidCreds + }; + } + else if (loginType === "m.login.password") { + data = { + user_id: userName, + password: password + }; + } + + if (sessionId) { + data.session = sessionId; + } + data.type = loginType; + console.log("doRegisterLogin >>> " + loginType); + return doRequest("POST", path, undefined, data); + }; return { /****** Home server API ******/ prefix: prefixPath, // Register an user - register: function(user_name, password, threepidCreds, useCaptcha) { - // The REST path spec + register: function(user_name, password, threepidCreds, useCaptcha) { + // registration is composed of multiple requests, to check you can + // register, then to actually register. This deferred will fire when + // all the requests are done, along with the final response. + var deferred = $q.defer(); var path = "/register"; - var data = { - user_id: user_name, - password: password, - threepidCreds: threepidCreds - }; + // check we can actually register with this HS. + doRequest("GET", path, undefined, undefined).then( + function(response) { + console.log("/register [1] : "+JSON.stringify(response)); + var flows = response.data.flows; + var knownTypes = [ + "m.login.password", + "m.login.recaptcha", + "m.login.email.identity" + ]; + // if they entered 3pid creds, we want to use a flow which uses it. + var useThreePidFlow = threepidCreds != undefined; + var flowIndex = 0; + var firstRegType = undefined; + + for (var i=0; i Date: Mon, 15 Sep 2014 14:52:39 +0100 Subject: Make captcha work again with the new registration logic. --- synapse/rest/register.py | 1 + webclient/components/matrix/matrix-service.js | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'webclient/components') diff --git a/synapse/rest/register.py b/synapse/rest/register.py index 8036c3c406..fe8f0ed23f 100644 --- a/synapse/rest/register.py +++ b/synapse/rest/register.py @@ -133,6 +133,7 @@ class RegisterRestServlet(RestServlet): logger.debug("Removing session %s", session) self.sessions.pop(session["id"]) + @defer.inlineCallbacks def _do_recaptcha(self, request, register_json, session): if not self.hs.config.enable_registration_captcha: raise SynapseError(400, "Captcha not required.") diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index d7d278a7f6..35ebca961c 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -154,6 +154,13 @@ angular.module('matrixService', []) } if (!useCaptcha && regType == "m.login.recaptcha") { console.error("Web client setup to not use captcha, but HS demands a captcha."); + deferred.reject({ + data: { + errcode: "M_CAPTCHA_NEEDED", + error: "Home server requires a captcha." + } + }); + return; } } } @@ -183,7 +190,20 @@ angular.module('matrixService', []) deferred.resolve(response); } else if (response.data.next) { - return doRegisterLogin(path, response.data.next, sessionId, user_name, password, threepidCreds).then( + var nextType = response.data.next; + if (response.data.next instanceof Array) { + for (var i=0; i Date: Mon, 15 Sep 2014 16:31:59 +0200 Subject: Recents uses data directly from $rootscope.events --- .../components/matrix/event-handler-service.js | 43 +++++-- webclient/recents/recents-controller.js | 127 +-------------------- webclient/recents/recents-filter.js | 30 +++-- webclient/recents/recents.html | 66 ++++++----- 4 files changed, 95 insertions(+), 171 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 4604ff6192..a2c807b3f0 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -63,13 +63,14 @@ angular.module('eventHandlerService', []) var initRoom = function(room_id) { if (!(room_id in $rootScope.events.rooms)) { console.log("Creating new handler entry for " + room_id); - $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 + $rootScope.events.rooms[room_id] = { + room_id: room_id, + messages: [], + members: {}, + // Pagination information + pagination: { + earliest_token: "END" // how far back we've paginated + } }; } }; @@ -257,7 +258,9 @@ angular.module('eventHandlerService', []) // 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); + if (event.room_id) { + initRoom(event.room_id); + } // Avoid duplicated events // Needed for rooms where initialSync has not been done. @@ -347,6 +350,30 @@ angular.module('eventHandlerService', []) resetRoomMessages: function(room_id) { resetRoomMessages(room_id); + }, + + /** + * Compute the room users number, ie the number of members who has joined the room. + * @param {String} room_id the room id + * @returns {undefined | Number} the room users number if available + */ + getUsersCountInRoom: function(room_id) { + var memberCount; + + var room = $rootScope.events.rooms[room_id]; + if (room) { + memberCount = 0; + + for (var i in room.members) { + var member = room.members[i]; + + if ("join" === member.membership) { + memberCount = memberCount + 1; + } + } + } + + return memberCount; } }; }]); diff --git a/webclient/recents/recents-controller.js b/webclient/recents/recents-controller.js index a0db0538f3..2006f13a57 100644 --- a/webclient/recents/recents-controller.js +++ b/webclient/recents/recents-controller.js @@ -16,134 +16,13 @@ 'use strict'; -angular.module('RecentsController', ['matrixService', 'matrixFilter', 'eventHandlerService']) -.controller('RecentsController', ['$rootScope', '$scope', 'matrixService', 'eventHandlerService', - function($rootScope, $scope, matrixService, eventHandlerService) { - - // FIXME: Angularjs reloads the controller (and resets its $scope) each time - // the page URL changes, use $rootScope to avoid to have to reload data - $rootScope.rooms; +angular.module('RecentsController', ['matrixService', 'matrixFilter']) +.controller('RecentsController', ['$rootScope', + function($rootScope) { // $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; - - var listenToEventStream = function() { - // Refresh the list on matrix invitation and message event - $rootScope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) { - if (isLive) { - if (!$rootScope.rooms[event.room_id]) { - // The user has joined a new room, which we do not have data yet. The reason is that - // the room has appeared in the scope of the user rooms after the global initialSync - // FIXME: an initialSync on this specific room should be done - $rootScope.rooms[event.room_id] = { - room_id:event.room_id - }; - } - else if (event.state_key === matrixService.config().user_id && "invite" !== event.membership && "join" !== event.membership) { - // The user has been kicked or banned from the room, remove this room from the recents - delete $rootScope.rooms[event.room_id]; - } - - if ($rootScope.rooms[event.room_id]) { - $rootScope.rooms[event.room_id].lastMsg = event; - } - - // Update room users count - $rootScope.rooms[event.room_id].numUsersInRoom = getUsersCountInRoom(event.room_id); - } - }); - $rootScope.$on(eventHandlerService.MSG_EVENT, function(ngEvent, event, isLive) { - if (isLive) { - $rootScope.rooms[event.room_id].lastMsg = event; - } - }); - $rootScope.$on(eventHandlerService.CALL_EVENT, function(ngEvent, event, isLive) { - if (isLive) { - $rootScope.rooms[event.room_id].lastMsg = event; - } - }); - $rootScope.$on(eventHandlerService.ROOM_CREATE_EVENT, function(ngEvent, event, isLive) { - if (isLive) { - $rootScope.rooms[event.room_id] = event; - } - }); - $rootScope.$on(eventHandlerService.NAME_EVENT, function(ngEvent, event, isLive) { - if (isLive) { - $rootScope.rooms[event.room_id].lastMsg = event; - } - }); - $rootScope.$on(eventHandlerService.TOPIC_EVENT, function(ngEvent, event, isLive) { - if (isLive) { - $rootScope.rooms[event.room_id].lastMsg = event; - } - }); - }; - - /** - * Compute the room users number, ie the number of members who has joined the room. - * @param {String} room_id the room id - * @returns {undefined | Number} the room users number if available - */ - var getUsersCountInRoom = function(room_id) { - var memberCount; - - var room = $rootScope.events.rooms[room_id]; - if (room) { - memberCount = 0; - - for (var i in room.members) { - var member = room.members[i]; - - if ("join" === member.membership) { - memberCount = memberCount + 1; - } - } - } - - return memberCount; - }; - $scope.onInit = function() { - // Init recents list only once - if ($rootScope.rooms) { - return; - } - - $rootScope.rooms = {}; - - // Use initialSync data to init the recents list - eventHandlerService.waitForInitialSyncCompletion().then( - function(initialSyncData) { - - var rooms = initialSyncData.data.rooms; - for (var i=0; i +
- + ng-class="{'recentsRoomSelected': (room.room_id === recentsSelectedRoomID)}"> @@ -25,67 +29,67 @@ {{ room.inviter | mUserDisplayName: room.room_id }} invited you -
+
- - {{ room.lastMsg.state_key | mUserDisplayName: room.room_id}} joined + + {{ lastMsg.state_key | mUserDisplayName: room.room_id}} joined - - - {{room.lastMsg.state_key | mUserDisplayName: room.room_id }} left + + + {{lastMsg.state_key | mUserDisplayName: room.room_id }} left - - {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} - {{ {"join": "kicked", "ban": "unbanned"}[room.lastMsg.content.prev] }} - {{ room.lastMsg.state_key | mUserDisplayName: room.room_id }} + + {{ lastMsg.user_id | mUserDisplayName: room.room_id }} + {{ {"join": "kicked", "ban": "unbanned"}[lastMsg.content.prev] }} + {{ lastMsg.state_key | mUserDisplayName: room.room_id }} - - : {{ room.lastMsg.content.reason }} + + : {{ lastMsg.content.reason }} - - {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} - {{ {"invite": "invited", "ban": "banned"}[room.lastMsg.content.membership] }} - {{ room.lastMsg.state_key | mUserDisplayName: room.room_id }} - - : {{ room.lastMsg.content.reason }} + + {{ lastMsg.user_id | mUserDisplayName: room.room_id }} + {{ {"invite": "invited", "ban": "banned"}[lastMsg.content.membership] }} + {{ lastMsg.state_key | mUserDisplayName: room.room_id }} + + : {{ lastMsg.content.reason }}
-
+
- {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} : - + {{ lastMsg.user_id | mUserDisplayName: room.room_id }} : +
- {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} sent an image + {{ lastMsg.user_id | mUserDisplayName: room.room_id }} sent an image
- +
- {{ room.lastMsg.content }} + {{ lastMsg.content }}
- {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} changed the topic to: {{ room.lastMsg.content.topic }} + {{ lastMsg.user_id | mUserDisplayName: room.room_id }} changed the topic to: {{ lastMsg.content.topic }}
- {{ room.lastMsg.user_id | mUserDisplayName: room.room_id }} changed the room name to: {{ room.lastMsg.content.name }} + {{ lastMsg.user_id | mUserDisplayName: room.room_id }} changed the room name to: {{ lastMsg.content.name }}
-
+
Call
-- cgit 1.4.1 From 2c00e1ecd9774463d687559d5df38b2a76340b32 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 15 Sep 2014 15:38:29 +0100 Subject: Be consistent when associating keys with login types for registration/login. --- cmdclient/console.py | 2 +- synapse/rest/register.py | 2 +- tests/rest/utils.py | 2 +- webclient/components/matrix/matrix-service.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'webclient/components') diff --git a/cmdclient/console.py b/cmdclient/console.py index 5a9d4c3c4c..d9c6ec6a70 100755 --- a/cmdclient/console.py +++ b/cmdclient/console.py @@ -162,7 +162,7 @@ class SynapseCmd(cmd.Cmd): "type": "m.login.password" } if "userid" in args: - body["user_id"] = args["userid"] + body["user"] = args["userid"] if password: body["password"] = password diff --git a/synapse/rest/register.py b/synapse/rest/register.py index fe8f0ed23f..c2c80e70c7 100644 --- a/synapse/rest/register.py +++ b/synapse/rest/register.py @@ -192,7 +192,7 @@ class RegisterRestServlet(RestServlet): raise SynapseError(400, "Captcha is required.") password = register_json["password"].encode("utf-8") - desired_user_id = (register_json["user_id"].encode("utf-8") if "user_id" + desired_user_id = (register_json["user"].encode("utf-8") if "user" in register_json else None) if desired_user_id and urllib.quote(desired_user_id) != desired_user_id: raise SynapseError( diff --git a/tests/rest/utils.py b/tests/rest/utils.py index 25ed1388cf..579441fb4a 100644 --- a/tests/rest/utils.py +++ b/tests/rest/utils.py @@ -99,7 +99,7 @@ class RestTestCase(unittest.TestCase): "POST", "/register", json.dumps({ - "user_id": user_id, + "user": user_id, "password": "test", "type": "m.login.password" })) diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 35ebca961c..069e02e939 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -100,7 +100,7 @@ angular.module('matrixService', []) } else if (loginType === "m.login.password") { data = { - user_id: userName, + user: userName, password: password }; } -- cgit 1.4.1 From 8aa4b7bf7fdc31b3a146fe8fdc07922a4bfb1f78 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 15 Sep 2014 17:31:07 +0200 Subject: Recents must not show temporary fake messages --- .../components/matrix/event-handler-service.js | 24 ++++++++++++++++++++++ webclient/recents/recents-controller.js | 7 +++++-- webclient/recents/recents-filter.js | 9 ++------ webclient/recents/recents.html | 2 +- 4 files changed, 32 insertions(+), 10 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index a2c807b3f0..6fd77c4f29 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -352,6 +352,30 @@ angular.module('eventHandlerService', []) resetRoomMessages(room_id); }, + /** + * Return the last message event of a room + * @param {String} room_id the room id + * @param {Boolean} filterFake true to not take into account fake messages + * @returns {undefined | Event} the last message event if available + */ + getLastMessage: function(room_id, filterEcho) { + var lastMessage; + + var room = $rootScope.events.rooms[room_id]; + if (room) { + for (var i = room.messages.length - 1; i >= 0; i--) { + var message = room.messages[i]; + + if (!filterEcho || undefined === message.echo_msg_state) { + lastMessage = message; + break; + } + } + } + + return lastMessage; + }, + /** * Compute the room users number, ie the number of members who has joined the room. * @param {String} room_id the room id diff --git a/webclient/recents/recents-controller.js b/webclient/recents/recents-controller.js index 2006f13a57..ee8a41c366 100644 --- a/webclient/recents/recents-controller.js +++ b/webclient/recents/recents-controller.js @@ -17,8 +17,11 @@ 'use strict'; angular.module('RecentsController', ['matrixService', 'matrixFilter']) -.controller('RecentsController', ['$rootScope', - function($rootScope) { +.controller('RecentsController', ['$rootScope', '$scope', 'eventHandlerService', + function($rootScope, $scope, eventHandlerService) { + + // Expose the service to the view + $scope.eventHandlerService = eventHandlerService; // $rootScope of the parent where the recents component is included can override this value // in order to highlight a specific room in the list diff --git a/webclient/recents/recents-filter.js b/webclient/recents/recents-filter.js index e8a706a5d5..67fe49d4b6 100644 --- a/webclient/recents/recents-filter.js +++ b/webclient/recents/recents-filter.js @@ -35,14 +35,9 @@ angular.module('RecentsController') // And time sort them // The room with the lastest message at first filtered.sort(function (roomA, roomB) { - var lastMsgRoomA, lastMsgRoomB; - if (roomA.messages && 0 < roomA.messages.length) { - lastMsgRoomA = roomA.messages[roomA.messages.length - 1]; - } - if (roomB.messages && 0 < roomB.messages.length) { - lastMsgRoomB = roomB.messages[roomB.messages.length - 1]; - } + var lastMsgRoomA = eventHandlerService.getLastMessage(roomA.room_id, true); + var lastMsgRoomB = eventHandlerService.getLastMessage(roomB.room_id, true); // Invite message does not have a body message nor ts // Puth them at the top of the list diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html index eb9e269a4b..789ffc9d20 100644 --- a/webclient/recents/recents.html +++ b/webclient/recents/recents.html @@ -16,7 +16,7 @@
-- cgit 1.4.1 From b0483cd47d72ea73760c8301f5729d840ceb7683 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Mon, 15 Sep 2014 18:22:38 +0200 Subject: Filter room where the user has been banned --- webclient/components/matrix/event-handler-service.js | 16 ++++++++++++++++ webclient/recents/recents-filter.js | 18 +++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 6fd77c4f29..4b0566fe33 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -398,6 +398,22 @@ angular.module('eventHandlerService', []) } return memberCount; + }, + + /** + * Get the member object of a room member + * @param {String} room_id the room id + * @param {String} user_id the id of the user + * @returns {undefined | Object} the member object of this user in this room if he is part of the room + */ + getMember: function(room_id, user_id) { + var member; + + var room = $rootScope.events.rooms[room_id]; + if (room) { + member = room.members[user_id]; + } + return member; } }; }]); diff --git a/webclient/recents/recents-filter.js b/webclient/recents/recents-filter.js index 67fe49d4b6..2fd4dbe98b 100644 --- a/webclient/recents/recents-filter.js +++ b/webclient/recents/recents-filter.js @@ -17,19 +17,27 @@ 'use strict'; angular.module('RecentsController') -.filter('orderRecents', ["eventHandlerService", function(eventHandlerService) { +.filter('orderRecents', ["matrixService", "eventHandlerService", function(matrixService, eventHandlerService) { return function(rooms) { + var user_id = matrixService.config().user_id; + // Transform the dict into an array // The key, room_id, is already in value objects var filtered = []; angular.forEach(rooms, function(room, room_id) { - // Count users here - // TODO: Compute it directly in eventHandlerService - room.numUsersInRoom = eventHandlerService.getUsersCountInRoom(room_id); + // Show the room only if the user has joined it or has been invited + // (ie, do not show it if he has been banned) + var member = eventHandlerService.getMember(room_id, user_id); + if (member && ("invite" === member.membership || "join" === member.membership)) { + + // Count users here + // TODO: Compute it directly in eventHandlerService + room.numUsersInRoom = eventHandlerService.getUsersCountInRoom(room_id); - filtered.push(room); + filtered.push(room); + } }); // And time sort them -- cgit 1.4.1 From dd2b933a0d92f24421a56ec350ae0f80e32d2d3e Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 Sep 2014 14:46:13 +0100 Subject: Use event age to recognise which calls are current and which aren't and hence support answering calls that were placed before we loaded the page. --- webclient/app-controller.js | 4 ++ webclient/components/matrix/matrix-call.js | 26 ++++++--- .../components/matrix/matrix-phone-service.js | 62 ++++++++++++++++++---- webclient/index.html | 3 +- 4 files changed, 79 insertions(+), 16 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 6c3759878b..6338624486 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -130,6 +130,10 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even angular.element('#ringAudio')[0].pause(); angular.element('#ringbackAudio')[0].pause(); angular.element('#busyAudio')[0].play(); + } else if (newVal == 'ended' && oldVal == 'invite_sent' && $rootScope.currentCall.hangupParty == 'local' && $rootScope.currentCall.hangupReason == 'invite_timeout') { + angular.element('#ringAudio')[0].pause(); + angular.element('#ringbackAudio')[0].pause(); + angular.element('#busyAudio')[0].play(); } else if (oldVal == 'invite_sent') { angular.element('#ringbackAudio')[0].pause(); } else if (oldVal == 'ringing') { diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index fd21198d24..650c415c83 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -53,6 +53,8 @@ angular.module('MatrixCall', []) this.candidateSendTries = 0; } + MatrixCall.CALL_TIMEOUT = 60000; + MatrixCall.prototype.createPeerConnection = function() { var stunServer = 'stun:stun.l.google.com:19302'; var pc; @@ -86,6 +88,14 @@ angular.module('MatrixCall', []) this.direction = 'inbound'; }; + // perverse as it may seem, sometimes we want to instantiate a call with a hangup message + // (because when getting the state of the room on load, events come in reverse order and + // we want to remember that a call has been hung up) + MatrixCall.prototype.initWithHangup = function(msg) { + this.msg = msg; + this.state = 'ended'; + }; + MatrixCall.prototype.answer = function() { console.log("Answering call "+this.call_id); var self = this; @@ -188,14 +198,12 @@ angular.module('MatrixCall', []) console.log("Ignoring remote ICE candidate because call has ended"); return; } - var candidateObject = new RTCIceCandidate({ - sdpMLineIndex: cand.label, - candidate: cand.candidate - }); - this.peerConn.addIceCandidate(candidateObject, function() {}, function(e) {}); + this.peerConn.addIceCandidate(new RTCIceCandidate(cand), function() {}, function(e) {}); }; MatrixCall.prototype.receivedAnswer = function(msg) { + if (this.state == 'ended') return; + this.peerConn.setRemoteDescription(new RTCSessionDescription(msg.answer), this.onSetRemoteDescriptionSuccess, this.onSetRemoteDescriptionError); this.state = 'connecting'; }; @@ -213,11 +221,17 @@ angular.module('MatrixCall', []) var content = { version: 0, call_id: this.call_id, - offer: description + offer: description, + lifetime: MatrixCall.CALL_TIMEOUT }; this.sendEventWithRetry('m.call.invite', content); var self = this; + $timeout(function() { + self.hangupReason = 'invite_timeout'; + self.hangup(); + }, MatrixCall.CALL_TIMEOUT); + $rootScope.$apply(function() { self.state = 'invite_sent'; }); diff --git a/webclient/components/matrix/matrix-phone-service.js b/webclient/components/matrix/matrix-phone-service.js index 2d0732a8da..3e99a7d112 100644 --- a/webclient/components/matrix/matrix-phone-service.js +++ b/webclient/components/matrix/matrix-phone-service.js @@ -24,22 +24,52 @@ angular.module('matrixPhoneService', []) matrixPhoneService.INCOMING_CALL_EVENT = "INCOMING_CALL_EVENT"; matrixPhoneService.REPLACED_CALL_EVENT = "REPLACED_CALL_EVENT"; matrixPhoneService.allCalls = {}; + // a place to save candidates that come in for calls we haven't got invites for yet (when paginating backwards) + matrixPhoneService.candidatesByCall = {}; matrixPhoneService.callPlaced = function(call) { matrixPhoneService.allCalls[call.call_id] = call; }; $rootScope.$on(eventHandlerService.CALL_EVENT, function(ngEvent, event, isLive) { - if (!isLive) return; // until matrix supports expiring messages if (event.user_id == matrixService.config().user_id) return; + var msg = event.content; + if (event.type == 'm.call.invite') { + if (event.age == undefined || msg.lifetime == undefined) { + // if the event doesn't have either an age (the HS is too old) or a lifetime + // (the sending client was too old when it sent it) then fall back to old behaviour + if (!isLive) return; // until matrix supports expiring messages + } + + if (event.age > msg.lifetime) { + console.log("Ignoring expired call event of type "+event.type); + return; + } + + var call = undefined; + if (!isLive) { + // if this event wasn't live then this call may already be over + call = matrixPhoneService.allCalls[msg.call_id]; + if (call && call.state == 'ended') { + return; + } + } + var MatrixCall = $injector.get('MatrixCall'); var call = new MatrixCall(event.room_id); call.call_id = msg.call_id; call.initWithInvite(msg); matrixPhoneService.allCalls[call.call_id] = call; + // if we stashed candidate events for that call ID, play them back now + if (!isLive && matrixPhoneService.candidatesByCall[call.call_id] != undefined) { + for (var i = 0; i < matrixPhoneService.candidatesByCall[call.call_id].length; ++i) { + call.gotRemoteIceCandidate(matrixPhoneService.candidatesByCall[call.call_id][i]); + } + } + // Were we trying to call that user (room)? var existingCall; var callIds = Object.keys(matrixPhoneService.allCalls); @@ -79,21 +109,35 @@ angular.module('matrixPhoneService', []) call.receivedAnswer(msg); } else if (event.type == 'm.call.candidates') { var call = matrixPhoneService.allCalls[msg.call_id]; - if (!call) { + if (!call && isLive) { console.log("Got candidates for unknown call ID "+msg.call_id); return; - } - for (var i = 0; i < msg.candidates.length; ++i) { - call.gotRemoteIceCandidate(msg.candidates[i]); + } else if (!call) { + if (matrixPhoneService.candidatesByCall[msg.call_id] == undefined) { + matrixPhoneService.candidatesByCall[msg.call_id] = []; + } + matrixPhoneService.candidatesByCall[msg.call_id] = matrixPhoneService.candidatesByCall[msg.call_id].concat(msg.candidates); + } else { + for (var i = 0; i < msg.candidates.length; ++i) { + call.gotRemoteIceCandidate(msg.candidates[i]); + } } } else if (event.type == 'm.call.hangup') { var call = matrixPhoneService.allCalls[msg.call_id]; - if (!call) { + if (!call && isLive) { console.log("Got hangup for unknown call ID "+msg.call_id); - return; + } else if (!call) { + // if not live, store the fact that the call has ended because we're probably getting events backwards so + // the hangup will come before the invite + var MatrixCall = $injector.get('MatrixCall'); + var call = new MatrixCall(event.room_id); + call.call_id = msg.call_id; + call.initWithHangup(msg); + matrixPhoneService.allCalls[msg.call_id] = call; + } else { + call.onHangupReceived(); + delete(matrixPhoneService.allCalls[msg.call_id]); } - call.onHangupReceived(); - delete(matrixPhoneService.allCalls[msg.call_id]); } }); diff --git a/webclient/index.html b/webclient/index.html index 9eea08215c..7e4dcb8345 100644 --- a/webclient/index.html +++ b/webclient/index.html @@ -62,7 +62,8 @@ Call Connecting... Call Connected Call Rejected - Call Canceled + Call Canceled + User Not Responding Call Ended Call Canceled Call Ended -- cgit 1.4.1 From 45592ccdfde4c0b9d2452d6ecec8daec903c7df4 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Tue, 16 Sep 2014 15:03:07 +0200 Subject: WEB-29: Improve room page content loading InitialSync: load the 30 last messages of each room so that a full page of messages can be displayed without additionnal request --- .../components/matrix/event-handler-service.js | 26 +++++++++++++++++----- .../components/matrix/event-stream-service.js | 6 +++-- webclient/room/room-controller.js | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 4b0566fe33..fc5a81617c 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -329,13 +329,29 @@ angular.module('eventHandlerService', []) }, // Handle messages from /initialSync or /messages - handleRoomMessages: function(room_id, messages, isLiveEvents) { + handleRoomMessages: function(room_id, messages, isLiveEvents, dir) { initRoom(room_id); - this.handleEvents(messages.chunk, isLiveEvents); - // 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; + var events = messages.chunk; + + // Handles messages according to their time order + if (dir && 'b' === dir) { + // paginateBackMessages requests messages to be in reverse chronological order + for (var i=0; i=0; i--) { + this.handleEvent(events[i], isLiveEvents, isLiveEvents); + } + // Store where to start pagination + $rootScope.events.rooms[room_id].pagination.earliest_token = messages.start; + } }, handleInitialSyncDone: function(initialSyncData) { diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index 03b805213d..6f92332246 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -104,8 +104,10 @@ angular.module('eventStreamService', []) settings.isActive = true; var deferred = $q.defer(); - // Initial sync: get all information and the last message of all rooms of the user - matrixService.initialSync(1, false).then( + // Initial sync: get all information and the last 30 messages of all rooms of the user + // 30 messages should be enough to display a full page of messages in a room + // without requiring to make an additional request + matrixService.initialSync(30, false).then( function(response) { var rooms = response.data.rooms; for (var i = 0; i < rooms.length; ++i) { diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 50d902ae47..8cea0511c1 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -235,7 +235,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) matrixService.paginateBackMessages($scope.room_id, $rootScope.events.rooms[$scope.room_id].pagination.earliest_token, numItems).then( function(response) { - eventHandlerService.handleRoomMessages($scope.room_id, response.data, false); + eventHandlerService.handleRoomMessages($scope.room_id, response.data, false, 'b'); 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. -- cgit 1.4.1 From 84372cef4a512ae4603767c97c8055c218ac1557 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 Sep 2014 15:25:51 +0100 Subject: Time out calls from both ends properly. --- webclient/components/matrix/matrix-call.js | 24 ++++++++++++++++------ .../components/matrix/matrix-phone-service.js | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 650c415c83..bf1e61ad7e 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -80,19 +80,29 @@ angular.module('MatrixCall', []) this.config = config; }; - MatrixCall.prototype.initWithInvite = function(msg) { - this.msg = msg; + MatrixCall.prototype.initWithInvite = function(event) { + this.msg = event.content; this.peerConn = this.createPeerConnection(); this.peerConn.setRemoteDescription(new RTCSessionDescription(this.msg.offer), this.onSetRemoteDescriptionSuccess, this.onSetRemoteDescriptionError); this.state = 'ringing'; this.direction = 'inbound'; + var self = this; + $timeout(function() { + if (self.state == 'ringing') { + self.state = 'ended'; + self.hangupParty = 'remote'; // effectively + self.stopAllMedia(); + if (self.peerConn.signalingState != 'closed') self.peerConn.close(); + if (self.onHangup) self.onHangup(self); + } + }, this.msg.lifetime - event.age); }; // perverse as it may seem, sometimes we want to instantiate a call with a hangup message // (because when getting the state of the room on load, events come in reverse order and // we want to remember that a call has been hung up) - MatrixCall.prototype.initWithHangup = function(msg) { - this.msg = msg; + MatrixCall.prototype.initWithHangup = function(event) { + this.msg = event.content; this.state = 'ended'; }; @@ -228,8 +238,10 @@ angular.module('MatrixCall', []) var self = this; $timeout(function() { - self.hangupReason = 'invite_timeout'; - self.hangup(); + if (self.state == 'invite_sent') { + self.hangupReason = 'invite_timeout'; + self.hangup(); + } }, MatrixCall.CALL_TIMEOUT); $rootScope.$apply(function() { diff --git a/webclient/components/matrix/matrix-phone-service.js b/webclient/components/matrix/matrix-phone-service.js index 3e99a7d112..d05eecf72a 100644 --- a/webclient/components/matrix/matrix-phone-service.js +++ b/webclient/components/matrix/matrix-phone-service.js @@ -60,7 +60,7 @@ angular.module('matrixPhoneService', []) var MatrixCall = $injector.get('MatrixCall'); var call = new MatrixCall(event.room_id); call.call_id = msg.call_id; - call.initWithInvite(msg); + call.initWithInvite(event); matrixPhoneService.allCalls[call.call_id] = call; // if we stashed candidate events for that call ID, play them back now @@ -132,7 +132,7 @@ angular.module('matrixPhoneService', []) var MatrixCall = $injector.get('MatrixCall'); var call = new MatrixCall(event.room_id); call.call_id = msg.call_id; - call.initWithHangup(msg); + call.initWithHangup(event); matrixPhoneService.allCalls[msg.call_id] = call; } else { call.onHangupReceived(); -- cgit 1.4.1 From 660364d6a7a4ad69bbb951990c20fc38d168e588 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 16 Sep 2014 14:32:50 +0100 Subject: Move the notification logic out of an individual room controller and into the general event handler, so we can notify for >1 room. --- webclient/components/matrix/event-handler-service.js | 20 +++++++++++++++++++- webclient/room/room-controller.js | 16 ---------------- 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index fc5a81617c..8783b9b1e0 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -27,7 +27,8 @@ Typically, this service will store events or broadcast them to any listeners if typically all the $on method would do is update its own $scope. */ angular.module('eventHandlerService', []) -.factory('eventHandlerService', ['matrixService', '$rootScope', '$q', function(matrixService, $rootScope, $q) { +.factory('eventHandlerService', ['matrixService', '$rootScope', '$q', '$timeout', 'mPresence', +function(matrixService, $rootScope, $q, $timeout, mPresence) { var ROOM_CREATE_EVENT = "ROOM_CREATE_EVENT"; var MSG_EVENT = "MSG_EVENT"; var MEMBER_EVENT = "MEMBER_EVENT"; @@ -137,6 +138,23 @@ angular.module('eventHandlerService', []) else { $rootScope.events.rooms[event.room_id].messages.push(event); } + + if (window.Notification) { + // Show notification when the window is hidden, or the user is idle + if (document.hidden || matrixService.presence.unavailable === mPresence.getState()) { + console.log("Displaying notification for "+JSON.stringify(event)); + var notification = new window.Notification( + ($rootScope.events.rooms[event.room_id].members[event.user_id].displayname || event.user_id) + + " (" + (matrixService.getRoomIdToAliasMapping(event.room_id) || event.room_id) + ")", // FIXME: don't leak room_ids here + { + "body": event.content.body, + "icon": $rootScope.events.rooms[event.room_id].members[event.user_id].avatar_url + }); + $timeout(function() { + notification.close(); + }, 5 * 1000); + } + } } else { $rootScope.events.rooms[event.room_id].messages.unshift(event); diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 4a91d298bc..4a1dfd6aac 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -139,22 +139,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) if (isLive && event.room_id === $scope.room_id) { scrollToBottom(); - - if (window.Notification) { - // Show notification when the window is hidden, or the user is idle - if (document.hidden || matrixService.presence.unavailable === mPresence.getState()) { - var notification = new window.Notification( - ($scope.members[event.user_id].displayname || event.user_id) + - " (" + ($scope.room_alias || $scope.room_id) + ")", // FIXME: don't leak room_ids here - { - "body": event.content.body, - "icon": $scope.members[event.user_id].avatar_url - }); - $timeout(function() { - notification.close(); - }, 5 * 1000); - } - } } }); -- cgit 1.4.1 From a402e0c5e6e432a175b48279c972bc9ae7e944bc Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 16 Sep 2014 15:15:19 +0100 Subject: Added bing detection logic. Persist the display name of the user in localstorage for use when binging. --- .../components/matrix/event-handler-service.js | 37 ++++++++++++++++++++-- webclient/home/home-controller.js | 4 +++ webclient/room/room-controller.js | 4 +-- webclient/settings/settings.html | 6 ++-- 4 files changed, 44 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 8783b9b1e0..16d5763ed2 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -140,8 +140,41 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { } if (window.Notification) { - // Show notification when the window is hidden, or the user is idle - if (document.hidden || matrixService.presence.unavailable === mPresence.getState()) { + var bingWords = matrixService.config().bingWords; + var content = event.content.body; + var shouldBing = false; + + // case-insensitive name check for user_id OR display_name if they exist + var myUserId = matrixService.config().user_id; + if (myUserId) { + myUserId = myUserId.toLocaleLowerCase(); + } + var myDisplayName = matrixService.config().display_name; + if (myDisplayName) { + myDisplayName = myDisplayName.toLocaleLowerCase(); + } + if ( (myDisplayName && content.toLocaleLowerCase().indexOf(myDisplayName) != -1) || + (myUserId && content.toLocaleLowerCase().indexOf(myUserId) != -1) ) { + shouldBing = true; + } + + // bing word list check + if (bingWords && !shouldBing) { + for (var i=0; iDesktop notifications
- Notifications are enabled. + Notifications are enabled. You will be alerted when a message contains your user ID or display name.
-

Words to alert on:

- Additional words to alert on: +
  • {{word}}
  • -- cgit 1.4.1 From b36a0c71d1da1fd23a154389c692f6644a3e7ac2 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 16 Sep 2014 15:31:18 +0100 Subject: Added utility function containsBingWord and hook up some css to it. --- webclient/app.css | 4 ++ .../components/matrix/event-handler-service.js | 71 +++++++++++++--------- webclient/room/room.html | 2 +- 3 files changed, 46 insertions(+), 31 deletions(-) (limited to 'webclient/components') diff --git a/webclient/app.css b/webclient/app.css index 4a4ba7b8f4..b947d8b663 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -538,6 +538,10 @@ a:active { color: #000; } color: #F00; } +.messageBing { + color: #00F; +} + #room-fullscreen-image { position: absolute; top: 0px; diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 16d5763ed2..389eee5471 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -45,6 +45,46 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { var eventMap = {}; $rootScope.presence = {}; + + // TODO: This is attached to the rootScope so .html can just go containsBingWord + // for determining classes so it is easy to highlight bing messages. It seems a + // bit strange to put the impl in this service though, but I can't think of a better + // file to put it in. + $rootScope.containsBingWord = function(content) { + if (!content) { + return false; + } + var bingWords = matrixService.config().bingWords; + var shouldBing = false; + + // case-insensitive name check for user_id OR display_name if they exist + var myUserId = matrixService.config().user_id; + if (myUserId) { + myUserId = myUserId.toLocaleLowerCase(); + } + var myDisplayName = matrixService.config().display_name; + if (myDisplayName) { + myDisplayName = myDisplayName.toLocaleLowerCase(); + } + if ( (myDisplayName && content.toLocaleLowerCase().indexOf(myDisplayName) != -1) || + (myUserId && content.toLocaleLowerCase().indexOf(myUserId) != -1) ) { + shouldBing = true; + } + + // bing word list check + if (bingWords && !shouldBing) { + for (var i=0; i Outgoing Call -- cgit 1.4.1 From 5aaa3c09c1066333f6fa51effb771432f1c63bf1 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 16 Sep 2014 15:34:33 +0100 Subject: hidden/minimise/focus disaster disclaimer with the TODO --- webclient/components/matrix/event-handler-service.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 389eee5471..d61a150463 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -183,6 +183,10 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { var shouldBing = $rootScope.containsBingWord(event.content.body); // TODO: Binging every message when idle doesn't make much sense. Can we use this more sensibly? + // Unfortunately document.hidden = false on ubuntu chrome if chrome is minimised / does not have focus; + // true when you swap tabs though. However, for the case where the chat screen is OPEN and there is + // another window on top, we want to be notifying for those events. This DOES mean that there will be + // notifications when currently viewing the chat screen though, but that is preferable to the alternative imo. var isIdle = (document.hidden || matrixService.presence.unavailable === mPresence.getState()); if (shouldBing) { -- cgit 1.4.1 From 3395a3305f9477e950849b18c2caacfef70c118b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 16 Sep 2014 15:47:29 +0100 Subject: Bing on all the things if there are 0 bing words. --- webclient/components/matrix/event-handler-service.js | 6 ++++++ webclient/settings/settings.html | 1 + 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 d61a150463..72859eae3e 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -189,6 +189,12 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { // notifications when currently viewing the chat screen though, but that is preferable to the alternative imo. var isIdle = (document.hidden || matrixService.presence.unavailable === mPresence.getState()); + // always bing if there are 0 bing words... apparently. + var bingWords = matrixService.config().bingWords; + if (bingWords && bingWords.length === 0) { + shouldBing = true; + } + if (shouldBing) { console.log("Displaying notification for "+JSON.stringify(event)); var notification = new window.Notification( diff --git a/webclient/settings/settings.html b/webclient/settings/settings.html index 1a42ae435a..b251cce569 100644 --- a/webclient/settings/settings.html +++ b/webclient/settings/settings.html @@ -54,6 +54,7 @@ Notifications are enabled. You will be alerted when a message contains your user ID or display name.

    Additional words to alert on:

    +

    Leave blank to alert on all messages.

      -- cgit 1.4.1 From d7b206cc9325e4cc7b460bf5bfc218ad2b304fc1 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 16 Sep 2014 16:01:38 +0100 Subject: Added basic RegExp support. --- webclient/components/matrix/event-handler-service.js | 6 ++---- webclient/settings/settings.html | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 72859eae3e..0c7c0d1696 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -74,10 +74,8 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { // bing word list check if (bingWords && !shouldBing) { for (var i=0; i

      Additional words to alert on:

      Leave blank to alert on all messages.

      -
      • {{word}}
      • -- cgit 1.4.1 From 95e171e19a323bd92da5f20d651a52517b47ce03 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 16 Sep 2014 16:23:20 +0100 Subject: Don't bing for sent messages. Handle cases where the member is unknown rather than erroring out. --- webclient/components/matrix/event-handler-service.js | 11 ++++++++--- webclient/room/room.html | 2 +- 2 files changed, 9 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 0c7c0d1696..55e8480a19 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -177,7 +177,7 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { $rootScope.events.rooms[event.room_id].messages.push(event); } - if (window.Notification) { + if (window.Notification && event.user_id != matrixService.config().user_id) { var shouldBing = $rootScope.containsBingWord(event.content.body); // TODO: Binging every message when idle doesn't make much sense. Can we use this more sensibly? @@ -195,12 +195,17 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { if (shouldBing) { console.log("Displaying notification for "+JSON.stringify(event)); + var member = $rootScope.events.rooms[event.room_id].members[event.user_id]; + var displayname = undefined; + if (member) { + displayname = member.displayname; + } var notification = new window.Notification( - ($rootScope.events.rooms[event.room_id].members[event.user_id].displayname || event.user_id) + + (displayname || event.user_id) + " (" + (matrixService.getRoomIdToAliasMapping(event.room_id) || event.room_id) + ")", // FIXME: don't leak room_ids here { "body": event.content.body, - "icon": $rootScope.events.rooms[event.room_id].members[event.user_id].avatar_url + "icon": member ? member.avatar_url : undefined }); $timeout(function() { notification.close(); diff --git a/webclient/room/room.html b/webclient/room/room.html index 86ec2e51ca..886c2afe64 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -105,7 +105,7 @@ Outgoing Call -- cgit 1.4.1 From d6c0cff3bdf5de6229d10503520535076c8aae5d Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 16 Sep 2014 16:31:16 +0100 Subject: Bugfix when content isn't a string. --- webclient/components/matrix/event-handler-service.js | 2 +- 1 file changed, 1 insertion(+), 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 55e8480a19..258de9a31e 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -51,7 +51,7 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { // bit strange to put the impl in this service though, but I can't think of a better // file to put it in. $rootScope.containsBingWord = function(content) { - if (!content) { + if (!content || $.type(content) != "string") { return false; } var bingWords = matrixService.config().bingWords; -- cgit 1.4.1 From 9aed6a06cf23ca3702dc2e7337f3aca6aa75a83f Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 17 Sep 2014 15:38:20 +0200 Subject: SYWEB-15: Always show the room alias as well as its name in the UI --- webclient/components/matrix/matrix-filter.js | 110 ++++++++++++++------------- 1 file changed, 56 insertions(+), 54 deletions(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-filter.js b/webclient/components/matrix/matrix-filter.js index 015a88bcad..8b168cdedb 100644 --- a/webclient/components/matrix/matrix-filter.js +++ b/webclient/components/matrix/matrix-filter.js @@ -26,72 +26,74 @@ angular.module('matrixFilter', []) // If there is an alias, use it // TODO: only one alias is managed for now var alias = matrixService.getRoomIdToAliasMapping(room_id); - if (alias) { - roomName = alias; - } - - if (undefined === roomName) { - 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) { - var member = room.members[i]; - if (member.state_key !== matrixService.config().user_id) { + 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 (alias) { + roomName = alias; + } + 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) { + var member = room.members[i]; + if (member.state_key !== matrixService.config().user_id) { - if (member.state_key in $rootScope.presence) { - // If the user is available in presence, use the displayname there - // as it is the most uptodate - roomName = $rootScope.presence[member.state_key].content.displayname; - } - else if (member.content.displayname) { - roomName = member.content.displayname; - } - else { - roomName = member.state_key; - } + if (member.state_key in $rootScope.presence) { + // If the user is available in presence, use the displayname there + // as it is the most uptodate + roomName = $rootScope.presence[member.state_key].content.displayname; } - } - } - else if (1 === Object.keys(room.members).length) { - // The other member may be in the invite list, get all invited users - var invitedUserIDs = []; - for (var i in room.messages) { - var message = room.messages[i]; - if ("m.room.member" === message.type && "invite" === message.membership) { - // Make sure there is no duplicate user - if (-1 === invitedUserIDs.indexOf(message.state_key)) { - invitedUserIDs.push(message.state_key); - } - } - } - - // For now, only 1:1 room needs to be renamed. It means only 1 invited user - if (1 === invitedUserIDs.length) { - var userID = invitedUserIDs[0]; - - // Try to resolve his displayname in presence global data - if (userID in $rootScope.presence) { - roomName = $rootScope.presence[userID].content.displayname; + else if (member.content.displayname) { + roomName = member.content.displayname; } else { - roomName = userID; + roomName = member.state_key; } } } } + else if (1 === Object.keys(room.members).length) { + // The other member may be in the invite list, get all invited users + var invitedUserIDs = []; + for (var i in room.messages) { + var message = room.messages[i]; + if ("m.room.member" === message.type && "invite" === message.membership) { + // Make sure there is no duplicate user + if (-1 === invitedUserIDs.indexOf(message.state_key)) { + invitedUserIDs.push(message.state_key); + } + } + } + + // For now, only 1:1 room needs to be renamed. It means only 1 invited user + if (1 === invitedUserIDs.length) { + var userID = invitedUserIDs[0]; + + // Try to resolve his displayname in presence global data + if (userID in $rootScope.presence) { + roomName = $rootScope.presence[userID].content.displayname; + } + else { + roomName = userID; + } + } + } } } + // Always show the alias in the room displayed name + if (roomName && alias && alias !== roomName) { + roomName += " (" + alias + ")"; + } + if (undefined === roomName) { // By default, use the room ID roomName = room_id; -- cgit 1.4.1 From 65c37cc85213c919add2b3fa9ac107edd869df2d Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 17 Sep 2014 16:12:55 +0200 Subject: SYWEB-22: Format emote('/me') messages correctly in desktop notification --- webclient/components/matrix/event-handler-service.js | 8 +++++++- 1 file changed, 7 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 258de9a31e..8eb8a6b180 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -200,11 +200,17 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { if (member) { displayname = member.displayname; } + + var message = event.content.body; + if (event.content.msgtype === "m.emote") { + message = "* " + displayname + " " + message; + } + var notification = new window.Notification( (displayname || event.user_id) + " (" + (matrixService.getRoomIdToAliasMapping(event.room_id) || event.room_id) + ")", // FIXME: don't leak room_ids here { - "body": event.content.body, + "body": message, "icon": member ? member.avatar_url : undefined }); $timeout(function() { -- cgit 1.4.1 From 16f55d42752ad8feb473963457834c4fa752245a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 17 Sep 2014 16:29:21 +0100 Subject: webclient SYWEB-3 : Public rooms are bold. Can't think of a nicer way which doesn't clutter the recents list. --- webclient/app.css | 4 ++++ webclient/components/matrix/event-handler-service.js | 11 +++++++++++ webclient/components/matrix/event-stream-service.js | 2 ++ webclient/recents/recents.html | 2 +- 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'webclient/components') diff --git a/webclient/app.css b/webclient/app.css index 704cd83947..736aea660c 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -603,6 +603,10 @@ a:active { color: #000; } width: auto; } +.recentsPublicRoom { + font-weight: bold; +} + .recentsRoomSummaryUsersCount, .recentsRoomSummaryTS { color: #888; font-size: 12px; diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 8eb8a6b180..0be294d745 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -511,6 +511,17 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { member = room.members[user_id]; } return member; + }, + + setRoomVisibility: function(room_id, visible) { + if (!visible) { + return; + } + + var room = $rootScope.events.rooms[room_id]; + if (room) { + room.visibility = visible; + } } }; }]); diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index 6f92332246..5af1ab2911 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -120,6 +120,8 @@ angular.module('eventStreamService', []) if ("state" in room) { eventHandlerService.handleEvents(room.state, false, true); } + + eventHandlerService.setRoomVisibility(room.room_id, room.visibility); } var presence = response.data.presence; diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html index e783d3a6b4..7fec8f03e5 100644 --- a/webclient/recents/recents.html +++ b/webclient/recents/recents.html @@ -5,7 +5,7 @@ class ="recentsRoom" ng-class="{'recentsRoomSelected': (room.room_id === recentsSelectedRoomID)}">
-
{{ room.room_id | mRoomName }} @@ -14,7 +14,11 @@ - {{ (room.lastMsg.ts) | date:'MMM d HH:mm' }} + + {{lastMsg = room.messages[room.messages.length - 1];""}} + + {{ (lastMsg.ts) | date:'MMM d HH:mm' }}
- {{lastMsg = room.messages[room.messages.length - 1];""}} + {{ lastMsg = eventHandlerService.getLastMessage(room.room_id, true);"" }} {{ (lastMsg.ts) | date:'MMM d HH:mm' }}
+ {{ room.room_id | mRoomName }} -- cgit 1.4.1 From aeaeceb92c648bf99bce60eb1bc77fcc81020d53 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 17 Sep 2014 16:38:32 +0100 Subject: Create room entries for public rooms too so their public state is transferred over correctly when you join it. --- webclient/components/matrix/event-handler-service.js | 1 + webclient/home/home-controller.js | 2 ++ 2 files changed, 3 insertions(+) (limited to 'webclient/components') diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 0be294d745..d3de10c7e2 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -517,6 +517,7 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { if (!visible) { return; } + initRoom(room_id); var room = $rootScope.events.rooms[room_id]; if (room) { diff --git a/webclient/home/home-controller.js b/webclient/home/home-controller.js index 8ba817ca68..e35219bebb 100644 --- a/webclient/home/home-controller.js +++ b/webclient/home/home-controller.js @@ -53,6 +53,8 @@ angular.module('HomeController', ['matrixService', 'eventHandlerService', 'Recen // Add room_alias & room_display_name members angular.extend(room, matrixService.getRoomAliasAndDisplayName(room)); + + eventHandlerService.setRoomVisibility(room.room_id, "public"); } } ); -- cgit 1.4.1 From 1c0408de083aa101c3f9bc0d0131324bc5782be6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 18 Sep 2014 11:59:27 +0100 Subject: unbreak calls in firefox --- webclient/components/matrix/matrix-call.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'webclient/components') diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index bf1e61ad7e..2ecb8b05ff 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -59,7 +59,7 @@ angular.module('MatrixCall', []) var stunServer = 'stun:stun.l.google.com:19302'; var pc; if (window.mozRTCPeerConnection) { - pc = window.mozRTCPeerConnection({'url': stunServer}); + pc = new window.mozRTCPeerConnection({'url': stunServer}); } else { pc = new window.RTCPeerConnection({"iceServers":[{"urls":"stun:stun.l.google.com:19302"}]}); } -- cgit 1.4.1 From ac8d73b2588684378bd6608c1ee40f059ff0eac3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 18 Sep 2014 10:35:44 +0100 Subject: Patch for SYWEB-40 : isStateEvent is not being set correctly, and really shouldn't be a configurable arg in the first place. As a result of being undefined, the events.rooms[rid].members object was not being updated in some cases, which combined with the recents-filter bug (32808e4), caused federated rooms to not appear in the recents list. --- webclient/components/matrix/event-handler-service.js | 10 ++++++++-- 1 file changed, 8 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 d3de10c7e2..ad69d297fa 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -243,8 +243,9 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { // add membership changes as if they were a room message if something interesting changed // 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) { + // with other other room messages XXX This is no longer true, you only get a single event, not a room message event. + // FIXME: This possibly reintroduces multiple join messages. + if (event.content.prev !== event.content.membership) { // && !isStateEvent if (isLiveEvent) { $rootScope.events.rooms[event.room_id].messages.push(event); } @@ -375,6 +376,7 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { handleMessage(event, isLiveEvent); break; case "m.room.member": + isStateEvent = true; handleRoomMember(event, isLiveEvent, isStateEvent); break; case "m.presence": @@ -404,6 +406,8 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { // isLiveEvents determines whether notifications should be shown, whether // messages get appended to the start/end of lists, etc. handleEvents: function(events, isLiveEvents, isStateEvents) { + // XXX FIXME TODO: isStateEvents is being left as undefined sometimes. It makes no sense + // to have isStateEvents as an arg, since things like m.room.member are ALWAYS state events. for (var i=0; i=0; i--) { + // FIXME: Being live != being state this.handleEvent(events[i], isLiveEvents, isLiveEvents); } // Store where to start pagination -- cgit 1.4.1