From 7346ea85c08d0153644398be8d271169351b0c57 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Thu, 4 Sep 2014 11:19:28 +0200 Subject: Moved mRoomName filter into matrix-filter.js, a place for all generic filters using Matrix data. --- webclient/room/room-controller.js | 2 +- webclient/room/room.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'webclient/room') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index c3f72c9d25..cdef9ab8a1 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -angular.module('RoomController', ['ngSanitize', 'mFileInput']) +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) { 'use strict'; diff --git a/webclient/room/room.html b/webclient/room/room.html index 6732a7b3ae..493884f601 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -3,7 +3,7 @@
[matrix]
- {{ room_id | roomName }} + {{ room_id | mRoomName }}
-- cgit 1.5.1 From 3bc7bba2625e291fcbf0c30f30c20fc2d1756a49 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 4 Sep 2014 09:40:15 -0700 Subject: switch IRC-style command parser to use regexps rather than split(" ") so that it doesn't choke on consecutive whitespaces yield better errors for invalid commands don't pass invalid commands through as messages support kick reasons --- webclient/components/matrix/matrix-service.js | 11 +- webclient/room/room-controller.js | 140 ++++++++++++++++---------- 2 files changed, 96 insertions(+), 55 deletions(-) (limited to 'webclient/room') diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 25222a9e9e..436ff5462a 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -169,14 +169,19 @@ angular.module('matrixService', []) // Change the membership of an another user setMembership: function(room_id, user_id, membershipValue) { + return this.setMemberShipObject(room_id, user_id, { + membership : membershipValue + }); + }, + + // Change the membership of an another user + setMembershipObject: function(room_id, user_id, membershipObject) { // The REST path spec var path = "/rooms/$room_id/state/m.room.member/$user_id"; path = path.replace("$room_id", encodeURIComponent(room_id)); path = path.replace("$user_id", user_id); - return doRequest("PUT", path, undefined, { - membership: membershipValue - }); + return doRequest("PUT", path, undefined, membershipObject); }, // Bans a user from from a room diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index cdef9ab8a1..6e6d6e6356 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -287,94 +287,130 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) $scope.state.sending = true; var promise; + var isCmd = false; // Check for IRC style commands first - if ($scope.textInput.indexOf("/") === 0) { - var args = $scope.textInput.split(' '); - var cmd = args[0]; + var line = $scope.textInput; + + // trim any trailing whitespace, as it can confuse the parser for IRC-style commands + line = line.replace(/\s+$/, ""); + + if (line[0] === "/" && line[1] !== "/") { + isCmd = true; + + var bits = line.match(/^(\S+?)( +(.*))?$/); + var cmd = bits[1]; + var args = bits[3]; + + console.log("cmd: " + cmd + ", args: " + args); switch (cmd) { case "/me": - var emoteMsg = args.slice(1).join(' '); - promise = matrixService.sendEmoteMessage($scope.room_id, emoteMsg); + promise = matrixService.sendEmoteMessage($scope.room_id, args); break; case "/nick": // Change user display name - if (2 === args.length) { - promise = matrixService.setDisplayName(args[1]); - } + promise = matrixService.setDisplayName(args); break; case "/kick": - // Kick a user from the room - if (2 === args.length) { - var user_id = args[1]; - - // Set his state in the room as leave - promise = matrixService.setMembership($scope.room_id, user_id, "leave"); + var matches = args.match(/^(\S+?)( +(.*))?$/); + if (matches.length === 2) { + promise = matrixService.setMembership($scope.room_id, matches[1], "leave"); + } + else if (matches.length === 4) { + promise = matrixService.setMembershipObject($scope.room_id, matches[1], { + membership: "leave", + reason: matches[3] // TODO: we need to specify resaon in the spec + }); + } + else { + $scope.feedback = "Usage: /kick []"; } break; - + case "/ban": - // Ban a user from the room - if (2 <= args.length) { - // TODO: The user may have entered the display name - // Need display name -> user_id resolution. Pb: how to manage user with same display names? - var user_id = args[1]; - - // Does the user provide a reason? - if (3 <= args.length) { - var reason = args.slice(2).join(' '); - } - promise = matrixService.ban($scope.room_id, user_id, reason); + // Ban a user from the room with optional reason + var matches = args.match(/^(\S+?)( +(.*))?$/); + if (matches) { + promise = matrixService.ban($scope.room_id, matches[1], matches[3]); + } + else { + $scope.feedback = "Usage: /ban []"; } break; - + case "/unban": // Unban a user from the room - if (2 === args.length) { - var user_id = args[1]; - - // Reset the user membership to leave to unban him - promise = matrixService.setMembership($scope.room_id, user_id, "leave"); + // FIXME: this feels horribly asymmetrical - why are we banning via RPC + // and unbanning by editing the membership list? + // Why can't we specify a reason? + var matches = args.match(/^(\S+)$/); + if (matches) { + // Reset the user membership to "leave" to unban him + promise = matrixService.setMembership($scope.room_id, args, "leave"); + } + else { + $scope.feedback = "Usage: /unban "; } break; case "/op": // Define the power level of a user - if (3 === args.length) { - var user_id = args[1]; - var powerLevel = parseInt(args[2]); - promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel); + var matches = args.match(/^(\S+?)( +(\d+))?$/); + var powerLevel = 50; // default power level for op + if (matches) { + var user_id = matches[1]; + if (matches.length == 4) { + powerLevel = parseInt(matches[3]); + } + if (powerLevel !== NaN) { + promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel); + } + } + if (!promise) { + $scope.feedback = "Usage: /op []"; } break; case "/deop": // Reset the power level of a user - if (2 === args.length) { - var user_id = args[1]; - promise = matrixService.setUserPowerLevel($scope.room_id, user_id, undefined); + var matches = args.match(/^(\S+)$/); + if (matches) { + promise = matrixService.setUserPowerLevel($scope.room_id, args, undefined); + } + else { + $scope.feedback = "Usage: /deop "; } break; + + default: + $scope.feedback = ("Unrecognised IRC-style command: " + cmd); + break; } } - if (!promise) { - // Send the text message + // By default send this as a message unless it's an IRC-style command + if (!promise && !isCmd) { promise = matrixService.sendTextMessage($scope.room_id, $scope.textInput); } - - promise.then( - function() { - console.log("Request successfully sent"); - $scope.textInput = ""; - $scope.state.sending = false; - }, - function(error) { - $scope.feedback = "Request failed: " + error.data.error; - $scope.state.sending = false; - }); + + if (promise) { + promise.then( + function() { + console.log("Request successfully sent"); + $scope.textInput = ""; + $scope.state.sending = false; + }, + function(error) { + $scope.feedback = "Request failed: " + error.data.error; + $scope.state.sending = false; + }); + } + else { + $scope.state.sending = false; + } }; $scope.onInit = function() { -- cgit 1.5.1 From 3bfffab2011262b21e1ae24250d88788477aa32e Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 10:40:59 +0200 Subject: Do not systematically scroll to the bottom on new events in the room --- webclient/room/room-controller.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'webclient/room') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 6e6d6e6356..d8716de020 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -54,8 +54,14 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) $scope.$on(eventHandlerService.MSG_EVENT, function(ngEvent, event, isLive) { if (isLive && event.room_id === $scope.room_id) { - scrollToBottom(); - + + // Do not autoscroll to the bottom to display this new event if the user is not at the bottom. + // Exception: if the event is from the user, scroll to the bottom + var objDiv = document.getElementById("messageTableWrapper"); + if ( (objDiv.offsetHeight + objDiv.scrollTop >= objDiv.scrollHeight) || event.user_id === $scope.state.user_id) { + scrollToBottom(); + } + if (window.Notification) { // Show notification when the user is idle if (matrixService.presence.offline === mPresence.getState()) { -- cgit 1.5.1 From 43369cbe06563203f95dc252d1e298551317a481 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 11:13:33 +0200 Subject: Cleaned all sending references as it not used --- webclient/room/room-controller.js | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) (limited to 'webclient/room') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index d8716de020..07e3695b69 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -33,8 +33,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) can_paginate: true, // this is toggled off when we run out of items paginating: false, // used to avoid concurrent pagination requests pulling in dup contents stream_failure: undefined, // the response when the stream fails - // FIXME: sending has been disabled, as surely messages should be sent in the background rather than locking the UI synchronously --Matthew - sending: false // true when a message is being sent. It helps to disable the UI when a process is running }; $scope.members = {}; $scope.autoCompleting = false; @@ -262,7 +260,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) normaliseMembersPowerLevels(); } - } + }; // Normalise users power levels so that the user with the higher power level // will have a bar covering 100% of the width of his avatar @@ -283,14 +281,12 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) member.powerLevelNorm = (member.powerLevel * 100) / maxPowerLevel; } } - } + }; $scope.send = function() { if ($scope.textInput === "") { return; } - - $scope.state.sending = true; var promise; var isCmd = false; @@ -368,7 +364,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) var powerLevel = 50; // default power level for op if (matches) { var user_id = matches[1]; - if (matches.length == 4) { + if (matches.length === 4) { powerLevel = parseInt(matches[3]); } if (powerLevel !== NaN) { @@ -407,16 +403,11 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) function() { console.log("Request successfully sent"); $scope.textInput = ""; - $scope.state.sending = false; }, function(error) { $scope.feedback = "Request failed: " + error.data.error; - $scope.state.sending = false; }); } - else { - $scope.state.sending = false; - } }; $scope.onInit = function() { @@ -573,25 +564,19 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) }; $scope.sendImage = function(url, body) { - $scope.state.sending = true; matrixService.sendImageMessage($scope.room_id, url, body).then( function() { console.log("Image sent"); - $scope.state.sending = false; }, function(error) { $scope.feedback = "Failed to send image: " + error.data.error; - $scope.state.sending = false; }); }; $scope.imageFileToSend; $scope.$watch("imageFileToSend", function(newValue, oldValue) { if ($scope.imageFileToSend) { - - $scope.state.sending = true; - // Upload this image with its thumbnail to Internet mFileUpload.uploadImageAndThumbnail($scope.imageFileToSend, THUMBNAIL_SIZE).then( function(imageMessage) { @@ -599,16 +584,13 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) matrixService.sendMessage($scope.room_id, undefined, imageMessage).then( function() { console.log("Image message sent"); - $scope.state.sending = false; }, function(error) { $scope.feedback = "Failed to send image message: " + error.data.error; - $scope.state.sending = false; }); }, function(error) { $scope.feedback = "Can't upload image"; - $scope.state.sending = false; } ); } @@ -624,6 +606,6 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) call.onHangup = $rootScope.onCallHangup; call.placeCall(); $rootScope.currentCall = call; - } + }; }]); -- cgit 1.5.1 From 584591c3e3e20022fb22f20c273940df50f05d0b Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 14:09:14 +0200 Subject: Fixed duplicated messages sending in slow network condition. Show the message sending flow state in the messages list: - While sending, the message appears semi transparent in the chat. - If successfully sent, it appears as before, ie normal - In case of failure, it appears in red with an Unsent text. --- webclient/app.css | 7 +++++++ webclient/room/room-controller.js | 44 ++++++++++++++++++++++++++++++++++++--- webclient/room/room.html | 9 ++++++-- 3 files changed, 55 insertions(+), 5 deletions(-) (limited to 'webclient/room') diff --git a/webclient/app.css b/webclient/app.css index 425d5bb11a..9a4666ad7b 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -417,6 +417,13 @@ a:active { color: #000; } text-align: left ! important; } +.bubble .messagePending { + opacity: 0.3 +} +.messageUnSent { + color: #F00; +} + #room-fullscreen-image { position: absolute; top: 0px; diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 07e3695b69..b9ba23dc48 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -32,7 +32,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) first_pagination: true, // this is toggled off when the first pagination is done can_paginate: true, // this is toggled off when we run out of items paginating: false, // used to avoid concurrent pagination requests pulling in dup contents - stream_failure: undefined, // the response when the stream fails + stream_failure: undefined // the response when the stream fails }; $scope.members = {}; $scope.autoCompleting = false; @@ -395,17 +395,55 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) // By default send this as a message unless it's an IRC-style command if (!promise && !isCmd) { - promise = matrixService.sendTextMessage($scope.room_id, $scope.textInput); + var message = $scope.textInput; + $scope.textInput = ""; + + // Echo the message to the room + // To do so, create a minimalist fake text message event and add it to the in-memory list of room messages + var echoMessage = { + content: { + body: message, + hsob_ts: "Sending...", // Hack timestamp to display this text in place of the message time + msgtype: "m.text" + }, + room_id: $scope.room_id, + type: "m.room.message", + user_id: $scope.state.user_id, + echo_msg_state: "messagePending" // Add custom field to indicate the state of this fake message to HTML + }; + + $rootScope.events.rooms[$scope.room_id].messages.push(echoMessage); + scrollToBottom(); + + // Make the request + promise = matrixService.sendTextMessage($scope.room_id, message); } if (promise) { promise.then( function() { console.log("Request successfully sent"); - $scope.textInput = ""; + + if (echoMessage) { + // Remove the fake echo message from the room messages + // It will be replaced by the one acknowledged by the server + var index = $rootScope.events.rooms[$scope.room_id].messages.indexOf(echoMessage); + if (index > -1) { + $rootScope.events.rooms[$scope.room_id].messages.splice(index, 1); + } + } + else { + $scope.textInput = ""; + } }, function(error) { $scope.feedback = "Request failed: " + error.data.error; + + if (echoMessage) { + // Mark the message as unsent for the rest of the page life + echoMessage.content.hsob_ts = "Unsent"; + echoMessage.echo_msg_state = "messageUnSent"; + } }); } }; diff --git a/webclient/room/room.html b/webclient/room/room.html index 493884f601..147113987e 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -40,7 +40,10 @@ ng-class="(events.rooms[room_id].messages[$index + 1].user_id !== msg.user_id ? 'differentUser' : '') + (msg.user_id === state.user_id ? ' mine' : '')" scroll-item>
{{ members[msg.user_id].displayname || msg.user_id }}
-
{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm' }}
+
+ {{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm' }} +
- +
-- cgit 1.5.1 From dcf0a6fbfd76257fbf3193ff128505dc0965f67d Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 16:45:59 +0200 Subject: Display ban & kick reason --- webclient/recents/recents.html | 6 ++++++ webclient/room/room.html | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'webclient/room') diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html index 1c059249bf..280d0632ab 100644 --- a/webclient/recents/recents.html +++ b/webclient/recents/recents.html @@ -34,11 +34,17 @@ {{ {"join": "kicked", "ban": "unbanned"}[room.lastMsg.content.prev] }} {{ room.lastMsg.state_key | mUserDisplayName: room.room_id }} + + : {{ room.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 }} +
diff --git a/webclient/room/room.html b/webclient/room/room.html index 147113987e..5bd2cc92d5 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -62,13 +62,20 @@ {{ members[msg.user_id].displayname || msg.user_id }} {{ {"join": "kicked", "ban": "unbanned"}[msg.content.prev] }} {{ members[msg.state_key].displayname || msg.state_key }} + + : {{ msg.content.reason }} + {{ members[msg.user_id].displayname || msg.user_id }} {{ {"invite": "invited", "ban": "banned"}[msg.content.membership] }} {{ members[msg.state_key].displayname || msg.state_key }} - + + : {{ msg.content.reason }} + + + Date: Fri, 5 Sep 2014 16:56:50 +0200 Subject: BF: Make /unban work again --- webclient/components/matrix/matrix-service.js | 2 +- webclient/room/room-controller.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'webclient/room') diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 436ff5462a..18a4841298 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -169,7 +169,7 @@ angular.module('matrixService', []) // Change the membership of an another user setMembership: function(room_id, user_id, membershipValue) { - return this.setMemberShipObject(room_id, user_id, { + return this.setMembershipObject(room_id, user_id, { membership : membershipValue }); }, diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index b9ba23dc48..5a2f06d8ee 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -351,7 +351,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) var matches = args.match(/^(\S+)$/); if (matches) { // Reset the user membership to "leave" to unban him - promise = matrixService.setMembership($scope.room_id, args, "leave"); + promise = matrixService.setMembership($scope.room_id, matches[1], "leave"); } else { $scope.feedback = "Usage: /unban "; -- cgit 1.5.1 From cf4c17deaf7110f52a22456fa3cb63a80825de14 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 17:23:41 +0200 Subject: Added sanity checks in commands --- webclient/room/room-controller.js | 89 ++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 34 deletions(-) (limited to 'webclient/room') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 5a2f06d8ee..39f8635d76 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -313,32 +313,44 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) case "/nick": // Change user display name - promise = matrixService.setDisplayName(args); + if (args) { + promise = matrixService.setDisplayName(args); + } + else { + $scope.feedback = "Usage: /nick "; + } break; case "/kick": - var matches = args.match(/^(\S+?)( +(.*))?$/); - if (matches.length === 2) { - promise = matrixService.setMembership($scope.room_id, matches[1], "leave"); - } - else if (matches.length === 4) { - promise = matrixService.setMembershipObject($scope.room_id, matches[1], { - membership: "leave", - reason: matches[3] // TODO: we need to specify resaon in the spec - }); + // Kick a user from the room with an optional reason + if (args) { + var matches = args.match(/^(\S+?)( +(.*))?$/); + if (matches.length === 2) { + promise = matrixService.setMembership($scope.room_id, matches[1], "leave"); + } + else if (matches.length === 4) { + promise = matrixService.setMembershipObject($scope.room_id, matches[1], { + membership: "leave", + reason: matches[3] // TODO: we need to specify resaon in the spec + }); + } } - else { + + if (!promise) { $scope.feedback = "Usage: /kick []"; } break; case "/ban": - // Ban a user from the room with optional reason - var matches = args.match(/^(\S+?)( +(.*))?$/); - if (matches) { - promise = matrixService.ban($scope.room_id, matches[1], matches[3]); + // Ban a user from the room with an optional reason + if (args) { + var matches = args.match(/^(\S+?)( +(.*))?$/); + if (matches) { + promise = matrixService.ban($scope.room_id, matches[1], matches[3]); + } } - else { + + if (!promise) { $scope.feedback = "Usage: /ban []"; } break; @@ -348,29 +360,35 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) // FIXME: this feels horribly asymmetrical - why are we banning via RPC // and unbanning by editing the membership list? // Why can't we specify a reason? - var matches = args.match(/^(\S+)$/); - if (matches) { - // Reset the user membership to "leave" to unban him - promise = matrixService.setMembership($scope.room_id, matches[1], "leave"); + if (args) { + var matches = args.match(/^(\S+)$/); + if (matches) { + // Reset the user membership to "leave" to unban him + promise = matrixService.setMembership($scope.room_id, matches[1], "leave"); + } } - else { + + if (!promise) { $scope.feedback = "Usage: /unban "; } break; case "/op": // Define the power level of a user - var matches = args.match(/^(\S+?)( +(\d+))?$/); - var powerLevel = 50; // default power level for op - if (matches) { - var user_id = matches[1]; - if (matches.length === 4) { - powerLevel = parseInt(matches[3]); - } - if (powerLevel !== NaN) { - promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel); + if (args) { + var matches = args.match(/^(\S+?)( +(\d+))?$/); + var powerLevel = 50; // default power level for op + if (matches) { + var user_id = matches[1]; + if (matches.length === 4) { + powerLevel = parseInt(matches[3]); + } + if (powerLevel !== NaN) { + promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel); + } } } + if (!promise) { $scope.feedback = "Usage: /op []"; } @@ -378,11 +396,14 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) case "/deop": // Reset the power level of a user - var matches = args.match(/^(\S+)$/); - if (matches) { - promise = matrixService.setUserPowerLevel($scope.room_id, args, undefined); + if (args) { + var matches = args.match(/^(\S+)$/); + if (matches) { + promise = matrixService.setUserPowerLevel($scope.room_id, args, undefined); + } } - else { + + if (!promise) { $scope.feedback = "Usage: /deop "; } break; -- cgit 1.5.1 From 3be615677407a4224202a3ae107762b1f3bc97ac Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 17:30:50 +0200 Subject: Created kick & unban methods in matrixService. Made some factorisation. --- webclient/components/matrix/matrix-service.js | 32 ++++++++++++++++++--------- webclient/room/room-controller.js | 15 +++---------- 2 files changed, 24 insertions(+), 23 deletions(-) (limited to 'webclient/room') diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 18a4841298..8a0223979c 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -168,23 +168,20 @@ angular.module('matrixService', []) }, // Change the membership of an another user - setMembership: function(room_id, user_id, membershipValue) { - return this.setMembershipObject(room_id, user_id, { - membership : membershipValue - }); - }, - - // Change the membership of an another user - setMembershipObject: function(room_id, user_id, membershipObject) { + setMembership: function(room_id, user_id, membershipValue, reason) { + // The REST path spec var path = "/rooms/$room_id/state/m.room.member/$user_id"; path = path.replace("$room_id", encodeURIComponent(room_id)); path = path.replace("$user_id", user_id); - return doRequest("PUT", path, undefined, membershipObject); + return doRequest("PUT", path, undefined, { + membership : membershipValue, + reason: reason + }); }, - // Bans a user from from a room + // Bans a user from a room ban: function(room_id, user_id, reason) { var path = "/rooms/$room_id/ban"; path = path.replace("$room_id", encodeURIComponent(room_id)); @@ -194,7 +191,20 @@ angular.module('matrixService', []) reason: reason }); }, - + + // Unbans a user in a room + unban: function(room_id, user_id) { + // FIXME: To update when there will be homeserver API for unban + // For now, do an unban by resetting the user membership to "leave" + return this.setMembership(room_id, user_id, "leave"); + }, + + // Kicks a user from a room + kick: function(room_id, user_id, reason) { + // Set the user membership to "leave" to kick him + return this.setMembership(room_id, user_id, "leave", reason); + }, + // Retrieves the room ID corresponding to a room alias resolveRoomAlias:function(room_alias) { var path = "/_matrix/client/api/v1/directory/room/$room_alias"; diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 39f8635d76..905a0723d8 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -325,15 +325,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) // Kick a user from the room with an optional reason if (args) { var matches = args.match(/^(\S+?)( +(.*))?$/); - if (matches.length === 2) { - promise = matrixService.setMembership($scope.room_id, matches[1], "leave"); + if (matches) { + promise = matrixService.kick($scope.room_id, matches[1], matches[3]); } - else if (matches.length === 4) { - promise = matrixService.setMembershipObject($scope.room_id, matches[1], { - membership: "leave", - reason: matches[3] // TODO: we need to specify resaon in the spec - }); - } } if (!promise) { @@ -357,14 +351,11 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) case "/unban": // Unban a user from the room - // FIXME: this feels horribly asymmetrical - why are we banning via RPC - // and unbanning by editing the membership list? - // Why can't we specify a reason? if (args) { var matches = args.match(/^(\S+)$/); if (matches) { // Reset the user membership to "leave" to unban him - promise = matrixService.setMembership($scope.room_id, matches[1], "leave"); + promise = matrixService.unban($scope.room_id, matches[1]); } } -- cgit 1.5.1 From 12a23f01b4d2b4a9a10b3db5373d092136e9a772 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 17:52:11 +0200 Subject: autoscroll down(if the scroller was already at the bottom) when receiving member events --- webclient/room/room-controller.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'webclient/room') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 905a0723d8..2267283fb8 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -42,23 +42,24 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) $scope.imageURLToSend = ""; $scope.userIDToInvite = ""; - var scrollToBottom = function() { + var scrollToBottom = function(force) { console.log("Scrolling to bottom"); - $timeout(function() { - var objDiv = document.getElementById("messageTableWrapper"); - objDiv.scrollTop = objDiv.scrollHeight; - }, 0); + + // Do not autoscroll to the bottom to display the new event if the user is not at the bottom. + // Exception: in case where the event is from the user, we want to force scroll to the bottom + var objDiv = document.getElementById("messageTableWrapper"); + if ((objDiv.offsetHeight + objDiv.scrollTop >= objDiv.scrollHeight) || force) { + + $timeout(function() { + objDiv.scrollTop = objDiv.scrollHeight; + }, 0); + } }; $scope.$on(eventHandlerService.MSG_EVENT, function(ngEvent, event, isLive) { if (isLive && event.room_id === $scope.room_id) { - - // Do not autoscroll to the bottom to display this new event if the user is not at the bottom. - // Exception: if the event is from the user, scroll to the bottom - var objDiv = document.getElementById("messageTableWrapper"); - if ( (objDiv.offsetHeight + objDiv.scrollTop >= objDiv.scrollHeight) || event.user_id === $scope.state.user_id) { - scrollToBottom(); - } + + scrollToBottom(); if (window.Notification) { // Show notification when the user is idle @@ -80,6 +81,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) $scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) { if (isLive) { + scrollToBottom(); updateMemberList(event); } }); @@ -288,6 +290,8 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) return; } + scrollToBottom(true); + var promise; var isCmd = false; @@ -614,7 +618,8 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) }; $scope.sendImage = function(url, body) { - + scrollToBottom(true); + matrixService.sendImageMessage($scope.room_id, url, body).then( function() { console.log("Image sent"); -- cgit 1.5.1 From 8a7f7f50044bb277e8d2e9c08dc3cee1d88ab1ab Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 18:05:23 +0200 Subject: BF: Update the members list on banned & kicked "events" --- webclient/room/room-controller.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'webclient/room') diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 2267283fb8..8203b6ed3f 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -175,16 +175,18 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) var updateMemberList = function(chunk) { if (chunk.room_id != $scope.room_id) return; - // Ignore banned and kicked (leave) people - if ("ban" === chunk.membership || "leave" === chunk.membership) { - return; - } // set target_user_id to keep things clear var target_user_id = chunk.state_key; var isNewMember = !(target_user_id in $scope.members); if (isNewMember) { + + // Ignore banned and kicked (leave) people + if ("ban" === chunk.membership || "leave" === chunk.membership) { + return; + } + // FIXME: why are we copying these fields around inside chunk? if ("presence" in chunk.content) { chunk.presence = chunk.content.presence; @@ -208,6 +210,13 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) } else { // selectively update membership and presence else it will nuke the picture and displayname too :/ + + // Remove banned and kicked (leave) people + if ("ban" === chunk.membership || "leave" === chunk.membership) { + delete $scope.members[target_user_id]; + return; + } + var member = $scope.members[target_user_id]; member.membership = chunk.content.membership; if ("presence" in chunk.content) { -- cgit 1.5.1 From b3be06667d7b0968b808c77487a2909578a729cd Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Fri, 5 Sep 2014 18:46:34 +0200 Subject: BF: tab completion did not work with commands. $scope.input contained only the typed chars not the result of the completion. Needed to fire an event so that ng update the input model --- webclient/room/room-directive.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'webclient/room') diff --git a/webclient/room/room-directive.js b/webclient/room/room-directive.js index 659bcbc60f..e033b003e1 100644 --- a/webclient/room/room-directive.js +++ b/webclient/room/room-directive.js @@ -48,6 +48,9 @@ angular.module('RoomController') var search = /@?([a-zA-Z0-9_\-:\.]+)$/.exec(text); if (targetIndex === 0) { element[0].value = text; + + // Force angular to wake up and update the input ng-model by firing up input event + angular.element(element[0]).triggerHandler('input'); } else if (search && search[1]) { // console.log("search found: " + search); @@ -81,7 +84,10 @@ angular.module('RoomController') expansion += " "; element[0].value = text.replace(/@?([a-zA-Z0-9_\-:\.]+)$/, expansion); // cancel blink - element[0].className = ""; + element[0].className = ""; + + // Force angular to wake up and update the input ng-model by firing up input event + angular.element(element[0]).triggerHandler('input'); } else { // console.log("wrapped!"); @@ -91,6 +97,9 @@ angular.module('RoomController') }, 150); element[0].value = text; scope.tabCompleteIndex = 0; + + // Force angular to wake up and update the input ng-model by firing up input event + angular.element(element[0]).triggerHandler('input'); } } else { -- cgit 1.5.1