diff options
author | Erik Johnston <erik@matrix.org> | 2014-09-18 13:05:07 +0100 |
---|---|---|
committer | Erik Johnston <erik@matrix.org> | 2014-09-18 13:05:07 +0100 |
commit | 704e7e9f44bb6ac4de03e47fd9276396d3c00af9 (patch) | |
tree | 2b3f49347cb9615bdacf276a8b786243aa16d324 /webclient/room | |
parent | freenode verification (diff) | |
parent | Merge branch 'develop' of github.com:matrix-org/synapse into release-v0.3.0 (diff) | |
download | synapse-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.js | 174 | ||||
-rw-r--r-- | webclient/room/room.html | 14 |
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> |