summary refs log tree commit diff
path: root/webclient/room
diff options
context:
space:
mode:
authorErik Johnston <erik@matrix.org>2014-09-18 13:05:07 +0100
committerErik Johnston <erik@matrix.org>2014-09-18 13:05:07 +0100
commit704e7e9f44bb6ac4de03e47fd9276396d3c00af9 (patch)
tree2b3f49347cb9615bdacf276a8b786243aa16d324 /webclient/room
parentfreenode verification (diff)
parentMerge branch 'develop' of github.com:matrix-org/synapse into release-v0.3.0 (diff)
downloadsynapse-704e7e9f44bb6ac4de03e47fd9276396d3c00af9.tar.xz
Merge branch 'release-v0.3.0' of github.com:matrix-org/synapse v0.3.0
Diffstat (limited to 'webclient/room')
-rw-r--r--webclient/room/room-controller.js174
-rw-r--r--webclient/room/room.html14
2 files changed, 152 insertions, 36 deletions
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js
index 50d902ae47..de50058743 100644
--- a/webclient/room/room-controller.js
+++ b/webclient/room/room-controller.js
@@ -15,8 +15,8 @@ limitations under the License.
 */
 
 angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
-.controller('RoomController', ['$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'eventHandlerService', 'mFileUpload', 'mPresence', 'matrixPhoneService', 'MatrixCall',
-                               function($filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, eventHandlerService, mFileUpload, mPresence, matrixPhoneService, MatrixCall) {
+.controller('RoomController', ['$filter', '$scope', '$timeout', '$routeParams', '$location', '$rootScope', 'matrixService', 'eventHandlerService', 'mFileUpload', 'matrixPhoneService', 'MatrixCall',
+                               function($filter, $scope, $timeout, $routeParams, $location, $rootScope, matrixService, eventHandlerService, mFileUpload, matrixPhoneService, MatrixCall) {
    'use strict';
     var MESSAGES_PER_PAGINATION = 30;
     var THUMBNAIL_SIZE = 320;
@@ -32,7 +32,8 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
         can_paginate: false, // this is toggled off when we are not ready yet to paginate or when we run out of items
         paginating: false, // used to avoid concurrent pagination requests pulling in dup contents
         stream_failure: undefined, // the response when the stream fails
-        waiting_for_joined_event: false  // true when the join request is pending. Back to false once the corresponding m.room.member event is received
+        waiting_for_joined_event: false,  // true when the join request is pending. Back to false once the corresponding m.room.member event is received
+        messages_visibility: "hidden" // In order to avoid flickering when scrolling down the message table at the page opening, delay the message table display
     };
     $scope.members = {};
     $scope.autoCompleting = false;
@@ -53,8 +54,13 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
                 return;
             };
 
-            // Use the filter applied in html to set the input value
-            $scope.name.newNameText = $filter('mRoomName')($scope.room_id);
+            var nameEvent = $rootScope.events.rooms[$scope.room_id]['m.room.name'];
+            if (nameEvent) {
+                $scope.name.newNameText = nameEvent.content.name;
+            }
+            else {
+                $scope.name.newNameText = "";
+            }
 
             // Force focus to the input
             $timeout(function() {
@@ -131,6 +137,13 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
             
             $timeout(function() {
                 objDiv.scrollTop = objDiv.scrollHeight;
+
+                // Show the message table once the first scrolldown is done 
+                if ("visible" !== $scope.state.messages_visibility) {
+                    $timeout(function() {
+                        $scope.state.messages_visibility = "visible";
+                    }, 0);
+                }
             }, 0);
         }
     };
@@ -139,27 +152,11 @@ 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);
-                }
-            }
         }
     });
     
     $scope.$on(eventHandlerService.MEMBER_EVENT, function(ngEvent, event, isLive) {
-        if (isLive) {
+        if (isLive && event.room_id === $scope.room_id) {
             if ($scope.state.waiting_for_joined_event) {
                 // The user has successfully joined the room, we can getting data for this room
                 $scope.state.waiting_for_joined_event = false;
@@ -177,19 +174,33 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
                 else {
                     user = event.user_id;
                 }
-
                  
                 if ("ban" === event.membership) {
                     $scope.state.permission_denied = "You have been banned by " + user;
                 }
                 else {
                     $scope.state.permission_denied = "You have been kicked by " + user;
-                }
-                
+                }  
             }
             else {
                 scrollToBottom();
                 updateMemberList(event); 
+
+                // Notify when a user joins
+                if ((document.hidden  || matrixService.presence.unavailable === mPresence.getState())
+                        && event.state_key !== $scope.state.user_id  && "join" === event.membership) {
+                    debugger;
+                    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);
+                }
             }
         }
     });
@@ -235,7 +246,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.
@@ -406,12 +417,15 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
     };
 
     $scope.send = function() {
-        if ($scope.textInput === "") {
+        if (undefined === $scope.textInput || $scope.textInput === "") {
             return;
         }
         
         scrollToBottom(true);
-        
+
+        // Store the command in the history
+        history.push($scope.textInput);
+
         var promise;
         var cmd;
         var args;
@@ -676,6 +690,10 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
     var onInit2 = function() {
         console.log("onInit2");
         
+        // Scroll down as soon as possible so that we point to the last message
+        // if it already exists in memory
+        scrollToBottom(true);
+
         // Make sure the initialSync has been before going further
         eventHandlerService.waitForInitialSyncCompletion().then(
             function() {
@@ -684,6 +702,10 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
                 
                 // The room members is available in the data fetched by initialSync
                 if ($rootScope.events.rooms[$scope.room_id]) {
+
+                    // There is no need to do a 1st pagination (initialSync provided enough to fill a page)
+                    $scope.state.first_pagination = false;
+
                     var members = $rootScope.events.rooms[$scope.room_id].members;
 
                     // Update the member list
@@ -729,7 +751,10 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
         // Make recents highlight the current room
         $scope.recentsSelectedRoomID = $scope.room_id;
 
-		// Get the up-to-date the current member list
+        // Init the history for this room
+        history.init();
+
+        // Get the up-to-date the current member list
         matrixService.getMemberList($scope.room_id).then(
             function(response) {
                 for (var i = 0; i < response.data.chunk.length; i++) {
@@ -743,9 +768,18 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
                 // Arm list timing update timer
                 updateMemberListPresenceAge();
 
-                // Start pagination
+                // Allow pagination
                 $scope.state.can_paginate = true;
-                paginate(MESSAGES_PER_PAGINATION);
+
+                // Do a first pagination only if it is required
+                // FIXME: Should be no more require when initialSync/{room_id} will be available
+                if ($scope.state.first_pagination) {
+                    paginate(MESSAGES_PER_PAGINATION);
+                }
+                else {
+                    // There are already messages, go to the last message
+                    scrollToBottom(true);
+                }
             },
             function(error) {
                 $scope.feedback = "Failed get member list: " + error.data.error;
@@ -832,4 +866,82 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
         $rootScope.currentCall = call;
     };
 
+    // Manage history of typed messages
+    // History is saved in sessionStoratge so that it survives when the user
+    // navigates through the rooms and when it refreshes the page
+    var history = {
+        // The list of typed messages. Index 0 is the more recents
+        data: [],
+
+        // The position in the history currently displayed
+        position: -1,
+
+        // The message the user has started to type before going into the history
+        typingMessage: undefined,
+
+        // Init/load data for the current room
+        init: function() {
+            var data = sessionStorage.getItem("history_" + $scope.room_id);
+            if (data) {
+                this.data = JSON.parse(data);
+            }
+        },
+
+        // Store a message in the history
+        push: function(message) {
+            this.data.unshift(message);
+
+            // Update the session storage
+            sessionStorage.setItem("history_" + $scope.room_id, JSON.stringify(this.data));
+
+            // Reset history position
+            this.position = -1;
+            this.typingMessage = undefined;
+        },
+
+        // Move in the history
+        go: function(offset) {
+
+            if (-1 === this.position) {
+                // User starts to go to into the history, save the current line
+                this.typingMessage = $scope.textInput;
+            }
+            else {
+                // If the user modified this line in history, keep the change
+                this.data[this.position] = $scope.textInput;
+            }
+
+            // Bounds the new position to valid data
+            var newPosition = this.position + offset;
+            newPosition = Math.max(-1, newPosition);
+            newPosition = Math.min(newPosition, this.data.length - 1);
+            this.position = newPosition;
+
+            if (-1 !== this.position) {
+                // Show the message from the history
+                $scope.textInput = this.data[this.position];
+            }
+            else if (undefined !== this.typingMessage) {
+                // Go back to the message the user started to type
+                $scope.textInput = this.typingMessage;
+            }
+        }
+    };
+
+    // Make history singleton methods available from HTML
+    $scope.history = {
+        goUp: function($event) {
+            if ($scope.room_id) {
+                history.go(1);
+            }
+            $event.preventDefault();
+        },
+        goDown: function($event) {
+            if ($scope.room_id) {
+                history.go(-1);
+            }
+            $event.preventDefault();
+        }
+    };
+
 }]);
diff --git a/webclient/room/room.html b/webclient/room/room.html
index 9d617eadd8..44a0e34d9f 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -9,7 +9,7 @@
                     {{ room_id | mRoomName }}
                 </div>
                 <form ng-submit="name.updateName()" ng-show="name.isEditing" class="roomNameForm">
-                    <input ng-model="name.newNameText" ng-blur="name.cancelEdit()" class="roomNameInput" />
+                    <input ng-model="name.newNameText" ng-blur="name.cancelEdit()" class="roomNameInput" placeholder="Room name"/>
                 </form>
             </div>
 
@@ -23,7 +23,7 @@
                         {{ events.rooms[room_id]['m.room.topic'].content.topic | limitTo: 200}}
                     </div>
                     <form ng-submit="topic.updateTopic()" ng-show="topic.isEditing" class="roomTopicForm">
-                        <input ng-model="topic.newTopicText" ng-blur="topic.cancelEdit()" class="roomTopicInput" />
+                        <input ng-model="topic.newTopicText" ng-blur="topic.cancelEdit()" class="roomTopicInput"  placeholder="Topic"/>
                     </form>
                 </div>
             </div>
@@ -56,7 +56,10 @@
         </table>
     </div>
     
-    <div id="messageTableWrapper" ng-hide="state.permission_denied" keep-scroll>
+    <div id="messageTableWrapper" 
+         ng-hide="state.permission_denied" 
+         ng-style="{ 'visibility': state.messages_visibility }"
+         keep-scroll>
         <!-- FIXME: need to have better timestamp semantics than the (msg.content.hsob_ts || msg.ts) hack below -->
         <table id="messageTable" infinite-scroll="paginateMore()">
             <tr ng-repeat="msg in events.rooms[room_id].messages"
@@ -105,7 +108,7 @@
                         
                         <span ng-show='msg.content.msgtype === "m.text"' 
                               class="message"
-                              ng-class="msg.echo_msg_state"
+                              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.content.body : '') | linky:'_blank'"/>
 
                         <span ng-show='msg.type === "m.call.invite" && msg.user_id == state.user_id'>Outgoing Call</span>
@@ -156,7 +159,8 @@
                     <td width="*">
                         <textarea id="mainInput" rows="1" ng-model="textInput" ng-enter="send()"
                                   ng-disabled="state.permission_denied"
-                                  ng-focus="true"  autocomplete="off" tab-complete/>
+                                  ng-keydown="(38 === $event.which) ? history.goUp($event) : ((40 === $event.which) ? history.goDown($event) : 0)"
+                                  ng-focus="true" autocomplete="off" tab-complete/>
                     </td>
                     <td id="buttonsCell">
                         <button ng-click="send()" ng-disabled="state.permission_denied">Send</button>