diff options
Diffstat (limited to 'webclient')
-rw-r--r-- | webclient/app-controller.js | 2 | ||||
-rwxr-xr-x | webclient/app.css | 1 | ||||
-rw-r--r-- | webclient/components/matrix/event-handler-service.js | 74 | ||||
-rw-r--r-- | webclient/components/matrix/matrix-filter.js | 11 | ||||
-rw-r--r-- | webclient/components/matrix/matrix-service.js | 8 | ||||
-rw-r--r-- | webclient/room/room-controller.js | 45 | ||||
-rw-r--r-- | webclient/room/room.html | 36 |
7 files changed, 156 insertions, 21 deletions
diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 7d61207554..e4b7cd286f 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -53,7 +53,7 @@ angular.module('MatrixWebClientController', ['matrixService', 'mPresence', 'even * Open a given page. * @param {String} url url of the page */ - $scope.goToPage = function(url) { + $rootScope.goToPage = function(url) { $location.url(url); }; diff --git a/webclient/app.css b/webclient/app.css index dc2d589c58..20a13aad81 100755 --- a/webclient/app.css +++ b/webclient/app.css @@ -416,7 +416,6 @@ textarea, input { text-align: right; float: right; margin-top: 15px; - width: 100%; } /*** Participant list ***/ diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index b1580b3d8a..3b1354cdef 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -58,14 +58,29 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { var shouldBing = false; // case-insensitive name check for user_id OR display_name if they exist + var userRegex = ""; var myUserId = matrixService.config().user_id; if (myUserId) { - myUserId = myUserId.toLocaleLowerCase(); + var localpart = getLocalPartFromUserId(myUserId); + if (localpart) { + localpart = localpart.toLocaleLowerCase(); + userRegex += "\\b" + localpart + "\\b"; + } } var myDisplayName = matrixService.config().display_name; if (myDisplayName) { myDisplayName = myDisplayName.toLocaleLowerCase(); + if (userRegex.length > 0) { + userRegex += "|"; + } + userRegex += "\\b" + myDisplayName + "\\b"; } + + var r = new RegExp(userRegex, 'i'); + if (content.search(r) >= 0) { + shouldBing = true; + } + if ( (myDisplayName && content.toLocaleLowerCase().indexOf(myDisplayName) != -1) || (myUserId && content.toLocaleLowerCase().indexOf(myUserId) != -1) ) { shouldBing = true; @@ -84,6 +99,18 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { return shouldBing; }; + var getLocalPartFromUserId = function(user_id) { + if (!user_id) { + return null; + } + var localpartRegex = /@(.*):\w+/i + var results = localpartRegex.exec(user_id); + if (results && results.length == 2) { + return results[1]; + } + return null; + }; + var initialSyncDeferred; var reset = function() { @@ -172,6 +199,17 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { }; var handleMessage = function(event, isLiveEvent) { + // Check for empty event content + var hasContent = false; + for (var prop in event.content) { + hasContent = true; + break; + } + if (!hasContent) { + // empty json object is a redacted event, so ignore. + return; + } + if (isLiveEvent) { if (event.user_id === matrixService.config().user_id && (event.content.msgtype === "m.text" || event.content.msgtype === "m.emote") ) { @@ -238,6 +276,12 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { "body": message, "icon": member ? member.avatar_url : undefined }); + + notification.onclick = function() { + console.log("notification.onclick() room=" + event.room_id); + $rootScope.goToPage('room/' + (event.room_id)); + }; + $timeout(function() { notification.close(); }, 5 * 1000); @@ -329,6 +373,31 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { $rootScope.events.rooms[event.room_id].messages.push(event); } }; + + var handleRedaction = function(event, isLiveEvent) { + if (!isLiveEvent) { + // we have nothing to remove, so just ignore it. + console.log("Received redacted event: "+JSON.stringify(event)); + return; + } + + // we need to remove something possibly: do we know the redacted + // event ID? + if (eventMap[event.redacts]) { + // remove event from list of messages in this room. + var eventList = $rootScope.events.rooms[event.room_id].messages; + for (var i=0; i<eventList.length; i++) { + if (eventList[i].event_id === event.redacts) { + console.log("Removing event " + event.redacts); + eventList.splice(i, 1); + break; + } + } + + // broadcast the redaction so controllers can nuke this + console.log("Redacted an event."); + } + } /** * Get the index of the event in $rootScope.events.rooms[room_id].messages @@ -491,6 +560,9 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) { case 'm.room.topic': handleRoomTopic(event, isLiveEvent, isStateEvent); break; + case 'm.room.redaction': + handleRedaction(event, isLiveEvent); + break; default: console.log("Unable to handle event type " + event.type); console.log(JSON.stringify(event, undefined, 4)); diff --git a/webclient/components/matrix/matrix-filter.js b/webclient/components/matrix/matrix-filter.js index e6f2acc5fd..3d64a569a1 100644 --- a/webclient/components/matrix/matrix-filter.js +++ b/webclient/components/matrix/matrix-filter.js @@ -47,7 +47,6 @@ angular.module('matrixFilter', []) else if (room.members && !isPublicRoom) { // Do not rename public room var user_id = matrixService.config().user_id; - // Else, build the name from its users // Limit the room renaming to 1:1 room if (2 === Object.keys(room.members).length) { @@ -65,8 +64,16 @@ angular.module('matrixFilter', []) var otherUserId; - if (Object.keys(room.members)[0] && Object.keys(room.members)[0] !== user_id) { + if (Object.keys(room.members)[0]) { otherUserId = Object.keys(room.members)[0]; + // this could be an invite event (from event stream) + if (otherUserId === user_id && + room.members[user_id].content.membership === "invite") { + // this is us being invited to this room, so the + // *user_id* is the other user ID and not the state + // key. + otherUserId = room.members[user_id].user_id; + } } else { // it's got to be an invite, or failing that a self-chat; diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index a4f0568bce..1840cf46c0 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -438,6 +438,14 @@ angular.module('matrixService', []) return this.sendMessage(room_id, msg_id, content); }, + redactEvent: function(room_id, event_id) { + var path = "/rooms/$room_id/redact/$event_id"; + path = path.replace("$room_id", room_id); + path = path.replace("$event_id", event_id); + var content = {}; + return doRequest("POST", path, undefined, content); + }, + // get a snapshot of the members in a room. getMemberList: function(room_id) { // Like the cmd client, escape room ids diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 78520a829d..841b5cccdd 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -133,7 +133,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) // 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) { + // add a 10px buffer to this check so if the message list is not *quite* + // at the bottom it still scrolls since it basically is at the bottom. + if ((10 + objDiv.offsetHeight + objDiv.scrollTop >= objDiv.scrollHeight) || force) { $timeout(function() { objDiv.scrollTop = objDiv.scrollHeight; @@ -983,10 +985,45 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput']) }; $scope.openJson = function(content) { - console.log("Displaying modal dialog for " + JSON.stringify(content)); + $scope.event_selected = content; + // scope this so the template can check power levels and enable/disable + // buttons + $scope.pow = matrixService.getUserPowerLevel; + var modalInstance = $modal.open({ - template: "<pre>" + angular.toJson(content, true) + "</pre>" + templateUrl: 'eventInfoTemplate.html', + controller: 'EventInfoController', + scope: $scope + }); + + modalInstance.result.then(function(action) { + if (action === "redact") { + var eventId = $scope.event_selected.event_id; + console.log("Redacting event ID " + eventId); + matrixService.redactEvent( + $scope.event_selected.room_id, + eventId + ).then(function(response) { + console.log("Redaction = " + JSON.stringify(response)); + }, function(error) { + console.error("Failed to redact event: "+JSON.stringify(error)); + if (error.data.error) { + $scope.feedback = error.data.error; + } + }); + } + }, function() { + // any dismiss code }); }; -}]); +}]) +.controller('EventInfoController', function($scope, $modalInstance) { + console.log("Displaying modal dialog for >>>> " + JSON.stringify($scope.event_selected)); + $scope.redact = function() { + console.log("User level = "+$scope.pow($scope.room_id, $scope.state.user_id)+ + " Redact level = "+$scope.events.rooms[$scope.room_id]["m.room.ops_levels"].content.redact_level); + console.log("Redact event >> " + JSON.stringify($scope.event_selected)); + $modalInstance.close("redact"); + }; +}); diff --git a/webclient/room/room.html b/webclient/room/room.html index e753b037fe..38b6d591ea 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -1,5 +1,18 @@ <div ng-controller="RoomController" data-ng-init="onInit()" class="room" style="height: 100%;"> + <script type="text/ng-template" id="eventInfoTemplate.html"> + <div class="modal-body"> + <pre> {{event_selected | json}} </pre> + </div> + <div class="modal-footer"> + <button ng-click="redact()" type="button" class="btn btn-danger" + ng-disabled="!events.rooms[room_id]['m.room.ops_levels'].content.redact_level || !pow(room_id, state.user_id) || pow(room_id, state.user_id) < events.rooms[room_id]['m.room.ops_levels'].content.redact_level" + title="Delete this event on all home servers. This cannot be undone."> + Redact + </button> + </div> + </script> + <div id="roomHeader"> <a href ng-click="goToPage('/')"><img src="img/logo-small.png" width="100" height="43" alt="[matrix]"/></a> <div class="roomHeaderInfo"> @@ -83,11 +96,11 @@ ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id || msg.user_id === state.user_id"/> </td> <td ng-class="(!msg.content.membership && ('m.room.topic' !== msg.type && 'm.room.name' !== msg.type))? (msg.content.msgtype === 'm.emote' ? 'emote text' : 'text') : 'membership text'"> - <div class="bubble"> - <span ng-if="'join' === msg.content.membership && msg.changedKey === 'membership'" ng-click="openJson(msg)"> + <div class="bubble" ng-click="openJson(msg)"> + <span ng-if="'join' === msg.content.membership && msg.changedKey === 'membership'"> {{ members[msg.state_key].displayname || msg.state_key }} joined </span> - <span ng-if="'leave' === msg.content.membership && msg.changedKey === 'membership'" ng-click="openJson(msg)"> + <span ng-if="'leave' === msg.content.membership && msg.changedKey === 'membership'"> <span ng-if="msg.user_id === msg.state_key"> {{ members[msg.state_key].displayname || msg.state_key }} left </span> @@ -101,7 +114,7 @@ </span> </span> <span ng-if="'invite' === msg.content.membership && msg.changedKey === 'membership' || - 'ban' === msg.content.membership && msg.changedKey === 'membership'" ng-click="openJson(msg)"> + 'ban' === msg.content.membership && msg.changedKey === 'membership'"> {{ members[msg.user_id].displayname || msg.user_id }} {{ {"invite": "invited", "ban": "banned"}[msg.content.membership] }} {{ members[msg.state_key].displayname || msg.state_key }} @@ -109,25 +122,24 @@ : {{ msg.content.reason }} </span> </span> - <span ng-if="msg.changedKey === 'displayname'" ng-click="openJson(msg)"> + <span ng-if="msg.changedKey === 'displayname'"> {{ msg.user_id }} changed their display name from {{ msg.prev_content.displayname }} to {{ msg.content.displayname }} </span> <span ng-show='msg.content.msgtype === "m.emote"' ng-class="msg.echo_msg_state" ng-bind-html="'* ' + (members[msg.user_id].displayname || msg.user_id) + ' ' + msg.content.body | linky:'_blank'" - ng-click="openJson(msg)"/> + /> <span ng-show='msg.content.msgtype === "m.text"' class="message" - ng-click="openJson(msg)" ng-class="containsBingWord(msg.content.body) && msg.user_id != state.user_id ? msg.echo_msg_state + ' messageBing' : msg.echo_msg_state" ng-bind-html="(msg.content.msgtype === 'm.text' && msg.type === 'm.room.message' && msg.content.format === 'org.matrix.custom.html') ? (msg.content.formatted_body | unsanitizedLinky) : (msg.content.msgtype === 'm.text' && msg.type === 'm.room.message') ? (msg.content.body | linky:'_blank') : '' "/> - <span ng-show='msg.type === "m.call.invite" && msg.user_id == state.user_id' ng-click="openJson(msg)">Outgoing Call{{ isWebRTCSupported ? '' : ' (But your browser does not support VoIP)' }}</span> - <span ng-show='msg.type === "m.call.invite" && msg.user_id != state.user_id' ng-click="openJson(msg)">Incoming Call{{ isWebRTCSupported ? '' : ' (But your browser does not support VoIP)' }}</span> + <span ng-show='msg.type === "m.call.invite" && msg.user_id == state.user_id'>Outgoing Call{{ isWebRTCSupported ? '' : ' (But your browser does not support VoIP)' }}</span> + <span ng-show='msg.type === "m.call.invite" && msg.user_id != state.user_id'>Incoming Call{{ isWebRTCSupported ? '' : ' (But your browser does not support VoIP)' }}</span> <div ng-show='msg.content.msgtype === "m.image"'> <div ng-hide='msg.content.thumbnail_url' ng-style="msg.content.body.h && { 'height' : (msg.content.body.h < 320) ? msg.content.body.h : 320}"> @@ -135,15 +147,15 @@ </div> <div ng-show='msg.content.thumbnail_url' ng-style="{ 'height' : msg.content.thumbnail_info.h }"> <img class="image mouse-pointer" ng-src="{{ msg.content.thumbnail_url }}" - ng-click="$parent.fullScreenImageURL = msg.content.url"/> + ng-click="$parent.fullScreenImageURL = msg.content.url; $event.stopPropagation();"/> </div> </div> - <span ng-if="'m.room.topic' === msg.type" ng-click="openJson(msg)"> + <span ng-if="'m.room.topic' === msg.type"> {{ members[msg.user_id].displayname || msg.user_id }} changed the topic to: {{ msg.content.topic }} </span> - <span ng-if="'m.room.name' === msg.type" ng-click="openJson(msg)"> + <span ng-if="'m.room.name' === msg.type"> {{ members[msg.user_id].displayname || msg.user_id }} changed the room name to: {{ msg.content.name }} </span> |