summary refs log tree commit diff
path: root/webclient/room
diff options
context:
space:
mode:
Diffstat (limited to 'webclient/room')
-rw-r--r--webclient/room/room-controller.js130
-rw-r--r--webclient/room/room.html74
2 files changed, 181 insertions, 23 deletions
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js
index a1d2e87039..486ead0da9 100644
--- a/webclient/room/room-controller.js
+++ b/webclient/room/room-controller.js
@@ -15,11 +15,21 @@ limitations under the License.
 */
 
 angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
-.controller('RoomController', ['$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'mPresence', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall',
-                               function($filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, mPresence, eventHandlerService, mFileUpload, matrixPhoneService, MatrixCall) {
+.controller('RoomController', ['$modal', '$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'mPresence', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall', 'notificationService',
+                               function($modal, $filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, mPresence, eventHandlerService, mFileUpload, matrixPhoneService, MatrixCall, notificationService) {
    'use strict';
     var MESSAGES_PER_PAGINATION = 30;
     var THUMBNAIL_SIZE = 320;
+    
+    // .html needs this
+    $scope.containsBingWord = function(content) {
+        return notificationService.containsBingWord(
+            matrixService.config().user_id,
+            matrixService.config().display_name,
+            matrixService.config().bingWords,
+            content
+        );
+    };
 
     // Room ids. Computed and resolved in onInit
     $scope.room_id = undefined;
@@ -133,7 +143,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;
@@ -189,16 +201,20 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
                 // Notify when a user joins
                 if ((document.hidden  || matrixService.presence.unavailable === mPresence.getState())
                         && event.state_key !== $scope.state.user_id  && "join" === event.membership) {
-                    var notification = new window.Notification(
-                        event.content.displayname +
-                        " (" + (matrixService.getRoomIdToAliasMapping(event.room_id) || event.room_id) + ")", // FIXME: don't leak room_ids here
-                    {
-                        "body": event.content.displayname + " joined",
-                        "icon": event.content.avatar_url ? event.content.avatar_url : undefined
-                    });
-                    $timeout(function() {
-                        notification.close();
-                    }, 5 * 1000);
+                    var userName = event.content.displayname;
+                    if (!userName) {
+                        userName = event.state_key;
+                    }
+                    notificationService.showNotification(
+                        userName +
+                        " (" + (matrixService.getRoomIdToAliasMapping(event.room_id) || event.room_id) + ")",
+                        userName + " joined",
+                        event.content.avatar_url ? event.content.avatar_url : undefined,
+                        function() {
+                            console.log("notification.onclick() room=" + event.room_id);
+                            $rootScope.goToPage('room/' + event.room_id); 
+                        }
+                    );
                 }
             }
         }
@@ -830,7 +846,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
                 $scope.userIDToInvite = "";
             },
             function(reason) {
-                $scope.feedback = "Failure: " + reason;
+                $scope.feedback = "Failure: " + reason.data.error;
             });
     };
 
@@ -982,4 +998,88 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
         }
     };
 
-}]);
+    $scope.openJson = function(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({
+            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
+        });
+    };
+
+    $scope.openRoomInfo = function() {
+        $scope.roomInfo = {};
+        $scope.roomInfo.newEvent = {
+            content: {},
+            type: "",
+            state_key: ""
+        };
+
+        var stateFilter = $filter("stateEventsFilter");
+        var stateEvents = stateFilter($scope.events.rooms[$scope.room_id]);
+        // The modal dialog will 2-way bind this field, so we MUST make a deep
+        // copy of the state events else we will be *actually adjusing our view
+        // of the world* when fiddling with the JSON!! Apparently parse/stringify
+        // is faster than jQuery's extend when doing deep copies.
+        $scope.roomInfo.stateEvents = JSON.parse(JSON.stringify(stateEvents));
+        var modalInstance = $modal.open({
+            templateUrl: 'roomInfoTemplate.html',
+            controller: 'RoomInfoController',
+            size: 'lg',
+            scope: $scope
+        });
+    };
+
+}])
+.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");
+    };
+})
+.controller('RoomInfoController', function($scope, $modalInstance, $filter, matrixService) {
+    console.log("Displaying room info.");
+
+    $scope.submit = function(event) {
+        if (event.content) {
+            console.log("submit >>> " + JSON.stringify(event.content));
+            matrixService.sendStateEvent($scope.room_id, event.type, 
+                event.content, event.state_key).then(function(response) {
+                    $modalInstance.dismiss();
+                }, function(err) {
+                    $scope.feedback = err.data.error;
+                }
+            );
+        }
+    };
+
+    $scope.dismiss = $modalInstance.dismiss;
+
+});
diff --git a/webclient/room/room.html b/webclient/room/room.html
index a3f8aee7ab..5265f42dd8 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -1,5 +1,59 @@
 <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>
+
+    <script type="text/ng-template" id="roomInfoTemplate.html">
+        <div class="modal-body">
+            <table class="room-info">
+            <tr ng-repeat="(key, event) in roomInfo.stateEvents" class="room-info-event">
+                <td class="room-info-event-meta" width="30%">
+                    <span class="monospace">{{ key }}</span>
+                    <br/>
+                    {{ (event.origin_server_ts) | date:'MMM d HH:mm' }}
+                    <br/>
+                    Set by: <span class="monospace">{{ event.user_id }}</span>
+                    <br/>
+                    <span ng-show="event.required_power_level >= 0">Required power level: {{event.required_power_level}}<br/></span>
+                    <button ng-click="submit(event)" type="button" class="btn btn-success" ng-disabled="!event.content">
+                        Submit
+                    </button>
+                </td>
+                <td class="room-info-event-content" width="70%">
+                    <textarea class="room-info-textarea-content" msd-elastic ng-model="event.content" asjson></textarea> 
+                </td>
+            </tr>
+            <tr>
+                <td class="room-info-event-meta" width="30%">
+                    <input ng-model="roomInfo.newEvent.type" placeholder="your.event.type" />
+                    <br/>
+                    <button ng-click="submit(roomInfo.newEvent)" type="button" class="btn btn-success" ng-disabled="!roomInfo.newEvent.content || !roomInfo.newEvent.type">
+                        Submit
+                    </button>
+                </td>
+                <td class="room-info-event-content" width="70%">
+                    <textarea class="room-info-textarea-content" msd-elastic ng-model="roomInfo.newEvent.content" asjson></textarea>
+                </td>
+            </tr>
+            </table>
+        </div>
+        <div class="modal-footer">
+            <button ng-click="dismiss()" type="button" class="btn">
+                Close
+            </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">
@@ -79,11 +133,11 @@
                     </div>
                 </td>
                 <td class="avatar">
-                    <img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.png' }}" width="32" height="32"
+                    <img class="avatarImage" ng-src="{{ members[msg.user_id].avatar_url || 'img/default-profile.png' }}" width="32" height="32" title="{{msg.user_id}}"
                          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">
+                    <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>
@@ -91,11 +145,11 @@
                             <span ng-if="msg.user_id === msg.state_key">
                                 {{ members[msg.state_key].displayname || msg.state_key }} left
                             </span>
-                            <span ng-if="msg.user_id !== msg.state_key">
+                            <span ng-if="msg.user_id !== msg.state_key && msg.prev_content">
                                 {{ members[msg.user_id].displayname || msg.user_id }}
-                                {{ {"join": "kicked", "ban": "unbanned"}[msg.content.prev] }}
+                                {{ {"invite": "kicked", "join": "kicked", "ban": "unbanned"}[msg.prev_content.membership] }}
                                 {{ members[msg.state_key].displayname || msg.state_key }}
-                                <span ng-if="'join' === msg.content.prev && msg.content.reason">
+                                <span ng-if="'join' === msg.prev_content.membership && msg.content.reason">
                                     : {{ msg.content.reason }}
                                 </span>
                             </span>
@@ -105,7 +159,7 @@
                             {{ members[msg.user_id].displayname || msg.user_id }}
                             {{ {"invite": "invited", "ban": "banned"}[msg.content.membership] }}
                             {{ members[msg.state_key].displayname || msg.state_key }}
-                            <span ng-if="'ban' === msg.content.prev && msg.content.reason">
+                            <span ng-if="msg.prev_content && 'ban' === msg.prev_content.membership && msg.content.reason">
                                 : {{ msg.content.reason }}
                             </span>
                         </span>                        
@@ -115,7 +169,8 @@
                         
                         <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-bind-html="'* ' + (members[msg.user_id].displayname || msg.user_id) + ' ' + msg.content.body | linky:'_blank'"
+                              />
                         
                         <span ng-show='msg.content.msgtype === "m.text"' 
                               class="message"
@@ -133,7 +188,7 @@
                             </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>
 
@@ -202,6 +257,9 @@
                         >
                     Video Call
                 </button>
+                <button ng-click="openRoomInfo()">
+                    Room Info
+                </button>
             </div>
         
             {{ feedback }}